Coverage Report

Created: 2026-03-29 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/giflib-code/gifalloc.c
Line
Count
Source
1
/*****************************************************************************
2
3
 GIF construction tools
4
5
****************************************************************************/
6
// SPDX-License-Identifier: MIT
7
// SPDX-FileCopyrightText: Copyright (C) Eric S. Raymond <esr@thyrsus.com>
8
9
#include <stdio.h>
10
#include <stdlib.h>
11
#include <string.h>
12
13
#include "gif_lib.h"
14
#include "gif_lib_private.h"
15
16
0
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
17
18
/******************************************************************************
19
 Miscellaneous utility functions
20
******************************************************************************/
21
22
/* return smallest bitfield size n will fit in */
23
7.43k
int GifBitSize(int n) {
24
7.43k
  register int i;
25
26
21.3k
  for (i = 1; i <= 8; i++) {
27
21.3k
    if ((1 << i) >= n) {
28
7.43k
      break;
29
7.43k
    }
30
21.3k
  }
31
7.43k
  return (i);
32
7.43k
}
33
34
/******************************************************************************
35
 Color map object functions
36
******************************************************************************/
37
38
/*
39
 * Allocate a color map of given size; initialize with contents of
40
 * ColorMap if that pointer is non-NULL.
41
 */
42
3.71k
ColorMapObject *GifMakeMapObject(int ColorCount, const GifColorType *ColorMap) {
43
3.71k
  ColorMapObject *Object;
44
45
  /*** FIXME: Our ColorCount has to be a power of two.  Is it necessary to
46
   * make the user know that or should we automatically round up instead?
47
   */
48
3.71k
  if (ColorCount != (1 << GifBitSize(ColorCount))) {
49
0
    return ((ColorMapObject *)NULL);
50
0
  }
51
52
3.71k
  Object = (ColorMapObject *)malloc(sizeof(ColorMapObject));
53
3.71k
  if (Object == (ColorMapObject *)NULL) {
54
0
    return ((ColorMapObject *)NULL);
55
0
  }
56
57
3.71k
  Object->Colors =
58
3.71k
      (GifColorType *)calloc(ColorCount, sizeof(GifColorType));
59
3.71k
  if (Object->Colors == (GifColorType *)NULL) {
60
0
    free(Object);
61
0
    return ((ColorMapObject *)NULL);
62
0
  }
63
64
3.71k
  Object->ColorCount = ColorCount;
65
3.71k
  Object->BitsPerPixel = GifBitSize(ColorCount);
66
3.71k
  Object->SortFlag = false;
67
68
3.71k
  if (ColorMap != NULL) {
69
1.74k
    memcpy((char *)Object->Colors, (char *)ColorMap,
70
1.74k
           ColorCount * sizeof(GifColorType));
71
1.74k
  }
72
73
3.71k
  return (Object);
74
3.71k
}
75
76
/*******************************************************************************
77
 Free a color map object
78
*******************************************************************************/
79
3.73k
void GifFreeMapObject(ColorMapObject *Object) {
80
3.73k
  if (Object != NULL) {
81
3.71k
    (void)free(Object->Colors);
82
3.71k
    (void)free(Object);
83
3.71k
  }
84
3.73k
}
85
86
#ifdef DEBUG
87
void DumpColorMap(ColorMapObject *Object, FILE *fp) {
88
  if (Object != NULL) {
89
    int i, j, Len = Object->ColorCount;
90
91
    for (i = 0; i < Len; i += 4) {
92
      for (j = 0; j < 4 && j < Len; j++) {
93
        (void)fprintf(fp, "%3d: %02x %02x %02x   ",
94
                      i + j, Object->Colors[i + j].Red,
95
                      Object->Colors[i + j].Green,
96
                      Object->Colors[i + j].Blue);
97
      }
98
      (void)fprintf(fp, "\n");
99
    }
100
  }
101
}
102
#endif /* DEBUG */
103
104
/*******************************************************************************
105
 Compute the union of two given color maps and return it.  If result can't
106
 fit into 256 colors, NULL is returned, the allocated union otherwise.
107
 ColorIn1 is copied as is to ColorUnion, while colors from ColorIn2 are
108
 copied iff they didn't exist before.  ColorTransIn2 maps the old
109
 ColorIn2 into the ColorUnion color map table./
110
*******************************************************************************/
111
ColorMapObject *GifUnionColorMap(const ColorMapObject *ColorIn1,
112
                                 const ColorMapObject *ColorIn2,
113
0
                                 GifPixelType ColorTransIn2[]) {
114
0
  int i, j, CrntSlot, RoundUpTo, NewGifBitSize;
115
0
  ColorMapObject *ColorUnion;
116
117
  /*
118
   * We don't worry about duplicates within either color map; if
119
   * the caller wants to resolve those, he can perform unions
120
   * with an empty color map.
121
   */
122
123
  /* Allocate table which will hold the result for sure. */
124
0
  ColorUnion = GifMakeMapObject(
125
0
      MAX(ColorIn1->ColorCount, ColorIn2->ColorCount) * 2, NULL);
126
127
0
  if (ColorUnion == NULL) {
128
0
    return (NULL);
129
0
  }
130
131
  /*
132
   * Copy ColorIn1 to ColorUnion.
133
   */
134
0
  for (i = 0; i < ColorIn1->ColorCount; i++) {
135
0
    ColorUnion->Colors[i] = ColorIn1->Colors[i];
136
0
  }
137
0
  CrntSlot = ColorIn1->ColorCount;
138
139
  /*
140
   * Potentially obnoxious hack:
141
   *
142
   * Back CrntSlot down past all contiguous {0, 0, 0} slots at the end
143
   * of table 1.  This is very useful if your display is limited to
144
   * 16 colors.
145
   */
146
0
  while (ColorIn1->Colors[CrntSlot - 1].Red == 0 &&
147
0
         ColorIn1->Colors[CrntSlot - 1].Green == 0 &&
148
0
         ColorIn1->Colors[CrntSlot - 1].Blue == 0) {
149
0
    CrntSlot--;
150
0
  }
151
152
  /* Copy ColorIn2 to ColorUnion (use old colors if they exist): */
153
0
  for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) {
154
    /* Let's see if this color already exists: */
155
0
    for (j = 0; j < ColorIn1->ColorCount; j++) {
156
0
      if (memcmp(&ColorIn1->Colors[j], &ColorIn2->Colors[i],
157
0
                 sizeof(GifColorType)) == 0) {
158
0
        break;
159
0
      }
160
0
    }
161
162
0
    if (j < ColorIn1->ColorCount) {
163
0
      ColorTransIn2[i] = j; /* color exists in Color1 */
164
0
    } else {
165
      /* Color is new - copy it to a new slot: */
166
0
      ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i];
167
0
      ColorTransIn2[i] = CrntSlot++;
168
0
    }
