Coverage Report

Created: 2026-03-29 06:25

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
23.0M
#define ABS(x) ((x) > 0 ? (x) : (-(x)))
23
24
24.5M
#define COLOR_ARRAY_SIZE 32768
25
101M
#define BITS_PER_PRIM_COLOR 5
26
24.5M
#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
375
                      GifColorType *OutputColorMap) {
67
68
375
  unsigned int Index, NumOfEntries;
69
375
  int i, j, MaxRGBError[3];
70
375
  unsigned int NewColorMapSize;
71
375
  long Red, Green, Blue;
72
375
  NewColorMapType NewColorSubdiv[256];
73
375
  QuantizedColorType *ColorArrayEntries, *QuantizedColor;
74
75
375
  ColorArrayEntries = (QuantizedColorType *)malloc(
76
375
      sizeof(QuantizedColorType) * COLOR_ARRAY_SIZE);
77
375
  if (ColorArrayEntries == NULL) {
78
0
    return GIF_ERROR;
79
0
  }
80
81
12.2M
  for (i = 0; i < COLOR_ARRAY_SIZE; i++) {
82
12.2M
    ColorArrayEntries[i].RGB[0] = i >> (2 * BITS_PER_PRIM_COLOR);
83
12.2M
    ColorArrayEntries[i].RGB[1] =
84
12.2M
        (i >> BITS_PER_PRIM_COLOR) & MAX_PRIM_COLOR;
85
12.2M
    ColorArrayEntries[i].RGB[2] = i & MAX_PRIM_COLOR;
86
12.2M
    ColorArrayEntries[i].Count = 0;
87
12.2M
  }
88
89
  /* Sample the colors and their distribution: */
90
7.67M
  for (i = 0; i < (int)(Width * Height); i++) {
91
7.67M
    Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR))
92
7.67M
             << (2 * BITS_PER_PRIM_COLOR)) +
93
7.67M
            ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR))
94
7.67M
             << BITS_PER_PRIM_COLOR) +
95
7.67M
            (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR));
96
7.67M
    ColorArrayEntries[Index].Count++;
97
7.67M
  }
98
99
  /* Put all the colors in the first entry of the color map, and call the
100
   * recursive subdivision process.  */
101
96.3k
  for (i = 0; i < 256; i++) {
102
96.0k
    NewColorSubdiv[i].QuantizedColors = NULL;
103
96.0k
    NewColorSubdiv[i].Count = NewColorSubdiv[i].NumEntries = 0;
104
384k
    for (j = 0; j < 3; j++) {
105
288k
      NewColorSubdiv[i].RGBMin[j] = 0;
106
288k
      NewColorSubdiv[i].RGBWidth[j] = 255;
107
288k
    }
108
96.0k
  }
109
110
  /* Find the non empty entries in the color table and chain them: */
111
1.09M
  for (i = 0; i < COLOR_ARRAY_SIZE; i++) {
112
1.09M
    if (ColorArrayEntries[i].Count > 0) {
113
375
      break;
114
375
    }
115
1.09M
  }
116
375
  QuantizedColor = NewColorSubdiv[0].QuantizedColors =
117
375
      &ColorArrayEntries[i];
118
375
  NumOfEntries = 1;
119
11.1M
  while (++i < COLOR_ARRAY_SIZE) {
120
11.1M
    if (ColorArrayEntries[i].Count > 0) {
121
1.60M
      QuantizedColor->Pnext = &ColorArrayEntries[i];
122
1.60M
      QuantizedColor = &ColorArrayEntries[i];
123
1.60M
      NumOfEntries++;
124
1.60M
    }
125
11.1M
  }
126
375
  QuantizedColor->Pnext = NULL;
127
128
375
  NewColorSubdiv[0].NumEntries =
129
375
      NumOfEntries; /* Different sampled colors */
130
375
  NewColorSubdiv[0].Count = ((long)Width) * Height; /* Pixels */
131
375
  NewColorMapSize = 1;
132
375
  if (SubdivColorMap(NewColorSubdiv, *ColorMapSize, &NewColorMapSize) !=
133
375
      GIF_OK) {
134
0
    free((char *)ColorArrayEntries);
135
0
    return GIF_ERROR;
136
0
  }
