Coverage Report

Created: 2025-10-30 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/giflib-code/quantize.c
Line
Count
Source
1
/*****************************************************************************
2
3
 quantize.c - quantize a high resolution image into lower one
4
5
 Based on: "Color Image Quantization for frame buffer Display", by
6
 Paul Heckbert SIGGRAPH 1982 page 297-307.
7
8
 This doesn't really belong in the core library, was undocumented,
9
 and was removed in 4.2.  Then it turned out some client apps were
10
 actually using it, so it was restored in 5.0.
11
12
******************************************************************************/
13
// SPDX-License-Identifier: MIT
14
// SPDX-FileCopyrightText: Copyright (C) Eric S. Raymond <esr@thyrsus.com>
15
16
#include <stdio.h>
17
#include <stdlib.h>
18
19
#include "gif_lib.h"
20
#include "gif_lib_private.h"
21
22
22.4M
#define ABS(x) ((x) > 0 ? (x) : (-(x)))
23
24
22.8M
#define COLOR_ARRAY_SIZE 32768
25
97.9M
#define BITS_PER_PRIM_COLOR 5
26
22.8M
#define MAX_PRIM_COLOR 0x1f
27
28
static int SortRGBAxis;
29
30
typedef struct QuantizedColorType {
31
  GifByteType RGB[3];
32
  GifByteType NewColorIndex;
33
  long Count;
34
  struct QuantizedColorType *Pnext;
35
} QuantizedColorType;
36
37
typedef struct NewColorMapType {
38
  GifByteType RGBMin[3], RGBWidth[3];
39
  unsigned int
40
      NumEntries;      /* # of QuantizedColorType in linked list below */
41
  unsigned long Count; /* Total number of pixels in all the entries */
42
  QuantizedColorType *QuantizedColors;
43
} NewColorMapType;
44
45
static int SubdivColorMap(NewColorMapType *NewColorSubdiv,
46
                          unsigned int ColorMapSize,
47
                          unsigned int *NewColorMapSize);
48
static int SortCmpRtn(const void *Entry1, const void *Entry2);
49
50
/******************************************************************************
51
 Quantize high resolution image into lower one. Input image consists of a
52
 2D array for each of the RGB colors with size Width by Height. There is no
53
 Color map for the input. Output is a quantized image with 2D array of
54
 indexes into the output color map.
55
   Note input image can be 24 bits at the most (8 for red/green/blue) and
56
 the output has 256 colors at the most (256 entries in the color map.).
57
 ColorMapSize specifies size of color map up to 256 and will be updated to
58
 real size before returning.
59
   Also non of the parameter are allocated by this routine.
60
   This function returns GIF_OK if successful, GIF_ERROR otherwise.
61
******************************************************************************/
62
int GifQuantizeBuffer(unsigned int Width, unsigned int Height,
63
                      int *ColorMapSize, const GifByteType *RedInput,
64
                      const GifByteType *GreenInput,
65
                      const GifByteType *BlueInput, GifByteType *OutputBuffer,
66
348
                      GifColorType *OutputColorMap) {
67
68
348
  unsigned int Index, NumOfEntries;
69
348
  int i, j, MaxRGBError[3];
70
348
  unsigned int NewColorMapSize;
71
348
  long Red, Green, Blue;
72
348
  NewColorMapType NewColorSubdiv[256];
73
348
  QuantizedColorType *ColorArrayEntries, *QuantizedColor;
74
75
348
  ColorArrayEntries = (QuantizedColorType *)malloc(
76
348
      sizeof(QuantizedColorType) * COLOR_ARRAY_SIZE);
77
348
  if (ColorArrayEntries == NULL) {
78
0
    return GIF_ERROR;
79
0
  }
80
81
11.4M
  for (i = 0; i < COLOR_ARRAY_SIZE; i++) {
82
11.4M
    ColorArrayEntries[i].RGB[0] = i >> (2 * BITS_PER_PRIM_COLOR);
83
11.4M
    ColorArrayEntries[i].RGB[1] =
84
11.4M
        (i >> BITS_PER_PRIM_COLOR) & MAX_PRIM_COLOR;
85
11.4M
    ColorArrayEntries[i].RGB[2] = i & MAX_PRIM_COLOR;
86
11.4M
    ColorArrayEntries[i].Count = 0;
87
11.4M
  }
88
89
  /* Sample the colors and their distribution: */
90
7.48M
  for (i = 0; i < (int)(Width * Height); i++) {
91
7.48M
    Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR))
92
7.48M
             << (2 * BITS_PER_PRIM_COLOR)) +
93
7.48M
            ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR))
94
7.48M
             << BITS_PER_PRIM_COLOR) +
95
7.48M
            (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR));
