summaryrefslogtreecommitdiffstats
path: root/third_party/jpeg-xl/lib/jxl/modular/transform/enc_palette.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/transform/enc_palette.cc80
1 files changed, 62 insertions, 18 deletions
diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/enc_palette.cc b/third_party/jpeg-xl/lib/jxl/modular/transform/enc_palette.cc
index 24c64f5aad..7f9399d3c4 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/transform/enc_palette.cc
+++ b/third_party/jpeg-xl/lib/jxl/modular/transform/enc_palette.cc
@@ -68,8 +68,8 @@ static int QuantizeColorToImplicitPaletteIndex(
int index = 0;
if (high_quality) {
int multiplier = 1;
- for (size_t c = 0; c < color.size(); c++) {
- int quantized = ((kLargeCube - 1) * color[c] + (1 << (bit_depth - 1))) /
+ for (int value : color) {
+ int quantized = ((kLargeCube - 1) * value + (1 << (bit_depth - 1))) /
((1 << bit_depth) - 1);
JXL_ASSERT((quantized % kLargeCube) == quantized);
index += quantized * multiplier;
@@ -78,8 +78,7 @@ static int QuantizeColorToImplicitPaletteIndex(
return index + palette_size + kLargeCubeOffset;
} else {
int multiplier = 1;
- for (size_t c = 0; c < color.size(); c++) {
- int value = color[c];
+ for (int value : color) {
value -= 1 << (std::max(0, bit_depth - 3));
value = std::max(0, value);
int quantized = ((kLargeCube - 1) * value + (1 << (bit_depth - 1))) /
@@ -171,6 +170,7 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c,
size_t w = input.channel[begin_c].w;
size_t h = input.channel[begin_c].h;
+ if (!lossy && nb_colors < 2) return false;
if (!lossy && nb == 1) {
// Channel palette special case
@@ -321,6 +321,20 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c,
}
}
+ std::map<std::vector<pixel_type>, bool> implicit_color;
+ std::vector<std::vector<pixel_type>> implicit_colors;
+ implicit_colors.reserve(palette_internal::kImplicitPaletteSize);
+ for (size_t k = 0; k < palette_internal::kImplicitPaletteSize; k++) {
+ for (size_t i = 0; i < nb; i++) {
+ color[i] = palette_internal::GetPaletteValue(nullptr, k, i, 0, 0,
+ input.bitdepth);
+ }
+ implicit_color[color] = true;
+ implicit_colors.push_back(color);
+ }
+
+ std::map<std::vector<pixel_type>, size_t> color_freq_map;
+ uint32_t implicit_colors_used = 0;
for (size_t y = 0; y < h; y++) {
for (uint32_t c = 0; c < nb; c++) {
p_in[c] = input.channel[begin_c + c].Row(y);
@@ -332,15 +346,39 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c,
}
const bool new_color = candidate_palette.insert(color).second;
if (new_color) {
- candidate_palette_imageorder.push_back(color);
- }
- if (candidate_palette.size() > nb_colors) {
- return false; // too many colors
+ if (implicit_color[color]) {
+ implicit_colors_used++;
+ } else {
+ candidate_palette_imageorder.push_back(color);
+ if (candidate_palette_imageorder.size() > nb_colors) {
+ return false; // too many colors
+ }
+ }
}
+ color_freq_map[color] += 1;
}
}
- nb_colors = nb_deltas + candidate_palette.size();
+ nb_colors = nb_deltas + candidate_palette_imageorder.size();
+
+ // not useful to make a single-color palette
+ if (!lossy && nb_colors + implicit_colors_used == 1) return false;
+ // TODO(jon): if this happens (e.g. solid white group), special-case it for
+ // faster encode
+
+ for (size_t k = 0; k < palette_internal::kImplicitPaletteSize; k++) {
+ color = implicit_colors[k];
+ // still add the color to the explicit palette if it is frequent enough
+ if (color_freq_map[color] > 10) {
+ nb_colors++;
+ candidate_palette_imageorder.push_back(color);
+ }
+ }
+ for (size_t k = 0; k < palette_internal::kImplicitPaletteSize; k++) {
+ color = implicit_colors[k];
+ inv_palette[color] = nb_colors + k;
+ }
+
JXL_DEBUG_V(6, "Channels %i-%i can be represented using a %i-color palette.",
begin_c, end_c, nb_colors);
@@ -360,25 +398,33 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c,
}
}
}
-
+ // Separate the palette in two buckets, first the common colors, then the
+ // rare colors.
+ // Within each bucket, the colors are sorted on luma (times alpha).
+ float freq_threshold = 4; // arbitrary threshold
int x = 0;
if (ordered && nb >= 3) {
JXL_DEBUG_V(7, "Palette of %i colors, using luma order", nb_colors);
// sort on luma (multiplied by alpha if available)
std::sort(candidate_palette_imageorder.begin(),
candidate_palette_imageorder.end(),
- [](std::vector<pixel_type> ap, std::vector<pixel_type> bp) {
+ [&](std::vector<pixel_type> ap, std::vector<pixel_type> bp) {
float ay;
float by;
ay = (0.299f * ap[0] + 0.587f * ap[1] + 0.114f * ap[2] + 0.1f);
if (ap.size() > 3) ay *= 1.f + ap[3];
by = (0.299f * bp[0] + 0.587f * bp[1] + 0.114f * bp[2] + 0.1f);
if (bp.size() > 3) by *= 1.f + bp[3];
+ // put common colors first, transparent dark to opaque bright,
+ // then rare colors, bright to dark
+ ay = color_freq_map[ap] > freq_threshold ? -ay : ay;
+ by = color_freq_map[bp] > freq_threshold ? -by : by;
return ay < by;
});
} else {
JXL_DEBUG_V(7, "Palette of %i colors, using image order", nb_colors);
}
+
for (auto pcol : candidate_palette_imageorder) {
JXL_DEBUG_V(9, " Color %i : ", x);
for (size_t i = 0; i < nb; i++) {
@@ -398,10 +444,10 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c,
// beneficial for both precision and encoding speed.
std::vector<std::vector<float>> error_row[3];
if (lossy) {
- for (int i = 0; i < 3; ++i) {
- error_row[i].resize(nb);
+ for (auto &row : error_row) {
+ row.resize(nb);
for (size_t c = 0; c < nb; ++c) {
- error_row[i][c].resize(w + 4);
+ row[c].resize(w + 4);
}
}
}
@@ -424,13 +470,11 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c,
std::vector<pixel_type> ideal_residual(nb, 0);
std::vector<pixel_type> quantized_val(nb);
std::vector<pixel_type> predictions(nb);
- static const double kDiffusionMultiplier[] = {0.55, 0.75};
- for (int diffusion_index = 0; diffusion_index < 2; ++diffusion_index) {
+ for (double diffusion_multiplier : {0.55, 0.75}) {
for (size_t c = 0; c < nb; c++) {
color_with_error[c] =
p_in[c][x] + (palette_iteration_data.final_run ? 1 : 0) *
- kDiffusionMultiplier[diffusion_index] *
- error_row[0][c][x + 2];
+ diffusion_multiplier * error_row[0][c][x + 2];
color[c] = Clamp1(lroundf(color_with_error[c]), 0l,
(1l << input.bitdepth) - 1);
}