137
375
  if (NewColorMapSize < *ColorMapSize) {
138
    /* And clear rest of color map: */
139
34.8k
    for (i = NewColorMapSize; i < *ColorMapSize; i++) {
140
34.6k
      OutputColorMap[i].Red = OutputColorMap[i].Green =
141
34.6k
          OutputColorMap[i].Blue = 0;
142
34.6k
    }
143
185
  }
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
61.6k
  for (i = 0; i < NewColorMapSize; i++) {
148
61.3k
    if ((j = NewColorSubdiv[i].NumEntries) > 0) {
149
61.3k
      QuantizedColor = NewColorSubdiv[i].QuantizedColors;
150
61.3k
      Red = Green = Blue = 0;
151
1.67M
      while (QuantizedColor) {
152
1.60M
        QuantizedColor->NewColorIndex = i;
153
1.60M
        Red += QuantizedColor->RGB[0];
154
1.60M
        Green += QuantizedColor->RGB[1];
155
1.60M
        Blue += QuantizedColor->RGB[2];
156
1.60M
        QuantizedColor = QuantizedColor->Pnext;
157
1.60M
      }
158
61.3k
      OutputColorMap[i].Red =
159
61.3k
          (Red << (8 - BITS_PER_PRIM_COLOR)) / j;
160
61.3k
      OutputColorMap[i].Green =
161
61.3k
          (Green << (8 - BITS_PER_PRIM_COLOR)) / j;
162
61.3k
      OutputColorMap[i].Blue =
163
61.3k
          (Blue << (8 - BITS_PER_PRIM_COLOR)) / j;
164
61.3k
    }
165
61.3k
  }
166
167
  /* Finally scan the input buffer again and put the mapped index in the
168
   * output buffer.  */
169
375
  MaxRGBError[0] = MaxRGBError[1] = MaxRGBError[2] = 0;
170
7.67M
  for (i = 0; i < (int)(Width * Height); i++) {
171
7.67M
    Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR))
172
7.67M
             << (2 * BITS_PER_PRIM_COLOR)) +
173
7.67M
            ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR))
174
7.67M
             << BITS_PER_PRIM_COLOR) +
175
7.67M
            (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR));
176
7.67M
    Index = ColorArrayEntries[Index].NewColorIndex;
177
7.67M
    OutputBuffer[i] = Index;
178
7.67M
    if (MaxRGBError[0] <
179
7.67M
        ABS(OutputColorMap[Index].Red - RedInput[i])) {
180
1.71k
      MaxRGBError[0] =
181
1.71k
          ABS(OutputColorMap[Index].Red - RedInput[i]);
182
1.71k
    }
183
7.67M
    if (MaxRGBError[1] <
184
7.67M
        ABS(OutputColorMap[Index].Green - GreenInput[i])) {
185
1.80k
      MaxRGBError[1] =
186
1.80k
          ABS(OutputColorMap[Index].Green - GreenInput[i]);
187
1.80k
    }
188
7.67M
    if (MaxRGBError[2] <
189
7.67M
        ABS(OutputColorMap[Index].Blue - BlueInput[i])) {
190
1.82k
      MaxRGBError[2] =
191
1.82k
          ABS(OutputColorMap[Index].Blue - BlueInput[i]);
192
1.82k
    }
193
7.67M
  }
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
375
  free((char *)ColorArrayEntries);
202
203
375
  *ColorMapSize = NewColorMapSize;
204
205
375
  return GIF_OK;