96
7.48M
    ColorArrayEntries[Index].Count++;
97
7.48M
  }
98
99
  /* Put all the colors in the first entry of the color map, and call the
100
   * recursive subdivision process.  */
101
89.4k
  for (i = 0; i < 256; i++) {
102
89.0k
    NewColorSubdiv[i].QuantizedColors = NULL;
103
89.0k
    NewColorSubdiv[i].Count = NewColorSubdiv[i].NumEntries = 0;
104
356k
    for (j = 0; j < 3; j++) {
105
267k
      NewColorSubdiv[i].RGBMin[j] = 0;
106
267k
      NewColorSubdiv[i].RGBWidth[j] = 255;
107
267k
    }
108
89.0k
  }
109
110
  /* Find the non empty entries in the color table and chain them: */
111
761k
  for (i = 0; i < COLOR_ARRAY_SIZE; i++) {
112
761k
    if (ColorArrayEntries[i].Count > 0) {
113
348
      break;
114
348
    }
115
761k
  }
116
348
  QuantizedColor = NewColorSubdiv[0].QuantizedColors =
117
348
      &ColorArrayEntries[i];
118
348
  NumOfEntries = 1;
119
10.6M
  while (++i < COLOR_ARRAY_SIZE) {
120
10.6M
    if (ColorArrayEntries[i].Count > 0) {
121
1.55M
      QuantizedColor->Pnext = &ColorArrayEntries[i];
122
1.55M
      QuantizedColor = &ColorArrayEntries[i];
123
1.55M
      NumOfEntries++;
124
1.55M
    }
125
10.6M
  }
126
348
  QuantizedColor->Pnext = NULL;
127
128
348
  NewColorSubdiv[0].NumEntries =
129
348
      NumOfEntries; /* Different sampled colors */
130
348
  NewColorSubdiv[0].Count = ((long)Width) * Height; /* Pixels */
131
348
  NewColorMapSize = 1;
132
348
  if (SubdivColorMap(NewColorSubdiv, *ColorMapSize, &NewColorMapSize) !=
133
348
      GIF_OK) {
134
0
    free((char *)ColorArrayEntries);
135
0
    return GIF_ERROR;
136
0
  }
137
348
  if (NewColorMapSize < *ColorMapSize) {
138
    /* And clear rest of color map: */
139
31.4k
    for (i = NewColorMapSize; i < *ColorMapSize; i++) {
140
31.2k
      OutputColorMap[i].Red = OutputColorMap[i].Green =
141
31.2k
          OutputColorMap[i].Blue = 0;
142
31.2k
    }
143
171
  }
144
145
  /* Average the colors in each entry to be the color to be used in the
146
   * output color map, and plug it into the output color map itself. */
147
58.1k
  for (i = 0; i < NewColorMapSize; i++) {
148
57.8k
    if ((j = NewColorSubdiv[i].NumEntries) > 0) {
149
57.8k
      QuantizedColor = NewColorSubdiv[i].QuantizedColors;
150
57.8k
      Red = Green = Blue = 0;
151
1.61M
      while (QuantizedColor) {
152
1.55M
        QuantizedColor->NewColorIndex = i;
153
1.55M
        Red += QuantizedColor->RGB[0];
154
1.55M
        Green += QuantizedColor->RGB[1];
155
1.55M
        Blue += QuantizedColor->RGB[2];
156
1.55M
        QuantizedColor = QuantizedColor->Pnext;
157
1.55M
      }
158
57.8k
      OutputColorMap[i].Red =
159
57.8k
          (Red << (8 - BITS_PER_PRIM_COLOR)) / j;
160
57.8k
      OutputColorMap[i].Green =
161
57.8k
          (Green << (8 - BITS_PER_PRIM_COLOR)) / j;
162
57.8k
      OutputColorMap[i].Blue =
163
57.8k
          (Blue << (8 - BITS_PER_PRIM_COLOR)) / j;
164
57.8k
    }
165
57.8k
  }
166
167
  /* Finally scan the input buffer again and put the mapped index in the
168
   * output buffer.  */
169
348
  MaxRGBError[0] = MaxRGBError[1] = MaxRGBError[2] = 0;
170
7.48M
  for (i = 0; i < (int)(Width * Height); i++) {
171
7.48M
    Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR))
172
7.48M
             << (2 * BITS_PER_PRIM_COLOR)) +
173
7.48M
            ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR))
174
7.48M
             << BITS_PER_PRIM_COLOR) +
175
7.48M
            (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR));
176
7.48M
    Index = ColorArrayEntries[Index].NewColorIndex;
177
7.48M
    OutputBuffer[i] = Index;
