forked from Inverted/tasbot_eyes
-
Notifications
You must be signed in to change notification settings - Fork 0
/
gif.c
157 lines (132 loc) · 5.83 KB
/
gif.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#include <stdlib.h>
#include <stdio.h>
#include "gif.h"
#include "arguments.h"
#include "led.h"
/**
* check if GIF has the exact right size of 28x8 pixels
* @param _image The GIF that is to check
* @return If image has right size or not
*/
bool checkIfImageHasRightSize(GifFileType* _image) {
return (_image->SWidth == LED_WIDTH && _image->SHeight == LED_HEIGHT);
}
/**
* Opens and reads the GIF file in a data structure. Container for AnimationFrames and various other needed infos
* @param _file The GIF animation, that is to read
* @return Data structure, that contains all needed information to display it on TASBots display
* TODO: This is pretty blob like. Can probably be shorten.
*/
Animation* readAnimation(char* _filePath) {
if (verbose) {
printf("[INFO] Going to try loading file %s\n", _filePath);
}
//Open file
int e;
GifFileType* image = DGifOpenFileName(_filePath, &e);
if (!image) {
fprintf(stderr, "[ERROR] readAnimation: Can't find or open GIF file (%s). DGifOpenFileName gave error code %d\n", _filePath, e);
return false;
}
//"Slurp" infos into struct
if (DGifSlurp(image) == GIF_ERROR) {
fprintf(stderr, "[ERROR] readAnimation: Can't load infos about GIF file. DGifSlurp gave error %d\n", image->Error);
if (DGifCloseFile(image, &e) != GIF_OK){
fprintf(stderr, "[WARNING] readAnimation: Could not close image. DGifCloseFile gave error code %d\n", e);
}
return false;
}
Animation* animation = NULL;
//Before we do anything, lets check if image has right size
if (checkIfImageHasRightSize(image)) {
//Obtain global color map if available
ColorMapObject* globalColorMap = image->SColorMap;
if (verbose) {
printf("[INFO] (Image info): Size: %ix%i; Frames: %i; Path: \"%s\"\n", image->SWidth, image->SHeight,
image->ImageCount, _filePath);
}
//Process frames
animation = malloc(sizeof(Animation));
if (!animation) {
failExit("[ERROR] readAnimation: Failed to allocate memory for Animation structure");
}
AnimationFrame** animationFrames = malloc(sizeof(AnimationFrame*) * image->ImageCount);
if (!animationFrames) {
failExit("[ERROR] readAnimation: Failed to allocate memory for AnimationFrame structure");
}
animation->frames = animationFrames; //cant be reached, when Animation or AnimationFrames is empty
animation->frameCount = image->ImageCount;
animation->monochrome = true;
animation->image = image;
GraphicsControlBlock gcb;
for (int i = 0; i < image->ImageCount; ++i) {
const SavedImage* frame = &image->SavedImages[i]; //get access to frame data
u_int16_t delayTime = DEFAULT_DELAY_TIME;
if (DGifSavedExtensionToGCB(image, i, &gcb) == GIF_ERROR) {
printf("[WARNING] Can't read frame delay. Using default delay time");
} else {
delayTime = gcb.DelayTime * 10;
if (delayTime < MIN_DELAY_TIME) {
printf("[WARNING] Delay time is smaller than allowed smallest delay time (%d ms). Using that.\n",
MIN_DELAY_TIME);
delayTime = MIN_DELAY_TIME;
}
}
if (verbose) {
printf("[INFO] (Frame #%i info): Size: %ix%i; Delay time: %i ms; Left: %i, Top: %i; Local color map: %s\n",
i, frame->ImageDesc.Width, frame->ImageDesc.Height, delayTime, frame->ImageDesc.Left,
frame->ImageDesc.Top, (frame->ImageDesc.ColorMap ? "Yes" : "No"));
}
animationFrames[i] = readFramePixels(frame, globalColorMap, &animation->monochrome);
animationFrames[i]->delayTime = delayTime;
}
if (verbose) {
printf("[INFO] Animation is monochrome: %s\n", animation->monochrome ? "true" : "false");
}
} else {
fprintf(stderr, "[ERROR] Image has wrong size (%dx%d). Required is (%dx%d)", image->SWidth, image->SHeight,
LED_WIDTH, LED_HEIGHT);
return false;
}
return animation;
}
/**
* Read the buffer of an animation frame and parse it into a data structure.
* @param frame
* @param _globalMap
* @param _monochrome
* @return
*/
AnimationFrame* readFramePixels(const SavedImage* frame, ColorMapObject* _globalMap, bool* _monochrome) {
const GifImageDesc desc = frame->ImageDesc; //get description of current frame
const ColorMapObject* colorMap = desc.ColorMap ? desc.ColorMap
: _globalMap; //choose either global or local color map
AnimationFrame* animationFrame = malloc(sizeof(AnimationFrame));
if (!animationFrame) {
fprintf(stderr, "[ERROR] readFramePixels: Failed to allocate memory for AnimationFrame structure");
clearLEDs();
exit(EXIT_FAILURE);
}
bool keepColor = false;
for (int y = 0; y < desc.Height; ++y) {
for (int x = 0; x < desc.Width; ++x) {
int c = frame->RasterBits[y * desc.Width + x];
if (colorMap) {
GifColorType* color = &colorMap->Colors[c];
animationFrame->color[x][y] = color;
//Check if animation is monochrome.
//When a single frame contains color, then preserve the animations color later while rendering.
if (!isGrayScale(color)) {
keepColor = true;
}
} else {
printf("[WARNING] No color map given. Can't process picture. Skip frame");
}
}
}
*_monochrome &= !keepColor;
return animationFrame;
}
bool isGrayScale(GifColorType* _color) {
return (_color->Red == _color->Green && _color->Red == _color->Blue);
}