206
375
}
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
375
                          unsigned int *NewColorMapSize) {
217
218
375
  unsigned int i, j, Index = 0;
219
375
  QuantizedColorType *QuantizedColor, **SortArray;
220
221
61.3k
  while (ColorMapSize > *NewColorMapSize) {
222
    /* Find candidate for subdivision: */
223
61.1k
    long Sum, Count;
224
61.1k
    int MaxSize = -1;
225
61.1k
    unsigned int NumEntries, MinColor, MaxColor;
226
7.06M
    for (i = 0; i < *NewColorMapSize; i++) {
227
28.0M
      for (j = 0; j < 3; j++) {
228
21.0M
        if ((((int)NewColorSubdiv[i].RGBWidth[j]) >
229
21.0M
             MaxSize) &&
230
1.77M
            (NewColorSubdiv[i].NumEntries > 1)) {
231
224k
          MaxSize = NewColorSubdiv[i].RGBWidth[j];
232
224k
          Index = i;
233
224k
          SortRGBAxis = j;
234
224k
        }
235
21.0M
      }
236
7.00M
    }
237
238
61.1k
    if (MaxSize == -1) {
239
185
      return GIF_OK;
240
185
    }
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
60.9k
    SortArray = (QuantizedColorType **)malloc(
247
60.9k
        sizeof(QuantizedColorType *) *
248
60.9k
        NewColorSubdiv[Index].NumEntries);
249
60.9k
    if (SortArray == NULL) {
250
0
      return GIF_ERROR;
251
0
    }
252
60.9k
    for (j = 0,
253
60.9k
        QuantizedColor = NewColorSubdiv[Index].QuantizedColors;
254
26.8M
         j < NewColorSubdiv[Index].NumEntries &&
255
26.7M
         QuantizedColor != NULL;
256
26.7M
         j++, QuantizedColor = QuantizedColor->Pnext) {
257
26.7M
      SortArray[j] = QuantizedColor;
258
26.7M
    }
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
60.9k
    qsort(SortArray, NewColorSubdiv[Index].NumEntries,
272
60.9k
          sizeof(QuantizedColorType *), SortCmpRtn);
273
274
    /* Relink the sorted list into one: */
275
26.7M
    for (j = 0; j < NewColorSubdiv[Index].NumEntries - 1; j++) {
276
26.7M
      SortArray[j]->Pnext = SortArray[j + 1];
277
26.7M
    }
278
60.9k
    SortArray[NewColorSubdiv[Index].NumEntries - 1]->Pnext = NULL;
279
60.9k
    NewColorSubdiv[Index].QuantizedColors = QuantizedColor =
280
60.9k
        SortArray[0];
281
60.9k
    free((char *)SortArray);
282
283
    /* Now simply add the Counts until we have half of the Count: */
284
60.9k
    Sum = NewColorSubdiv[Index].Count / 2 - QuantizedColor->Count;
285
60.9k
    NumEntries = 1;
286
60.9k
    Count = QuantizedColor->Count;
287
5.90M
    while (QuantizedColor->Pnext != NULL &&
288
5.90M
           (Sum -= QuantizedColor->Pnext->Count) >= 0 &&
289
5.85M
           QuantizedColor->Pnext->Pnext != NULL) {
290
5.84M
      QuantizedColor = QuantizedColor->Pnext;
291
5.84M
      NumEntries++;
292
5.84M
      Count += QuantizedColor->Count;
293
5.84M
    }
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
60.9k
    MaxColor =
300
60.9k
        QuantizedColor->RGB[SortRGBAxis]; /* Max. of first half */
301
    /* coverity[var_deref_op] */
302
60.9k
    MinColor =
303
        // cppcheck-suppress nullPointerRedundantCheck
304
60.9k
        QuantizedColor->Pnext->RGB[SortRGBAxis]; /* of second */
305
60.9k
    MaxColor <<= (8 - BITS_PER_PRIM_COLOR);
306
60.9k
    MinColor <<= (8 - BITS_PER_PRIM_COLOR);
307
308
    /* Partition right here: */
309
60.9k
    NewColorSubdiv[*NewColorMapSize].QuantizedColors =
310
60.9k
        QuantizedColor->Pnext;
311
60.9k
    QuantizedColor->Pnext = NULL;
312
60.9k
    NewColorSubdiv[*NewColorMapSize].Count = Count;
313
60.9k
    NewColorSubdiv[Index].Count -= Count;
314
60.9k
    NewColorSubdiv[*NewColorMapSize].NumEntries =
315
60.9k
        NewColorSubdiv[Index].NumEntries - NumEntries;
316
60.9k
    NewColorSubdiv[Index].NumEntries = NumEntries;
317
243k
    for (j = 0; j < 3; j++) {
318
182k
      NewColorSubdiv[*NewColorMapSize].RGBMin[j] =
319
182k
          NewColorSubdiv[Index].RGBMin[j];
320
182k
      NewColorSubdiv[*NewColorMapSize].RGBWidth[j] =
321
182k
          NewColorSubdiv[Index].RGBWidth[j];
322
182k
    }
323
60.9k
    NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] =
324
60.9k
        NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] +
325
60.9k
        NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] -
326
60.9k
        MinColor;
327
60.9k
    NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] = MinColor;
328
329
60.9k
    NewColorSubdiv[Index].RGBWidth[SortRGBAxis] =
330
60.9k
        MaxColor - NewColorSubdiv[Index].RGBMin[SortRGBAxis];
331
332
60.9k
    (*NewColorMapSize)++;
333
60.9k
  }
334
335
190
  return GIF_OK;
336
375
}
337
338
/****************************************************************************
339
 Routine called by qsort to compare two entries.
340
 *****************************************************************************/
341
342
182M
static int SortCmpRtn(const void *Entry1, const void *Entry2) {
343
182M
  QuantizedColorType *entry1 = (*((QuantizedColorType **)Entry1));
344
182M
  QuantizedColorType *entry2 = (*((QuantizedColorType **)Entry2));
345
346
  /* sort on all axes of the color space! */
347
182M
  int hash1 = entry1->RGB[SortRGBAxis] * 256 * 256 +
348
182M
              entry1->RGB[(SortRGBAxis + 1) % 3] * 256 +
349
182M
              entry1->RGB[(SortRGBAxis + 2) % 3];
350
182M
  int hash2 = entry2->RGB[SortRGBAxis] * 256 * 256 +
351
182M
              entry2->RGB[(SortRGBAxis + 1) % 3] * 256 +
352
182M
              entry2->RGB[(SortRGBAxis + 2) % 3];
353
354
182M
  return hash1 - hash2;
355
182M
}
356
357
/* end */