178
7.48M
    if (MaxRGBError[0] <
179
7.48M
        ABS(OutputColorMap[Index].Red - RedInput[i])) {
180
1.67k
      MaxRGBError[0] =
181
1.67k
          ABS(OutputColorMap[Index].Red - RedInput[i]);
182
1.67k
    }
183
7.48M
    if (MaxRGBError[1] <
184
7.48M
        ABS(OutputColorMap[Index].Green - GreenInput[i])) {
185
1.68k
      MaxRGBError[1] =
186
1.68k
          ABS(OutputColorMap[Index].Green - GreenInput[i]);
187
1.68k
    }
188
7.48M
    if (MaxRGBError[2] <
189
7.48M
        ABS(OutputColorMap[Index].Blue - BlueInput[i])) {
190
1.72k
      MaxRGBError[2] =
191
1.72k
          ABS(OutputColorMap[Index].Blue - BlueInput[i]);
192
1.72k
    }
193
7.48M
  }
194
195
#ifdef DEBUG
196
  fprintf(stderr,
197
          "Quantization L(0) errors: Red = %d, Green = %d, Blue = %d.\n",
198
          MaxRGBError[0], MaxRGBError[1], MaxRGBError[2]);
199
#endif /* DEBUG */
200
201
348
  free((char *)ColorArrayEntries);
202
203
348
  *ColorMapSize = NewColorMapSize;
204
205
348
  return GIF_OK;
206
348
}
207
208
/******************************************************************************
209
 Routine to subdivide the RGB space recursively using median cut in each
210
 axes alternatingly until ColorMapSize different cubes exists.
211
 The biggest cube in one dimension is subdivide unless it has only one entry.
212
 Returns GIF_ERROR if failed, otherwise GIF_OK.
213
*******************************************************************************/
214
static int SubdivColorMap(NewColorMapType *NewColorSubdiv,
215
                          unsigned int ColorMapSize,
216
348
                          unsigned int *NewColorMapSize) {
217
218
348
  unsigned int i, j, Index = 0;
219
348
  QuantizedColorType *QuantizedColor, **SortArray;
220
221
57.8k
  while (ColorMapSize > *NewColorMapSize) {
222
    /* Find candidate for subdivision: */
223
57.6k
    long Sum, Count;
224
57.6k
    int MaxSize = -1;
225
57.6k
    unsigned int NumEntries, MinColor, MaxColor;
226
6.67M
    for (i = 0; i < *NewColorMapSize; i++) {
227
26.4M
      for (j = 0; j < 3; j++) {
228
19.8M
        if ((((int)NewColorSubdiv[i].RGBWidth[j]) >
229
19.8M
             MaxSize) &&
230
1.59M
            (NewColorSubdiv[i].NumEntries > 1)) {
231
213k
          MaxSize = NewColorSubdiv[i].RGBWidth[j];
232
213k
          Index = i;
233
213k
          SortRGBAxis = j;
234
213k
        }
235
19.8M
      }
236
6.61M
    }
237
238
57.6k
    if (MaxSize == -1) {
239
171
      return GIF_OK;
240
171
    }
241
242
    /* Split the entry Index into two along the axis SortRGBAxis: */
243
244
    /* Sort all elements in that entry along the given axis and
245
     * split at the median.  */
246
57.4k
    SortArray = (QuantizedColorType **)malloc(
247
57.4k
        sizeof(QuantizedColorType *) *
248
57.4k
        NewColorSubdiv[Index].NumEntries);
249
57.4k
    if (SortArray == NULL) {
250
0
      return GIF_ERROR;
251
0
    }
252
57.4k
    for (j = 0,
253
57.4k
        QuantizedColor = NewColorSubdiv[Index].QuantizedColors;
254
23.9M
         j < NewColorSubdiv[Index].NumEntries &&
255
23.8M
         QuantizedColor != NULL;
256
23.8M
         j++, QuantizedColor = QuantizedColor->Pnext) {
257
23.8M
      SortArray[j] = QuantizedColor;
258
23.8M
    }
259
260
    /*
261
     * Because qsort isn't stable, this can produce differing
262
     * results for the order of tuples depending on platform
263
     * details of how qsort() is implemented.
264
     *
265
     * We mitigate this problem by sorting on all three axes rather
266
     * than only the one specied by SortRGBAxis; that way the
267
     * instability can only become an issue if there are multiple
268
     * color indices referring to identical RGB tuples.  Older
269
     * versions of this sorted on only the one axis.
270
     */
271
57.4k
    qsort(SortArray, NewColorSubdiv[Index].NumEntries,
272
57.4k
          sizeof(QuantizedColorType *), SortCmpRtn);
273
274
    /* Relink the sorted list into one: */
275
23.8M
    for (j = 0; j < NewColorSubdiv[Index].NumEntries - 1; j++) {
276
23.7M
      SortArray[j]->Pnext = SortArray[j + 1];
277
23.7M
    }
278
57.4k
    SortArray[NewColorSubdiv[Index].NumEntries - 1]->Pnext = NULL;
279
57.4k
    NewColorSubdiv[Index].QuantizedColors = QuantizedColor =
280
57.4k
        SortArray[0];
281
57.4k
    free((char *)SortArray);
282
283
    /* Now simply add the Counts until we have half of the Count: */
284
57.4k
    Sum = NewColorSubdiv[Index].Count / 2 - QuantizedColor->Count;
285
57.4k
    NumEntries = 1;
286
57.4k
    Count = QuantizedColor->Count;
287
5.65M
    while (QuantizedColor->Pnext != NULL &&
288
5.65M
           (Sum -= QuantizedColor->Pnext->Count) >= 0 &&
289
5.59M
           QuantizedColor->Pnext->Pnext != NULL) {
290
5.59M
      QuantizedColor = QuantizedColor->Pnext;
291
5.59M
      NumEntries++;
292
5.59M
      Count += QuantizedColor->Count;
293
5.59M
    }
294
    /* Save the values of the last color of the first half, and
295
     * first of the second half so we can update the Bounding Boxes
296
     * later. Also as the colors are quantized and the BBoxes are
297
     * full 0..255, they need to be rescaled.
298
     */
299
57.4k
    MaxColor =
300
57.4k
        QuantizedColor->RGB[SortRGBAxis]; /* Max. of first half */
301
    /* coverity[var_deref_op] */
302
57.4k
    MinColor =
303
        // cppcheck-suppress nullPointerRedundantCheck
304
57.4k
        QuantizedColor->Pnext->RGB[SortRGBAxis]; /* of second */
305
57.4k
    MaxColor <<= (8 - BITS_PER_PRIM_COLOR);
306
57.4k
    MinColor <<= (8 - BITS_PER_PRIM_COLOR);
307
308
    /* Partition right here: */
309
57.4k
    NewColorSubdiv[*NewColorMapSize].QuantizedColors =
310
57.4k
        QuantizedColor->Pnext;
311
57.4k
    QuantizedColor->Pnext = NULL;
312
57.4k
    NewColorSubdiv[*NewColorMapSize].Count = Count;
313
57.4k
    NewColorSubdiv[Index].Count -= Count;
314
57.4k
    NewColorSubdiv[*NewColorMapSize].NumEntries =
315
57.4k
        NewColorSubdiv[Index].NumEntries - NumEntries;
316
57.4k
    NewColorSubdiv[Index].NumEntries = NumEntries;
317
229k
    for (j = 0; j < 3; j++) {
318
172k
      NewColorSubdiv[*NewColorMapSize].RGBMin[j] =
319
172k
          NewColorSubdiv[Index].RGBMin[j];
320
172k
      NewColorSubdiv[*NewColorMapSize].RGBWidth[j] =
321
172k
          NewColorSubdiv[Index].RGBWidth[j];
322
172k
    }
323
57.4k
    NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] =
324
57.4k
        NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] +
