Coverage Report

Created: 2026-06-15 06:33

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
2.95k
int GifBitSize(int n) {
24
2.95k
  register int i;
25
26
15.2k
  for (i = 1; i <= 8; i++) {
27
15.2k
    if ((1 << i) >= n) {
28
2.95k
      break;
29
2.95k
    }
30
15.2k
  }
31
2.95k
  return (i);
32
2.95k
}
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
1.47k
ColorMapObject *GifMakeMapObject(int ColorCount, const GifColorType *ColorMap) {
43
1.47k
  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
1.47k
  if (ColorCount != (1 << GifBitSize(ColorCount))) {
49
0
    return ((ColorMapObject *)NULL);
50
0
  }
51
52
1.47k
  Object = (ColorMapObject *)malloc(sizeof(ColorMapObject));
53
1.47k
  if (Object == (ColorMapObject *)NULL) {
54
0
    return ((ColorMapObject *)NULL);
55
0
  }
56
57
1.47k
  Object->Colors =
58
1.47k
      (GifColorType *)calloc(ColorCount, sizeof(GifColorType));
59
1.47k
  if (Object->Colors == (GifColorType *)NULL) {
60
0
    free(Object);
61
0
    return ((ColorMapObject *)NULL);
62
0
  }
63
64
1.47k
  Object->ColorCount = ColorCount;
65
1.47k
  Object->BitsPerPixel = GifBitSize(ColorCount);
66
1.47k
  Object->SortFlag = false;
67
68
1.47k
  if (ColorMap != NULL) {
69
683
    memcpy((char *)Object->Colors, (char *)ColorMap,
70
683
           ColorCount * sizeof(GifColorType));
71
683
  }
72
73
1.47k
  return (Object);
74
1.47k
}
75
76
/*******************************************************************************
77
 Free a color map object
78
*******************************************************************************/
79
1.48k
void GifFreeMapObject(ColorMapObject *Object) {
80
1.48k
  if (Object != NULL) {
81
1.47k
    (void)free(Object->Colors);
82
1.47k
    (void)free(Object);
83
1.47k
  }
84
1.48k
}
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
0
  if (ColorIn1 == NULL || ColorIn2 == NULL) {
118
0
    return (NULL);
119
0
  }
120
121
  /*
122
   * We don't worry about duplicates within either color map; if
123
   * the caller wants to resolve those, he can perform unions
124
   * with an empty color map.
125
   */
126
127
  /* Allocate table which will hold the result for sure. */
128
0
  ColorUnion = GifMakeMapObject(
129
0
      MAX(ColorIn1->ColorCount, ColorIn2->ColorCount) * 2, NULL);
130
131
0
  if (ColorUnion == NULL) {
132
0
    return (NULL);
133
0
  }
134
135
  /*
136
   * Copy ColorIn1 to ColorUnion.
137
   */
138
0
  for (i = 0; i < ColorIn1->ColorCount; i++) {
139
0
    ColorUnion->Colors[i] = ColorIn1->Colors[i];
140
0
  }
141
0
  CrntSlot = ColorIn1->ColorCount;
142
143
  /*
144
   * Potentially obnoxious hack:
145
   *
146
   * Back CrntSlot down past all contiguous {0, 0, 0} slots at the end
147
   * of table 1.  This is very useful if your display is limited to
148
   * 16 colors.
149
   */
150
0
  while (CrntSlot > 0 && (ColorIn1->Colors[CrntSlot - 1].Red == 0 &&
151
0
         ColorIn1->Colors[CrntSlot - 1].Green == 0 &&
152
0
          ColorIn1->Colors[CrntSlot - 1].Blue == 0)) {
153
0
    CrntSlot--;
154
0
  }
155
156
  /* Copy ColorIn2 to ColorUnion (use old colors if they exist): */
