1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file.
6 #include "lib/jpegli/color_quantize.h"
10 #include <unordered_map>
12 #include "lib/jpegli/decode_internal.h"
13 #include "lib/jpegli/error.h"
19 static constexpr int kNumColorCellBits
[kMaxComponents
] = {3, 4, 3, 3};
20 static constexpr int kCompW
[kMaxComponents
] = {2, 3, 1, 1};
22 int Pow(int a
, int b
) {
24 for (int i
= 0; i
< b
; ++i
) {
30 int ComponentOrder(j_decompress_ptr cinfo
, int i
) {
31 if (cinfo
->out_color_components
== 3) {
32 return i
< 2 ? 1 - i
: i
;
37 int GetColorComponent(int i
, int N
) {
38 return (i
* 255 + (N
- 1) / 2) / (N
- 1);
43 void ChooseColorMap1Pass(j_decompress_ptr cinfo
) {
44 jpeg_decomp_master
* m
= cinfo
->master
;
45 int components
= cinfo
->out_color_components
;
46 int desired
= std::min(cinfo
->desired_number_of_colors
, 256);
48 while (Pow(num
+ 1, components
) <= desired
) {
52 JPEGLI_ERROR("Too few colors (%d) in requested colormap", desired
);
54 int actual
= Pow(num
, components
);
55 for (int i
= 0; i
< components
; ++i
) {
56 m
->num_colors_
[i
] = num
;
58 while (actual
< desired
) {
60 for (int i
= 0; i
< components
; ++i
) {
61 int c
= ComponentOrder(cinfo
, i
);
62 int new_total
= (actual
/ m
->num_colors_
[c
]) * (m
->num_colors_
[c
] + 1);
63 if (new_total
<= desired
) {
68 if (actual
== total
) {
72 cinfo
->actual_number_of_colors
= actual
;
73 cinfo
->colormap
= (*cinfo
->mem
->alloc_sarray
)(
74 reinterpret_cast<j_common_ptr
>(cinfo
), JPOOL_IMAGE
, actual
, components
);
75 int next_color
[kMaxComponents
] = {0};
76 for (int i
= 0; i
< actual
; ++i
) {
77 for (int c
= 0; c
< components
; ++c
) {
78 cinfo
->colormap
[c
][i
] =
79 GetColorComponent(next_color
[c
], m
->num_colors_
[c
]);
81 int c
= components
- 1;
82 while (c
> 0 && next_color
[c
] + 1 == m
->num_colors_
[c
]) {
87 if (!m
->colormap_lut_
) {
88 m
->colormap_lut_
= Allocate
<uint8_t>(cinfo
, components
* 256, JPOOL_IMAGE
);
91 for (int c
= 0; c
< components
; ++c
) {
92 int N
= m
->num_colors_
[c
];
94 for (int i
= 0; i
< 256; ++i
) {
95 int index
= ((2 * i
- 1) * (N
- 1) + 254) / 510;
96 m
->colormap_lut_
[c
* 256 + i
] = index
* stride
;
103 // 2^13 priority levels for the PQ seems to be a good compromise between
104 // accuracy, running time and stack space usage.
105 static const int kMaxPriority
= 1 << 13;
106 static const int kMaxLevel
= 3;
108 // This function is used in the multi-resolution grid to be able to compute
109 // the keys for the different resolutions by just shifting the first key.
110 inline int InterlaceBitsRGB(uint8_t r
, uint8_t g
, uint8_t b
) {
112 for (int i
= 0; i
< 7; ++i
) {
127 // This function will compute the actual priorities of the colors based on
128 // the current distance from the palette, the population count and the signals
129 // from the multi-resolution grid.
130 inline int Priority(int d
, int n
, const int* density
, const int* radius
) {
132 for (int level
= 0; level
< kMaxLevel
; ++level
) {
133 if (d
> radius
[level
]) {
134 p
+= density
[level
] * (d
- radius
[level
]);
137 return std::min(kMaxPriority
- 1, p
>> 4);
140 inline int ColorIntQuadDistanceRGB(uint8_t r1
, uint8_t g1
, uint8_t b1
,
141 uint8_t r2
, uint8_t g2
, uint8_t b2
) {
142 // weights for the intensity calculation
143 static constexpr int ired
= 2;
144 static constexpr int igreen
= 5;
145 static constexpr int iblue
= 1;
146 // normalization factor for the intensity calculation (2^ishift)
147 static constexpr int ishift
= 3;
148 const int rd
= r1
- r2
;
149 const int gd
= g1
- g2
;
150 const int bd
= b1
- b2
;
151 const int id
= ired
* rd
+ igreen
* gd
+ iblue
* bd
;
152 return rd
* rd
+ gd
* gd
+ bd
* bd
+ ((id
* id
) >> (2 * ishift
));
155 inline int ScaleQuadDistanceRGB(int d
) {
156 return static_cast<int>(sqrt(d
* 0.25) + 0.5);
159 // The function updates the minimal distances, the clustering and the
160 // quantization error after the insertion of the new color into the palette.
161 void AddToRGBPalette(const uint8_t* red
, const uint8_t* green
,
163 const int* count
, // histogram of colors
164 const int index
, // index of color to be added
165 const int k
, // size of current palette
166 const int n
, // number of colors
167 int* dist
, // array of distances from palette
168 int* cluster
, // mapping of color indices to palette
169 int* center
, // the inverse mapping
170 int64_t* error
) { // measure of the quantization error
174 static_cast<int64_t>(dist
[index
]) * static_cast<int64_t>(count
[index
]);
176 for (int j
= 0; j
< n
; ++j
) {
178 const int d
= ColorIntQuadDistanceRGB(
179 red
[index
], green
[index
], blue
[index
], red
[j
], green
[j
], blue
[j
]);
181 *error
+= static_cast<int64_t>((d
- dist
[j
])) *
182 static_cast<int64_t>(count
[j
]);
190 struct RGBPixelHasher
{
191 // A quick but good-enough hash to get 24 bits of RGB into the lower 12 bits.
192 size_t operator()(uint32_t a
) const { return (a
^ (a
>> 12)) * 0x9e3779b9; }
196 // Thomas Wang's Hash. Nearly perfect and still quite fast. Above (for
197 // pixels) we use a simpler hash because the number of hash calls is
198 // proportional to the number of pixels and that hash dominates; we want the
199 // cost to be minimal and we start with a large table. We can use a better
200 // hash for the histogram since the number of hash calls is proportional to
201 // the number of unique colors in the image, which is hopefully much smaller.
202 // Note that the difference is slight; e.g. replacing RGBPixelHasher with
203 // WangHasher only slows things down by 5% on an Opteron.
204 size_t operator()(uint32_t a
) const {
205 a
= (a
^ 61) ^ (a
>> 16);
214 // Build an index of all the different colors in the input
215 // image. To do this we map the 24 bit RGB representation of the colors
216 // to a unique integer index assigned to the different colors in order of
217 // appearance in the image. Return the number of unique colors found.
218 // The colors are pre-quantized to 3 * 6 bits precision.
219 static int BuildRGBColorIndex(const uint8_t* const image
, int const num_pixels
,
220 int* const count
, uint8_t* const red
,
221 uint8_t* const green
, uint8_t* const blue
) {
222 // Impossible because rgb are in the low 24 bits, and the upper 8 bits is 0.
223 const uint32_t impossible_pixel_value
= 0x10000000;
224 std::unordered_map
<uint32_t, int, RGBPixelHasher
> index_map(1 << 12);
225 std::unordered_map
<uint32_t, int, RGBPixelHasher
>::iterator index_map_lookup
;
226 const uint8_t* imagep
= &image
[0];
227 uint32_t prev_pixel
= impossible_pixel_value
;
230 for (int i
= 0; i
< num_pixels
; ++i
) {
231 uint8_t r
= ((*imagep
++) & 0xfc) + 2;
232 uint8_t g
= ((*imagep
++) & 0xfc) + 2;
233 uint8_t b
= ((*imagep
++) & 0xfc) + 2;
234 uint32_t pixel
= (b
<< 16) | (g
<< 8) | r
;
235 if (pixel
!= prev_pixel
) {
237 index_map_lookup
= index_map
.find(pixel
);
238 if (index_map_lookup
!= index_map
.end()) {
239 index
= index_map_lookup
->second
;
241 index_map
[pixel
] = index
= n
++;
254 void ChooseColorMap2Pass(j_decompress_ptr cinfo
) {
255 if (cinfo
->out_color_space
!= JCS_RGB
) {
256 JPEGLI_ERROR("Two-pass quantizer must use RGB output color space.");
258 jpeg_decomp_master
* m
= cinfo
->master
;
259 const size_t num_pixels
= cinfo
->output_width
* cinfo
->output_height
;
260 const int max_color_count
= std::max
<size_t>(num_pixels
, 1u << 18);
261 const int max_palette_size
= cinfo
->desired_number_of_colors
;
262 std::unique_ptr
<uint8_t[]> red(new uint8_t[max_color_count
]);
263 std::unique_ptr
<uint8_t[]> green(new uint8_t[max_color_count
]);
264 std::unique_ptr
<uint8_t[]> blue(new uint8_t[max_color_count
]);
265 std::vector
<int> count(max_color_count
, 0);
267 int n
= BuildRGBColorIndex(m
->pixels_
, num_pixels
, &count
[0], &red
[0],
268 &green
[0], &blue
[0]);
270 std::vector
<int> dist(n
, std::numeric_limits
<int>::max());
271 std::vector
<int> cluster(n
);
272 std::vector
<bool> in_palette(n
, false);
274 int k
= 0; // palette size
275 const int count_threshold
= (num_pixels
* 4) / max_palette_size
;
276 static constexpr int kAveragePixelErrorThreshold
= 1;
277 const int64_t error_threshold
= num_pixels
* kAveragePixelErrorThreshold
;
278 int64_t error
= 0; // quantization error
282 for (int i
= 0; i
< n
; ++i
) {
283 if (count
[i
] > max_count
) {
284 max_count
= count
[i
];
287 if (!in_palette
[i
] && count
[i
] > count_threshold
) {
288 AddToRGBPalette(&red
[0], &green
[0], &blue
[0], &count
[0], i
, k
++, n
,
289 &dist
[0], &cluster
[0], ¢er
[0], &error
);
290 in_palette
[i
] = true;
294 AddToRGBPalette(&red
[0], &green
[0], &blue
[0], &count
[0], winner
, k
++, n
,
295 &dist
[0], &cluster
[0], ¢er
[0], &error
);
296 in_palette
[winner
] = true;
299 // Calculation of the multi-resolution density grid.
300 std::vector
<int> density(n
* kMaxLevel
);
301 std::vector
<int> radius(n
* kMaxLevel
);
302 std::unordered_map
<uint32_t, int, WangHasher
> histogram
[kMaxLevel
];
303 for (int level
= 0; level
< kMaxLevel
; ++level
) {
304 // This value is never used because key = InterlaceBitsRGB(...) >> 6
307 for (int i
= 0; i
< n
; ++i
) {
308 if (!in_palette
[i
]) {
309 const int key
= InterlaceBitsRGB(red
[i
], green
[i
], blue
[i
]) >> 6;
310 for (int level
= 0; level
< kMaxLevel
; ++level
) {
311 histogram
[level
][key
>> (3 * level
)] += count
[i
];
315 for (int i
= 0; i
< n
; ++i
) {
316 if (!in_palette
[i
]) {
317 for (int level
= 0; level
< kMaxLevel
; ++level
) {
318 const int mask
= (4 << level
) - 1;
319 const int rd
= std::max(red
[i
] & mask
, mask
- (red
[i
] & mask
));
320 const int gd
= std::max(green
[i
] & mask
, mask
- (green
[i
] & mask
));
321 const int bd
= std::max(blue
[i
] & mask
, mask
- (blue
[i
] & mask
));
322 radius
[i
* kMaxLevel
+ level
] =
323 ScaleQuadDistanceRGB(ColorIntQuadDistanceRGB(0, 0, 0, rd
, gd
, bd
));
325 const int key
= InterlaceBitsRGB(red
[i
], green
[i
], blue
[i
]) >> 6;
327 density
[i
* kMaxLevel
] = histogram
[0][key
] - count
[i
];
329 for (int level
= 1; level
< kMaxLevel
; ++level
) {
330 density
[i
* kMaxLevel
+ level
] =
331 (histogram
[level
][key
>> (3 * level
)] -
332 histogram
[level
- 1][key
>> (3 * level
- 3)]);
337 // Calculate the initial error now that the palette has been initialized.
339 for (int i
= 0; i
< n
; ++i
) {
340 error
+= static_cast<int64_t>(dist
[i
]) * static_cast<int64_t>(count
[i
]);
343 std::unique_ptr
<std::vector
<int>[]> bucket_array(
344 new std::vector
<int>[kMaxPriority
]);
345 int top_priority
= -1;
346 for (int i
= 0; i
< n
; ++i
) {
347 if (!in_palette
[i
]) {
348 int priority
= Priority(ScaleQuadDistanceRGB(dist
[i
]), count
[i
],
349 &density
[i
* kMaxLevel
], &radius
[i
* kMaxLevel
]);
350 bucket_array
[priority
].push_back(i
);
351 top_priority
= std::max(priority
, top_priority
);
354 double error_accum
= 0;
355 while (top_priority
>= 0 && k
< max_palette_size
) {
356 if (error
< error_threshold
) {
357 error_accum
+= std::min(error_threshold
, error_threshold
- error
);
358 if (error_accum
>= 10 * error_threshold
) {
362 int i
= bucket_array
[top_priority
].back();
363 int priority
= Priority(ScaleQuadDistanceRGB(dist
[i
]), count
[i
],
364 &density
[i
* kMaxLevel
], &radius
[i
* kMaxLevel
]);
365 if (priority
< top_priority
) {
366 bucket_array
[priority
].push_back(i
);
368 AddToRGBPalette(&red
[0], &green
[0], &blue
[0], &count
[0], i
, k
++, n
,
369 &dist
[0], &cluster
[0], ¢er
[0], &error
);
371 bucket_array
[top_priority
].pop_back();
372 while (top_priority
>= 0 && bucket_array
[top_priority
].empty()) {
377 cinfo
->actual_number_of_colors
= k
;
378 cinfo
->colormap
= (*cinfo
->mem
->alloc_sarray
)(
379 reinterpret_cast<j_common_ptr
>(cinfo
), JPOOL_IMAGE
, k
, 3);
380 for (int i
= 0; i
< k
; ++i
) {
381 int index
= center
[i
];
382 cinfo
->colormap
[0][i
] = red
[index
];
383 cinfo
->colormap
[1][i
] = green
[index
];
384 cinfo
->colormap
[2][i
] = blue
[index
];
390 void FindCandidatesForCell(j_decompress_ptr cinfo
, int ncomp
, int cell
[],
391 std::vector
<uint8_t>* candidates
) {
392 int cell_min
[kMaxComponents
];
393 int cell_max
[kMaxComponents
];
394 int cell_center
[kMaxComponents
];
395 for (int c
= 0; c
< ncomp
; ++c
) {
396 cell_min
[c
] = cell
[c
] << (8 - kNumColorCellBits
[c
]);
397 cell_max
[c
] = cell_min
[c
] + (1 << (8 - kNumColorCellBits
[c
])) - 1;
398 cell_center
[c
] = (cell_min
[c
] + cell_max
[c
]) >> 1;
400 int min_maxdist
= std::numeric_limits
<int>::max();
402 for (int i
= 0; i
< cinfo
->actual_number_of_colors
; ++i
) {
405 for (int c
= 0; c
< ncomp
; ++c
) {
406 int palette_c
= cinfo
->colormap
[c
][i
];
407 int dminc
= 0, dmaxc
;
408 if (palette_c
< cell_min
[c
]) {
409 dminc
= cell_min
[c
] - palette_c
;
410 dmaxc
= cell_max
[c
] - palette_c
;
411 } else if (palette_c
> cell_max
[c
]) {
412 dminc
= palette_c
- cell_max
[c
];
413 dmaxc
= palette_c
- cell_min
[c
];
414 } else if (palette_c
> cell_center
[c
]) {
415 dmaxc
= palette_c
- cell_min
[c
];
417 dmaxc
= cell_max
[c
] - palette_c
;
421 dmin
+= dminc
* dminc
;
422 dmax
+= dmaxc
* dmaxc
;
425 min_maxdist
= std::min(dmax
, min_maxdist
);
427 for (int i
= 0; i
< cinfo
->actual_number_of_colors
; ++i
) {
428 if (mindist
[i
] < min_maxdist
) {
429 candidates
->push_back(i
);
436 void CreateInverseColorMap(j_decompress_ptr cinfo
) {
437 jpeg_decomp_master
* m
= cinfo
->master
;
438 int ncomp
= cinfo
->out_color_components
;
440 for (int c
= 0; c
< ncomp
; ++c
) {
441 num_cells
*= (1 << kNumColorCellBits
[c
]);
443 m
->candidate_lists_
.resize(num_cells
);
445 int next_cell
[kMaxComponents
] = {0};
446 for (int i
= 0; i
< num_cells
; ++i
) {
447 m
->candidate_lists_
[i
].clear();
448 FindCandidatesForCell(cinfo
, ncomp
, next_cell
, &m
->candidate_lists_
[i
]);
450 while (c
> 0 && next_cell
[c
] + 1 == (1 << kNumColorCellBits
[c
])) {
455 m
->regenerate_inverse_colormap_
= false;
458 int LookupColorIndex(j_decompress_ptr cinfo
, JSAMPLE
* pixel
) {
459 jpeg_decomp_master
* m
= cinfo
->master
;
460 int num_channels
= cinfo
->out_color_components
;
462 if (m
->quant_mode_
== 1) {
463 for (int c
= 0; c
< num_channels
; ++c
) {
464 index
+= m
->colormap_lut_
[c
* 256 + pixel
[c
]];
469 for (int c
= num_channels
- 1; c
>= 0; --c
) {
470 cell_idx
+= (pixel
[c
] >> (8 - kNumColorCellBits
[c
])) * stride
;
471 stride
<<= kNumColorCellBits
[c
];
473 JXL_ASSERT(cell_idx
< m
->candidate_lists_
.size());
474 int mindist
= std::numeric_limits
<int>::max();
475 const auto& candidates
= m
->candidate_lists_
[cell_idx
];
476 for (uint8_t i
: candidates
) {
478 for (int c
= 0; c
< num_channels
; ++c
) {
479 int d
= (cinfo
->colormap
[c
][i
] - pixel
[c
]) * kCompW
[c
];
482 if (dist
< mindist
) {
488 JXL_ASSERT(index
< cinfo
->actual_number_of_colors
);
492 void CreateOrderedDitherTables(j_decompress_ptr cinfo
) {
493 jpeg_decomp_master
* m
= cinfo
->master
;
494 static constexpr size_t kDitherSize
= 4;
495 static constexpr size_t kDitherMask
= kDitherSize
- 1;
496 static constexpr float kBaseDitherMatrix
[] = {
502 m
->dither_size_
= kDitherSize
;
503 m
->dither_mask_
= kDitherMask
;
504 size_t ncells
= m
->dither_size_
* m
->dither_size_
;
505 for (int c
= 0; c
< cinfo
->out_color_components
; ++c
) {
506 float spread
= 1.0f
/ (m
->num_colors_
[c
] - 1);
507 float mul
= spread
/ ncells
;
508 float offset
= 0.5f
* spread
;
509 if (m
->dither_
[c
] == nullptr) {
510 m
->dither_
[c
] = Allocate
<float>(cinfo
, ncells
, JPOOL_IMAGE_ALIGNED
);
512 for (size_t idx
= 0; idx
< ncells
; ++idx
) {
513 m
->dither_
[c
][idx
] = kBaseDitherMatrix
[idx
] * mul
- offset
;
518 void InitFSDitherState(j_decompress_ptr cinfo
) {
519 jpeg_decomp_master
* m
= cinfo
->master
;
520 for (int c
= 0; c
< cinfo
->out_color_components
; ++c
) {
521 if (m
->error_row_
[c
] == nullptr) {
523 Allocate
<float>(cinfo
, cinfo
->output_width
, JPOOL_IMAGE_ALIGNED
);
524 m
->error_row_
[c
+ kMaxComponents
] =
525 Allocate
<float>(cinfo
, cinfo
->output_width
, JPOOL_IMAGE_ALIGNED
);
527 memset(m
->error_row_
[c
], 0.0, cinfo
->output_width
* sizeof(float));
528 memset(m
->error_row_
[c
+ kMaxComponents
], 0.0,
529 cinfo
->output_width
* sizeof(float));
533 } // namespace jpegli