169
0
  }
170
171
0
  if (CrntSlot > 256) {
172
0
    GifFreeMapObject(ColorUnion);
173
0
    return ((ColorMapObject *)NULL);
174
0
  }
175
176
0
  NewGifBitSize = GifBitSize(CrntSlot);
177
0
  RoundUpTo = (1 << NewGifBitSize);
178
179
0
  if (RoundUpTo != ColorUnion->ColorCount) {
180
0
    register GifColorType *Map = ColorUnion->Colors;
181
182
    /*
183
     * Zero out slots up to next power of 2.
184
     * We know these slots exist because of the way ColorUnion's
185
     * start dimension was computed.
186
     */
187
0
    for (j = CrntSlot; j < RoundUpTo; j++) {
188
0
      Map[j].Red = Map[j].Green = Map[j].Blue = 0;
189
0
    }
190
191
    /* perhaps we can shrink the map? */
192
0
    if (RoundUpTo < ColorUnion->ColorCount) {
193
0
      GifColorType *new_map = (GifColorType *)reallocarray(
194
0
          Map, RoundUpTo, sizeof(GifColorType));
195
0
      if (new_map == NULL) {
196
0
        GifFreeMapObject(ColorUnion);
197
0
        return ((ColorMapObject *)NULL);
198
0
      }
199
0
      ColorUnion->Colors = new_map;
200
0
    }
201
0
  }
202
203
0
  ColorUnion->ColorCount = RoundUpTo;
204
0
  ColorUnion->BitsPerPixel = NewGifBitSize;
205
206
0
  return (ColorUnion);