157
0
  for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) {
158
    /* Let's see if this color already exists: */
159
0
    for (j = 0; j < ColorIn1->ColorCount; j++) {
160
0
      if (memcmp(&ColorIn1->Colors[j], &ColorIn2->Colors[i],
161
0
                 sizeof(GifColorType)) == 0) {
162
0
        break;
163
0
      }
164
0
    }
165
166
0
    if (j < ColorIn1->ColorCount) {
167
0
      ColorTransIn2[i] = j; /* color exists in Color1 */
168
0
    } else {
169
      /* Color is new - copy it to a new slot: */
170
0
      ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i];
171
0
      ColorTransIn2[i] = CrntSlot++;
172
0
    }
173
0
  }
174
175
0
  if (CrntSlot > 256) {
176
0
    GifFreeMapObject(ColorUnion);
177
0
    return ((ColorMapObject *)NULL);
178
0
  }
179
180
0
  NewGifBitSize = GifBitSize(CrntSlot);
181
0
  RoundUpTo = (1 << NewGifBitSize);
182
183
0
  if (RoundUpTo != ColorUnion->ColorCount) {
184
0
    register GifColorType *Map = ColorUnion->Colors;
185
186
    /*
187
     * Zero out slots up to next power of 2.
188
     * We know these slots exist because of the way ColorUnion's
189
     * start dimension was computed.
190
     */
191
0
    for (j = CrntSlot; j < RoundUpTo; j++) {
192
0
      Map[j].Red = Map[j].Green = Map[j].Blue = 0;
193
0
    }
194
195
    /* perhaps we can shrink the map? */
196
0
    if (RoundUpTo < ColorUnion->ColorCount) {
197
0
      GifColorType *new_map = (GifColorType *)reallocarray(
198
0
          Map, RoundUpTo, sizeof(GifColorType));
199
0
      if (new_map == NULL) {
200
0
        GifFreeMapObject(ColorUnion);
201
0
        return ((ColorMapObject *)NULL);
202
0
      }
203
0
      ColorUnion->Colors = new_map;
204
0
    }
205
0
  }
206
207
0
  ColorUnion->ColorCount = RoundUpTo;
208
0
  ColorUnion->BitsPerPixel = NewGifBitSize;
209
210
0
  return (ColorUnion);
211
0
}
212
213
/*******************************************************************************
214
 Apply a given color translation to the raster bits of an image
215
*******************************************************************************/
216
0
void GifApplyTranslation(SavedImage *Image, const GifPixelType Translation[]) {
217
0
  size_t i;
218
0
  size_t RasterSize =
219
0
      (size_t)Image->ImageDesc.Height * Image->ImageDesc.Width;
220
221
0
  for (i = 0; i < RasterSize; i++) {
222
0
    Image->RasterBits[i] = Translation[Image->RasterBits[i]];
223
0
  }
224
0
}
225
226
/******************************************************************************
227
 Extension record functions
228
******************************************************************************/
229
int GifAddExtensionBlock(int *ExtensionBlockCount,
230
                         ExtensionBlock **ExtensionBlocks, int Function,
231
23.5k
                         unsigned int Len, unsigned char ExtData[]) {
232
23.5k
  ExtensionBlock *ep;
233
234
23.5k
  if (*ExtensionBlocks == NULL) {
235
708
    *ExtensionBlocks =
236
708
        (ExtensionBlock *)malloc(sizeof(ExtensionBlock));
237
22.8k
  } else {
238
22.8k
    ExtensionBlock *ep_new = (ExtensionBlock *)reallocarray(
239
22.8k
        *ExtensionBlocks, (*ExtensionBlockCount + 1),
240
22.8k
        sizeof(ExtensionBlock));
241
22.8k
    if (ep_new == NULL) {
242
0
      return (GIF_ERROR);
243
0
    }
244
22.8k
    *ExtensionBlocks = ep_new;
245
22.8k
  }
246
247
23.5k
  if (*ExtensionBlocks == NULL) {
248
0
    return (GIF_ERROR);
249
0
  }
250
251
23.5k
  ep = &(*ExtensionBlocks)[(*ExtensionBlockCount)++];
252
253
23.5k
  ep->Function = Function;
254
23.5k
  ep->ByteCount = Len;
255
23.5k
  ep->Bytes = (GifByteType *)malloc(ep->ByteCount);
256
23.5k
  if (ep->Bytes == NULL) {
257
0
    return (GIF_ERROR);
258
0
  }
259
260
23.5k
  if (ExtData != NULL) {
261
23.5k
    memcpy(ep->Bytes, ExtData, Len);
262
23.5k
  }
263
264
23.5k
  return (GIF_OK);
265
23.5k
}
266
267
void GifFreeExtensions(int *ExtensionBlockCount,
268
4.95k
                       ExtensionBlock **ExtensionBlocks) {
269
4.95k
  ExtensionBlock *ep;
270
271
4.95k
  if (*ExtensionBlocks == NULL) {
272
4.24k
    return;
273
4.24k
  }
274
275
708
  for (ep = *ExtensionBlocks;
276
24.2k
       ep < (*ExtensionBlocks + *ExtensionBlockCount); ep++) {
277
23.5k
    (void)free((char *)ep->Bytes);
278
23.5k
  }
279
708
  (void)free((char *)*ExtensionBlocks);
280
708
  *ExtensionBlocks = NULL;
281
708
  *ExtensionBlockCount = 0;
282
708
}
283
284
/******************************************************************************
285
   Image block allocation functions
286
******************************************************************************/
287
288
/* Private Function:
289
 * Frees the last image in the GifFile->SavedImages array
290
 */