325
57.4k
        NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] -
326
57.4k
        MinColor;
327
57.4k
    NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] = MinColor;
328
329
57.4k
    NewColorSubdiv[Index].RGBWidth[SortRGBAxis] =
330
57.4k
        MaxColor - NewColorSubdiv[Index].RGBMin[SortRGBAxis];
331
332
57.4k
    (*NewColorMapSize)++;
333
57.4k
  }
334
335
177
  return GIF_OK;
336
348
}
337
338
/****************************************************************************
339
 Routine called by qsort to compare two entries.
340
 *****************************************************************************/
341
342
161M
static int SortCmpRtn(const void *Entry1, const void *Entry2) {
343
161M
  QuantizedColorType *entry1 = (*((QuantizedColorType **)Entry1));
344
161M
  QuantizedColorType *entry2 = (*((QuantizedColorType **)Entry2));
345
346
  /* sort on all axes of the color space! */
347
161M
  int hash1 = entry1->RGB[SortRGBAxis] * 256 * 256 +
348
161M
              entry1->RGB[(SortRGBAxis + 1) % 3] * 256 +
349
161M
              entry1->RGB[(SortRGBAxis + 2) % 3];
350
161M
  int hash2 = entry2->RGB[SortRGBAxis] * 256 * 256 +
351
161M
              entry2->RGB[(SortRGBAxis + 1) % 3] * 256 +
352
161M
              entry2->RGB[(SortRGBAxis + 2) % 3];
353
354
161M
  return hash1 - hash2;
355
161M
}
356
357
/* end */