Coverage Report

Created: 2025-06-16 07:00

/src/libjxl/lib/jxl/ac_strategy.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_AC_STRATEGY_H_
7
#define LIB_JXL_AC_STRATEGY_H_
8
9
#include <jxl/memory_manager.h>
10
11
#include <cstddef>
12
#include <cstdint>
13
#include <hwy/base.h>  // kMaxVectorSize
14
15
#include "lib/jxl/base/compiler_specific.h"
16
#include "lib/jxl/base/rect.h"
17
#include "lib/jxl/base/status.h"
18
#include "lib/jxl/coeff_order_fwd.h"
19
#include "lib/jxl/frame_dimensions.h"
20
#include "lib/jxl/image.h"
21
#include "lib/jxl/image_ops.h"
22
23
// Defines the different kinds of transforms, and heuristics to choose between
24
// them.
25
// `AcStrategy` represents what transform should be used, and which sub-block of
26
// that transform we are currently in. Note that DCT4x4 is applied on all four
27
// 4x4 sub-blocks of an 8x8 block.
28
// `AcStrategyImage` defines which strategy should be used for each 8x8 block
29
// of the image. The highest 4 bits represent the strategy to be used, the
30
// lowest 4 represent the index of the block inside that strategy.
31
32
namespace jxl {
33
34
// Raw strategy types.
35
enum class AcStrategyType : uint32_t {
36
  // Regular block size DCT
37
  DCT = 0,
38
  // Encode pixels without transforming
39
  IDENTITY = 1,
40
  // Use 2-by-2 DCT
41
  DCT2X2 = 2,
42
  // Use 4-by-4 DCT
43
  DCT4X4 = 3,
44
  // Use 16-by-16 DCT
45
  DCT16X16 = 4,
46
  // Use 32-by-32 DCT
47
  DCT32X32 = 5,
48
  // Use 16-by-8 DCT
49
  DCT16X8 = 6,
50
  // Use 8-by-16 DCT
51
  DCT8X16 = 7,
52
  // Use 32-by-8 DCT
53
  DCT32X8 = 8,
54
  // Use 8-by-32 DCT
55
  DCT8X32 = 9,
56
  // Use 32-by-16 DCT
57
  DCT32X16 = 10,
58
  // Use 16-by-32 DCT
59
  DCT16X32 = 11,
60
  // 4x8 and 8x4 DCT
61
  DCT4X8 = 12,
62
  DCT8X4 = 13,
63
  // Corner-DCT.
64
  AFV0 = 14,
65
  AFV1 = 15,
66
  AFV2 = 16,
67
  AFV3 = 17,
68
  // Larger DCTs
69
  DCT64X64 = 18,
70
  DCT64X32 = 19,
71
  DCT32X64 = 20,
72
  // No transforms smaller than 64x64 are allowed below.
73
  DCT128X128 = 21,
74
  DCT128X64 = 22,
75
  DCT64X128 = 23,
76
  DCT256X256 = 24,
77
  DCT256X128 = 25,
78
  DCT128X256 = 26
79
};
80
81
class AcStrategy {
82
 public:
83
  // Extremal values for the number of blocks/coefficients of a single strategy.
84
  static constexpr size_t kMaxCoeffBlocks = 32;
85
  static constexpr size_t kMaxBlockDim = kBlockDim * kMaxCoeffBlocks;
86
  // Maximum number of coefficients in a block. Guaranteed to be a multiple of
87
  // the vector size.
88
  static constexpr size_t kMaxCoeffArea = kMaxBlockDim * kMaxBlockDim;
89
  static_assert((kMaxCoeffArea * sizeof(float)) % hwy::kMaxVectorSize == 0,
90
                "Coefficient area is not a multiple of vector size");
91
92
  static constexpr uint8_t kNumValidStrategies =
93
      static_cast<uint8_t>(AcStrategyType::DCT128X256) + 1;
94
95
0
  static constexpr uint32_t TypeBit(const AcStrategyType type) {
96
0
    return 1u << static_cast<uint32_t>(type);
97
0
  }
98
99
  // Returns true if this block is the first 8x8 block (i.e. top-left) of a
100
  // possibly multi-block strategy.
101
9.11M
  JXL_INLINE bool IsFirstBlock() const { return is_first_; }
102
103
0
  JXL_INLINE bool IsMultiblock() const {
104
0
    constexpr uint32_t bits =
105
0
        TypeBit(AcStrategyType::DCT16X16) | TypeBit(AcStrategyType::DCT32X32) |
106
0
        TypeBit(AcStrategyType::DCT16X8) | TypeBit(AcStrategyType::DCT8X16) |
107
0
        TypeBit(AcStrategyType::DCT32X8) | TypeBit(AcStrategyType::DCT8X32) |
108
0
        TypeBit(AcStrategyType::DCT16X32) | TypeBit(AcStrategyType::DCT32X16) |
109
0
        TypeBit(AcStrategyType::DCT32X64) | TypeBit(AcStrategyType::DCT64X32) |
110
0
        TypeBit(AcStrategyType::DCT64X64) | TypeBit(AcStrategyType::DCT64X128) |
111
0
        TypeBit(AcStrategyType::DCT128X64) |
112
0
        TypeBit(AcStrategyType::DCT128X128) |
113
0
        TypeBit(AcStrategyType::DCT128X256) |
114
0
        TypeBit(AcStrategyType::DCT256X128) |
115
0
        TypeBit(AcStrategyType::DCT256X256);
116
0
    return ((1u << static_cast<uint32_t>(Strategy())) & bits) != 0;
117
0
  }
118
119
  // Returns the raw strategy value. Should only be used for tokenization.
120
2.75M
  JXL_INLINE uint8_t RawStrategy() const {
121
2.75M
    return static_cast<uint8_t>(strategy_);
122
2.75M
  }
123
124
72.1M
  JXL_INLINE AcStrategyType Strategy() const { return strategy_; }
125
126
  // Inverse check
127
318k
  static JXL_INLINE constexpr bool IsRawStrategyValid(int raw_strategy) {
128
318k
    return raw_strategy < kNumValidStrategies && raw_strategy >= 0;
129
318k
  }
130
438k
  static JXL_INLINE AcStrategy FromRawStrategy(uint8_t raw_strategy) {
131
438k
    return FromRawStrategy(static_cast<AcStrategyType>(raw_strategy));
132
438k
  }
133
8.01M
  static JXL_INLINE AcStrategy FromRawStrategy(AcStrategyType raw_strategy) {
134
8.01M
    JXL_DASSERT(IsRawStrategyValid(static_cast<uint32_t>(raw_strategy)));
135
8.01M
    return AcStrategy(raw_strategy, /*is_first=*/true);
136
8.01M
  }
137
138
  // "Natural order" means the order of increasing of "anisotropic" frequency of
139
  // continuous version of DCT basis.
140
  // Round-trip, for any given strategy s:
141
  //  X = NaturalCoeffOrder(s)[NaturalCoeffOrderLutN(s)[X]]
142
  //  X = NaturalCoeffOrderLut(s)[NaturalCoeffOrderN(s)[X]]
143
  void ComputeNaturalCoeffOrder(coeff_order_t* order) const;
144
  void ComputeNaturalCoeffOrderLut(coeff_order_t* lut) const;
145
146
  // Number of 8x8 blocks that this strategy will cover. 0 for non-top-left
147
  // blocks inside a multi-block transform.
148
318M
  JXL_INLINE size_t covered_blocks_x() const {
149
318M
    static constexpr uint8_t kLut[] = {1, 1, 1, 1,  2, 4,  1,  2,  1,
150
318M
                                       4, 2, 4, 1,  1, 1,  1,  1,  1,
151
318M
                                       8, 4, 8, 16, 8, 16, 32, 16, 32};
152
318M
    static_assert(sizeof(kLut) / sizeof(*kLut) == kNumValidStrategies,
153
318M
                  "Update LUT");
154
318M
    return kLut[static_cast<size_t>(strategy_)];
155
318M
  }
156
157
58.0M
  JXL_INLINE size_t covered_blocks_y() const {
158
58.0M
    static constexpr uint8_t kLut[] = {1, 1, 1, 1,  2,  4, 2,  1,  4,
159
58.0M
                                       1, 4, 2, 1,  1,  1, 1,  1,  1,
160
58.0M
                                       8, 8, 4, 16, 16, 8, 32, 32, 16};
161
58.0M
    static_assert(sizeof(kLut) / sizeof(*kLut) == kNumValidStrategies,
162
58.0M
                  "Update LUT");
163
58.0M
    return kLut[static_cast<size_t>(strategy_)];
164
58.0M
  }
165
166
6.12M
  JXL_INLINE size_t log2_covered_blocks() const {
167
6.12M
    static constexpr uint8_t kLut[] = {0, 0, 0, 0, 2, 4, 1,  1, 2,
168
6.12M
                                       2, 3, 3, 0, 0, 0, 0,  0, 0,
169
6.12M
                                       6, 5, 5, 8, 7, 7, 10, 9, 9};
170
6.12M
    static_assert(sizeof(kLut) / sizeof(*kLut) == kNumValidStrategies,
171
6.12M
                  "Update LUT");
172
6.12M
    return kLut[static_cast<size_t>(strategy_)];
173
6.12M
  }
174
175
 private:
176
  friend class AcStrategyRow;
177
  JXL_INLINE AcStrategy(AcStrategyType strategy, bool is_first)
178
21.2M
      : strategy_(strategy), is_first_(is_first) {
179
21.2M
    JXL_DASSERT(IsMultiblock() || is_first == true);
180
21.2M
  }
181
182
  AcStrategyType strategy_;
183
  bool is_first_;
184
};
185
186
// Class to use a certain row of the AC strategy.
187
class AcStrategyRow {
188
 public:
189
3.78M
  explicit AcStrategyRow(const uint8_t* row) : row_(row) {}
190
13.1M
  AcStrategy operator[](size_t x) const {
191
13.1M
    AcStrategyType strategy = static_cast<AcStrategyType>(row_[x] >> 1);
192
13.1M
    bool is_first = static_cast<bool>(row_[x] & 1);
193
13.1M
    return AcStrategy(strategy, is_first);
194
13.1M
  }
195
196
 private:
197
  const uint8_t* JXL_RESTRICT row_;
198
};
199
200
class AcStrategyImage {
201
 public:
202
36.5k
  AcStrategyImage() = default;
203
  static StatusOr<AcStrategyImage> Create(JxlMemoryManager* memory_manager,
204
                                          size_t xsize, size_t ysize);
205
206
52.4k
  AcStrategyImage(AcStrategyImage&&) = default;
207
26.2k
  AcStrategyImage& operator=(AcStrategyImage&&) = default;
208
209
0
  void FillDCT8(const Rect& rect) {
210
0
    FillPlane<uint8_t>((static_cast<uint8_t>(AcStrategyType::DCT) << 1) | 1,
211
0
                       &layers_, rect);
212
0
  }
213
0
  void FillDCT8() { FillDCT8(Rect(layers_)); }
214
215
4.73k
  void FillInvalid() { FillImage(INVALID, &layers_); }
216
217
460k
  Status Set(size_t x, size_t y, AcStrategyType type) {
218
#if (JXL_IS_DEBUG_BUILD)
219
    AcStrategy acs = AcStrategy::FromRawStrategy(type);
220
    JXL_DASSERT(y + acs.covered_blocks_y() <= layers_.ysize());
221
    JXL_DASSERT(x + acs.covered_blocks_x() <= layers_.xsize());
222
#endif
223
460k
    JXL_RETURN_IF_ERROR(SetNoBoundsCheck(x, y, type, /*check=*/false));
224
460k
    return true;
225
460k
  }
226
227
  Status SetNoBoundsCheck(size_t x, size_t y, AcStrategyType type,
228
779k
                          bool check = true) {
229
779k
    AcStrategy acs = AcStrategy::FromRawStrategy(type);
230
1.64M
    for (size_t iy = 0; iy < acs.covered_blocks_y(); iy++) {
231
1.93M
      for (size_t ix = 0; ix < acs.covered_blocks_x(); ix++) {
232
1.07M
        size_t pos = (y + iy) * stride_ + x + ix;
233
1.07M
        if (check && row_[pos] != INVALID) {
234
1
          return JXL_FAILURE("Invalid AC strategy: block overlap");
235
1
        }
236
1.07M
        row_[pos] =
237
1.07M
            (static_cast<uint8_t>(type) << 1) | ((iy | ix) == 0 ? 1 : 0);
238
1.07M
      }
239
861k
    }
240
779k
    return true;
241
779k
  }
242
243
328k
  bool IsValid(size_t x, size_t y) { return row_[y * stride_ + x] != INVALID; }
244
245
3.78M
  AcStrategyRow ConstRow(size_t y, size_t x_prefix = 0) const {
246
3.78M
    return AcStrategyRow(layers_.ConstRow(y) + x_prefix);
247
3.78M
  }
248
249
213k
  AcStrategyRow ConstRow(const Rect& rect, size_t y) const {
250
213k
    return ConstRow(rect.y0() + y, rect.x0());
251
213k
  }
252
253
0
  size_t PixelsPerRow() const { return layers_.PixelsPerRow(); }
254
255
2.23M
  size_t xsize() const { return layers_.xsize(); }
256
2.09M
  size_t ysize() const { return layers_.ysize(); }
257
258
  // Count the number of blocks of a given type.
259
  size_t CountBlocks(AcStrategyType type) const;
260
261
186
  JxlMemoryManager* memory_manager() const { return layers_.memory_manager(); }
262
263
 private:
264
  ImageB layers_;
265
  uint8_t* JXL_RESTRICT row_;
266
  size_t stride_;
267
268
  // A value that does not represent a valid combined AC strategy
269
  // value. Used as a sentinel.
270
  static constexpr uint8_t INVALID = 0xFF;
271
};
272
273
}  // namespace jxl
274
275
#endif  // LIB_JXL_AC_STRATEGY_H_