291
0
void FreeLastSavedImage(GifFileType *GifFile) {
292
0
  SavedImage *sp;
293
294
0
  if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) {
295
0
    return;
296
0
  }
297
298
  /* Remove one SavedImage from the GifFile */
299
0
  GifFile->ImageCount--;
300
0
  sp = &GifFile->SavedImages[GifFile->ImageCount];
301
302
  /* Deallocate its Colormap */
303
0
  if (sp->ImageDesc.ColorMap != NULL) {
304
0
    GifFreeMapObject(sp->ImageDesc.ColorMap);
305
0
    sp->ImageDesc.ColorMap = NULL;
306
0
  }
307
308
  /* Deallocate the image data */
309
0
  if (sp->RasterBits != NULL) {
310
0
    free((char *)sp->RasterBits);
311
0
  }
312
313
  /* Deallocate any extensions */
314
0
  GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
315
316
  /*** FIXME: We could realloc the GifFile->SavedImages structure but is
317
   * there a point to it? Saves some memory but we'd have to do it every
318
   * time.  If this is used in GifFreeSavedImages then it would be
319
   * inefficient (The whole array is going to be deallocated.)  If we just
320
   * use it when we want to free the last Image it's convenient to do it
321
   * here.
322
   */
323
0
}
324
325
/*
326
 * Append an image block to the SavedImages array
327
 */
328
SavedImage *GifMakeSavedImage(GifFileType *GifFile,
329
0
                              const SavedImage *CopyFrom) {
330
  // cppcheck-suppress ctunullpointer
331
0
  if (GifFile->SavedImages == NULL) {
332
0
    GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage));
333
0
  } else {
334
0
    SavedImage *newSavedImages = (SavedImage *)reallocarray(
335
0
        GifFile->SavedImages, (GifFile->ImageCount + 1),
336
0
        sizeof(SavedImage));
337
0
    if (newSavedImages == NULL) {
338
0
      return ((SavedImage *)NULL);
339
0
    }
340
0
    GifFile->SavedImages = newSavedImages;
341
0
  }
