/src/libjxl/lib/jxl/modular/encoding/context_predict.h
Line | Count | Source (jump to first uncovered line) |
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 | | #ifndef LIB_JXL_MODULAR_ENCODING_CONTEXT_PREDICT_H_ |
7 | | #define LIB_JXL_MODULAR_ENCODING_CONTEXT_PREDICT_H_ |
8 | | |
9 | | #include <utility> |
10 | | #include <vector> |
11 | | |
12 | | #include "lib/jxl/fields.h" |
13 | | #include "lib/jxl/modular/modular_image.h" |
14 | | #include "lib/jxl/modular/options.h" |
15 | | |
16 | | namespace jxl { |
17 | | |
18 | | namespace weighted { |
19 | | constexpr static size_t kNumPredictors = 4; |
20 | | constexpr static int64_t kPredExtraBits = 3; |
21 | | constexpr static int64_t kPredictionRound = ((1 << kPredExtraBits) >> 1) - 1; |
22 | | constexpr static size_t kNumProperties = 1; |
23 | | |
24 | | struct Header : public Fields { |
25 | | JXL_FIELDS_NAME(WeightedPredictorHeader) |
26 | | // TODO(janwas): move to cc file, avoid including fields.h. |
27 | 0 | Header() { Bundle::Init(this); } |
28 | | |
29 | 0 | Status VisitFields(Visitor *JXL_RESTRICT visitor) override { |
30 | 0 | if (visitor->AllDefault(*this, &all_default)) { |
31 | | // Overwrite all serialized fields, but not any nonserialized_*. |
32 | 0 | visitor->SetDefault(this); |
33 | 0 | return true; |
34 | 0 | } |
35 | 0 | auto visit_p = [visitor](pixel_type val, pixel_type *p) { |
36 | 0 | uint32_t up = *p; |
37 | 0 | JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(5, val, &up)); |
38 | 0 | *p = up; |
39 | 0 | return Status(true); |
40 | 0 | }; |
41 | 0 | JXL_QUIET_RETURN_IF_ERROR(visit_p(16, &p1C)); |
42 | 0 | JXL_QUIET_RETURN_IF_ERROR(visit_p(10, &p2C)); |
43 | 0 | JXL_QUIET_RETURN_IF_ERROR(visit_p(7, &p3Ca)); |
44 | 0 | JXL_QUIET_RETURN_IF_ERROR(visit_p(7, &p3Cb)); |
45 | 0 | JXL_QUIET_RETURN_IF_ERROR(visit_p(7, &p3Cc)); |
46 | 0 | JXL_QUIET_RETURN_IF_ERROR(visit_p(0, &p3Cd)); |
47 | 0 | JXL_QUIET_RETURN_IF_ERROR(visit_p(0, &p3Ce)); |
48 | 0 | JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(4, 0xd, &w[0])); |
49 | 0 | JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(4, 0xc, &w[1])); |
50 | 0 | JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(4, 0xc, &w[2])); |
51 | 0 | JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(4, 0xc, &w[3])); |
52 | 0 | return true; |
53 | 0 | } Unexecuted instantiation: jxl::weighted::Header::VisitFields(jxl::Visitor*) Unexecuted instantiation: jxl::weighted::Header::VisitFields(jxl::Visitor*) |
54 | | |
55 | | bool all_default; |
56 | | pixel_type p1C = 0, p2C = 0, p3Ca = 0, p3Cb = 0, p3Cc = 0, p3Cd = 0, p3Ce = 0; |
57 | | uint32_t w[kNumPredictors] = {}; |
58 | | }; |
59 | | |
60 | | struct State { |
61 | | pixel_type_w prediction[kNumPredictors] = {}; |
62 | | pixel_type_w pred = 0; // *before* removing the added bits. |
63 | | std::vector<uint32_t> pred_errors[kNumPredictors]; |
64 | | std::vector<int32_t> error; |
65 | | const Header header; |
66 | | |
67 | | // Allows to approximate division by a number from 1 to 64. |
68 | | // for (int i = 0; i < 64; i++) divlookup[i] = (1 << 24) / (i + 1); |
69 | | |
70 | | const uint32_t divlookup[64] = { |
71 | | 16777216, 8388608, 5592405, 4194304, 3355443, 2796202, 2396745, 2097152, |
72 | | 1864135, 1677721, 1525201, 1398101, 1290555, 1198372, 1118481, 1048576, |
73 | | 986895, 932067, 883011, 838860, 798915, 762600, 729444, 699050, |
74 | | 671088, 645277, 621378, 599186, 578524, 559240, 541200, 524288, |
75 | | 508400, 493447, 479349, 466033, 453438, 441505, 430185, 419430, |
76 | | 409200, 399457, 390167, 381300, 372827, 364722, 356962, 349525, |
77 | | 342392, 335544, 328965, 322638, 316551, 310689, 305040, 299593, |
78 | | 294337, 289262, 284359, 279620, 275036, 270600, 266305, 262144}; |
79 | | |
80 | 0 | constexpr static pixel_type_w AddBits(pixel_type_w x) { |
81 | 0 | return uint64_t(x) << kPredExtraBits; |
82 | 0 | } |
83 | | |
84 | 0 | State(Header header, size_t xsize, size_t ysize) : header(header) { |
85 | | // Extra margin to avoid out-of-bounds writes. |
86 | | // All have space for two rows of data. |
87 | 0 | for (size_t i = 0; i < 4; i++) { |
88 | 0 | pred_errors[i].resize((xsize + 2) * 2); |
89 | 0 | } |
90 | 0 | error.resize((xsize + 2) * 2); |
91 | 0 | } |
92 | | |
93 | | // Approximates 4+(maxweight<<24)/(x+1), avoiding division |
94 | 0 | JXL_INLINE uint32_t ErrorWeight(uint64_t x, uint32_t maxweight) const { |
95 | 0 | int shift = static_cast<int>(FloorLog2Nonzero(x + 1)) - 5; |
96 | 0 | if (shift < 0) shift = 0; |
97 | 0 | return 4 + ((maxweight * divlookup[x >> shift]) >> shift); |
98 | 0 | } |
99 | | |
100 | | // Approximates the weighted average of the input values with the given |
101 | | // weights, avoiding division. Weights must sum to at least 16. |
102 | | JXL_INLINE pixel_type_w |
103 | | WeightedAverage(const pixel_type_w *JXL_RESTRICT p, |
104 | 0 | std::array<uint32_t, kNumPredictors> w) const { |
105 | 0 | uint32_t weight_sum = 0; |
106 | 0 | for (size_t i = 0; i < kNumPredictors; i++) { |
107 | 0 | weight_sum += w[i]; |
108 | 0 | } |
109 | 0 | JXL_DASSERT(weight_sum > 15); |
110 | 0 | uint32_t log_weight = FloorLog2Nonzero(weight_sum); // at least 4. |
111 | 0 | weight_sum = 0; |
112 | 0 | for (size_t i = 0; i < kNumPredictors; i++) { |
113 | 0 | w[i] >>= log_weight - 4; |
114 | 0 | weight_sum += w[i]; |
115 | 0 | } |
116 | | // for rounding. |
117 | 0 | pixel_type_w sum = (weight_sum >> 1) - 1; |
118 | 0 | for (size_t i = 0; i < kNumPredictors; i++) { |
119 | 0 | sum += p[i] * w[i]; |
120 | 0 | } |
121 | 0 | return (sum * divlookup[weight_sum - 1]) >> 24; |
122 | 0 | } Unexecuted instantiation: jxl::weighted::State::WeightedAverage(long const*, std::__1::array<unsigned int, 4ul>) const Unexecuted instantiation: jxl::weighted::State::WeightedAverage(long const*, std::__1::array<unsigned int, 4ul>) const |
123 | | |
124 | | template <bool compute_properties> |
125 | | JXL_INLINE pixel_type_w Predict(size_t x, size_t y, size_t xsize, |
126 | | pixel_type_w N, pixel_type_w W, |
127 | | pixel_type_w NE, pixel_type_w NW, |
128 | | pixel_type_w NN, Properties *properties, |
129 | 0 | size_t offset) { |
130 | 0 | size_t cur_row = y & 1 ? 0 : (xsize + 2); |
131 | 0 | size_t prev_row = y & 1 ? (xsize + 2) : 0; |
132 | 0 | size_t pos_N = prev_row + x; |
133 | 0 | size_t pos_NE = x < xsize - 1 ? pos_N + 1 : pos_N; |
134 | 0 | size_t pos_NW = x > 0 ? pos_N - 1 : pos_N; |
135 | 0 | std::array<uint32_t, kNumPredictors> weights; |
136 | 0 | for (size_t i = 0; i < kNumPredictors; i++) { |
137 | | // pred_errors[pos_N] also contains the error of pixel W. |
138 | | // pred_errors[pos_NW] also contains the error of pixel WW. |
139 | 0 | weights[i] = pred_errors[i][pos_N] + pred_errors[i][pos_NE] + |
140 | 0 | pred_errors[i][pos_NW]; |
141 | 0 | weights[i] = ErrorWeight(weights[i], header.w[i]); |
142 | 0 | } |
143 | |
|
144 | 0 | N = AddBits(N); |
145 | 0 | W = AddBits(W); |
146 | 0 | NE = AddBits(NE); |
147 | 0 | NW = AddBits(NW); |
148 | 0 | NN = AddBits(NN); |
149 | |
|
150 | 0 | pixel_type_w teW = x == 0 ? 0 : error[cur_row + x - 1]; |
151 | 0 | pixel_type_w teN = error[pos_N]; |
152 | 0 | pixel_type_w teNW = error[pos_NW]; |
153 | 0 | pixel_type_w sumWN = teN + teW; |
154 | 0 | pixel_type_w teNE = error[pos_NE]; |
155 | |
|
156 | 0 | if (compute_properties) { |
157 | 0 | pixel_type_w p = teW; |
158 | 0 | if (std::abs(teN) > std::abs(p)) p = teN; |
159 | 0 | if (std::abs(teNW) > std::abs(p)) p = teNW; |
160 | 0 | if (std::abs(teNE) > std::abs(p)) p = teNE; |
161 | 0 | (*properties)[offset++] = p; |
162 | 0 | } |
163 | |
|
164 | 0 | prediction[0] = W + NE - N; |
165 | 0 | prediction[1] = N - (((sumWN + teNE) * header.p1C) >> 5); |
166 | 0 | prediction[2] = W - (((sumWN + teNW) * header.p2C) >> 5); |
167 | 0 | prediction[3] = |
168 | 0 | N - ((teNW * header.p3Ca + teN * header.p3Cb + teNE * header.p3Cc + |
169 | 0 | (NN - N) * header.p3Cd + (NW - W) * header.p3Ce) >> |
170 | 0 | 5); |
171 | |
|
172 | 0 | pred = WeightedAverage(prediction, weights); |
173 | | |
174 | | // If all three have the same sign, skip clamping. |
175 | 0 | if (((teN ^ teW) | (teN ^ teNW)) > 0) { |
176 | 0 | return (pred + kPredictionRound) >> kPredExtraBits; |
177 | 0 | } |
178 | | |
179 | | // Otherwise, clamp to min/max of neighbouring pixels (just W, NE, N). |
180 | 0 | pixel_type_w mx = std::max(W, std::max(NE, N)); |
181 | 0 | pixel_type_w mn = std::min(W, std::min(NE, N)); |
182 | 0 | pred = std::max(mn, std::min(mx, pred)); |
183 | 0 | return (pred + kPredictionRound) >> kPredExtraBits; |
184 | 0 | } Unexecuted instantiation: long jxl::weighted::State::Predict<false>(unsigned long, unsigned long, unsigned long, long, long, long, long, long, std::__1::vector<int, std::__1::allocator<int> >*, unsigned long) Unexecuted instantiation: long jxl::weighted::State::Predict<true>(unsigned long, unsigned long, unsigned long, long, long, long, long, long, std::__1::vector<int, std::__1::allocator<int> >*, unsigned long) |
185 | | |
186 | | JXL_INLINE void UpdateErrors(pixel_type_w val, size_t x, size_t y, |
187 | 0 | size_t xsize) { |
188 | 0 | size_t cur_row = y & 1 ? 0 : (xsize + 2); |
189 | 0 | size_t prev_row = y & 1 ? (xsize + 2) : 0; |
190 | 0 | val = AddBits(val); |
191 | 0 | error[cur_row + x] = pred - val; |
192 | 0 | for (size_t i = 0; i < kNumPredictors; i++) { |
193 | 0 | pixel_type_w err = |
194 | 0 | (std::abs(prediction[i] - val) + kPredictionRound) >> kPredExtraBits; |
195 | | // For predicting in the next row. |
196 | 0 | pred_errors[i][cur_row + x] = err; |
197 | | // Add the error on this pixel to the error on the NE pixel. This has the |
198 | | // effect of adding the error on this pixel to the E and EE pixels. |
199 | 0 | pred_errors[i][prev_row + x + 1] += err; |
200 | 0 | } |
201 | 0 | } |
202 | | }; |
203 | | |
204 | | // Encoder helper function to set the parameters to some presets. |
205 | 0 | inline void PredictorMode(int i, Header *header) { |
206 | 0 | switch (i) { |
207 | 0 | case 0: |
208 | 0 | // ~ lossless16 predictor |
209 | 0 | header->w[0] = 0xd; |
210 | 0 | header->w[1] = 0xc; |
211 | 0 | header->w[2] = 0xc; |
212 | 0 | header->w[3] = 0xc; |
213 | 0 | header->p1C = 16; |
214 | 0 | header->p2C = 10; |
215 | 0 | header->p3Ca = 7; |
216 | 0 | header->p3Cb = 7; |
217 | 0 | header->p3Cc = 7; |
218 | 0 | header->p3Cd = 0; |
219 | 0 | header->p3Ce = 0; |
220 | 0 | break; |
221 | 0 | case 1: |
222 | 0 | // ~ default lossless8 predictor |
223 | 0 | header->w[0] = 0xd; |
224 | 0 | header->w[1] = 0xc; |
225 | 0 | header->w[2] = 0xc; |
226 | 0 | header->w[3] = 0xb; |
227 | 0 | header->p1C = 8; |
228 | 0 | header->p2C = 8; |
229 | 0 | header->p3Ca = 4; |
230 | 0 | header->p3Cb = 0; |
231 | 0 | header->p3Cc = 3; |
232 | 0 | header->p3Cd = 23; |
233 | 0 | header->p3Ce = 2; |
234 | 0 | break; |
235 | 0 | case 2: |
236 | 0 | // ~ west lossless8 predictor |
237 | 0 | header->w[0] = 0xd; |
238 | 0 | header->w[1] = 0xc; |
239 | 0 | header->w[2] = 0xd; |
240 | 0 | header->w[3] = 0xc; |
241 | 0 | header->p1C = 10; |
242 | 0 | header->p2C = 9; |
243 | 0 | header->p3Ca = 7; |
244 | 0 | header->p3Cb = 0; |
245 | 0 | header->p3Cc = 0; |
246 | 0 | header->p3Cd = 16; |
247 | 0 | header->p3Ce = 9; |
248 | 0 | break; |
249 | 0 | case 3: |
250 | 0 | // ~ north lossless8 predictor |
251 | 0 | header->w[0] = 0xd; |
252 | 0 | header->w[1] = 0xd; |
253 | 0 | header->w[2] = 0xc; |
254 | 0 | header->w[3] = 0xc; |
255 | 0 | header->p1C = 16; |
256 | 0 | header->p2C = 8; |
257 | 0 | header->p3Ca = 0; |
258 | 0 | header->p3Cb = 16; |
259 | 0 | header->p3Cc = 0; |
260 | 0 | header->p3Cd = 23; |
261 | 0 | header->p3Ce = 0; |
262 | 0 | break; |
263 | 0 | case 4: |
264 | 0 | default: |
265 | 0 | // something else, because why not |
266 | 0 | header->w[0] = 0xd; |
267 | 0 | header->w[1] = 0xc; |
268 | 0 | header->w[2] = 0xc; |
269 | 0 | header->w[3] = 0xc; |
270 | 0 | header->p1C = 10; |
271 | 0 | header->p2C = 10; |
272 | 0 | header->p3Ca = 5; |
273 | 0 | header->p3Cb = 5; |
274 | 0 | header->p3Cc = 5; |
275 | 0 | header->p3Cd = 12; |
276 | 0 | header->p3Ce = 4; |
277 | 0 | break; |
278 | 0 | } |
279 | 0 | } |
280 | | } // namespace weighted |
281 | | |
282 | | // Stores a node and its two children at the same time. This significantly |
283 | | // reduces the number of branches needed during decoding. |
284 | | struct FlatDecisionNode { |
285 | | // Property + splitval of the top node. |
286 | | int32_t property0; // -1 if leaf. |
287 | | union { |
288 | | PropertyVal splitval0; |
289 | | Predictor predictor; |
290 | | }; |
291 | | // Property+splitval of the two child nodes. |
292 | | union { |
293 | | PropertyVal splitvals[2]; |
294 | | int32_t multiplier; |
295 | | }; |
296 | | uint32_t childID; // childID is ctx id if leaf. |
297 | | union { |
298 | | int16_t properties[2]; |
299 | | int32_t predictor_offset; |
300 | | }; |
301 | | }; |
302 | | using FlatTree = std::vector<FlatDecisionNode>; |
303 | | |
304 | | class MATreeLookup { |
305 | | public: |
306 | 0 | explicit MATreeLookup(const FlatTree &tree) : nodes_(tree) {} |
307 | | struct LookupResult { |
308 | | uint32_t context; |
309 | | Predictor predictor; |
310 | | int32_t offset; |
311 | | int32_t multiplier; |
312 | | }; |
313 | 0 | JXL_INLINE LookupResult Lookup(const Properties &properties) const { |
314 | 0 | uint32_t pos = 0; |
315 | 0 | while (true) { |
316 | 0 | #define TRAVERSE_THE_TREE \ |
317 | 0 | { \ |
318 | 0 | const FlatDecisionNode &node = nodes_[pos]; \ |
319 | 0 | if (node.property0 < 0) { \ |
320 | 0 | return {node.childID, node.predictor, node.predictor_offset, \ |
321 | 0 | node.multiplier}; \ |
322 | 0 | } \ |
323 | 0 | bool p0 = properties[node.property0] <= node.splitval0; \ |
324 | 0 | uint32_t off0 = properties[node.properties[0]] <= node.splitvals[0]; \ |
325 | 0 | uint32_t off1 = 2 | (properties[node.properties[1]] <= node.splitvals[1]); \ |
326 | 0 | pos = node.childID + (p0 ? off1 : off0); \ |
327 | 0 | } |
328 | |
|
329 | 0 | TRAVERSE_THE_TREE; |
330 | 0 | TRAVERSE_THE_TREE; |
331 | 0 | } |
332 | 0 | } Unexecuted instantiation: jxl::MATreeLookup::Lookup(std::__1::vector<int, std::__1::allocator<int> > const&) const Unexecuted instantiation: jxl::MATreeLookup::Lookup(std::__1::vector<int, std::__1::allocator<int> > const&) const |
333 | | |
334 | | private: |
335 | | const FlatTree &nodes_; |
336 | | }; |
337 | | |
338 | | static constexpr size_t kExtraPropsPerChannel = 4; |
339 | | static constexpr size_t kNumNonrefProperties = |
340 | | kNumStaticProperties + 13 + weighted::kNumProperties; |
341 | | |
342 | | constexpr size_t kWPProp = kNumNonrefProperties - weighted::kNumProperties; |
343 | | constexpr size_t kGradientProp = 9; |
344 | | |
345 | | // Clamps gradient to the min/max of n, w (and l, implicitly). |
346 | | static JXL_INLINE int32_t ClampedGradient(const int32_t n, const int32_t w, |
347 | 0 | const int32_t l) { |
348 | 0 | const int32_t m = std::min(n, w); |
349 | 0 | const int32_t M = std::max(n, w); |
350 | | // The end result of this operation doesn't overflow or underflow if the |
351 | | // result is between m and M, but the intermediate value may overflow, so we |
352 | | // do the intermediate operations in uint32_t and check later if we had an |
353 | | // overflow or underflow condition comparing m, M and l directly. |
354 | | // grad = M + m - l = n + w - l |
355 | 0 | const int32_t grad = |
356 | 0 | static_cast<int32_t>(static_cast<uint32_t>(n) + static_cast<uint32_t>(w) - |
357 | 0 | static_cast<uint32_t>(l)); |
358 | | // We use two sets of ternary operators to force the evaluation of them in |
359 | | // any case, allowing the compiler to avoid branches and use cmovl/cmovg in |
360 | | // x86. |
361 | 0 | const int32_t grad_clamp_M = (l < m) ? M : grad; |
362 | 0 | return (l > M) ? m : grad_clamp_M; |
363 | 0 | } Unexecuted instantiation: decode.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: encoding.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: modular_image.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: transform.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: quant_weights.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: dec_frame.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: dec_modular.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: dec_patch_dictionary.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: palette.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: rct.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: squeeze.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: coeff_order.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: encode.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_jpeg_data.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_frame.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_group.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_heuristics.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_icc_codec.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_modular.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_patch_dictionary.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_quant_weights.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_splines.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_toc.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_encoding.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_ma.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_transform.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_ac_strategy.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_adaptive_quantization.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_ans.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_ar_control_field.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_cache.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_chroma_from_luma.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_cluster.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_coeff_order.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_context_map.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_debug_image.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_dot_dictionary.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_entropy_coder.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_palette.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_rct.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_squeeze.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: enc_detect_dots.cc:jxl::ClampedGradient(int, int, int) Unexecuted instantiation: fields_fuzzer.cc:jxl::ClampedGradient(int, int, int) |
364 | | |
365 | 0 | inline pixel_type_w Select(pixel_type_w a, pixel_type_w b, pixel_type_w c) { |
366 | 0 | pixel_type_w p = a + b - c; |
367 | 0 | pixel_type_w pa = std::abs(p - a); |
368 | 0 | pixel_type_w pb = std::abs(p - b); |
369 | 0 | return pa < pb ? a : b; |
370 | 0 | } |
371 | | |
372 | | inline void PrecomputeReferences(const Channel &ch, size_t y, |
373 | | const Image &image, uint32_t i, |
374 | 0 | Channel *references) { |
375 | 0 | ZeroFillImage(&references->plane); |
376 | 0 | uint32_t offset = 0; |
377 | 0 | size_t num_extra_props = references->w; |
378 | 0 | intptr_t onerow = references->plane.PixelsPerRow(); |
379 | 0 | for (int32_t j = static_cast<int32_t>(i) - 1; |
380 | 0 | j >= 0 && offset < num_extra_props; j--) { |
381 | 0 | if (image.channel[j].w != image.channel[i].w || |
382 | 0 | image.channel[j].h != image.channel[i].h) { |
383 | 0 | continue; |
384 | 0 | } |
385 | 0 | if (image.channel[j].hshift != image.channel[i].hshift) continue; |
386 | 0 | if (image.channel[j].vshift != image.channel[i].vshift) continue; |
387 | 0 | pixel_type *JXL_RESTRICT rp = references->Row(0) + offset; |
388 | 0 | const pixel_type *JXL_RESTRICT rpp = image.channel[j].Row(y); |
389 | 0 | const pixel_type *JXL_RESTRICT rpprev = image.channel[j].Row(y ? y - 1 : 0); |
390 | 0 | for (size_t x = 0; x < ch.w; x++, rp += onerow) { |
391 | 0 | pixel_type_w v = rpp[x]; |
392 | 0 | rp[0] = std::abs(v); |
393 | 0 | rp[1] = v; |
394 | 0 | pixel_type_w vleft = (x ? rpp[x - 1] : 0); |
395 | 0 | pixel_type_w vtop = (y ? rpprev[x] : vleft); |
396 | 0 | pixel_type_w vtopleft = (x && y ? rpprev[x - 1] : vleft); |
397 | 0 | pixel_type_w vpredicted = ClampedGradient(vleft, vtop, vtopleft); |
398 | 0 | rp[2] = std::abs(v - vpredicted); |
399 | 0 | rp[3] = v - vpredicted; |
400 | 0 | } |
401 | |
|
402 | 0 | offset += kExtraPropsPerChannel; |
403 | 0 | } |
404 | 0 | } |
405 | | |
406 | | struct PredictionResult { |
407 | | int context = 0; |
408 | | pixel_type_w guess = 0; |
409 | | Predictor predictor; |
410 | | int32_t multiplier; |
411 | | }; |
412 | | |
413 | | inline void InitPropsRow( |
414 | | Properties *p, |
415 | | const std::array<pixel_type, kNumStaticProperties> &static_props, |
416 | 0 | const int y) { |
417 | 0 | for (size_t i = 0; i < kNumStaticProperties; i++) { |
418 | 0 | (*p)[i] = static_props[i]; |
419 | 0 | } |
420 | 0 | (*p)[2] = y; |
421 | 0 | (*p)[9] = 0; // local gradient. |
422 | 0 | } |
423 | | |
424 | | namespace detail { |
425 | | enum PredictorMode { |
426 | | kUseTree = 1, |
427 | | kUseWP = 2, |
428 | | kForceComputeProperties = 4, |
429 | | kAllPredictions = 8, |
430 | | kNoEdgeCases = 16 |
431 | | }; |
432 | | |
433 | | JXL_INLINE pixel_type_w PredictOne(Predictor p, pixel_type_w left, |
434 | | pixel_type_w top, pixel_type_w toptop, |
435 | | pixel_type_w topleft, pixel_type_w topright, |
436 | | pixel_type_w leftleft, |
437 | | pixel_type_w toprightright, |
438 | 0 | pixel_type_w wp_pred) { |
439 | 0 | switch (p) { |
440 | 0 | case Predictor::Zero: |
441 | 0 | return pixel_type_w{0}; |
442 | 0 | case Predictor::Left: |
443 | 0 | return left; |
444 | 0 | case Predictor::Top: |
445 | 0 | return top; |
446 | 0 | case Predictor::Select: |
447 | 0 | return Select(left, top, topleft); |
448 | 0 | case Predictor::Weighted: |
449 | 0 | return wp_pred; |
450 | 0 | case Predictor::Gradient: |
451 | 0 | return pixel_type_w{ClampedGradient(left, top, topleft)}; |
452 | 0 | case Predictor::TopLeft: |
453 | 0 | return topleft; |
454 | 0 | case Predictor::TopRight: |
455 | 0 | return topright; |
456 | 0 | case Predictor::LeftLeft: |
457 | 0 | return leftleft; |
458 | 0 | case Predictor::Average0: |
459 | 0 | return (left + top) / 2; |
460 | 0 | case Predictor::Average1: |
461 | 0 | return (left + topleft) / 2; |
462 | 0 | case Predictor::Average2: |
463 | 0 | return (topleft + top) / 2; |
464 | 0 | case Predictor::Average3: |
465 | 0 | return (top + topright) / 2; |
466 | 0 | case Predictor::Average4: |
467 | 0 | return (6 * top - 2 * toptop + 7 * left + 1 * leftleft + |
468 | 0 | 1 * toprightright + 3 * topright + 8) / |
469 | 0 | 16; |
470 | 0 | default: |
471 | 0 | return pixel_type_w{0}; |
472 | 0 | } |
473 | 0 | } |
474 | | |
475 | | template <int mode> |
476 | | JXL_INLINE PredictionResult Predict( |
477 | | Properties *p, size_t w, const pixel_type *JXL_RESTRICT pp, |
478 | | const intptr_t onerow, const size_t x, const size_t y, Predictor predictor, |
479 | | const MATreeLookup *lookup, const Channel *references, |
480 | 0 | weighted::State *wp_state, pixel_type_w *predictions) { |
481 | | // We start in position 3 because of 2 static properties + y. |
482 | 0 | size_t offset = 3; |
483 | 0 | constexpr bool compute_properties = |
484 | 0 | mode & kUseTree || mode & kForceComputeProperties; |
485 | 0 | constexpr bool nec = mode & kNoEdgeCases; |
486 | 0 | pixel_type_w left = (nec || x ? pp[-1] : (y ? pp[-onerow] : 0)); |
487 | 0 | pixel_type_w top = (nec || y ? pp[-onerow] : left); |
488 | 0 | pixel_type_w topleft = (nec || (x && y) ? pp[-1 - onerow] : left); |
489 | 0 | pixel_type_w topright = (nec || (x + 1 < w && y) ? pp[1 - onerow] : top); |
490 | 0 | pixel_type_w leftleft = (nec || x > 1 ? pp[-2] : left); |
491 | 0 | pixel_type_w toptop = (nec || y > 1 ? pp[-onerow - onerow] : top); |
492 | 0 | pixel_type_w toprightright = |
493 | 0 | (nec || (x + 2 < w && y) ? pp[2 - onerow] : topright); |
494 | |
|
495 | 0 | if (compute_properties) { |
496 | | // location |
497 | 0 | (*p)[offset++] = x; |
498 | | // neighbors |
499 | 0 | (*p)[offset++] = top > 0 ? top : -top; |
500 | 0 | (*p)[offset++] = left > 0 ? left : -left; |
501 | 0 | (*p)[offset++] = top; |
502 | 0 | (*p)[offset++] = left; |
503 | | |
504 | | // local gradient |
505 | 0 | (*p)[offset] = left - (*p)[offset + 1]; |
506 | 0 | offset++; |
507 | | // local gradient |
508 | 0 | (*p)[offset++] = left + top - topleft; |
509 | | |
510 | | // FFV1 context properties |
511 | 0 | (*p)[offset++] = left - topleft; |
512 | 0 | (*p)[offset++] = topleft - top; |
513 | 0 | (*p)[offset++] = top - topright; |
514 | 0 | (*p)[offset++] = top - toptop; |
515 | 0 | (*p)[offset++] = left - leftleft; |
516 | 0 | } |
517 | |
|
518 | 0 | pixel_type_w wp_pred = 0; |
519 | 0 | if (mode & kUseWP) { |
520 | 0 | wp_pred = wp_state->Predict<compute_properties>( |
521 | 0 | x, y, w, top, left, topright, topleft, toptop, p, offset); |
522 | 0 | } |
523 | 0 | if (!nec && compute_properties) { |
524 | 0 | offset += weighted::kNumProperties; |
525 | | // Extra properties. |
526 | 0 | const pixel_type *JXL_RESTRICT rp = references->Row(x); |
527 | 0 | for (size_t i = 0; i < references->w; i++) { |
528 | 0 | (*p)[offset++] = rp[i]; |
529 | 0 | } |
530 | 0 | } |
531 | 0 | PredictionResult result; |
532 | 0 | if (mode & kUseTree) { |
533 | 0 | MATreeLookup::LookupResult lr = lookup->Lookup(*p); |
534 | 0 | result.context = lr.context; |
535 | 0 | result.guess = lr.offset; |
536 | 0 | result.multiplier = lr.multiplier; |
537 | 0 | predictor = lr.predictor; |
538 | 0 | } |
539 | 0 | if (mode & kAllPredictions) { |
540 | 0 | for (size_t i = 0; i < kNumModularPredictors; i++) { |
541 | 0 | predictions[i] = PredictOne((Predictor)i, left, top, toptop, topleft, |
542 | 0 | topright, leftleft, toprightright, wp_pred); |
543 | 0 | } |
544 | 0 | } |
545 | 0 | result.guess += PredictOne(predictor, left, top, toptop, topleft, topright, |
546 | 0 | leftleft, toprightright, wp_pred); |
547 | 0 | result.predictor = predictor; |
548 | |
|
549 | 0 | return result; |
550 | 0 | } Unexecuted instantiation: jxl::PredictionResult jxl::detail::Predict<0>(std::__1::vector<int, std::__1::allocator<int> >*, unsigned long, int const*, long, unsigned long, unsigned long, jxl::Predictor, jxl::MATreeLookup const*, jxl::Channel const*, jxl::weighted::State*, long*) Unexecuted instantiation: jxl::PredictionResult jxl::detail::Predict<2>(std::__1::vector<int, std::__1::allocator<int> >*, unsigned long, int const*, long, unsigned long, unsigned long, jxl::Predictor, jxl::MATreeLookup const*, jxl::Channel const*, jxl::weighted::State*, long*) Unexecuted instantiation: jxl::PredictionResult jxl::detail::Predict<1>(std::__1::vector<int, std::__1::allocator<int> >*, unsigned long, int const*, long, unsigned long, unsigned long, jxl::Predictor, jxl::MATreeLookup const*, jxl::Channel const*, jxl::weighted::State*, long*) Unexecuted instantiation: jxl::PredictionResult jxl::detail::Predict<17>(std::__1::vector<int, std::__1::allocator<int> >*, unsigned long, int const*, long, unsigned long, unsigned long, jxl::Predictor, jxl::MATreeLookup const*, jxl::Channel const*, jxl::weighted::State*, long*) Unexecuted instantiation: jxl::PredictionResult jxl::detail::Predict<3>(std::__1::vector<int, std::__1::allocator<int> >*, unsigned long, int const*, long, unsigned long, unsigned long, jxl::Predictor, jxl::MATreeLookup const*, jxl::Channel const*, jxl::weighted::State*, long*) Unexecuted instantiation: jxl::PredictionResult jxl::detail::Predict<19>(std::__1::vector<int, std::__1::allocator<int> >*, unsigned long, int const*, long, unsigned long, unsigned long, jxl::Predictor, jxl::MATreeLookup const*, jxl::Channel const*, jxl::weighted::State*, long*) Unexecuted instantiation: jxl::PredictionResult jxl::detail::Predict<6>(std::__1::vector<int, std::__1::allocator<int> >*, unsigned long, int const*, long, unsigned long, unsigned long, jxl::Predictor, jxl::MATreeLookup const*, jxl::Channel const*, jxl::weighted::State*, long*) Unexecuted instantiation: jxl::PredictionResult jxl::detail::Predict<14>(std::__1::vector<int, std::__1::allocator<int> >*, unsigned long, int const*, long, unsigned long, unsigned long, jxl::Predictor, jxl::MATreeLookup const*, jxl::Channel const*, jxl::weighted::State*, long*) Unexecuted instantiation: jxl::PredictionResult jxl::detail::Predict<22>(std::__1::vector<int, std::__1::allocator<int> >*, unsigned long, int const*, long, unsigned long, unsigned long, jxl::Predictor, jxl::MATreeLookup const*, jxl::Channel const*, jxl::weighted::State*, long*) Unexecuted instantiation: jxl::PredictionResult jxl::detail::Predict<30>(std::__1::vector<int, std::__1::allocator<int> >*, unsigned long, int const*, long, unsigned long, unsigned long, jxl::Predictor, jxl::MATreeLookup const*, jxl::Channel const*, jxl::weighted::State*, long*) Unexecuted instantiation: jxl::PredictionResult jxl::detail::Predict<8>(std::__1::vector<int, std::__1::allocator<int> >*, unsigned long, int const*, long, unsigned long, unsigned long, jxl::Predictor, jxl::MATreeLookup const*, jxl::Channel const*, jxl::weighted::State*, long*) |
551 | | } // namespace detail |
552 | | |
553 | | inline PredictionResult PredictNoTreeNoWP(size_t w, |
554 | | const pixel_type *JXL_RESTRICT pp, |
555 | | const intptr_t onerow, const int x, |
556 | 0 | const int y, Predictor predictor) { |
557 | 0 | return detail::Predict</*mode=*/0>( |
558 | 0 | /*p=*/nullptr, w, pp, onerow, x, y, predictor, /*lookup=*/nullptr, |
559 | 0 | /*references=*/nullptr, /*wp_state=*/nullptr, /*predictions=*/nullptr); |
560 | 0 | } |
561 | | |
562 | | inline PredictionResult PredictNoTreeWP(size_t w, |
563 | | const pixel_type *JXL_RESTRICT pp, |
564 | | const intptr_t onerow, const int x, |
565 | | const int y, Predictor predictor, |
566 | 0 | weighted::State *wp_state) { |
567 | 0 | return detail::Predict<detail::kUseWP>( |
568 | 0 | /*p=*/nullptr, w, pp, onerow, x, y, predictor, /*lookup=*/nullptr, |
569 | 0 | /*references=*/nullptr, wp_state, /*predictions=*/nullptr); |
570 | 0 | } |
571 | | |
572 | | inline PredictionResult PredictTreeNoWP(Properties *p, size_t w, |
573 | | const pixel_type *JXL_RESTRICT pp, |
574 | | const intptr_t onerow, const int x, |
575 | | const int y, |
576 | | const MATreeLookup &tree_lookup, |
577 | 0 | const Channel &references) { |
578 | 0 | return detail::Predict<detail::kUseTree>( |
579 | 0 | p, w, pp, onerow, x, y, Predictor::Zero, &tree_lookup, &references, |
580 | 0 | /*wp_state=*/nullptr, /*predictions=*/nullptr); |
581 | 0 | } |
582 | | // Only use for y > 1, x > 1, x < w-2, and empty references |
583 | | JXL_INLINE PredictionResult |
584 | | PredictTreeNoWPNEC(Properties *p, size_t w, const pixel_type *JXL_RESTRICT pp, |
585 | | const intptr_t onerow, const int x, const int y, |
586 | 0 | const MATreeLookup &tree_lookup, const Channel &references) { |
587 | 0 | return detail::Predict<detail::kUseTree | detail::kNoEdgeCases>( |
588 | 0 | p, w, pp, onerow, x, y, Predictor::Zero, &tree_lookup, &references, |
589 | 0 | /*wp_state=*/nullptr, /*predictions=*/nullptr); |
590 | 0 | } |
591 | | |
592 | | inline PredictionResult PredictTreeWP(Properties *p, size_t w, |
593 | | const pixel_type *JXL_RESTRICT pp, |
594 | | const intptr_t onerow, const int x, |
595 | | const int y, |
596 | | const MATreeLookup &tree_lookup, |
597 | | const Channel &references, |
598 | 0 | weighted::State *wp_state) { |
599 | 0 | return detail::Predict<detail::kUseTree | detail::kUseWP>( |
600 | 0 | p, w, pp, onerow, x, y, Predictor::Zero, &tree_lookup, &references, |
601 | 0 | wp_state, /*predictions=*/nullptr); |
602 | 0 | } |
603 | | JXL_INLINE PredictionResult PredictTreeWPNEC(Properties *p, size_t w, |
604 | | const pixel_type *JXL_RESTRICT pp, |
605 | | const intptr_t onerow, const int x, |
606 | | const int y, |
607 | | const MATreeLookup &tree_lookup, |
608 | | const Channel &references, |
609 | 0 | weighted::State *wp_state) { |
610 | 0 | return detail::Predict<detail::kUseTree | detail::kUseWP | |
611 | 0 | detail::kNoEdgeCases>( |
612 | 0 | p, w, pp, onerow, x, y, Predictor::Zero, &tree_lookup, &references, |
613 | 0 | wp_state, /*predictions=*/nullptr); |
614 | 0 | } |
615 | | |
616 | | inline PredictionResult PredictLearn(Properties *p, size_t w, |
617 | | const pixel_type *JXL_RESTRICT pp, |
618 | | const intptr_t onerow, const int x, |
619 | | const int y, Predictor predictor, |
620 | | const Channel &references, |
621 | 0 | weighted::State *wp_state) { |
622 | 0 | return detail::Predict<detail::kForceComputeProperties | detail::kUseWP>( |
623 | 0 | p, w, pp, onerow, x, y, predictor, /*lookup=*/nullptr, &references, |
624 | 0 | wp_state, /*predictions=*/nullptr); |
625 | 0 | } |
626 | | |
627 | | inline void PredictLearnAll(Properties *p, size_t w, |
628 | | const pixel_type *JXL_RESTRICT pp, |
629 | | const intptr_t onerow, const int x, const int y, |
630 | | const Channel &references, |
631 | | weighted::State *wp_state, |
632 | 0 | pixel_type_w *predictions) { |
633 | 0 | detail::Predict<detail::kForceComputeProperties | detail::kUseWP | |
634 | 0 | detail::kAllPredictions>( |
635 | 0 | p, w, pp, onerow, x, y, Predictor::Zero, |
636 | 0 | /*lookup=*/nullptr, &references, wp_state, predictions); |
637 | 0 | } |
638 | | inline PredictionResult PredictLearnNEC(Properties *p, size_t w, |
639 | | const pixel_type *JXL_RESTRICT pp, |
640 | | const intptr_t onerow, const int x, |
641 | | const int y, Predictor predictor, |
642 | | const Channel &references, |
643 | 0 | weighted::State *wp_state) { |
644 | 0 | return detail::Predict<detail::kForceComputeProperties | detail::kUseWP | |
645 | 0 | detail::kNoEdgeCases>( |
646 | 0 | p, w, pp, onerow, x, y, predictor, /*lookup=*/nullptr, &references, |
647 | 0 | wp_state, /*predictions=*/nullptr); |
648 | 0 | } |
649 | | |
650 | | inline void PredictLearnAllNEC(Properties *p, size_t w, |
651 | | const pixel_type *JXL_RESTRICT pp, |
652 | | const intptr_t onerow, const int x, const int y, |
653 | | const Channel &references, |
654 | | weighted::State *wp_state, |
655 | 0 | pixel_type_w *predictions) { |
656 | 0 | detail::Predict<detail::kForceComputeProperties | detail::kUseWP | |
657 | 0 | detail::kAllPredictions | detail::kNoEdgeCases>( |
658 | 0 | p, w, pp, onerow, x, y, Predictor::Zero, |
659 | 0 | /*lookup=*/nullptr, &references, wp_state, predictions); |
660 | 0 | } |
661 | | |
662 | | inline void PredictAllNoWP(size_t w, const pixel_type *JXL_RESTRICT pp, |
663 | | const intptr_t onerow, const int x, const int y, |
664 | 0 | pixel_type_w *predictions) { |
665 | 0 | detail::Predict<detail::kAllPredictions>( |
666 | 0 | /*p=*/nullptr, w, pp, onerow, x, y, Predictor::Zero, |
667 | 0 | /*lookup=*/nullptr, |
668 | 0 | /*references=*/nullptr, /*wp_state=*/nullptr, predictions); |
669 | 0 | } |
670 | | } // namespace jxl |
671 | | |
672 | | #endif // LIB_JXL_MODULAR_ENCODING_CONTEXT_PREDICT_H_ |