Line | Count | Source |
1 | | #include <stdlib.h> |
2 | | #include <string.h> |
3 | | |
4 | | #include "cgif_raw.h" |
5 | | |
6 | 4.07k | #define SIZE_MAIN_HEADER (13) |
7 | 2.01k | #define SIZE_APP_EXT (19) |
8 | 8.15k | #define SIZE_FRAME_HEADER (10) |
9 | 5.76k | #define SIZE_GRAPHIC_EXT ( 8) |
10 | | |
11 | 6.11k | #define HEADER_OFFSET_SIGNATURE (0x00) |
12 | 6.11k | #define HEADER_OFFSET_VERSION (0x03) |
13 | 2.03k | #define HEADER_OFFSET_WIDTH (0x06) |
14 | 2.03k | #define HEADER_OFFSET_HEIGHT (0x08) |
15 | 982 | #define HEADER_OFFSET_PACKED_FIELD (0x0A) |
16 | | #define HEADER_OFFSET_BACKGROUND (0x0B) |
17 | | #define HEADER_OFFSET_MAP (0x0C) |
18 | | |
19 | 4.47k | #define IMAGE_OFFSET_LEFT (0x01) |
20 | 4.47k | #define IMAGE_OFFSET_TOP (0x03) |
21 | 4.47k | #define IMAGE_OFFSET_WIDTH (0x05) |
22 | 4.47k | #define IMAGE_OFFSET_HEIGHT (0x07) |
23 | 5.52k | #define IMAGE_OFFSET_PACKED_FIELD (0x09) |
24 | | |
25 | 5.52k | #define IMAGE_PACKED_FIELD(a) (*((uint8_t*) (a + IMAGE_OFFSET_PACKED_FIELD))) |
26 | | |
27 | 14.0k | #define APPEXT_OFFSET_NAME (0x03) |
28 | 1.00k | #define APPEXT_NETSCAPE_OFFSET_LOOPS (APPEXT_OFFSET_NAME + 13) |
29 | | |
30 | 2.88k | #define GEXT_OFFSET_DELAY (0x04) |
31 | | |
32 | 16.0M | #define MAX_CODE_LEN 12 // maximum code length for lzw |
33 | 8.01M | #define MAX_DICT_LEN (1uL << MAX_CODE_LEN) // maximum length of the dictionary |
34 | 251k | #define BLOCK_SIZE 0xFF // number of bytes in one block of the image data |
35 | | |
36 | 5.88k | #define MULU16(a, b) (((uint32_t)a) * ((uint32_t)b)) // helper macro to correctly multiply two U16's without default signed int promotion |
37 | | |
38 | | typedef struct { |
39 | | uint8_t* pRasterData; |
40 | | uint32_t sizeRasterData; |
41 | | } LZWResult; |
42 | | |
43 | | typedef struct { |
44 | | uint16_t* pTreeInit; // LZW dictionary tree for the initial dictionary (0-255 max) |
45 | | uint16_t* pTreeListMap; // LZW tree list: mapPos per node |
46 | | uint8_t* pTreeListColor; // LZW tree list: child color per node |
47 | | uint16_t* pTreeListIdx; // LZW tree list: child LZW index per node |
48 | | uint16_t* pTreeMap; // LZW dictionary tree as map (backup to pTreeList in case more than 1 child is present) |
49 | | uint16_t* pLZWData; // pointer to LZW data |
50 | | const uint8_t* pImageData; // pointer to image data |
51 | | uint32_t numPixel; // number of pixels per frame |
52 | | uint32_t LZWPos; // position of the current LZW code |
53 | | uint16_t dictPos; // currrent position in dictionary, we need to store 0-4096 -- so there are at least 13 bits needed here |
54 | | uint16_t mapPos; // current position in LZW tree mapping table |
55 | | } LZWGenState; |
56 | | |
57 | | /* converts host U16 to little-endian (LE) U16 */ |
58 | 25.8k | static uint16_t hU16toLE(const uint16_t n) { |
59 | 25.8k | int isBE; |
60 | 25.8k | uint16_t newVal; |
61 | 25.8k | uint16_t one; |
62 | | |
63 | 25.8k | one = 1; |
64 | 25.8k | isBE = *((uint8_t*)&one) ? 0 : 1; |
65 | 25.8k | if(isBE) { |
66 | 0 | newVal = (n >> 8) | (n << 8); |
67 | 25.8k | } else { |
68 | 25.8k | newVal = n; // already LE |
69 | 25.8k | } |
70 | 25.8k | return newVal; |
71 | 25.8k | } |
72 | | |
73 | | /* calculate next power of two exponent of given number (n MUST be <= 256) */ |
74 | 5.97k | static uint8_t calcNextPower2Ex(uint16_t n) { |
75 | 5.97k | uint8_t nextPow2; |
76 | | |
77 | 23.4k | for (nextPow2 = 0; n > (1uL << nextPow2); ++nextPow2); |
78 | 5.97k | return nextPow2; |
79 | 5.97k | } |
80 | | |
81 | | /* compute which initial LZW-code length is needed */ |
82 | 4.47k | static uint8_t calcInitCodeLen(uint16_t numEntries) { |
83 | 4.47k | uint8_t index; |
84 | | |
85 | 4.47k | index = calcNextPower2Ex(numEntries); |
86 | 4.47k | return (index < 3) ? 3 : index + 1; |
87 | 4.47k | } |
88 | | |
89 | | /* reset the dictionary of known LZW codes -- will reset the current code length as well */ |
90 | 6.41k | static void resetDict(LZWGenState* pContext, const uint16_t initDictLen) { |
91 | 6.41k | pContext->dictPos = initDictLen + 2; // reset current position in dictionary (number of colors + 2 for start and end code) |
92 | 6.41k | pContext->mapPos = 1; |
93 | 6.41k | pContext->pLZWData[pContext->LZWPos] = initDictLen; // issue clear-code |
94 | 6.41k | ++(pContext->LZWPos); // increment position in LZW data |
95 | | // reset LZW list |
96 | 6.41k | memset(pContext->pTreeInit, 0, initDictLen * sizeof(uint16_t) * initDictLen); |
97 | 6.41k | memset(pContext->pTreeListMap, 0, sizeof(uint16_t) * MAX_DICT_LEN); |
98 | 6.41k | memset(pContext->pTreeListColor, 0, sizeof(uint8_t) * MAX_DICT_LEN); |
99 | 6.41k | memset(pContext->pTreeListIdx, 0, sizeof(uint16_t) * MAX_DICT_LEN); |
100 | 6.41k | } |
101 | | |
102 | | /* add new child node */ |
103 | 1.60M | static void add_child(LZWGenState* pContext, const uint16_t parentIndex, const uint16_t LZWIndex, const uint16_t initDictLen, const uint8_t nextColor) { |
104 | 1.60M | uint16_t mapPos; |
105 | | |
106 | 1.60M | mapPos = pContext->pTreeListMap[parentIndex]; |
107 | 1.60M | if(!mapPos) { // if pTreeMap is not used yet for the parent node |
108 | 1.50M | if(pContext->pTreeListIdx[parentIndex]) { // if at least one child node exists, switch to pTreeMap |
109 | 102k | mapPos = pContext->mapPos; |
110 | | // add child to mapping table (pTreeMap) |
111 | 102k | memset(pContext->pTreeMap + ((mapPos - 1) * initDictLen), 0, initDictLen * sizeof(uint16_t)); |
112 | 102k | pContext->pTreeMap[(mapPos - 1) * initDictLen + nextColor] = LZWIndex; |
113 | 102k | pContext->pTreeListMap[parentIndex] = mapPos; |
114 | 102k | ++(pContext->mapPos); |
115 | 1.40M | } else { // use the free spot in pTreeList for the child node |
116 | 1.40M | pContext->pTreeListColor[parentIndex] = nextColor; // color that leads to child node |
117 | 1.40M | pContext->pTreeListIdx[parentIndex] = LZWIndex; // position of child node |
118 | 1.40M | } |
119 | 1.50M | } else { // directly add child node to pTreeMap |
120 | 97.5k | pContext->pTreeMap[(mapPos - 1) * initDictLen + nextColor] = LZWIndex; |
121 | 97.5k | } |
122 | 1.60M | ++(pContext->dictPos); // increase current position in the dictionary |
123 | 1.60M | } |
124 | | |
125 | | /* find next LZW code representing the longest pixel sequence that is still in the dictionary*/ |
126 | 7.98M | static int lzw_crawl_tree(LZWGenState* pContext, uint32_t* pStrPos, uint16_t parentIndex, const uint16_t initDictLen) { |
127 | 7.98M | uint16_t* pTreeInit; |
128 | 7.98M | uint32_t strPos; |
129 | 7.98M | uint16_t nextParent; |
130 | 7.98M | uint16_t mapPos; |
131 | | |
132 | 7.98M | if(parentIndex >= initDictLen) { |
133 | 533 | return CGIF_EINDEX; // error: index in image data out-of-bounds |
134 | 533 | } |
135 | 7.98M | pTreeInit = pContext->pTreeInit; |
136 | 7.98M | strPos = *pStrPos; |
137 | | // get the next LZW code from pTreeInit: |
138 | | // the initial nodes (0-255 max) have more children on average. |
139 | | // use the mapping approach right from the start for these nodes. |
140 | 7.98M | if(strPos < (pContext->numPixel - 1)) { |
141 | 7.97M | if(pContext->pImageData[strPos + 1] >= initDictLen) { |
142 | 156 | return CGIF_EINDEX; // error: index in image data out-of-bounds |
143 | 156 | } |
144 | 7.97M | nextParent = pTreeInit[parentIndex * initDictLen + pContext->pImageData[strPos + 1]]; |
145 | 7.97M | if(nextParent) { |
146 | 1.60M | parentIndex = nextParent; |
147 | 1.60M | ++strPos; |
148 | 6.37M | } else { |
149 | 6.37M | pContext->pLZWData[pContext->LZWPos] = parentIndex; // write last LZW code in LZW data |
150 | 6.37M | ++(pContext->LZWPos); |
151 | 6.37M | if(pContext->dictPos < MAX_DICT_LEN) { |
152 | 6.37M | pTreeInit[parentIndex * initDictLen + pContext->pImageData[strPos + 1]] = pContext->dictPos; |
153 | 6.37M | ++(pContext->dictPos); |
154 | 6.37M | } else { |
155 | 1.29k | resetDict(pContext, initDictLen); |
156 | 1.29k | } |
157 | 6.37M | ++strPos; |
158 | 6.37M | *pStrPos = strPos; |
159 | 6.37M | return CGIF_OK; |
160 | 6.37M | } |
161 | 7.97M | } |
162 | | // inner loop for codes > initDictLen |
163 | 7.59M | while(strPos < (pContext->numPixel - 1)) { |
164 | 7.59M | if(pContext->pImageData[strPos + 1] >= initDictLen) { |
165 | 95 | return CGIF_EINDEX; // error: index in image data out-of-bounds |
166 | 95 | } |
167 | | // first try to find child in LZW list |
168 | 7.59M | if(pContext->pTreeListIdx[parentIndex] && pContext->pTreeListColor[parentIndex] == pContext->pImageData[strPos + 1]) { |
169 | 5.66M | parentIndex = pContext->pTreeListIdx[parentIndex]; |
170 | 5.66M | ++strPos; |
171 | 5.66M | continue; |
172 | 5.66M | } |
173 | | // not found child yet? try to look into the LZW mapping table |
174 | 1.92M | mapPos = pContext->pTreeListMap[parentIndex]; |
175 | 1.92M | if(mapPos) { |
176 | 418k | nextParent = pContext->pTreeMap[(mapPos - 1) * initDictLen + pContext->pImageData[strPos + 1]]; |
177 | 418k | if(nextParent) { |
178 | 320k | parentIndex = nextParent; |
179 | 320k | ++strPos; |
180 | 320k | continue; |
181 | 320k | } |
182 | 418k | } |
183 | | // still not found child? add current parentIndex to LZW data and add new child |
184 | 1.60M | pContext->pLZWData[pContext->LZWPos] = parentIndex; // write last LZW code in LZW data |
185 | 1.60M | ++(pContext->LZWPos); |
186 | 1.60M | if(pContext->dictPos < MAX_DICT_LEN) { // if LZW-dictionary is not full yet |
187 | 1.60M | add_child(pContext, parentIndex, pContext->dictPos, initDictLen, pContext->pImageData[strPos + 1]); // add new LZW code to dictionary |
188 | 1.60M | } else { |
189 | | // the dictionary reached its maximum code => reset it (not required by GIF-standard but mostly done like this) |
190 | 652 | resetDict(pContext, initDictLen); |
191 | 652 | } |
192 | 1.60M | ++strPos; |
193 | 1.60M | *pStrPos = strPos; |
194 | 1.60M | return CGIF_OK; |
195 | 1.92M | } |
196 | 3.68k | pContext->pLZWData[pContext->LZWPos] = parentIndex; // if the end of the image is reached, write last LZW code |
197 | 3.68k | ++(pContext->LZWPos); |
198 | 3.68k | ++strPos; |
199 | 3.68k | *pStrPos = strPos; |
200 | 3.68k | return CGIF_OK; |
201 | 1.60M | } |
202 | | |
203 | | /* generate LZW-codes that compress the image data*/ |
204 | 4.47k | static int lzw_generate(LZWGenState* pContext, uint16_t initDictLen) { |
205 | 4.47k | uint32_t strPos; |
206 | 4.47k | int r; |
207 | 4.47k | uint8_t parentIndex; |
208 | | |
209 | 4.47k | strPos = 0; // start at beginning of the image data |
210 | 4.47k | resetDict(pContext, initDictLen); // reset dictionary and issue clear-code at first |
211 | 7.98M | while(strPos < pContext->numPixel) { // while there are still image data to be encoded |
212 | 7.98M | parentIndex = pContext->pImageData[strPos]; // start at root node |
213 | | // get longest sequence that is still in dictionary, return new position in image data |
214 | 7.98M | r = lzw_crawl_tree(pContext, &strPos, (uint16_t)parentIndex, initDictLen); |
215 | 7.98M | if(r != CGIF_OK) { |
216 | 784 | return r; // error: return error code to callee |
217 | 784 | } |
218 | 7.98M | } |
219 | 3.68k | pContext->pLZWData[pContext->LZWPos] = initDictLen + 1; // termination code |
220 | 3.68k | ++(pContext->LZWPos); |
221 | 3.68k | return CGIF_OK; |
222 | 4.47k | } |
223 | | |
224 | | /* pack the LZW data into a byte sequence*/ |
225 | 3.68k | static uint32_t create_byte_list(uint8_t *byteList, uint32_t lzwPos, uint16_t *lzwStr, uint16_t initDictLen, uint8_t initCodeLen){ |
226 | 3.68k | uint32_t i; |
227 | 3.68k | uint32_t dictPos; // counting new LZW codes |
228 | 3.68k | uint16_t n = 2 * initDictLen; // if n - initDictLen == dictPos, the LZW code size is incremented by 1 bit |
229 | 3.68k | uint32_t bytePos = 0; // position of current byte |
230 | 3.68k | uint8_t bitOffset = 0; // number of bits used in the last byte |
231 | 3.68k | uint8_t lzwCodeLen = initCodeLen; // dynamically increasing length of the LZW codes |
232 | 3.68k | int correctLater = 0; // 1: one empty byte too much if end is reached after current code, 0 otherwise |
233 | | |
234 | 3.68k | byteList[0] = 0; // except from the 1st byte all other bytes should be initialized stepwise (below) |
235 | | // the very first symbol might be the clear-code. However, this is not mandatory. Quote: |
236 | | // "Encoders should output a Clear code as the first code of each image data stream." |
237 | | // We keep the option to NOT output the clear code as the first symbol in this function. |
238 | 3.68k | dictPos = 1; |
239 | 7.99M | for(i = 0; i < lzwPos; ++i) { // loop over all LZW codes |
240 | 7.98M | if((lzwCodeLen < MAX_CODE_LEN) && ((uint32_t)(n - (initDictLen)) == dictPos)) { // larger code is used for the 1st time at i = 256 ...+ 512 ...+ 1024 -> 256, 768, 1792 |
241 | 7.14k | ++lzwCodeLen; // increment the length of the LZW codes (bit units) |
242 | 7.14k | n *= 2; // set threshold for next increment of LZW code size |
243 | 7.14k | } |
244 | 7.98M | correctLater = 0; // 1 indicates that one empty byte is too much at the end |
245 | 7.98M | byteList[bytePos] |= ((uint8_t)(lzwStr[i] << bitOffset)); // add 1st bits of the new LZW code to the byte containing part of the previous code |
246 | 7.98M | if(lzwCodeLen + bitOffset >= 8) { // if the current byte is not enough of the LZW code |
247 | 7.98M | if(lzwCodeLen + bitOffset == 8) { // if just this byte is filled exactly |
248 | 2.94k | byteList[++bytePos] = 0; // byte is full -- go to next byte and initialize as 0 |
249 | 2.94k | correctLater = 1; // use if one 0byte to much at the end |
250 | 7.97M | } else if(lzwCodeLen + bitOffset < 16) { // if the next byte is not completely filled |
251 | 4.77M | byteList[++bytePos] = (uint8_t)(lzwStr[i] >> (8-bitOffset)); |
252 | 4.77M | } else if(lzwCodeLen + bitOffset == 16) { // if the next byte is exactly filled by LZW code |
253 | 1.09M | byteList[++bytePos] = (uint8_t)(lzwStr[i] >> (8-bitOffset)); |
254 | 1.09M | byteList[++bytePos] = 0; // byte is full -- go to next byte and initialize as 0 |
255 | 1.09M | correctLater = 1; // use if one 0byte to much at the end |
256 | 2.10M | } else { // lzw-code ranges over 3 bytes in total |
257 | 2.10M | byteList[++bytePos] = (uint8_t)(lzwStr[i] >> (8-bitOffset)); // write part of LZW code to next byte |
258 | 2.10M | byteList[++bytePos] = (uint8_t)(lzwStr[i] >> (16-bitOffset)); // write part of LZW code to byte after next byte |
259 | 2.10M | } |
260 | 7.98M | } |
261 | 7.98M | bitOffset = (lzwCodeLen + bitOffset) % 8; // how many bits of the last byte are used? |
262 | 7.98M | ++dictPos; // increment count of LZW codes |
263 | 7.98M | if(lzwStr[i] == initDictLen) { // if a clear code appears in the LZW data |
264 | 5.63k | lzwCodeLen = initCodeLen; // reset length of LZW codes |
265 | 5.63k | n = 2 * initDictLen; // reset threshold for next increment of LZW code length |
266 | 5.63k | dictPos = 1; // reset (see comment below) |
267 | | // take first code already into account to increment lzwCodeLen exactly when the code length cannot represent the current maximum symbol. |
268 | | // Note: This is usually done implicitly, as the very first symbol is a clear-code itself. |
269 | 5.63k | } |
270 | 7.98M | } |
271 | | // comment: the last byte can be zero in the following case only: |
272 | | // terminate code has been written (initial dict length + 1), but current code size is larger so padding zero bits were added and extend into the next byte(s). |
273 | 3.68k | if(correctLater) { // if an unneccessaray empty 0-byte was initialized at the end |
274 | 568 | --bytePos; // don't consider the last empty byte |
275 | 568 | } |
276 | 3.68k | return bytePos; |
277 | 3.68k | } |
278 | | |
279 | | /* put byte sequence in blocks as required by GIF-format */ |
280 | 3.68k | static uint32_t create_byte_list_block(uint8_t *byteList, uint8_t *byteListBlock, const uint32_t numBytes) { |
281 | 3.68k | uint32_t i; |
282 | 3.68k | uint32_t numBlock = numBytes / BLOCK_SIZE; // number of byte blocks with length BLOCK_SIZE |
283 | 3.68k | uint8_t numRest = numBytes % BLOCK_SIZE; // number of bytes in last block (if not completely full) |
284 | | |
285 | 47.2k | for(i = 0; i < numBlock; ++i) { // loop over all blocks |
286 | 43.5k | byteListBlock[i * (BLOCK_SIZE+1)] = BLOCK_SIZE; // number of bytes in the following block |
287 | 43.5k | memcpy(byteListBlock + 1+i*(BLOCK_SIZE+1), byteList + i*BLOCK_SIZE, BLOCK_SIZE); // copy block from byteList to byteListBlock |
288 | 43.5k | } |
289 | 3.68k | if(numRest>0) { |
290 | 3.65k | byteListBlock[numBlock*(BLOCK_SIZE+1)] = numRest; // number of bytes in the following block |
291 | 3.65k | memcpy(byteListBlock + 1+numBlock*(BLOCK_SIZE+1), byteList + numBlock*BLOCK_SIZE, numRest); // copy block from byteList to byteListBlock |
292 | 3.65k | byteListBlock[1 + numBlock * (BLOCK_SIZE + 1) + numRest] = 0; // set 0 at end of frame |
293 | 3.65k | return 1 + numBlock * (BLOCK_SIZE + 1) + numRest; // index of last entry in byteListBlock |
294 | 3.65k | } |
295 | | // all LZW blocks in the frame have the same block size (BLOCK_SIZE), so there are no remaining bytes to be writen. |
296 | 36 | byteListBlock[numBlock *(BLOCK_SIZE + 1)] = 0; // set 0 at end of frame |
297 | 36 | return numBlock *(BLOCK_SIZE + 1); // index of last entry in byteListBlock |
298 | 3.68k | } |
299 | | |
300 | | /* create all LZW raster data in GIF-format */ |
301 | 4.47k | static int LZW_GenerateStream(LZWResult* pResult, const uint32_t numPixel, const uint8_t* pImageData, const uint16_t initDictLen, const uint8_t initCodeLen){ |
302 | 4.47k | LZWGenState* pContext; |
303 | 4.47k | uint32_t lzwPos, bytePos, entriesPerCycle, maxResets; |
304 | 4.47k | uint32_t bytePosBlock; |
305 | 4.47k | int r; |
306 | | // TBD recycle LZW tree list and map (if possible) to decrease the number of allocs |
307 | 4.47k | pContext = malloc(sizeof(LZWGenState)); |
308 | 4.47k | if(pContext == NULL) { |
309 | 0 | return CGIF_EALLOC; |
310 | 0 | } |
311 | 4.47k | memset(pContext, 0, sizeof(LZWGenState)); |
312 | 4.47k | pContext->pTreeInit = malloc((initDictLen * sizeof(uint16_t)) * initDictLen); |
313 | 4.47k | if(pContext->pTreeInit == NULL) { |
314 | 0 | r = CGIF_EALLOC; |
315 | 0 | goto LZWGENERATE_Cleanup; |
316 | 0 | } |
317 | 4.47k | pContext->pTreeListMap = malloc(sizeof(uint16_t) * MAX_DICT_LEN); |
318 | 4.47k | pContext->pTreeListColor = malloc(sizeof(uint8_t) * MAX_DICT_LEN); |
319 | 4.47k | pContext->pTreeListIdx = malloc(sizeof(uint16_t) * MAX_DICT_LEN); |
320 | 4.47k | if(pContext->pTreeListMap == NULL || pContext->pTreeListColor == NULL || pContext->pTreeListIdx == NULL) { |
321 | 0 | r = CGIF_EALLOC; |
322 | 0 | goto LZWGENERATE_Cleanup; |
323 | 0 | } |
324 | 4.47k | pContext->pTreeMap = malloc(((MAX_DICT_LEN / 2) + 1) * (initDictLen * sizeof(uint16_t))); |
325 | 4.47k | if(pContext->pTreeMap == NULL) { |
326 | 0 | r = CGIF_EALLOC; |
327 | 0 | goto LZWGENERATE_Cleanup; |
328 | 0 | } |
329 | 4.47k | pContext->numPixel = numPixel; |
330 | 4.47k | pContext->pImageData = pImageData; |
331 | | // Buffer must hold at max (conservative upper bound): 1 initial clear + numPixel data codes + N reset clears + 1 termination |
332 | | // where N = max dictionary resets = numPixel / (MAX_DICT_LEN - initDictLen - 2) |
333 | 4.47k | entriesPerCycle = MAX_DICT_LEN - initDictLen - 2; // maximum added number of dictionary entries per cycle: -2 to account for start and end code |
334 | 4.47k | maxResets = numPixel / entriesPerCycle; |
335 | 4.47k | pContext->pLZWData = malloc(sizeof(uint16_t) * ((size_t)numPixel + 2 + maxResets)); |
336 | 4.47k | if(pContext->pLZWData == NULL) { |
337 | 0 | r = CGIF_EALLOC; |
338 | 0 | goto LZWGENERATE_Cleanup; |
339 | 0 | } |
340 | 4.47k | pContext->LZWPos = 0; |
341 | | |
342 | | // actually generate the LZW sequence. |
343 | 4.47k | r = lzw_generate(pContext, initDictLen); |
344 | 4.47k | if(r != CGIF_OK) { |
345 | 784 | goto LZWGENERATE_Cleanup; |
346 | 784 | } |
347 | 3.68k | lzwPos = pContext->LZWPos; |
348 | | |
349 | | // pack the generated LZW data into blocks of 255 bytes |
350 | 3.68k | uint8_t *byteList; // lzw-data packed in byte-list |
351 | 3.68k | uint8_t *byteListBlock; // lzw-data packed in byte-list with 255-block structure |
352 | 3.68k | uint64_t MaxByteListLen = MAX_CODE_LEN * lzwPos / 8ull + 2ull + 1ull; // conservative upper bound |
353 | 3.68k | uint64_t MaxByteListBlockLen = MAX_CODE_LEN * lzwPos * (BLOCK_SIZE + 1ull) / 8ull / BLOCK_SIZE + 2ull + 1ull +1ull; // conservative upper bound |
354 | 3.68k | byteList = malloc(MaxByteListLen); |
355 | 3.68k | byteListBlock = malloc(MaxByteListBlockLen); |
356 | 3.68k | if(byteList == NULL || byteListBlock == NULL) { |
357 | 0 | free(byteList); |
358 | 0 | free(byteListBlock); |
359 | 0 | r = CGIF_EALLOC; |
360 | 0 | goto LZWGENERATE_Cleanup; |
361 | 0 | } |
362 | 3.68k | bytePos = create_byte_list(byteList,lzwPos, pContext->pLZWData, initDictLen, initCodeLen); |
363 | 3.68k | bytePosBlock = create_byte_list_block(byteList, byteListBlock, bytePos+1); |
364 | 3.68k | free(byteList); |
365 | 3.68k | pResult->sizeRasterData = bytePosBlock + 1; // save |
366 | 3.68k | pResult->pRasterData = byteListBlock; |
367 | 4.47k | LZWGENERATE_Cleanup: |
368 | 4.47k | free(pContext->pLZWData); |
369 | 4.47k | free(pContext->pTreeInit); |
370 | 4.47k | free(pContext->pTreeListMap); |
371 | 4.47k | free(pContext->pTreeListColor); |
372 | 4.47k | free(pContext->pTreeListIdx); |
373 | 4.47k | free(pContext->pTreeMap); |
374 | 4.47k | free(pContext); |
375 | 4.47k | return r; |
376 | 3.68k | } |
377 | | |
378 | | /* initialize the header of the GIF */ |
379 | 2.03k | static void initMainHeader(const CGIFRaw_Config* pConfig, uint8_t* pHeader) { |
380 | 2.03k | uint16_t width, height; |
381 | 2.03k | uint8_t pow2GlobalPalette; |
382 | | |
383 | 2.03k | width = pConfig->width; |
384 | 2.03k | height = pConfig->height; |
385 | | |
386 | | // set header to a clean state |
387 | 2.03k | memset(pHeader, 0, SIZE_MAIN_HEADER); |
388 | | |
389 | | // set Signature field to value "GIF" |
390 | 2.03k | pHeader[HEADER_OFFSET_SIGNATURE] = 'G'; |
391 | 2.03k | pHeader[HEADER_OFFSET_SIGNATURE + 1] = 'I'; |
392 | 2.03k | pHeader[HEADER_OFFSET_SIGNATURE + 2] = 'F'; |
393 | | |
394 | | // set Version field to value "89a" |
395 | 2.03k | pHeader[HEADER_OFFSET_VERSION] = '8'; |
396 | 2.03k | pHeader[HEADER_OFFSET_VERSION + 1] = '9'; |
397 | 2.03k | pHeader[HEADER_OFFSET_VERSION + 2] = 'a'; |
398 | | |
399 | | // set width of screen (LE ordering) |
400 | 2.03k | const uint16_t widthLE = hU16toLE(width); |
401 | 2.03k | memcpy(pHeader + HEADER_OFFSET_WIDTH, &widthLE, sizeof(uint16_t)); |
402 | | |
403 | | // set height of screen (LE ordering) |
404 | 2.03k | const uint16_t heightLE = hU16toLE(height); |
405 | 2.03k | memcpy(pHeader + HEADER_OFFSET_HEIGHT, &heightLE, sizeof(uint16_t)); |
406 | | |
407 | | // init packed field |
408 | 2.03k | if(pConfig->sizeGCT) { |
409 | 491 | pHeader[HEADER_OFFSET_PACKED_FIELD] = (1 << 7); // M = 1 (see GIF specc): global color table is present |
410 | | // calculate needed size of global color table (GCT). |
411 | | // MUST be a power of two. |
412 | 491 | pow2GlobalPalette = calcNextPower2Ex(pConfig->sizeGCT); |
413 | 491 | pow2GlobalPalette = (pow2GlobalPalette < 1) ? 1 : pow2GlobalPalette; // minimum size is 2^1 |
414 | 491 | pHeader[HEADER_OFFSET_PACKED_FIELD] |= ((pow2GlobalPalette - 1) << 0); // set size of GCT (0 - 7 in header + 1) |
415 | 491 | } |
416 | 2.03k | } |
417 | | |
418 | | /* initialize NETSCAPE app extension block (needed for animation) */ |
419 | 1.00k | static void initAppExtBlock(uint8_t* pAppExt, uint16_t numLoops) { |
420 | 1.00k | memset(pAppExt, 0, SIZE_APP_EXT); |
421 | | // set data |
422 | 1.00k | pAppExt[0] = 0x21; |
423 | 1.00k | pAppExt[1] = 0xFF; // start of block |
424 | 1.00k | pAppExt[2] = 0x0B; // eleven bytes to follow |
425 | | |
426 | | // write identifier for Netscape animation extension |
427 | 1.00k | pAppExt[APPEXT_OFFSET_NAME] = 'N'; |
428 | 1.00k | pAppExt[APPEXT_OFFSET_NAME + 1] = 'E'; |
429 | 1.00k | pAppExt[APPEXT_OFFSET_NAME + 2] = 'T'; |
430 | 1.00k | pAppExt[APPEXT_OFFSET_NAME + 3] = 'S'; |
431 | 1.00k | pAppExt[APPEXT_OFFSET_NAME + 4] = 'C'; |
432 | 1.00k | pAppExt[APPEXT_OFFSET_NAME + 5] = 'A'; |
433 | 1.00k | pAppExt[APPEXT_OFFSET_NAME + 6] = 'P'; |
434 | 1.00k | pAppExt[APPEXT_OFFSET_NAME + 7] = 'E'; |
435 | 1.00k | pAppExt[APPEXT_OFFSET_NAME + 8] = '2'; |
436 | 1.00k | pAppExt[APPEXT_OFFSET_NAME + 9] = '.'; |
437 | 1.00k | pAppExt[APPEXT_OFFSET_NAME + 10] = '0'; |
438 | 1.00k | pAppExt[APPEXT_OFFSET_NAME + 11] = 0x03; // 3 bytes to follow |
439 | 1.00k | pAppExt[APPEXT_OFFSET_NAME + 12] = 0x01; // TBD clarify |
440 | | // set number of repetitions (animation; LE ordering) |
441 | 1.00k | const uint16_t netscapeLE = hU16toLE(numLoops); |
442 | 1.00k | memcpy(pAppExt + APPEXT_NETSCAPE_OFFSET_LOOPS, &netscapeLE, sizeof(uint16_t)); |
443 | 1.00k | } |
444 | | |
445 | | /* write numBytes dummy bytes */ |
446 | 986 | static int writeDummyBytes(cgif_write_fn* pWriteFn, void* pContext, int numBytes) { |
447 | 986 | int rWrite = 0; |
448 | 986 | const uint8_t dummyByte = 0; |
449 | | |
450 | 27.2k | for(int i = 0; i < numBytes; ++i) { |
451 | 26.2k | rWrite |= pWriteFn(pContext, &dummyByte, 1); |
452 | 26.2k | } |
453 | 986 | return rWrite; |
454 | 986 | } |
455 | | |
456 | 2.04k | CGIFRaw* cgif_raw_newgif(const CGIFRaw_Config* pConfig) { |
457 | 2.04k | uint8_t aAppExt[SIZE_APP_EXT]; |
458 | 2.04k | uint8_t aHeader[SIZE_MAIN_HEADER]; |
459 | 2.04k | CGIFRaw* pGIF; |
460 | 2.04k | int rWrite; |
461 | | // check for invalid GCT size |
462 | 2.04k | if(pConfig->sizeGCT > 256) { |
463 | 10 | return NULL; // invalid GCT size |
464 | 10 | } |
465 | 2.03k | pGIF = malloc(sizeof(CGIFRaw)); |
466 | 2.03k | if(!pGIF) { |
467 | 0 | return NULL; |
468 | 0 | } |
469 | 2.03k | memcpy(&(pGIF->config), pConfig, sizeof(CGIFRaw_Config)); |
470 | | // initiate all sections we can at this stage: |
471 | | // - main GIF header |
472 | | // - global color table (GCT), if required |
473 | | // - netscape application extension (for animation), if required |
474 | 2.03k | initMainHeader(pConfig, aHeader); |
475 | 2.03k | rWrite = pConfig->pWriteFn(pConfig->pContext, aHeader, SIZE_MAIN_HEADER); |
476 | | |
477 | | // GCT required? => write it. |
478 | 2.03k | if(pConfig->sizeGCT) { |
479 | 491 | rWrite |= pConfig->pWriteFn(pConfig->pContext, pConfig->pGCT, pConfig->sizeGCT * 3); |
480 | 491 | uint8_t pow2GCT = calcNextPower2Ex(pConfig->sizeGCT); |
481 | 491 | pow2GCT = (pow2GCT < 1) ? 1 : pow2GCT; // minimum size is 2^1 |
482 | 491 | const uint16_t numBytesLeft = ((1 << pow2GCT) - pConfig->sizeGCT) * 3; |
483 | 491 | rWrite |= writeDummyBytes(pConfig->pWriteFn, pConfig->pContext, numBytesLeft); |
484 | 491 | } |
485 | | // GIF should be animated? => init & write app extension header ("NETSCAPE2.0") |
486 | | // No loop? Don't write NETSCAPE extension. |
487 | 2.03k | if((pConfig->attrFlags & CGIF_RAW_ATTR_IS_ANIMATED) && !(pConfig->attrFlags & CGIF_RAW_ATTR_NO_LOOP)) { |
488 | 1.00k | initAppExtBlock(aAppExt, pConfig->numLoops); |
489 | 1.00k | rWrite |= pConfig->pWriteFn(pConfig->pContext, aAppExt, SIZE_APP_EXT); |
490 | 1.00k | } |
491 | | // check for write errors |
492 | 2.03k | if(rWrite) { |
493 | 0 | free(pGIF); |
494 | 0 | return NULL; |
495 | 0 | } |
496 | | |
497 | | // assume error per default. |
498 | | // set to CGIF_OK by the first successful cgif_raw_addframe() call, as a GIF without frames is invalid. |
499 | 2.03k | pGIF->curResult = CGIF_PENDING; |
500 | 2.03k | return pGIF; |
501 | 2.03k | } |
502 | | |
503 | | /* add new frame to the raw GIF stream */ |
504 | 4.55k | cgif_result cgif_raw_addframe(CGIFRaw* pGIF, const CGIFRaw_FrameConfig* pConfig) { |
505 | 4.55k | uint8_t aFrameHeader[SIZE_FRAME_HEADER]; |
506 | 4.55k | uint8_t aGraphicExt[SIZE_GRAPHIC_EXT]; |
507 | 4.55k | LZWResult encResult; |
508 | 4.55k | int r, rWrite; |
509 | 4.55k | const int useLCT = pConfig->sizeLCT; // LCT stands for "local color table" |
510 | 4.55k | const int isInterlaced = (pConfig->attrFlags & CGIF_RAW_FRAME_ATTR_INTERLACED) ? 1 : 0; |
511 | 4.55k | uint16_t numEffColors; // number of effective colors |
512 | 4.55k | uint16_t initDictLen; |
513 | 4.55k | uint8_t pow2LCT, initCodeLen; |
514 | | |
515 | 4.55k | if(pGIF->curResult != CGIF_OK && pGIF->curResult != CGIF_PENDING) { |
516 | 0 | return pGIF->curResult; // return previous error |
517 | 0 | } |
518 | | // check for invalid LCT size |
519 | 4.55k | if(pConfig->sizeLCT > 256) { |
520 | 85 | pGIF->curResult = CGIF_ERROR; // invalid LCT size |
521 | 85 | return pGIF->curResult; |
522 | 85 | } |
523 | | |
524 | 4.47k | rWrite = 0; |
525 | | // set frame header to a clean state |
526 | 4.47k | memset(aFrameHeader, 0, SIZE_FRAME_HEADER); |
527 | | // set needed fields in frame header |
528 | 4.47k | aFrameHeader[0] = ','; // set frame seperator |
529 | 4.47k | if(useLCT) { |
530 | 525 | pow2LCT = calcNextPower2Ex(pConfig->sizeLCT); |
531 | 525 | pow2LCT = (pow2LCT < 1) ? 1 : pow2LCT; // minimum size is 2^1 |
532 | 525 | IMAGE_PACKED_FIELD(aFrameHeader) = (1 << 7); |
533 | | // set size of local color table (0-7 in header + 1) |
534 | 525 | IMAGE_PACKED_FIELD(aFrameHeader) |= ((pow2LCT- 1) << 0); |
535 | 525 | numEffColors = pConfig->sizeLCT; |
536 | 3.94k | } else { |
537 | 3.94k | numEffColors = pGIF->config.sizeGCT; // global color table in use |
538 | 3.94k | } |
539 | | // encode frame interlaced? |
540 | 4.47k | IMAGE_PACKED_FIELD(aFrameHeader) |= (isInterlaced << 6); |
541 | | |
542 | | // transparency in use? we might need to increase numEffColors |
543 | 4.47k | if((pGIF->config.attrFlags & (CGIF_RAW_ATTR_IS_ANIMATED)) && (pConfig->attrFlags & (CGIF_RAW_FRAME_ATTR_HAS_TRANS)) && pConfig->transIndex >= numEffColors) { |
544 | 1.66k | numEffColors = pConfig->transIndex + 1; |
545 | 1.66k | } |
546 | | |
547 | | // calculate initial code length and initial dict length |
548 | 4.47k | initCodeLen = calcInitCodeLen(numEffColors); |
549 | 4.47k | initDictLen = 1uL << (initCodeLen - 1); |
550 | 4.47k | const uint8_t initialCodeSize = initCodeLen - 1; |
551 | | |
552 | 4.47k | const uint16_t frameWidthLE = hU16toLE(pConfig->width); |
553 | 4.47k | const uint16_t frameHeightLE = hU16toLE(pConfig->height); |
554 | 4.47k | const uint16_t frameTopLE = hU16toLE(pConfig->top); |
555 | 4.47k | const uint16_t frameLeftLE = hU16toLE(pConfig->left); |
556 | 4.47k | memcpy(aFrameHeader + IMAGE_OFFSET_WIDTH, &frameWidthLE, sizeof(uint16_t)); |
557 | 4.47k | memcpy(aFrameHeader + IMAGE_OFFSET_HEIGHT, &frameHeightLE, sizeof(uint16_t)); |
558 | 4.47k | memcpy(aFrameHeader + IMAGE_OFFSET_TOP, &frameTopLE, sizeof(uint16_t)); |
559 | 4.47k | memcpy(aFrameHeader + IMAGE_OFFSET_LEFT, &frameLeftLE, sizeof(uint16_t)); |
560 | | // apply interlaced pattern |
561 | | // TBD creating a copy of pImageData is not ideal, but changes on the LZW encoding would |
562 | | // be necessary otherwise. |
563 | 4.47k | if(isInterlaced) { |
564 | 1.41k | uint8_t* pInterlaced = malloc(MULU16(pConfig->width, pConfig->height)); |
565 | 1.41k | if(pInterlaced == NULL) { |
566 | 0 | pGIF->curResult = CGIF_EALLOC; |
567 | 0 | return pGIF->curResult; |
568 | 0 | } |
569 | 1.41k | uint8_t* p = pInterlaced; |
570 | | // every 8th row (starting with row 0) |
571 | 539k | for(uint32_t i = 0; i < pConfig->height; i += 8) { |
572 | 538k | memcpy(p, pConfig->pImageData + i * pConfig->width, pConfig->width); |
573 | 538k | p += pConfig->width; |
574 | 538k | } |
575 | | // every 8th row (starting with row 4) |
576 | 538k | for(uint32_t i = 4; i < pConfig->height; i += 8) { |
577 | 537k | memcpy(p, pConfig->pImageData + i * pConfig->width, pConfig->width); |
578 | 537k | p += pConfig->width; |
579 | 537k | } |
580 | | // every 4th row (starting with row 2) |
581 | 1.07M | for(uint32_t i = 2; i < pConfig->height; i += 4) { |
582 | 1.07M | memcpy(p, pConfig->pImageData + i * pConfig->width, pConfig->width); |
583 | 1.07M | p += pConfig->width; |
584 | 1.07M | } |
585 | | // every 2th row (starting with row 1) |
586 | 2.14M | for(uint32_t i = 1; i < pConfig->height; i += 2) { |
587 | 2.14M | memcpy(p, pConfig->pImageData + i * pConfig->width, pConfig->width); |
588 | 2.14M | p += pConfig->width; |
589 | 2.14M | } |
590 | 1.41k | r = LZW_GenerateStream(&encResult, MULU16(pConfig->width, pConfig->height), pInterlaced, initDictLen, initCodeLen); |
591 | 1.41k | free(pInterlaced); |
592 | 3.05k | } else { |
593 | 3.05k | r = LZW_GenerateStream(&encResult, MULU16(pConfig->width, pConfig->height), pConfig->pImageData, initDictLen, initCodeLen); |
594 | 3.05k | } |
595 | | |
596 | | // generate LZW raster data (actual image data) |
597 | | // check for errors |
598 | 4.47k | if(r != CGIF_OK) { |
599 | 784 | pGIF->curResult = r; |
600 | 784 | return r; |
601 | 784 | } |
602 | | |
603 | | // check whether the Graphic Control Extension is required or not: |
604 | | // It's required for animations and frames with transparency. |
605 | 3.68k | int needsGraphicCtrlExt = (pGIF->config.attrFlags & CGIF_RAW_ATTR_IS_ANIMATED) | (pConfig->attrFlags & CGIF_RAW_FRAME_ATTR_HAS_TRANS); |
606 | | // do things for animation / transparency, if required. |
607 | 3.68k | if(needsGraphicCtrlExt) { |
608 | 2.88k | memset(aGraphicExt, 0, SIZE_GRAPHIC_EXT); |
609 | 2.88k | aGraphicExt[0] = 0x21; |
610 | 2.88k | aGraphicExt[1] = 0xF9; |
611 | 2.88k | aGraphicExt[2] = 0x04; |
612 | 2.88k | aGraphicExt[3] = pConfig->disposalMethod; |
613 | | // set flag indicating that transparency is used, if required. |
614 | 2.88k | if(pConfig->attrFlags & CGIF_RAW_FRAME_ATTR_HAS_TRANS) { |
615 | 2.43k | aGraphicExt[3] |= 0x01; |
616 | 2.43k | aGraphicExt[6] = pConfig->transIndex; |
617 | 2.43k | } |
618 | | // set delay (LE ordering) |
619 | 2.88k | const uint16_t delayLE = hU16toLE(pConfig->delay); |
620 | 2.88k | memcpy(aGraphicExt + GEXT_OFFSET_DELAY, &delayLE, sizeof(uint16_t)); |
621 | | // write Graphic Control Extension |
622 | 2.88k | rWrite |= pGIF->config.pWriteFn(pGIF->config.pContext, aGraphicExt, SIZE_GRAPHIC_EXT); |
623 | 2.88k | } |
624 | | |
625 | | // write frame |
626 | 3.68k | rWrite |= pGIF->config.pWriteFn(pGIF->config.pContext, aFrameHeader, SIZE_FRAME_HEADER); |
627 | 3.68k | if(useLCT) { |
628 | 495 | rWrite |= pGIF->config.pWriteFn(pGIF->config.pContext, pConfig->pLCT, pConfig->sizeLCT * 3); |
629 | 495 | const uint16_t numBytesLeft = ((1 << pow2LCT) - pConfig->sizeLCT) * 3; |
630 | 495 | rWrite |= writeDummyBytes(pGIF->config.pWriteFn, pGIF->config.pContext, numBytesLeft); |
631 | 495 | } |
632 | 3.68k | rWrite |= pGIF->config.pWriteFn(pGIF->config.pContext, &initialCodeSize, 1); |
633 | 3.68k | rWrite |= pGIF->config.pWriteFn(pGIF->config.pContext, encResult.pRasterData, encResult.sizeRasterData); |
634 | | |
635 | | // check for write errors |
636 | 3.68k | if(rWrite) { |
637 | 0 | pGIF->curResult = CGIF_EWRITE; |
638 | 3.68k | } else { |
639 | 3.68k | pGIF->curResult = CGIF_OK; |
640 | 3.68k | } |
641 | | // cleanup |
642 | 3.68k | free(encResult.pRasterData); |
643 | 3.68k | return pGIF->curResult; |
644 | 4.47k | } |
645 | | |
646 | 2.03k | cgif_result cgif_raw_close(CGIFRaw* pGIF) { |
647 | 2.03k | int rWrite; |
648 | 2.03k | cgif_result result; |
649 | | |
650 | 2.03k | rWrite = pGIF->config.pWriteFn(pGIF->config.pContext, (unsigned char*) ";", 1); // write term symbol |
651 | | // check for write errors |
652 | 2.03k | if(rWrite) { |
653 | 0 | pGIF->curResult = CGIF_EWRITE; |
654 | 0 | } |
655 | 2.03k | result = pGIF->curResult; |
656 | 2.03k | free(pGIF); |
657 | 2.03k | return result; |
658 | 2.03k | } |