342
0
  if (GifFile->SavedImages == NULL) {
343
0
    return ((SavedImage *)NULL);
344
0
  } else {
345
0
    SavedImage *sp = &GifFile->SavedImages[GifFile->ImageCount++];
346
347
0
    if (CopyFrom != NULL) {
348
0
      memcpy((char *)sp, CopyFrom, sizeof(SavedImage));
349
350
      /*
351
       * Make our own allocated copies of the heap fields in
352
       * the copied record.  This guards against potential
353
       * aliasing problems.
354
       */
355
356
      /* Null out aliased pointers before any allocations
357
       * so that FreeLastSavedImage won't free CopyFrom's
358
       * data if an allocation fails partway through. */
359
0
      sp->ImageDesc.ColorMap = NULL;
360
0
      sp->RasterBits = NULL;
361
0
      sp->ExtensionBlocks = NULL;
362
0
      sp->ExtensionBlockCount = 0;
363
 
364
      /* first, the local color map */
365
0
      if (CopyFrom->ImageDesc.ColorMap != NULL) {
366
0
        sp->ImageDesc.ColorMap = GifMakeMapObject(
367
0
            CopyFrom->ImageDesc.ColorMap->ColorCount,
368
0
            CopyFrom->ImageDesc.ColorMap->Colors);
369
0
        if (sp->ImageDesc.ColorMap == NULL) {
370
0
          FreeLastSavedImage(GifFile);
371
0
          return (SavedImage *)(NULL);
372
0
        }
373
0
      }
374
375
      /* next, the raster */
376
0
      sp->RasterBits = (unsigned char *)reallocarray(
377
0
          NULL,
378
0
          (size_t)CopyFrom->ImageDesc.Height *
379
0
              CopyFrom->ImageDesc.Width,
380
0
          sizeof(GifPixelType));
381
0
      if (sp->RasterBits == NULL) {
382
0
        FreeLastSavedImage(GifFile);
383
0
        return (SavedImage *)(NULL);
384
0
      }
385
0
      memcpy(sp->RasterBits, CopyFrom->RasterBits,
386
0
             (size_t)CopyFrom->ImageDesc.Height *
387
0
                 CopyFrom->ImageDesc.Width *
388
0
                 sizeof(GifPixelType));
389
390
      /* finally, the extension blocks */
391
0
      if (CopyFrom->ExtensionBlocks != NULL) {
392
0
        int k;
393
0
        sp->ExtensionBlocks =
394
0
            (ExtensionBlock *)calloc(
395
0
                CopyFrom->ExtensionBlockCount,
396
0
                sizeof(ExtensionBlock));
397
0
        if (sp->ExtensionBlocks == NULL) {
398
0
          FreeLastSavedImage(GifFile);
399
0
          return (SavedImage *)(NULL);
400
0
        }
401
0
        for (k = 0; k < CopyFrom->ExtensionBlockCount;
402
0
             k++) {
403
0
          ExtensionBlock *dst =
404
0
              &sp->ExtensionBlocks[k];
405
0
          ExtensionBlock *src =
406
0
              &CopyFrom->ExtensionBlocks[k];
407
0
          dst->Function = src->Function;
408
0
          dst->ByteCount = src->ByteCount;
409
0
          if (src->ByteCount > 0) {
410
0
            dst->Bytes =
411
0
                (GifByteType *)malloc(
412
0
                    src->ByteCount);
413
0
            if (dst->Bytes == NULL) {
414
0
              FreeLastSavedImage(
415
0
                  GifFile);
416
0
              return (SavedImage *)(NULL);
417
0
            }
418
0
            memcpy(dst->Bytes, src->Bytes,
419
0
                   src->ByteCount);
420
0
          }
421
0
        }
422
0
      }
423
0
    } else {
424
0
      memset((char *)sp, '\0', sizeof(SavedImage));
425
0
    }
426
427
0
    return (sp);
428
0
  }
429
0
}
430
431
171
void GifFreeSavedImages(GifFileType *GifFile) {
432
171
  SavedImage *sp;
433
434
171
  if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) {
435
0
    return;
436
0
  }
437
171
  for (sp = GifFile->SavedImages;
438
4.10k
       sp < GifFile->SavedImages + GifFile->ImageCount; sp++) {
439
3.93k
    if (sp->ImageDesc.ColorMap != NULL) {
440
295
      GifFreeMapObject(sp->ImageDesc.ColorMap);
441
295
      sp->ImageDesc.ColorMap = NULL;
442
295
    }
443
444
3.93k
    if (sp->RasterBits != NULL) {
445
3.93k
      free((char *)sp->RasterBits);
446
3.93k
    }
447
448
3.93k
    GifFreeExtensions(&sp->ExtensionBlockCount,
449
3.93k
                      &sp->ExtensionBlocks);
450
3.93k
  }
451
171
  free((char *)GifFile->SavedImages);
452
  GifFile->SavedImages = NULL;
453
171
}
454
455
/* end */