/src/libjxl/lib/jxl/modular/transform/palette.cc
Line | Count | Source |
1 | | // Copyright (c) the JPEG XL Project Authors. All rights reserved. |
2 | | // |
3 | | // Use of this source code is governed by a BSD-style |
4 | | // license that can be found in the LICENSE file. |
5 | | |
6 | | #include "lib/jxl/modular/transform/palette.h" |
7 | | |
8 | | #include <jxl/memory_manager.h> |
9 | | |
10 | | #include <algorithm> |
11 | | #include <cstddef> |
12 | | #include <cstdint> |
13 | | #include <utility> |
14 | | #include <vector> |
15 | | |
16 | | #include "lib/jxl/base/common.h" |
17 | | #include "lib/jxl/base/compiler_specific.h" |
18 | | #include "lib/jxl/base/data_parallel.h" |
19 | | #include "lib/jxl/base/status.h" |
20 | | #include "lib/jxl/image.h" |
21 | | #include "lib/jxl/modular/encoding/context_predict.h" |
22 | | #include "lib/jxl/modular/modular_image.h" |
23 | | #include "lib/jxl/modular/options.h" |
24 | | #include "lib/jxl/modular/transform/transform.h" // CheckEqualChannels |
25 | | |
26 | | namespace jxl { |
27 | | |
28 | | Status InvPalette(Image &input, uint32_t begin_c, uint32_t nb_colors, |
29 | | uint32_t nb_deltas, Predictor predictor, |
30 | 4.67k | const weighted::Header &wp_header, ThreadPool *pool) { |
31 | 4.67k | JxlMemoryManager *memory_manager = input.memory_manager(); |
32 | 4.67k | if (input.nb_meta_channels < 1) { |
33 | 0 | return JXL_FAILURE("Error: Palette transform without palette."); |
34 | 0 | } |
35 | 4.67k | int nb = input.channel[0].h; |
36 | 4.67k | uint32_t c0 = begin_c + 1; |
37 | 4.67k | if (c0 >= input.channel.size()) { |
38 | 0 | return JXL_FAILURE("Channel is out of range."); |
39 | 0 | } |
40 | 4.67k | size_t w = input.channel[c0].w; |
41 | 4.67k | size_t h = input.channel[c0].h; |
42 | 4.67k | if (nb < 1) return JXL_FAILURE("Corrupted transforms"); |
43 | 11.3k | for (int i = 1; i < nb; i++) { |
44 | 6.66k | JXL_ASSIGN_OR_RETURN(Channel c, Channel::Create(memory_manager, w, h, |
45 | 6.66k | input.channel[c0].hshift, |
46 | 6.66k | input.channel[c0].vshift)); |
47 | 6.66k | input.channel.insert(input.channel.begin() + c0 + 1, std::move(c)); |
48 | 6.66k | } |
49 | 4.67k | const Channel &palette = input.channel[0]; |
50 | 4.67k | const pixel_type *JXL_RESTRICT p_palette = input.channel[0].Row(0); |
51 | 4.67k | ptrdiff_t onerow = input.channel[0].plane.PixelsPerRow(); |
52 | 4.67k | ptrdiff_t onerow_image = input.channel[c0].plane.PixelsPerRow(); |
53 | 4.67k | const int bit_depth = std::min(input.bitdepth, 24); |
54 | | |
55 | 4.67k | if (w == 0) { |
56 | | // Nothing to do. |
57 | | // Avoid touching "empty" channels with non-zero height. |
58 | 4.65k | } else if (nb_deltas == 0 && predictor == Predictor::Zero) { |
59 | 1.77k | if (nb == 1) { |
60 | 893 | const auto process_row = [&](const uint32_t task, |
61 | 39.9k | size_t /* thread */) -> Status { |
62 | 39.9k | const size_t y = task; |
63 | 39.9k | pixel_type *p = input.channel[c0].Row(y); |
64 | 9.32M | for (size_t x = 0; x < w; x++) { |
65 | 9.28M | const int index = |
66 | 9.28M | Clamp1<int>(p[x], 0, static_cast<pixel_type>(palette.w) - 1); |
67 | 9.28M | p[x] = palette_internal::GetPaletteValue(p_palette, index, /*c=*/0, |
68 | 9.28M | /*palette_size=*/palette.w, |
69 | 9.28M | /*onerow=*/onerow, |
70 | 9.28M | /*bit_depth=*/bit_depth); |
71 | 9.28M | } |
72 | 39.9k | return true; |
73 | 39.9k | }; |
74 | 893 | JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, h, ThreadPool::NoInit, process_row, |
75 | 893 | "UndoChannelPalette")); |
76 | 893 | } else { |
77 | 883 | const auto process_row = [&](const uint32_t task, |
78 | 127k | size_t /* thread */) -> Status { |
79 | 127k | const size_t y = task; |
80 | 127k | std::vector<pixel_type *> p_out(nb); |
81 | 127k | const pixel_type *p_index = input.channel[c0].Row(y); |
82 | 581k | for (int c = 0; c < nb; c++) p_out[c] = input.channel[c0 + c].Row(y); |
83 | 23.4M | for (size_t x = 0; x < w; x++) { |
84 | 23.3M | const int index = p_index[x]; |
85 | 102M | for (int c = 0; c < nb; c++) { |
86 | 78.9M | p_out[c][x] = palette_internal::GetPaletteValue( |
87 | 78.9M | p_palette, index, /*c=*/c, |
88 | 78.9M | /*palette_size=*/palette.w, |
89 | 78.9M | /*onerow=*/onerow, /*bit_depth=*/bit_depth); |
90 | 78.9M | } |
91 | 23.3M | } |
92 | 127k | return true; |
93 | 127k | }; |
94 | 883 | JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, h, ThreadPool::NoInit, process_row, |
95 | 883 | "UndoPalette")); |
96 | 883 | } |
97 | 2.88k | } else { |
98 | | // Parallelized per channel. |
99 | 2.88k | ImageI indices; |
100 | 2.88k | ImageI &plane = input.channel[c0].plane; |
101 | 2.88k | JXL_ASSIGN_OR_RETURN( |
102 | 2.88k | indices, ImageI::Create(memory_manager, plane.xsize(), plane.ysize())); |
103 | 2.88k | plane.Swap(indices); |
104 | 2.88k | if (predictor == Predictor::Weighted) { |
105 | 610 | const auto process_channel = [&](const uint32_t c, |
106 | 2.04k | size_t /* thread */) -> Status { |
107 | 2.04k | Channel &channel = input.channel[c0 + c]; |
108 | 2.04k | weighted::State wp_state(wp_header, channel.w, channel.h); |
109 | 200k | for (size_t y = 0; y < channel.h; y++) { |
110 | 198k | pixel_type *JXL_RESTRICT p = channel.Row(y); |
111 | 198k | const pixel_type *JXL_RESTRICT idx = indices.Row(y); |
112 | 26.2M | for (size_t x = 0; x < channel.w; x++) { |
113 | 26.0M | int index = idx[x]; |
114 | 26.0M | pixel_type_w val = 0; |
115 | 26.0M | const pixel_type palette_entry = palette_internal::GetPaletteValue( |
116 | 26.0M | p_palette, index, /*c=*/c, |
117 | 26.0M | /*palette_size=*/palette.w, /*onerow=*/onerow, |
118 | 26.0M | /*bit_depth=*/bit_depth); |
119 | 26.0M | if (index < static_cast<int32_t>(nb_deltas)) { |
120 | 2.61M | PredictionResult pred = PredictNoTreeWP( |
121 | 2.61M | channel.w, p + x, onerow_image, x, y, predictor, &wp_state); |
122 | 2.61M | val = pred.guess + palette_entry; |
123 | 23.3M | } else { |
124 | 23.3M | val = palette_entry; |
125 | 23.3M | } |
126 | 26.0M | p[x] = val; |
127 | 26.0M | wp_state.UpdateErrors(p[x], x, y, channel.w); |
128 | 26.0M | } |
129 | 198k | } |
130 | 2.04k | return true; |
131 | 2.04k | }; |
132 | 610 | JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, nb, ThreadPool::NoInit, |
133 | 610 | process_channel, "UndoDeltaPaletteWP")); |
134 | 2.27k | } else { |
135 | 2.27k | const auto process_channel = [&](const uint32_t c, |
136 | 5.16k | size_t /* thread */) -> Status { |
137 | 5.16k | Channel &channel = input.channel[c0 + c]; |
138 | 532k | for (size_t y = 0; y < channel.h; y++) { |
139 | 527k | pixel_type *JXL_RESTRICT p = channel.Row(y); |
140 | 527k | const pixel_type *JXL_RESTRICT idx = indices.Row(y); |
141 | 100M | for (size_t x = 0; x < channel.w; x++) { |
142 | 99.5M | int index = idx[x]; |
143 | 99.5M | pixel_type_w val = 0; |
144 | 99.5M | const pixel_type palette_entry = palette_internal::GetPaletteValue( |
145 | 99.5M | p_palette, index, /*c=*/c, |
146 | 99.5M | /*palette_size=*/palette.w, |
147 | 99.5M | /*onerow=*/onerow, /*bit_depth=*/bit_depth); |
148 | 99.5M | if (index < static_cast<int32_t>(nb_deltas)) { |
149 | 64.4M | PredictionResult pred = PredictNoTreeNoWP( |
150 | 64.4M | channel.w, p + x, onerow_image, x, y, predictor); |
151 | 64.4M | val = pred.guess + palette_entry; |
152 | 64.4M | } else { |
153 | 35.0M | val = palette_entry; |
154 | 35.0M | } |
155 | 99.5M | p[x] = val; |
156 | 99.5M | } |
157 | 527k | } |
158 | 5.16k | return true; |
159 | 5.16k | }; |
160 | 2.27k | JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, nb, ThreadPool::NoInit, |
161 | 2.27k | process_channel, "UndoDeltaPaletteNoWP")); |
162 | 2.27k | } |
163 | 2.88k | } |
164 | 4.67k | if (c0 >= input.nb_meta_channels) { |
165 | | // Palette was done on normal channels |
166 | 4.42k | input.nb_meta_channels--; |
167 | 4.42k | } else { |
168 | | // Palette was done on metachannels |
169 | 251 | JXL_ENSURE(static_cast<int>(input.nb_meta_channels) >= 2 - nb); |
170 | 251 | input.nb_meta_channels -= 2 - nb; |
171 | 251 | JXL_ENSURE(begin_c + nb - 1 < input.nb_meta_channels); |
172 | 251 | } |
173 | 4.67k | input.channel.erase(input.channel.begin(), input.channel.begin() + 1); |
174 | 4.67k | return true; |
175 | 4.67k | } |
176 | | |
177 | | Status MetaPalette(Image &input, uint32_t begin_c, uint32_t end_c, |
178 | 4.76k | uint32_t nb_colors, uint32_t nb_deltas, bool lossy) { |
179 | 4.76k | JXL_RETURN_IF_ERROR(CheckEqualChannels(input, begin_c, end_c)); |
180 | 4.74k | JxlMemoryManager *memory_manager = input.memory_manager(); |
181 | | |
182 | 4.74k | size_t nb = end_c - begin_c + 1; |
183 | 4.74k | if (begin_c >= input.nb_meta_channels) { |
184 | | // Palette was done on normal channels |
185 | 4.47k | input.nb_meta_channels++; |
186 | 4.47k | } else { |
187 | | // Palette was done on metachannels |
188 | 268 | JXL_ENSURE(end_c < input.nb_meta_channels); |
189 | | // we remove nb-1 metachannels and add one |
190 | 268 | input.nb_meta_channels += 2 - nb; |
191 | 268 | } |
192 | 4.74k | input.channel.erase(input.channel.begin() + begin_c + 1, |
193 | 4.74k | input.channel.begin() + end_c + 1); |
194 | 4.74k | JXL_ASSIGN_OR_RETURN( |
195 | 4.74k | Channel pch, Channel::Create(memory_manager, nb_colors + nb_deltas, nb)); |
196 | 4.74k | pch.hshift = -1; |
197 | 4.74k | pch.vshift = -1; |
198 | 4.74k | input.channel.insert(input.channel.begin(), std::move(pch)); |
199 | 4.74k | return true; |
200 | 4.74k | } |
201 | | |
202 | | } // namespace jxl |