Coverage Report

Created: 2026-08-14 06:29

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.26k
int GifBitSize(int n) {
24
7.26k
  register int i;
25
26
21.5k
  for (i = 1; i <= 8; i++) {
27
21.5k
    if ((1 << i) >= n) {
28
7.26k
      break;
29
7.26k
    }
30
21.5k
  }
31
7.26k
  return (i);
32
7.26k
}
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.63k
ColorMapObject *GifMakeMapObject(int ColorCount, const GifColorType *ColorMap) {
43
3.63k
  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.63k
  if (ColorCount != (1 << GifBitSize(ColorCount))) {
49
0
    return ((ColorMapObject *)NULL);
50
0
  }
51
52
3.63k
  Object = (ColorMapObject *)malloc(sizeof(ColorMapObject));
53
3.63k
  if (Object == (ColorMapObject *)NULL) {
54
0
    return ((ColorMapObject *)NULL);
55
0
  }
56
57
3.63k
  Object->Colors =
58
3.63k
      (GifColorType *)calloc(ColorCount, sizeof(GifColorType));
59
3.63k
  if (Object->Colors == (GifColorType *)NULL) {
60
0
    free(Object);
61
0
    return ((ColorMapObject *)NULL);
62
0
  }
63
64
3.63k
  Object->ColorCount = ColorCount;
65
3.63k
  Object->BitsPerPixel = GifBitSize(ColorCount);
66
3.63k
  Object->SortFlag = false;
67
68
3.63k
  if (ColorMap != NULL) {
69
1.71k
    memcpy((char *)Object->Colors, (char *)ColorMap,
70
1.71k
           ColorCount * sizeof(GifColorType));
71
1.71k
  }
72
73
3.63k
  return (Object);
74
3.63k
}
75
76
/*******************************************************************************
77
 Free a color map object
78
*******************************************************************************/
79
3.64k
void GifFreeMapObject(ColorMapObject *Object) {
80
3.64k
  if (Object != NULL) {
81
3.63k
    (void)free(Object->Colors);
82
3.63k
    (void)free(Object);
83
3.63k
  }
84
3.64k
}
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
79.2k
                         unsigned int Len, unsigned char ExtData[]) {
232
79.2k
  ExtensionBlock *ep;
233
234
79.2k
  if (*ExtensionBlocks == NULL) {
235
2.50k
    *ExtensionBlocks =
236
2.50k
        (ExtensionBlock *)malloc(sizeof(ExtensionBlock));
237
76.7k
  } else {
238
76.7k
    ExtensionBlock *ep_new = (ExtensionBlock *)reallocarray(
239
76.7k
        *ExtensionBlocks, (*ExtensionBlockCount + 1),
240
76.7k
        sizeof(ExtensionBlock));
241
76.7k
    if (ep_new == NULL) {
242
0
      return (GIF_ERROR);
243
0
    }
244
76.7k
    *ExtensionBlocks = ep_new;
245
76.7k
  }
246
247
79.2k
  if (*ExtensionBlocks == NULL) {
248
0
    return (GIF_ERROR);
249
0
  }
250
251
79.2k
  ep = &(*ExtensionBlocks)[(*ExtensionBlockCount)++];
252
253
79.2k
  ep->Function = Function;
254
79.2k
  ep->ByteCount = Len;
255
79.2k
  ep->Bytes = (GifByteType *)malloc(ep->ByteCount);
256
79.2k
  if (ep->Bytes == NULL) {
257
0
    return (GIF_ERROR);
258
0
  }
259
260
79.2k
  if (ExtData != NULL) {
261
79.2k
    memcpy(ep->Bytes, ExtData, Len);
262
79.2k
  }
263
264
79.2k
  return (GIF_OK);
265
79.2k
}
266
267
void GifFreeExtensions(int *ExtensionBlockCount,
268
30.1k
                       ExtensionBlock **ExtensionBlocks) {
269
30.1k
  ExtensionBlock *ep;
270
271
30.1k
  if (*ExtensionBlocks == NULL) {
272
27.6k
    return;
273
27.6k
  }
274
275
2.50k
  for (ep = *ExtensionBlocks;
276
81.7k
       ep < (*ExtensionBlocks + *ExtensionBlockCount); ep++) {
277
79.2k
    (void)free((char *)ep->Bytes);
278
79.2k
  }
279
2.50k
  (void)free((char *)*ExtensionBlocks);
280
2.50k
  *ExtensionBlocks = NULL;
281
2.50k
  *ExtensionBlockCount = 0;
282
2.50k
}
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
363
void GifFreeSavedImages(GifFileType *GifFile) {
432
363
  SavedImage *sp;
433
434
363
  if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) {
435
0
    return;
436
0
  }
437
363
  for (sp = GifFile->SavedImages;
438
28.6k
       sp < GifFile->SavedImages + GifFile->ImageCount; sp++) {
439
28.2k
    if (sp->ImageDesc.ColorMap != NULL) {
440
1.31k
      GifFreeMapObject(sp->ImageDesc.ColorMap);
441
1.31k
      sp->ImageDesc.ColorMap = NULL;
442
1.31k
    }
443
444
28.2k
    if (sp->RasterBits != NULL) {
445
28.2k
      free((char *)sp->RasterBits);
446
28.2k
    }
447
448
28.2k
    GifFreeExtensions(&sp->ExtensionBlockCount,
449
28.2k
                      &sp->ExtensionBlocks);
450
28.2k
  }
451
363
  free((char *)GifFile->SavedImages);
452
  GifFile->SavedImages = NULL;
453
363
}
454
455
/* end */