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