207
0
}
208
209
/*******************************************************************************
210
 Apply a given color translation to the raster bits of an image
211
*******************************************************************************/
212
0
void GifApplyTranslation(SavedImage *Image, const GifPixelType Translation[]) {
213
0
  register int i;
214
0
  register int RasterSize =
215
0
      Image->ImageDesc.Height * Image->ImageDesc.Width;
216
217
0
  for (i = 0; i < RasterSize; i++) {
218
0
    Image->RasterBits[i] = Translation[Image->RasterBits[i]];
219
0
  }
220
0
}
221
222
/******************************************************************************
223
 Extension record functions
224
******************************************************************************/
225
int GifAddExtensionBlock(int *ExtensionBlockCount,
226
                         ExtensionBlock **ExtensionBlocks, int Function,
227
116k
                         unsigned int Len, unsigned char ExtData[]) {
228
116k
  ExtensionBlock *ep;
229
230
116k
  if (*ExtensionBlocks == NULL) {
231
5.65k
    *ExtensionBlocks =
232
5.65k
        (ExtensionBlock *)malloc(sizeof(ExtensionBlock));
233
111k
  } else {
234
111k
    ExtensionBlock *ep_new = (ExtensionBlock *)reallocarray(
235
111k
        *ExtensionBlocks, (*ExtensionBlockCount + 1),
236
111k
        sizeof(ExtensionBlock));
237
111k
    if (ep_new == NULL) {
238
0
      return (GIF_ERROR);
239
0
    }
240
111k
    *ExtensionBlocks = ep_new;
241
111k
  }
242
243
116k
  if (*ExtensionBlocks == NULL) {
244
0
    return (GIF_ERROR);
245
0
  }
246
247
116k
  ep = &(*ExtensionBlocks)[(*ExtensionBlockCount)++];
248
249
116k
  ep->Function = Function;
250
116k
  ep->ByteCount = Len;
251
116k
  ep->Bytes = (GifByteType *)malloc(ep->ByteCount);
252
116k
  if (ep->Bytes == NULL) {
253
0
    return (GIF_ERROR);
254
0
  }
255
256
116k
  if (ExtData != NULL) {
257
116k
    memcpy(ep->Bytes, ExtData, Len);
258
116k
  }
259
260
116k
  return (GIF_OK);
261
116k
}
262
263
void GifFreeExtensions(int *ExtensionBlockCount,
264
81.4k
                       ExtensionBlock **ExtensionBlocks) {
265
81.4k
  ExtensionBlock *ep;
266
267
81.4k
  if (*ExtensionBlocks == NULL) {
268
75.7k
    return;
269
75.7k
  }
270
271
5.65k
  for (ep = *ExtensionBlocks;
272
122k
       ep < (*ExtensionBlocks + *ExtensionBlockCount); ep++) {
273
116k
    (void)free((char *)ep->Bytes);
274
116k
  }
275
5.65k
  (void)free((char *)*ExtensionBlocks);
276
5.65k
  *ExtensionBlocks = NULL;
277
5.65k
  *ExtensionBlockCount = 0;
278
5.65k
}
279
280
/******************************************************************************
281
   Image block allocation functions
282
******************************************************************************/
283
284
/* Private Function:
285
 * Frees the last image in the GifFile->SavedImages array
286
 */
287
0
void FreeLastSavedImage(GifFileType *GifFile) {
288
0
  SavedImage *sp;
289
290
0
  if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) {
291
0
    return;
292
0
  }
293
294
  /* Remove one SavedImage from the GifFile */
295
0
  GifFile->ImageCount--;
296
0
  sp = &GifFile->SavedImages[GifFile->ImageCount];
297
298
  /* Deallocate its Colormap */
299
0
  if (sp->ImageDesc.ColorMap != NULL) {
300
0
    GifFreeMapObject(sp->ImageDesc.ColorMap);
301
0
    sp->ImageDesc.ColorMap = NULL;
302
0
  }
303
304
  /* Deallocate the image data */
305
0
  if (sp->RasterBits != NULL) {
306
0
    free((char *)sp->RasterBits);
307
0
  }
308
309
  /* Deallocate any extensions */
310
0
  GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
311
312
  /*** FIXME: We could realloc the GifFile->SavedImages structure but is
313
   * there a point to it? Saves some memory but we'd have to do it every
314
   * time.  If this is used in GifFreeSavedImages then it would be
315
   * inefficient (The whole array is going to be deallocated.)  If we just
316
   * use it when we want to free the last Image it's convenient to do it
317
   * here.
318
   */
319
0
}
320
321
/*
322
 * Append an image block to the SavedImages array
323
 */
324
SavedImage *GifMakeSavedImage(GifFileType *GifFile,
325
0
                              const SavedImage *CopyFrom) {
326
  // cppcheck-suppress ctunullpointer
327
0
  if (GifFile->SavedImages == NULL) {
328
0
    GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage));
329
0
  } else {
330
0
    SavedImage *newSavedImages = (SavedImage *)reallocarray(
331
0
        GifFile->SavedImages, (GifFile->ImageCount + 1),
332
0
        sizeof(SavedImage));
333
0
    if (newSavedImages == NULL) {
334
0
      return ((SavedImage *)NULL);
335
0
    }
336
0
    GifFile->SavedImages = newSavedImages;
337
0
  }
