2019-06-05 04:25:40 +00:00
|
|
|
/**
|
|
|
|
Animations are structured in AnimationData as follows:
|
|
|
|
|
|
|
|
.colors contains .color_count*3 uint8_t values for R, G and B.
|
|
|
|
|
|
|
|
.offsets contains the frame start offsets within .data + the length of the data. (So
|
|
|
|
you can always do something like `for (int i=anim.offsets[i]; i<anim.offsets[i+1]; i++)`.
|
|
|
|
Accordingly it has a length of .frame_count+1.
|
|
|
|
|
|
|
|
.data contains all frames data with a run-length compression with escape char 255.
|
|
|
|
This data references the index of one or multiple pixels in .colors. It starts at the
|
|
|
|
top left pixel and walks each row to the right before starting the next row.
|
|
|
|
To decode it: Start at a frame (indicated by .offsets). Get one byte as x.
|
|
|
|
If x is <255: This is a single pixel of color x.
|
|
|
|
if x is 255: Run-length-encoding. The next byte indicates of often the byte after that
|
|
|
|
will be repeated. So, {255, 4, 10} is equal to {10, 10, 10, 10}.
|
|
|
|
A special case that may happen in larger GIFs is that there are more than 255 repetitions
|
|
|
|
of a color. Those will be split, so 355*color #10 will be: {255, 255, 10, 255, 100, 10},
|
|
|
|
e.g. 255*10 + 100*10. Usually this shouldn't need special handling within a decoder.
|
|
|
|
Regarding colors in .data:
|
|
|
|
Color 0 means "keep the color from the previous frame". This color should never appear on frame #0.
|
|
|
|
Color 1 means "show the background color".
|
|
|
|
All other color values point to a color in .colors - with an offset of 2.
|
|
|
|
So if in .data there's a color 3, paint this pixel in .colors[1].
|
|
|
|
|
|
|
|
.individual_delays contains either 1 or .frame_count delays. They are given in ms and
|
|
|
|
indicate how long the matching frame should be displayed. If all times are equal, then
|
|
|
|
it contains only one entry and .individual_delays will be false.
|
|
|
|
|
|
|
|
.w and .h contain the dimensions of the image.
|
|
|
|
**/
|
|
|
|
|
|
|
|
#include "Animation.h"
|
|
|
|
#include "functions.h"
|
|
|
|
|
2019-06-07 04:07:29 +00:00
|
|
|
Animation::Animation(AnimationData *d) {
|
2019-06-05 04:25:40 +00:00
|
|
|
this->data = d;
|
|
|
|
|
2019-06-06 04:40:30 +00:00
|
|
|
this->endFrame = d->frame_count-1;
|
2019-06-05 04:25:40 +00:00
|
|
|
this->colors = new CRGB[this->data->color_count];
|
|
|
|
uint8_t *color_data = new uint8_t[this->data->color_count * 3];
|
|
|
|
memcpy_P(color_data, this->data->colors, this->data->color_count * 3);
|
|
|
|
for (int i = 0; i<this->data->color_count; i++) colors[i] = CRGB(color_data[i * 3], color_data[i * 3 + 1], color_data[i * 3 + 2]);
|
|
|
|
delete color_data;
|
|
|
|
int length = this->data->offsets[this->data->frame_count];
|
|
|
|
this->animation_data = new uint8_t[length];
|
|
|
|
memcpy_P(this->animation_data, this->data->data, length);
|
|
|
|
}
|
|
|
|
|
2019-06-07 04:07:29 +00:00
|
|
|
void Animation::setFgColor(CRGB* fg_color) {
|
|
|
|
this->fgColor = fg_color;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Animation::setBgColor(CRGB* bg_color) {
|
|
|
|
this->bgColor = bg_color;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Animation::invert() {
|
|
|
|
if (this->fgColor == NULL) return false;
|
|
|
|
CRGB* temp = this->fgColor;
|
|
|
|
this->fgColor = this->bgColor;
|
|
|
|
this->bgColor = temp;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Animation::setOffsets(int8_t x, int8_t y) {
|
|
|
|
this->xOffset = x;
|
|
|
|
this->yOffset = y;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Animation::setStartFrame(uint8_t sf) {
|
|
|
|
if (sf <= this->endFrame && sf < this->data->frame_count ) this->startFrame = sf;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Animation::setEndFrame(uint8_t ef) {
|
|
|
|
if (ef >= this->startFrame && ef < this->data->frame_count) this->endFrame = ef;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Animation::setFrameRange(uint8_t sf, uint8_t ef) {
|
|
|
|
if (sf<=ef && ef < this->data->frame_count) {
|
|
|
|
this->startFrame = sf;
|
|
|
|
this->endFrame = ef;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Animation::setSingleFrame(uint8_t frame) {
|
|
|
|
if (frame < this->data->frame_count) {
|
|
|
|
this->startFrame = frame;
|
|
|
|
this->endFrame = frame;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-05 04:25:40 +00:00
|
|
|
Animation::~Animation() {
|
|
|
|
delete this->colors;
|
|
|
|
delete this->animation_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Animation::draw() {
|
|
|
|
for (uint16_t i=0; i<this->currentFrame; i++) this->drawFrame(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Animation::drawFrame() {
|
|
|
|
this->drawFrame(currentFrame);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Animation::drawFrame(uint8_t frame_index) {
|
|
|
|
uint16_t led_index = 0;
|
|
|
|
for (uint16_t i=this->data->offsets[frame_index]; i<this->data->offsets[frame_index + 1]; i++) {
|
|
|
|
uint8_t color_index;
|
|
|
|
uint8_t count = 1;
|
|
|
|
if (this->animation_data[i]==255) { // Run-length encoded data
|
|
|
|
color_index = this->animation_data[i + 2];
|
|
|
|
count = this->animation_data[i + 1];
|
|
|
|
i += 2;
|
|
|
|
} else {
|
|
|
|
color_index = this->animation_data[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (color_index == 0) { // color #0 = skip this pixels
|
|
|
|
led_index += count;
|
|
|
|
} else {
|
|
|
|
CRGB* color = this->getColor(color_index);
|
|
|
|
for (int j=0; j<count; j++) this->drawPixel(led_index++, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Animation::advance() {
|
|
|
|
if (this->currentFrameSince == 0) {
|
|
|
|
this->currentFrameSince = millis();
|
|
|
|
} else if (this->currentFrameSince + this->getFrameDelay(currentFrame) < millis() || this->currentFrameSince > millis()) {
|
2019-06-06 04:40:30 +00:00
|
|
|
currentFrame++;
|
|
|
|
if (currentFrame > endFrame) currentFrame = startFrame;
|
2019-06-05 04:25:40 +00:00
|
|
|
this->currentFrameSince = millis();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Animation::drawPixel(int index, CRGB* color) {
|
|
|
|
setPixel(this->xOffset + (index % this->data->w), this->yOffset + (index / this->data->h), *color);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t Animation::getFrameDelay(int frame) {
|
|
|
|
if (this->data->individual_delays) return this->data->delays[frame];
|
|
|
|
return this->data->delays[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
CRGB* Animation::getColor(uint8_t index) {
|
|
|
|
if (index==1) return this->bgColor;
|
2019-06-07 04:07:29 +00:00
|
|
|
else if (this->fgColor) return this->fgColor;
|
2019-06-05 04:25:40 +00:00
|
|
|
else return &this->colors[index - 2];
|
|
|
|
}
|