Coverage Report

Created: 2026-05-30 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cgif/src/cgif_raw.c
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
}