Coverage Report

Created: 2023-12-08 06:35

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