summaryrefslogtreecommitdiffstats
path: root/third_party/jpeg-xl/lib/jxl/modular
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/encoding/context_predict.h4
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/encoding/enc_ma.cc8
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/encoding/encoding.cc12
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/transform/enc_palette.cc80
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/transform/enc_squeeze.cc12
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/transform/palette.h2
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.cc22
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.h4
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/transform/transform.cc2
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular/transform/transform.h2
-rw-r--r--third_party/jpeg-xl/lib/jxl/modular_test.cc15
11 files changed, 105 insertions, 58 deletions
diff --git a/third_party/jpeg-xl/lib/jxl/modular/encoding/context_predict.h b/third_party/jpeg-xl/lib/jxl/modular/encoding/context_predict.h
index 7bec5128fc..df54a9425e 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/encoding/context_predict.h
+++ b/third_party/jpeg-xl/lib/jxl/modular/encoding/context_predict.h
@@ -63,7 +63,7 @@ struct State {
pixel_type_w pred = 0; // *before* removing the added bits.
std::vector<uint32_t> pred_errors[kNumPredictors];
std::vector<int32_t> error;
- const Header header;
+ const Header &header;
// Allows to approximate division by a number from 1 to 64.
// for (int i = 0; i < 64; i++) divlookup[i] = (1 << 24) / (i + 1);
@@ -82,7 +82,7 @@ struct State {
return static_cast<uint64_t>(x) << kPredExtraBits;
}
- State(Header header, size_t xsize, size_t ysize) : header(header) {
+ State(const Header &header, size_t xsize, size_t ysize) : header(header) {
// Extra margin to avoid out-of-bounds writes.
// All have space for two rows of data.
for (auto &pred_error : pred_errors) {
diff --git a/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_ma.cc b/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_ma.cc
index de629ad038..23100bba8b 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_ma.cc
+++ b/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_ma.cc
@@ -197,7 +197,7 @@ void FindBestSplit(TreeSamples &tree_samples, float threshold,
float rcost = std::numeric_limits<float>::max();
Predictor lpred = Predictor::Zero;
Predictor rpred = Predictor::Zero;
- float Cost() { return lcost + rcost; }
+ float Cost() const { return lcost + rcost; }
};
SplitInfo best_split_static_constant;
@@ -242,14 +242,14 @@ void FindBestSplit(TreeSamples &tree_samples, float threshold,
// The multiplier ranges cut halfway through the current ranges of static
// properties. We do this even if the current node is not a leaf, to
// minimize the number of nodes in the resulting tree.
- for (size_t i = 0; i < mul_info.size(); i++) {
+ for (const auto &mmi : mul_info) {
uint32_t axis;
uint32_t val;
IntersectionType t =
- BoxIntersects(static_prop_range, mul_info[i].range, axis, val);
+ BoxIntersects(static_prop_range, mmi.range, axis, val);
if (t == IntersectionType::kNone) continue;
if (t == IntersectionType::kInside) {
- (*tree)[pos].multiplier = mul_info[i].multiplier;
+ (*tree)[pos].multiplier = mmi.multiplier;
break;
}
if (t == IntersectionType::kPartial) {
diff --git a/third_party/jpeg-xl/lib/jxl/modular/encoding/encoding.cc b/third_party/jpeg-xl/lib/jxl/modular/encoding/encoding.cc
index bb690b74ba..7e7aa019e3 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/encoding/encoding.cc
+++ b/third_party/jpeg-xl/lib/jxl/modular/encoding/encoding.cc
@@ -113,7 +113,7 @@ FlatTree FilterTree(const Tree &global_tree,
}
}
- for (size_t j = 0; j < 2; j++) mark_property(flat.properties[j]);
+ for (int16_t property : flat.properties) mark_property(property);
mark_property(flat.property0);
output.push_back(flat);
}
@@ -159,9 +159,9 @@ Status DecodeModularChannelMAANS(BitReader *br, ANSSymbolReader *reader,
// From here on, tree lookup returns a *clustered* context ID.
// This avoids an extra memory lookup after tree traversal.
- for (size_t i = 0; i < tree.size(); i++) {
- if (tree[i].property0 == -1) {
- tree[i].childID = context_map[tree[i].childID];
+ for (auto &node : tree) {
+ if (node.property0 == -1) {
+ node.childID = context_map[node.childID];
}
}
@@ -538,8 +538,8 @@ Status ModularDecode(BitReader *br, Image &image, GroupHeader &header,
// Don't do/undo transforms if header is incomplete.
header.transforms.clear();
image.transform = header.transforms;
- for (size_t c = 0; c < image.channel.size(); c++) {
- ZeroFillImage(&image.channel[c].plane);
+ for (auto &ch : image.channel) {
+ ZeroFillImage(&ch.plane);
}
return Status(StatusCode::kNotEnoughBytes);
}
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);
}
diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/enc_squeeze.cc b/third_party/jpeg-xl/lib/jxl/modular/transform/enc_squeeze.cc
index 0d924c0ace..7371830743 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/transform/enc_squeeze.cc
+++ b/third_party/jpeg-xl/lib/jxl/modular/transform/enc_squeeze.cc
@@ -124,13 +124,13 @@ Status FwdSqueeze(Image &input, std::vector<SqueezeParams> parameters,
}
// if nothing to do, don't do squeeze
if (parameters.empty()) return false;
- for (size_t i = 0; i < parameters.size(); i++) {
+ for (auto &parameter : parameters) {
JXL_RETURN_IF_ERROR(
- CheckMetaSqueezeParams(parameters[i], input.channel.size()));
- bool horizontal = parameters[i].horizontal;
- bool in_place = parameters[i].in_place;
- uint32_t beginc = parameters[i].begin_c;
- uint32_t endc = parameters[i].begin_c + parameters[i].num_c - 1;
+ CheckMetaSqueezeParams(parameter, input.channel.size()));
+ bool horizontal = parameter.horizontal;
+ bool in_place = parameter.in_place;
+ uint32_t beginc = parameter.begin_c;
+ uint32_t endc = parameter.begin_c + parameter.num_c - 1;
uint32_t offset;
if (in_place) {
offset = endc + 1;
diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/palette.h b/third_party/jpeg-xl/lib/jxl/modular/transform/palette.h
index e0405a2162..2a9e5c71f4 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/transform/palette.h
+++ b/third_party/jpeg-xl/lib/jxl/modular/transform/palette.h
@@ -30,6 +30,8 @@ static constexpr int kSmallCube = 4;
static constexpr int kSmallCubeBits = 2;
// kSmallCube ** 3
static constexpr int kLargeCubeOffset = kSmallCube * kSmallCube * kSmallCube;
+static constexpr int kImplicitPaletteSize =
+ kLargeCubeOffset + kLargeCube * kLargeCube * kLargeCube;
static inline pixel_type Scale(uint64_t value, uint64_t bit_depth,
uint64_t denom) {
diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.cc b/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.cc
index 580829741a..b71c8dc248 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.cc
+++ b/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.cc
@@ -385,21 +385,21 @@ void DefaultSqueezeParameters(std::vector<SqueezeParams> *parameters,
params.in_place = true;
if (!wide) {
- if (h > JXL_MAX_FIRST_PREVIEW_SIZE) {
+ if (h > kMaxFirstPreviewSize) {
params.horizontal = false;
parameters->push_back(params);
h = (h + 1) / 2;
JXL_DEBUG_V(7, "Vertical (%" PRIuS "x%" PRIuS "), ", w, h);
}
}
- while (w > JXL_MAX_FIRST_PREVIEW_SIZE || h > JXL_MAX_FIRST_PREVIEW_SIZE) {
- if (w > JXL_MAX_FIRST_PREVIEW_SIZE) {
+ while (w > kMaxFirstPreviewSize || h > kMaxFirstPreviewSize) {
+ if (w > kMaxFirstPreviewSize) {
params.horizontal = true;
parameters->push_back(params);
w = (w + 1) / 2;
JXL_DEBUG_V(7, "Horizontal (%" PRIuS "x%" PRIuS "), ", w, h);
}
- if (h > JXL_MAX_FIRST_PREVIEW_SIZE) {
+ if (h > kMaxFirstPreviewSize) {
params.horizontal = false;
parameters->push_back(params);
h = (h + 1) / 2;
@@ -424,13 +424,13 @@ Status MetaSqueeze(Image &image, std::vector<SqueezeParams> *parameters) {
DefaultSqueezeParameters(parameters, image);
}
- for (size_t i = 0; i < parameters->size(); i++) {
+ for (auto &parameter : *parameters) {
JXL_RETURN_IF_ERROR(
- CheckMetaSqueezeParams((*parameters)[i], image.channel.size()));
- bool horizontal = (*parameters)[i].horizontal;
- bool in_place = (*parameters)[i].in_place;
- uint32_t beginc = (*parameters)[i].begin_c;
- uint32_t endc = (*parameters)[i].begin_c + (*parameters)[i].num_c - 1;
+ CheckMetaSqueezeParams(parameter, image.channel.size()));
+ bool horizontal = parameter.horizontal;
+ bool in_place = parameter.in_place;
+ uint32_t beginc = parameter.begin_c;
+ uint32_t endc = parameter.begin_c + parameter.num_c - 1;
uint32_t offset;
if (beginc < image.nb_meta_channels) {
@@ -441,7 +441,7 @@ Status MetaSqueeze(Image &image, std::vector<SqueezeParams> *parameters) {
return JXL_FAILURE(
"Invalid squeeze: meta channels require in-place residuals");
}
- image.nb_meta_channels += (*parameters)[i].num_c;
+ image.nb_meta_channels += parameter.num_c;
}
if (in_place) {
offset = endc + 1;
diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.h b/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.h
index bbd16c59c0..f0333da6fd 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.h
+++ b/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.h
@@ -29,10 +29,10 @@
#include "lib/jxl/modular/modular_image.h"
#include "lib/jxl/modular/transform/transform.h"
-#define JXL_MAX_FIRST_PREVIEW_SIZE 8
-
namespace jxl {
+constexpr size_t kMaxFirstPreviewSize = 8;
+
/*
int avg=(A+B)>>1;
int diff=(A-B);
diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/transform.cc b/third_party/jpeg-xl/lib/jxl/modular/transform/transform.cc
index 33f7a10cc9..a609cfb3fb 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/transform/transform.cc
+++ b/third_party/jpeg-xl/lib/jxl/modular/transform/transform.cc
@@ -23,7 +23,7 @@ Transform::Transform(TransformId id) {
}
Status Transform::Inverse(Image &input, const weighted::Header &wp_header,
- ThreadPool *pool) {
+ ThreadPool *pool) const {
JXL_DEBUG_V(6, "Input channels (%" PRIuS ", %" PRIuS " meta): ",
input.channel.size(), input.nb_meta_channels);
switch (id) {
diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/transform.h b/third_party/jpeg-xl/lib/jxl/modular/transform/transform.h
index b68861706f..70c383834a 100644
--- a/third_party/jpeg-xl/lib/jxl/modular/transform/transform.h
+++ b/third_party/jpeg-xl/lib/jxl/modular/transform/transform.h
@@ -134,7 +134,7 @@ class Transform : public Fields {
JXL_FIELDS_NAME(Transform)
Status Inverse(Image &input, const weighted::Header &wp_header,
- ThreadPool *pool = nullptr);
+ ThreadPool *pool = nullptr) const;
Status MetaApply(Image &input);
};
diff --git a/third_party/jpeg-xl/lib/jxl/modular_test.cc b/third_party/jpeg-xl/lib/jxl/modular_test.cc
index bd1a947493..ceebf59c0b 100644
--- a/third_party/jpeg-xl/lib/jxl/modular_test.cc
+++ b/third_party/jpeg-xl/lib/jxl/modular_test.cc
@@ -80,15 +80,15 @@ void TestLosslessGroups(size_t group_size_shift) {
TEST(ModularTest, RoundtripLosslessGroups128) { TestLosslessGroups(0); }
-TEST(ModularTest, JXL_TSAN_SLOW_TEST(RoundtripLosslessGroups512)) {
+JXL_TSAN_SLOW_TEST(ModularTest, RoundtripLosslessGroups512) {
TestLosslessGroups(2);
}
-TEST(ModularTest, JXL_TSAN_SLOW_TEST(RoundtripLosslessGroups1024)) {
+JXL_TSAN_SLOW_TEST(ModularTest, RoundtripLosslessGroups1024) {
TestLosslessGroups(3);
}
-TEST(ModularTest, RoundtripLosslessCustomWP_PermuteRCT) {
+TEST(ModularTest, RoundtripLosslessCustomWpPermuteRCT) {
const std::vector<uint8_t> orig =
ReadTestData("external/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
TestImage t;
@@ -144,6 +144,7 @@ TEST(ModularTest, RoundtripLossyDeltaPaletteWP) {
cparams.SetLossless();
cparams.lossy_palette = true;
cparams.palette_colors = 0;
+ // TODO(jon): this is currently ignored, and Avg4 is always used instead
cparams.options.predictor = jxl::Predictor::Weighted;
CodecInOut io_out;
@@ -154,12 +155,12 @@ TEST(ModularTest, RoundtripLossyDeltaPaletteWP) {
size_t compressed_size;
JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io_out, _, &compressed_size));
- EXPECT_LE(compressed_size, 7000u);
+ EXPECT_LE(compressed_size, 6500u);
EXPECT_SLIGHTLY_BELOW(
ButteraugliDistance(io.frames, io_out.frames, ButteraugliParams(),
*JxlGetDefaultCms(),
/*distmap=*/nullptr),
- 10.1);
+ 1.5);
}
TEST(ModularTest, RoundtripLossy) {
@@ -346,8 +347,8 @@ TEST_P(ModularTestParam, RoundtripLossless) {
const float* in = io.Main().color()->PlaneRow(c, y);
const float* out = io2.Main().color()->PlaneRow(c, y);
for (size_t x = 0; x < xsize; x++) {
- uint32_t uin = in[x] * factor + 0.5;
- uint32_t uout = out[x] * factor + 0.5;
+ uint32_t uin = std::lroundf(in[x] * factor);
+ uint32_t uout = std::lroundf(out[x] * factor);
// check that the integer values are identical
if (uin != uout) different++;
}