Coverage Report

Created: 2023-08-28 07:24

/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_