338
0
  if (GifFile->SavedImages == NULL) {
339
0
    return ((SavedImage *)NULL);
340
0
  } else {
341
0
    SavedImage *sp = &GifFile->SavedImages[GifFile->ImageCount++];
342
343
0
    if (CopyFrom != NULL) {
344
0
      memcpy((char *)sp, CopyFrom, sizeof(SavedImage));
345
346
      /*
347
       * Make our own allocated copies of the heap fields in
348
       * the copied record.  This guards against potential
349
       * aliasing problems.
350
       */
351
352
      /* Null out aliased pointers before any allocations
353
       * so that FreeLastSavedImage won't free CopyFrom's
354
       * data if an allocation fails partway through. */
355
0
      sp->ImageDesc.ColorMap = NULL;
356
0
      sp->RasterBits = NULL;
357
0
      sp->ExtensionBlocks = NULL;
358
0
      sp->ExtensionBlockCount = 0;
359
 
360
      /* first, the local color map */
361
0
      if (CopyFrom->ImageDesc.ColorMap != NULL) {
362
0
        sp->ImageDesc.ColorMap = GifMakeMapObject(
363
0
            CopyFrom->ImageDesc.ColorMap->ColorCount,
364
0
            CopyFrom->ImageDesc.ColorMap->Colors);
365
0
        if (sp->ImageDesc.ColorMap == NULL) {
366
0
          FreeLastSavedImage(GifFile);
367
0
          return (SavedImage *)(NULL);
368
0
        }
369
0
      }
370
371
      /* next, the raster */
372
0
      sp->RasterBits = (unsigned char *)reallocarray(
373
0
          NULL,
374
0
          (CopyFrom->ImageDesc.Height *
375
0
           CopyFrom->ImageDesc.Width),
376
0
          sizeof(GifPixelType));
377
0
      if (sp->RasterBits == NULL) {
378
0
        FreeLastSavedImage(GifFile);
379
0
        return (SavedImage *)(NULL);
380
0
      }
381
0
      memcpy(sp->RasterBits, CopyFrom->RasterBits,
382
0
             sizeof(GifPixelType) *
383
0
                 CopyFrom->ImageDesc.Height *
384
0
                 CopyFrom->ImageDesc.Width);
385
386
      /* finally, the extension blocks */
387
0
      if (CopyFrom->ExtensionBlocks != NULL) {
388
0
        int k;
389
0
        sp->ExtensionBlocks =
390
0
            (ExtensionBlock *)calloc(
391
0
                CopyFrom->ExtensionBlockCount,
392
0
                sizeof(ExtensionBlock));
393
0
        if (sp->ExtensionBlocks == NULL) {
394
0
          FreeLastSavedImage(GifFile);
395
0
          return (SavedImage *)(NULL);
396
0
        }
397
0
        for (k = 0; k < CopyFrom->ExtensionBlockCount;
398
0
             k++) {
399
0
          ExtensionBlock *dst =
400
0
              &sp->ExtensionBlocks[k];
401
0
          ExtensionBlock *src =
402
0
              &CopyFrom->ExtensionBlocks[k];
403
0
          dst->Function = src->Function;
404
0
          dst->ByteCount = src->ByteCount;
405
0
          if (src->ByteCount > 0) {
406
0
            dst->Bytes =
407
0
                (GifByteType *)malloc(
408
0
                    src->ByteCount);
409
0
            if (dst->Bytes == NULL) {
410
0
              FreeLastSavedImage(
411
0
                  GifFile);
412
0
              return (SavedImage *)(NULL);
413
0
            }
414
0
            memcpy(dst->Bytes, src->Bytes,
415
0
                   src->ByteCount);
416
0
          }
417
0
        }
418
0
      }
419
0
    } else {
420
0
      memset((char *)sp, '\0', sizeof(SavedImage));
421
0
    }
422
423
0
    return (sp);
424
0
  }
425
0
}
426
427
1.28k
void GifFreeSavedImages(GifFileType *GifFile) {
428
1.28k
  SavedImage *sp;
429
430
1.28k
  if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) {
431
0
    return;
432
0
  }
433
1.28k
  for (sp = GifFile->SavedImages;
434
80.8k
       sp < GifFile->SavedImages + GifFile->ImageCount; sp++) {
435
79.5k
    if (sp->ImageDesc.ColorMap != NULL) {
436
1.34k
      GifFreeMapObject(sp->ImageDesc.ColorMap);
437
1.34k
      sp->ImageDesc.ColorMap = NULL;
438
1.34k
    }
439
440
79.5k
    if (sp->RasterBits != NULL) {
441
79.5k
      free((char *)sp->RasterBits);
442
79.5k
    }
443
444
79.5k
    GifFreeExtensions(&sp->ExtensionBlockCount,
445
79.5k
                      &sp->ExtensionBlocks);
446
79.5k
  }
447
1.28k
  free((char *)GifFile->SavedImages);
448
  GifFile->SavedImages = NULL;
449
1.28k
}
450
451
/* end */