/src/serenity/Userland/Libraries/LibGfx/ImageFormats/WebPLoaderLossy.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2023, Nico Weber <thakis@chromium.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <AK/Debug.h> |
8 | | #include <AK/Endian.h> |
9 | | #include <AK/Format.h> |
10 | | #include <AK/MemoryStream.h> |
11 | | #include <AK/Vector.h> |
12 | | #include <LibGfx/ImageFormats/BooleanDecoder.h> |
13 | | #include <LibGfx/ImageFormats/WebPLoaderLossy.h> |
14 | | #include <LibGfx/ImageFormats/WebPLoaderLossyTables.h> |
15 | | |
16 | | // Lossy format: https://datatracker.ietf.org/doc/html/rfc6386 |
17 | | |
18 | | // Summary: |
19 | | // A lossy webp image is a VP8 keyframe. |
20 | | // A VP8 keyframe consists of 16x16 pixel tiles called macroblocks. Each macroblock is subdivided into 4x4 pixel tiles called subblocks. |
21 | | // Pixel values are stored as YUV 4:2:0. That is, each 4x4 luma pixels are covered by 1 pixel U chroma and 1 pixel V chroma. |
22 | | // This means one macroblock is covered by 4x4 Y subblocks and 2x2 U and V subblocks each. |
23 | | // VP8 data consists of: |
24 | | // * A tiny bit of uncompressed data, storing image dimensions and the size of the first compressed chunk of data, called the first partition |
25 | | // * The first partition, which is a entropy-coded bitstream storing: |
26 | | // 1. A fixed-size header. |
27 | | // The main piece of data this stores is a probability distribution for how pixel values of each macroblock are predicted from previously decoded data. |
28 | | // It also stores how may independent entropy-coded bitstreams are used to store the actual pixel data (for all images I've seen so far, just one). |
29 | | // 2. For each macroblock, it stores how that macroblock's pixel values are predicted from previously decoded data (and some more per-macroblock metadata). |
30 | | // There are independent prediction modes for Y, U, V. |
31 | | // U and V store a single prediction mode per macroblock. |
32 | | // Y can store a single prediction mode per macroblock, or it can store one subblock prediction mode for each of the 4x4 luma subblocks. |
33 | | // * One or more additional entropy-coded bitstreams ("partitions") that store the discrete cosine transform ("DCT") coefficients for the actual pixel data for each macroblock. |
34 | | // Each macroblock is subdivided into 4x4 tiles called "subblocks". A 16x16 pixel macroblock consists of: |
35 | | // 0. If the macroblock stores 4x4 luma subblock prediction modes, the 4x4 DC coefficients of each subblock's DCT are stored at the start of the macroblock's data, |
36 | | // as coefficients of an inverse Walsh-Hadamard Transform (WHT). |
37 | | // 1. 4x4 luma subblocks |
38 | | // 2. 2x2 U chrome subblocks |
39 | | // 3. 2x2 U chrome subblocks |
40 | | // That is, each macroblock stores 24 or 25 sets of coefficients. |
41 | | // Each set of coefficients stores 16 numbers, using a combination of a custom prefix tree and dequantization. |
42 | | // The inverse DCT output is added to the output of the prediction. |
43 | | |
44 | | namespace Gfx { |
45 | | |
46 | | // https://developers.google.com/speed/webp/docs/riff_container#simple_file_format_lossy |
47 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-19 "Annex A: Bitstream Syntax" |
48 | | ErrorOr<VP8Header> decode_webp_chunk_VP8_header(ReadonlyBytes vp8_data) |
49 | 1.93k | { |
50 | 1.93k | if (vp8_data.size() < 3) |
51 | 6 | return Error::from_string_literal("WebPImageDecoderPlugin: 'VP8 ' chunk too small"); |
52 | 1.93k | unsigned frame_tag_size = 3; |
53 | | |
54 | | // FIXME: Eventually, this should probably call into LibVideo/VP8, |
55 | | // and image decoders should move into LibImageDecoders which depends on both LibGfx and LibVideo. |
56 | | // (LibVideo depends on LibGfx, so LibGfx can't depend on LibVideo itself.) |
57 | | |
58 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-4 "Overview of Compressed Data Format" |
59 | | // "The decoder is simply presented with a sequence of compressed frames [...] |
60 | | // The first frame presented to the decompressor is [...] a key frame. [...] |
61 | | // [E]very compressed frame has three or more pieces. It begins with an uncompressed data chunk comprising 10 bytes in the case of key frames" |
62 | | |
63 | 1.93k | u8 const* data = vp8_data.data(); |
64 | | |
65 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-9.1 "Uncompressed Data Chunk" |
66 | 1.93k | u32 frame_tag = data[0] | (data[1] << 8) | (data[2] << 16); |
67 | 1.93k | bool is_key_frame = (frame_tag & 1) == 0; // https://www.rfc-editor.org/errata/eid5534 |
68 | 1.93k | u8 version = (frame_tag & 0xe) >> 1; |
69 | 1.93k | bool show_frame = (frame_tag & 0x10) != 0; |
70 | 1.93k | u32 size_of_first_partition = frame_tag >> 5; |
71 | | |
72 | 1.93k | if (version > 3) |
73 | 3 | return Error::from_string_literal("WebPImageDecoderPlugin: unknown version number in 'VP8 ' chunk"); |
74 | | |
75 | 1.92k | Optional<VP8Header::KeyframeData> keyframe_data; |
76 | 1.92k | if (is_key_frame) { |
77 | 1.91k | if (vp8_data.size() < 10) |
78 | 2 | return Error::from_string_literal("WebPImageDecoderPlugin: 'VP8 ' keyframe chunk too small"); |
79 | 1.91k | frame_tag_size = 10; |
80 | | |
81 | 1.91k | u32 start_code = data[3] | (data[4] << 8) | (data[5] << 16); |
82 | 1.91k | if (start_code != 0x2a019d) // https://www.rfc-editor.org/errata/eid7370 |
83 | 41 | return Error::from_string_literal("WebPImageDecoderPlugin: 'VP8 ' chunk invalid start_code"); |
84 | | |
85 | | // "The scaling specifications for each dimension are encoded as follows. |
86 | | // 0 | No upscaling (the most common case). |
87 | | // 1 | Upscale by 5/4. |
88 | | // 2 | Upscale by 5/3. |
89 | | // 3 | Upscale by 2." |
90 | | // This is a display-time operation and doesn't affect decoding." |
91 | 1.87k | u16 width_and_horizontal_scale = data[6] | (data[7] << 8); |
92 | 1.87k | u16 width = width_and_horizontal_scale & 0x3fff; |
93 | 1.87k | u8 horizontal_scale = width_and_horizontal_scale >> 14; |
94 | | |
95 | 1.87k | u16 height_and_vertical_scale = data[8] | (data[9] << 8); |
96 | 1.87k | u16 height = height_and_vertical_scale & 0x3fff; |
97 | 1.87k | u8 vertical_scale = height_and_vertical_scale >> 14; |
98 | | |
99 | 1.87k | dbgln_if(WEBP_DEBUG, "version {}, show_frame {}, size_of_first_partition {}, width {}, horizontal_scale {}, height {}, vertical_scale {}", |
100 | 1.87k | version, show_frame, size_of_first_partition, width, horizontal_scale, height, vertical_scale); |
101 | | |
102 | 1.87k | keyframe_data = VP8Header::KeyframeData { width, horizontal_scale, height, vertical_scale }; |
103 | 1.87k | } else { |
104 | 9 | dbgln_if(WEBP_DEBUG, "version {}, show_frame {}, size_of_first_partition {}", |
105 | 9 | version, show_frame, size_of_first_partition); |
106 | 9 | } |
107 | | |
108 | 1.88k | if (vp8_data.size() < frame_tag_size + size_of_first_partition) |
109 | 16 | return Error::from_string_literal("WebPImageDecoderPlugin: 'VP8 ' chunk too small for full first partition"); |
110 | | |
111 | 1.86k | return VP8Header { version, show_frame, keyframe_data, vp8_data.slice(frame_tag_size), vp8_data.slice(frame_tag_size + size_of_first_partition) }; |
112 | 1.88k | } |
113 | | |
114 | | namespace { |
115 | | |
116 | | // Reads n bits followed by a sign bit (0: positive, 1: negative). |
117 | | i8 read_signed_literal(BooleanDecoder& decoder, u8 n) |
118 | 2.09k | { |
119 | 2.09k | VERIFY(n <= 7); |
120 | 2.09k | i8 i = decoder.read_literal(n); |
121 | 2.09k | if (decoder.read_literal(1)) |
122 | 855 | i = -i; |
123 | 2.09k | return i; |
124 | 2.09k | } |
125 | | |
126 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-19 "Annex A: Bitstream Syntax" |
127 | 37.5k | #define L(n) decoder.read_literal(n) |
128 | 422M | #define B(prob) decoder.read_bool(prob) |
129 | 2.09k | #define L_signed(n) read_signed_literal(decoder, n) |
130 | | |
131 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-9.3 Segment-Based Adjustments" |
132 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-19.2 "Frame Header" |
133 | | enum class SegmentFeatureMode { |
134 | | // Spec 19.2 says 0 is delta, 1 absolute; spec 9.3 has it the other way round. 19.2 is correct. |
135 | | // https://www.rfc-editor.org/errata/eid7519 |
136 | | DeltaValueMode = 0, |
137 | | AbsoluteValueMode = 1, |
138 | | |
139 | | }; |
140 | | struct Segmentation { |
141 | | bool update_macroblock_segmentation_map { false }; |
142 | | SegmentFeatureMode segment_feature_mode { SegmentFeatureMode::DeltaValueMode }; |
143 | | |
144 | | i8 quantizer_update_value[4] {}; |
145 | | i8 loop_filter_update_value[4] {}; |
146 | | |
147 | | u8 macroblock_segment_tree_probabilities[3] = { 255, 255, 255 }; |
148 | | }; |
149 | | Segmentation decode_VP8_frame_header_segmentation(BooleanDecoder&); |
150 | | |
151 | | // Also https://datatracker.ietf.org/doc/html/rfc6386#section-9.6 "Dequantization Indices" |
152 | | struct QuantizationIndices { |
153 | | u8 y_ac { 0 }; |
154 | | i8 y_dc_delta { 0 }; |
155 | | |
156 | | i8 y2_dc_delta { 0 }; |
157 | | i8 y2_ac_delta { 0 }; |
158 | | |
159 | | i8 uv_dc_delta { 0 }; |
160 | | i8 uv_ac_delta { 0 }; |
161 | | }; |
162 | | QuantizationIndices decode_VP8_frame_header_quantization_indices(BooleanDecoder&); |
163 | | |
164 | | struct LoopFilterAdjustment { |
165 | | bool enable_loop_filter_adjustment { false }; |
166 | | i8 ref_frame_delta[4] {}; |
167 | | i8 mb_mode_delta[4] {}; |
168 | | }; |
169 | | LoopFilterAdjustment decode_VP8_frame_header_loop_filter_adjustment(BooleanDecoder&); |
170 | | |
171 | | using CoefficientProbabilities = Prob[4][8][3][num_dct_tokens - 1]; |
172 | | ErrorOr<void> decode_VP8_frame_header_coefficient_probabilities(BooleanDecoder&, CoefficientProbabilities); |
173 | | |
174 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-15 "Loop Filter" |
175 | | // "The first is a flag (filter_type) selecting the type of filter (normal or simple)" |
176 | | enum class FilterType { |
177 | | Normal = 0, |
178 | | Simple = 1, |
179 | | }; |
180 | | |
181 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-9.2 "Color Space and Pixel Type (Key Frames Only)" |
182 | | enum class ColorSpaceAndPixelType { |
183 | | YUV = 0, |
184 | | ReservedForFutureUse = 1, |
185 | | }; |
186 | | enum class ClampingSpecification { |
187 | | DecoderMustClampTo0To255 = 0, |
188 | | NoClampingNecessary = 1, |
189 | | }; |
190 | | |
191 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-19.2 "Frame Header" |
192 | | struct FrameHeader { |
193 | | ColorSpaceAndPixelType color_space {}; |
194 | | ClampingSpecification clamping_type {}; |
195 | | |
196 | | bool is_segmentation_enabled {}; |
197 | | Segmentation segmentation {}; |
198 | | |
199 | | FilterType filter_type {}; |
200 | | u8 loop_filter_level {}; |
201 | | u8 sharpness_level {}; |
202 | | LoopFilterAdjustment loop_filter_adjustment {}; |
203 | | |
204 | | u8 number_of_dct_partitions {}; |
205 | | |
206 | | QuantizationIndices quantization_indices {}; |
207 | | |
208 | | CoefficientProbabilities coefficient_probabilities; |
209 | | |
210 | | bool enable_skipping_of_macroblocks_containing_only_zero_coefficients {}; |
211 | | u8 probability_skip_false; |
212 | | }; |
213 | | |
214 | | ErrorOr<FrameHeader> decode_VP8_frame_header(BooleanDecoder& decoder) |
215 | 1.03k | { |
216 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-19.2 "Frame Header" |
217 | 1.03k | FrameHeader header; |
218 | | |
219 | | // In the VP8 spec, this is in an `if (key_frames)`, but webp files only have key frames. |
220 | 1.03k | header.color_space = ColorSpaceAndPixelType { L(1) }; |
221 | 1.03k | header.clamping_type = ClampingSpecification { L(1) }; |
222 | 1.03k | dbgln_if(WEBP_DEBUG, "color_space {} clamping_type {}", (int)header.color_space, (int)header.clamping_type); |
223 | | |
224 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-9.3 "Segment-Based Adjustments" |
225 | 1.03k | header.is_segmentation_enabled = L(1); |
226 | 1.03k | dbgln_if(WEBP_DEBUG, "segmentation_enabled {}", header.is_segmentation_enabled); |
227 | | |
228 | 1.03k | if (header.is_segmentation_enabled) |
229 | 202 | header.segmentation = decode_VP8_frame_header_segmentation(decoder); |
230 | | |
231 | 1.03k | header.filter_type = FilterType { L(1) }; |
232 | 1.03k | header.loop_filter_level = L(6); |
233 | 1.03k | header.sharpness_level = L(3); |
234 | 1.03k | dbgln_if(WEBP_DEBUG, "filter_type {} loop_filter_level {} sharpness_level {}", (int)header.filter_type, header.loop_filter_level, header.sharpness_level); |
235 | | |
236 | 1.03k | header.loop_filter_adjustment = decode_VP8_frame_header_loop_filter_adjustment(decoder); |
237 | | |
238 | 1.03k | u8 log2_nbr_of_dct_partitions = L(2); |
239 | 1.03k | dbgln_if(WEBP_DEBUG, "log2_nbr_of_dct_partitions {}", log2_nbr_of_dct_partitions); |
240 | 1.03k | header.number_of_dct_partitions = 1 << log2_nbr_of_dct_partitions; |
241 | | |
242 | 1.03k | header.quantization_indices = decode_VP8_frame_header_quantization_indices(decoder); |
243 | | |
244 | | // In the VP8 spec, this is in an `if (key_frames)` followed by a lengthy `else`, but webp files only have key frames. |
245 | 1.03k | u8 refresh_entropy_probs = L(1); // Has no effect in webp files. |
246 | 1.03k | dbgln_if(WEBP_DEBUG, "refresh_entropy_probs {}", refresh_entropy_probs); |
247 | | |
248 | 1.03k | memcpy(header.coefficient_probabilities, DEFAULT_COEFFICIENT_PROBABILITIES, sizeof(header.coefficient_probabilities)); |
249 | 1.03k | TRY(decode_VP8_frame_header_coefficient_probabilities(decoder, header.coefficient_probabilities)); |
250 | | |
251 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-9.11 "Remaining Frame Header Data (Key Frame)" |
252 | 1.03k | header.enable_skipping_of_macroblocks_containing_only_zero_coefficients = L(1); |
253 | 1.03k | dbgln_if(WEBP_DEBUG, "mb_no_skip_coeff {}", header.enable_skipping_of_macroblocks_containing_only_zero_coefficients); |
254 | 1.03k | if (header.enable_skipping_of_macroblocks_containing_only_zero_coefficients) { |
255 | 207 | header.probability_skip_false = L(8); |
256 | 207 | dbgln_if(WEBP_DEBUG, "prob_skip_false {}", header.probability_skip_false); |
257 | 207 | } |
258 | | |
259 | | // In the VP8 spec, there is a length `if (!key_frames)` here, but webp files only have key frames. |
260 | | |
261 | 1.03k | return header; |
262 | 1.03k | } |
263 | | |
264 | | Segmentation decode_VP8_frame_header_segmentation(BooleanDecoder& decoder) |
265 | 202 | { |
266 | | // Corresponds to "update_segmentation()" in section 19.2 of the spec. |
267 | 202 | Segmentation segmentation; |
268 | | |
269 | 202 | segmentation.update_macroblock_segmentation_map = L(1); |
270 | 202 | u8 update_segment_feature_data = L(1); |
271 | | |
272 | 202 | dbgln_if(WEBP_DEBUG, "update_mb_segmentation_map {} update_segment_feature_data {}", |
273 | 202 | segmentation.update_macroblock_segmentation_map, update_segment_feature_data); |
274 | | |
275 | 202 | if (update_segment_feature_data) { |
276 | 111 | segmentation.segment_feature_mode = static_cast<SegmentFeatureMode>(L(1)); |
277 | 111 | dbgln_if(WEBP_DEBUG, "segment_feature_mode {}", (int)segmentation.segment_feature_mode); |
278 | | |
279 | 555 | for (int i = 0; i < 4; ++i) { |
280 | 444 | u8 quantizer_update = L(1); |
281 | 444 | dbgln_if(WEBP_DEBUG, "quantizer_update {}", quantizer_update); |
282 | 444 | if (quantizer_update) { |
283 | 248 | i8 quantizer_update_value = L_signed(7); |
284 | 248 | dbgln_if(WEBP_DEBUG, "quantizer_update_value {}", quantizer_update_value); |
285 | 248 | segmentation.quantizer_update_value[i] = quantizer_update_value; |
286 | 248 | } |
287 | 444 | } |
288 | 555 | for (int i = 0; i < 4; ++i) { |
289 | 444 | u8 loop_filter_update = L(1); |
290 | 444 | dbgln_if(WEBP_DEBUG, "loop_filter_update {}", loop_filter_update); |
291 | 444 | if (loop_filter_update) { |
292 | 155 | i8 loop_filter_update_value = L_signed(6); |
293 | 155 | dbgln_if(WEBP_DEBUG, "loop_filter_update_value {}", loop_filter_update_value); |
294 | 155 | segmentation.loop_filter_update_value[i] = loop_filter_update_value; |
295 | 155 | } |
296 | 444 | } |
297 | 111 | } |
298 | | |
299 | 202 | if (segmentation.update_macroblock_segmentation_map) { |
300 | | // This reads mb_segment_tree_probs for https://datatracker.ietf.org/doc/html/rfc6386#section-10. |
301 | 596 | for (int i = 0; i < 3; ++i) { |
302 | 447 | u8 segment_prob_update = L(1); |
303 | 447 | dbgln_if(WEBP_DEBUG, "segment_prob_update {}", segment_prob_update); |
304 | 447 | if (segment_prob_update) { |
305 | 150 | u8 segment_prob = L(8); |
306 | 150 | dbgln_if(WEBP_DEBUG, "segment_prob {}", segment_prob); |
307 | 150 | segmentation.macroblock_segment_tree_probabilities[i] = segment_prob; |
308 | 150 | } |
309 | 447 | } |
310 | 149 | } |
311 | | |
312 | 202 | return segmentation; |
313 | 202 | } |
314 | | |
315 | | QuantizationIndices decode_VP8_frame_header_quantization_indices(BooleanDecoder& decoder) |
316 | 1.03k | { |
317 | | // Corresponds to "quant_indices()" in section 19.2 of the spec. |
318 | 1.03k | QuantizationIndices quantization_indices; |
319 | | |
320 | | // "The first 7-bit index gives the dequantization table index for |
321 | | // Y-plane AC coefficients, called yac_qi. It is always coded and acts |
322 | | // as a baseline for the other 5 quantization indices, each of which is |
323 | | // represented by a delta from this baseline index." |
324 | 1.03k | quantization_indices.y_ac = L(7); |
325 | 1.03k | dbgln_if(WEBP_DEBUG, "y_ac_qi {}", quantization_indices.y_ac); |
326 | | |
327 | 5.18k | auto read_delta = [&decoder](StringView name, i8* destination) -> void { |
328 | 5.18k | u8 is_present = L(1); |
329 | 5.18k | dbgln_if(WEBP_DEBUG, "{}_present {}", name, is_present); |
330 | 5.18k | if (is_present) { |
331 | 1.38k | i8 delta = L_signed(4); |
332 | 1.38k | dbgln_if(WEBP_DEBUG, "{} {}", name, delta); |
333 | 1.38k | *destination = delta; |
334 | 1.38k | } |
335 | 5.18k | }; |
336 | 1.03k | read_delta("y_dc_delta"sv, &quantization_indices.y_dc_delta); |
337 | 1.03k | read_delta("y2_dc_delta"sv, &quantization_indices.y2_dc_delta); |
338 | 1.03k | read_delta("y2_ac_delta"sv, &quantization_indices.y2_ac_delta); |
339 | 1.03k | read_delta("uv_dc_delta"sv, &quantization_indices.uv_dc_delta); |
340 | 1.03k | read_delta("uv_ac_delta"sv, &quantization_indices.uv_ac_delta); |
341 | | |
342 | 1.03k | return quantization_indices; |
343 | 1.03k | } |
344 | | |
345 | | LoopFilterAdjustment decode_VP8_frame_header_loop_filter_adjustment(BooleanDecoder& decoder) |
346 | 1.03k | { |
347 | | // Corresponds to "mb_lf_adjustments()" in section 19.2 of the spec. |
348 | 1.03k | LoopFilterAdjustment adjustment; |
349 | | |
350 | 1.03k | adjustment.enable_loop_filter_adjustment = L(1); |
351 | 1.03k | if (adjustment.enable_loop_filter_adjustment) { |
352 | 186 | u8 mode_ref_lf_delta_update = L(1); |
353 | 186 | dbgln_if(WEBP_DEBUG, "mode_ref_lf_delta_update {}", mode_ref_lf_delta_update); |
354 | 186 | if (mode_ref_lf_delta_update) { |
355 | 555 | for (int i = 0; i < 4; ++i) { |
356 | 444 | u8 ref_frame_delta_update_flag = L(1); |
357 | 444 | dbgln_if(WEBP_DEBUG, "ref_frame_delta_update_flag {}", ref_frame_delta_update_flag); |
358 | 444 | if (ref_frame_delta_update_flag) { |
359 | 178 | i8 delta = L_signed(6); |
360 | 178 | dbgln_if(WEBP_DEBUG, "delta {}", delta); |
361 | 178 | adjustment.ref_frame_delta[i] = delta; |
362 | 178 | } |
363 | 444 | } |
364 | 555 | for (int i = 0; i < 4; ++i) { |
365 | 444 | u8 mb_mode_delta_update_flag = L(1); |
366 | 444 | dbgln_if(WEBP_DEBUG, "mb_mode_delta_update_flag {}", mb_mode_delta_update_flag); |
367 | 444 | if (mb_mode_delta_update_flag) { |
368 | 130 | i8 delta = L_signed(6); |
369 | 130 | dbgln_if(WEBP_DEBUG, "delta {}", delta); |
370 | 130 | adjustment.mb_mode_delta[i] = delta; |
371 | 130 | } |
372 | 444 | } |
373 | 111 | } |
374 | 186 | } |
375 | | |
376 | 1.03k | return adjustment; |
377 | 1.03k | } |
378 | | |
379 | | ErrorOr<void> decode_VP8_frame_header_coefficient_probabilities(BooleanDecoder& decoder, CoefficientProbabilities coefficient_probabilities) |
380 | 1.03k | { |
381 | | // Corresponds to "token_prob_update()" in section 19.2 of the spec. |
382 | 5.18k | for (int i = 0; i < 4; i++) { |
383 | 37.2k | for (int j = 0; j < 8; j++) { |
384 | 132k | for (int k = 0; k < 3; k++) { |
385 | 1.19M | for (int l = 0; l < 11; l++) { |
386 | | // token_prob_update() says L(1) and L(8), but it's actually B(p) and L(8). |
387 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-13.4 "Token Probability Updates" describes it correctly. |
388 | 1.09M | if (B(COEFFICIENT_UPDATE_PROBABILITIES[i][j][k][l])) |
389 | 17.6k | coefficient_probabilities[i][j][k][l] = L(8); |
390 | 1.09M | } |
391 | 99.4k | } |
392 | 33.1k | } |
393 | 4.14k | } |
394 | | |
395 | 1.03k | return {}; |
396 | 1.03k | } |
397 | | |
398 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-8.1 "Tree Coding Implementation" |
399 | | u8 tree_decode(BooleanDecoder& decoder, ReadonlySpan<TreeIndex> tree, ReadonlyBytes probabilities, TreeIndex initial_i = 0) |
400 | 382M | { |
401 | 382M | TreeIndex i = initial_i; |
402 | 419M | while (true) { |
403 | 419M | u8 b = B(probabilities[i >> 1]); |
404 | 419M | i = tree[i + b]; |
405 | 419M | if (i <= 0) |
406 | 382M | return -i; |
407 | 419M | } |
408 | 382M | } |
409 | | |
410 | | // Similar to BlockContext in LibVideo/VP9/Context.h |
411 | | struct MacroblockMetadata { |
412 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-10 "Segment-Based Feature Adjustments" |
413 | | // Read only if `update_mb_segmentation_map` is set. |
414 | | u8 segment_id { 0 }; // 0, 1, 2, or 3. Fits in two bits. |
415 | | |
416 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-11.1 "mb_skip_coeff" |
417 | | bool skip_coefficients { false }; |
418 | | |
419 | | IntraMacroblockMode intra_y_mode; |
420 | | IntraMacroblockMode uv_mode; |
421 | | |
422 | | IntraBlockMode intra_b_modes[16]; |
423 | | }; |
424 | | |
425 | | ErrorOr<Vector<MacroblockMetadata>> decode_VP8_macroblock_metadata(BooleanDecoder& decoder, FrameHeader const& header, int macroblock_width, int macroblock_height) |
426 | 1.03k | { |
427 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-19.3 |
428 | | |
429 | | // Corresponds to "macroblock_header()" in section 19.3 of the spec. |
430 | | |
431 | 1.03k | Vector<MacroblockMetadata> macroblock_metadata; |
432 | | |
433 | | // Key frames must use intra prediction, that is new macroblocks are predicted from old macroblocks in the same frame. |
434 | | // (Inter prediction on the other hand predicts new macroblocks from the corresponding macroblock in the previous frame.) |
435 | | |
436 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-11.3 "Subblock Mode Contexts" |
437 | | // "For macroblocks on the top row or left edge of the image, some of |
438 | | // the predictors will be non-existent. Such predictors are taken |
439 | | // to have had the value B_DC_PRED, which, perhaps conveniently, |
440 | | // takes the value 0 in the enumeration above. |
441 | | // A simple management scheme for these contexts might maintain a row |
442 | | // of above predictors and four left predictors. Before decoding the |
443 | | // frame, the entire row is initialized to B_DC_PRED; before decoding |
444 | | // each row of macroblocks, the four left predictors are also set to |
445 | | // B_DC_PRED. After decoding a macroblock, the bottom four subblock |
446 | | // modes are copied into the row predictor (at the current position, |
447 | | // which then advances to be above the next macroblock), and the |
448 | | // right four subblock modes are copied into the left predictor." |
449 | 1.03k | Vector<IntraBlockMode> above; |
450 | 1.03k | TRY(above.try_resize(macroblock_width * 4)); // One per 4x4 subblock. |
451 | | |
452 | | // It's possible to not decode all macroblock metadata at once. Instead, this could for example decode one row of metadata, |
453 | | // then decode the coefficients for one row of macroblocks, convert that row to pixels, and then go on to the next row of macroblocks. |
454 | | // That'd require slightly less memory. But MacroblockMetadata is fairly small, and this way we can keep the context |
455 | | // (`above`, `left`) in stack variables instead of having to have a class for that. So keep it simple for now. |
456 | 75.8k | for (int mb_y = 0; mb_y < macroblock_height; ++mb_y) { |
457 | 74.7k | IntraBlockMode left[4] {}; |
458 | | |
459 | 20.6M | for (int mb_x = 0; mb_x < macroblock_width; ++mb_x) { |
460 | 20.5M | MacroblockMetadata metadata; |
461 | | |
462 | 20.5M | if (header.segmentation.update_macroblock_segmentation_map) |
463 | 6.08M | metadata.segment_id = tree_decode(decoder, MACROBLOCK_SEGMENT_TREE, header.segmentation.macroblock_segment_tree_probabilities); |
464 | | |
465 | 20.5M | if (header.enable_skipping_of_macroblocks_containing_only_zero_coefficients) |
466 | 1.72M | metadata.skip_coefficients = B(header.probability_skip_false); |
467 | | |
468 | 20.5M | int intra_y_mode = tree_decode(decoder, KEYFRAME_YMODE_TREE, KEYFRAME_YMODE_PROBABILITIES); |
469 | 20.5M | metadata.intra_y_mode = (IntraMacroblockMode)intra_y_mode; |
470 | | |
471 | | // "If the Ymode is B_PRED, it is followed by a (tree-coded) mode for each of the 16 Y subblocks." |
472 | 20.5M | if (intra_y_mode == B_PRED) { |
473 | 102M | for (int y = 0; y < 4; ++y) { |
474 | 409M | for (int x = 0; x < 4; ++x) { |
475 | | // "The outer two dimensions of this array are indexed by the already- |
476 | | // coded subblock modes above and to the left of the current block, |
477 | | // respectively." |
478 | 327M | int A = above[mb_x * 4 + x]; |
479 | 327M | int L = left[y]; |
480 | | |
481 | 327M | auto intra_b_mode = static_cast<IntraBlockMode>(tree_decode(decoder, BLOCK_MODE_TREE, KEYFRAME_BLOCK_MODE_PROBABILITIES[A][L])); |
482 | 327M | metadata.intra_b_modes[y * 4 + x] = intra_b_mode; |
483 | | |
484 | 327M | above[mb_x * 4 + x] = intra_b_mode; |
485 | 327M | left[y] = intra_b_mode; |
486 | 327M | } |
487 | 81.9M | } |
488 | 20.4M | } else { |
489 | 64.9k | VERIFY(intra_y_mode < B_PRED); |
490 | 64.9k | constexpr IntraBlockMode b_mode_from_y_mode[] = { B_DC_PRED, B_VE_PRED, B_HE_PRED, B_TM_PRED }; |
491 | 64.9k | IntraBlockMode intra_b_mode = b_mode_from_y_mode[intra_y_mode]; |
492 | 324k | for (int i = 0; i < 4; ++i) { |
493 | 259k | above[mb_x * 4 + i] = intra_b_mode; |
494 | 259k | left[i] = intra_b_mode; |
495 | 259k | } |
496 | 64.9k | } |
497 | | |
498 | 20.5M | metadata.uv_mode = (IntraMacroblockMode)tree_decode(decoder, UV_MODE_TREE, KEYFRAME_UV_MODE_PROBABILITIES); |
499 | | |
500 | 20.5M | TRY(macroblock_metadata.try_append(metadata)); |
501 | 20.5M | } |
502 | 74.7k | } |
503 | | |
504 | 1.03k | return macroblock_metadata; |
505 | 1.03k | } |
506 | | |
507 | | // Every macroblock stores: |
508 | | // - One optional set of coefficients for Y2 |
509 | | // - 16 sets of Y coefficients for the 4x4 Y subblocks of the macroblock |
510 | | // - 4 sets of U coefficients for the 2x2 U subblocks of the macroblock |
511 | | // - 4 sets of V coefficients for the 2x2 V subblocks of the macroblock |
512 | | // That's 24 or 25 sets of coefficients total. This struct identifies one of these sets by index. |
513 | | // If a macroblock does not have Y2, then i goes from [1..25], else it goes [0..25]. |
514 | | struct CoefficientBlockIndex { |
515 | | int i; |
516 | | |
517 | | CoefficientBlockIndex(int i) |
518 | 3.40M | : i(i) |
519 | 3.40M | { |
520 | 3.40M | VERIFY(i >= 0); |
521 | 3.40M | VERIFY(i <= 25); |
522 | 3.40M | } |
523 | | |
524 | 32.9M | bool is_y2() const { return i == 0; } |
525 | 1.04M | bool is_y() const { return i >= 1 && i <= 16; } |
526 | 32.4M | bool is_u() const { return i >= 17 && i <= 20; } |
527 | 27.1M | bool is_v() const { return i >= 21; } |
528 | | |
529 | | u8 sub_x() const |
530 | 5.97M | { |
531 | 5.97M | VERIFY(i > 0); |
532 | 5.97M | if (i <= 16) |
533 | 3.98M | return (i - 1) % 4; |
534 | 1.99M | if (i <= 20) |
535 | 995k | return (i - 17) % 2; |
536 | 995k | return (i - 21) % 2; |
537 | 1.99M | } |
538 | | |
539 | | u8 sub_y() const |
540 | 5.97M | { |
541 | 5.97M | VERIFY(i > 0); |
542 | 5.97M | if (i <= 16) |
543 | 3.98M | return (i - 1) / 4; |
544 | 1.99M | if (i <= 20) |
545 | 995k | return (i - 17) / 2; |
546 | 995k | return (i - 21) / 2; |
547 | 1.99M | } |
548 | | }; |
549 | | |
550 | | int plane_index(CoefficientBlockIndex index, bool have_y2) |
551 | 7.60M | { |
552 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-13.3 "Token Probabilities" |
553 | | // "o 0 - Y beginning at coefficient 1 (i.e., Y after Y2) |
554 | | // o 1 - Y2 |
555 | | // o 2 - U or V |
556 | | // o 3 - Y beginning at coefficient 0 (i.e., Y in the absence of Y2)." |
557 | 7.60M | if (index.is_y2()) |
558 | 120k | return 1; |
559 | 7.48M | if (index.is_u() || index.is_v()) |
560 | 2.46M | return 2; |
561 | 5.02M | if (have_y2) |
562 | 1.75M | return 0; |
563 | 3.26M | return 3; |
564 | 5.02M | } |
565 | | |
566 | | i16 coefficient_value_for_token(BooleanDecoder& decoder, u8 token) |
567 | 5.24M | { |
568 | | // Implements the second half of https://datatracker.ietf.org/doc/html/rfc6386#section-13.2 "Coding of Individual Coefficient Values" |
569 | 5.24M | i16 v = static_cast<i16>(token); // For DCT_0 to DCT4 |
570 | | |
571 | 5.24M | if (token >= dct_cat1 && token <= dct_cat6) { |
572 | 4.89M | static int constexpr starts[] = { 5, 7, 11, 19, 35, 67 }; |
573 | 4.89M | static int constexpr bits[] = { 1, 2, 3, 4, 5, 11 }; |
574 | | |
575 | 4.89M | static Prob constexpr Pcat1[] = { 159 }; |
576 | 4.89M | static Prob constexpr Pcat2[] = { 165, 145 }; |
577 | 4.89M | static Prob constexpr Pcat3[] = { 173, 148, 140 }; |
578 | 4.89M | static Prob constexpr Pcat4[] = { 176, 155, 140, 135 }; |
579 | 4.89M | static Prob constexpr Pcat5[] = { 180, 157, 141, 134, 130 }; |
580 | 4.89M | static Prob constexpr Pcat6[] = { 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129 }; |
581 | 4.89M | static Prob const* const Pcats[] = { Pcat1, Pcat2, Pcat3, Pcat4, Pcat5, Pcat6 }; |
582 | | |
583 | 4.89M | v = 0; |
584 | | |
585 | | // This loop corresponds to `DCTextra` in the spec in section 13.2. |
586 | 58.7M | for (int i = 0; i < bits[token - dct_cat1]; ++i) |
587 | 53.8M | v = (v << 1) | decoder.read_bool(Pcats[token - dct_cat1][i]); |
588 | | |
589 | 4.89M | v += starts[token - dct_cat1]; |
590 | 4.89M | } |
591 | | |
592 | 5.24M | if (v) { |
593 | 5.11M | if (decoder.read_bool(128)) |
594 | 5.00M | v = -v; |
595 | 5.11M | } |
596 | | |
597 | 5.24M | return v; |
598 | 5.24M | } |
599 | | |
600 | | i16 dequantize_value(i16 value, bool is_dc, QuantizationIndices const& quantization_indices, Segmentation const& segmentation, int segment_id, CoefficientBlockIndex index) |
601 | 5.24M | { |
602 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-9.6 "Dequantization Indices" |
603 | | // "before inverting the transform, each decoded coefficient |
604 | | // is multiplied by one of six dequantization factors, the choice of |
605 | | // which depends on the plane (Y, chroma = U or V, Y2) and coefficient |
606 | | // position (DC = coefficient 0, AC = coefficients 1-15). The six |
607 | | // values are specified using 7-bit indices into six corresponding fixed |
608 | | // tables (the tables are given in Section 14)." |
609 | | // Section 14 then lists two (!) fixed tables (which are in WebPLoaderLossyTables.h) |
610 | | |
611 | | // "Lookup values from the above two tables are directly used in the DC |
612 | | // and AC coefficients in Y1, respectively. For Y2 and chroma, values |
613 | | // from the above tables undergo either scaling or clamping before the |
614 | | // multiplies. Details regarding these scaling and clamping processes |
615 | | // can be found in related lookup functions in dixie.c (Section 20.4)." |
616 | | // Apparently spec writing became too much work at this point. In section 20.4, in dequant_init(): |
617 | | // * For y2, the output (!) of dc_qlookup is multiplied by 2, the output of ac_qlookup is multiplied by 155 / 100 |
618 | | // * Also for y2, ac_qlookup is at least 8 for lower table entries |
619 | | // * For uv, the dc_qlookup index is clamped to 117 (instead of 127 for everything else) |
620 | | // (or, alternatively, the value is clamped to 132 at most) |
621 | | |
622 | 5.24M | int y_ac_base = quantization_indices.y_ac; |
623 | 5.24M | if (segmentation.update_macroblock_segmentation_map) { |
624 | 139k | if (segmentation.segment_feature_mode == SegmentFeatureMode::DeltaValueMode) |
625 | 38.8k | y_ac_base += segmentation.quantizer_update_value[segment_id]; |
626 | 100k | else |
627 | 100k | y_ac_base = segmentation.quantizer_update_value[segment_id]; |
628 | 139k | } |
629 | | |
630 | 5.24M | int dequantization_index; |
631 | 5.24M | if (index.is_y2()) |
632 | 83.1k | dequantization_index = y_ac_base + (is_dc ? quantization_indices.y2_dc_delta : quantization_indices.y2_ac_delta); |
633 | 5.16M | else if (index.is_u() || index.is_v()) |
634 | 1.68M | dequantization_index = y_ac_base + (is_dc ? quantization_indices.uv_dc_delta : quantization_indices.uv_ac_delta); |
635 | 3.47M | else |
636 | 3.47M | dequantization_index = is_dc ? (y_ac_base + quantization_indices.y_dc_delta) : y_ac_base; |
637 | | |
638 | | // clamp index |
639 | 5.24M | if ((index.is_u() || index.is_v()) && is_dc) |
640 | 129k | dequantization_index = clamp(dequantization_index, 0, 117); |
641 | 5.11M | else |
642 | 5.11M | dequantization_index = clamp(dequantization_index, 0, 127); |
643 | | |
644 | | // "the multiplies are computed and stored using 16-bit signed integers." |
645 | 5.24M | i16 dequantization_factor; |
646 | 5.24M | if (is_dc) |
647 | 336k | dequantization_factor = (i16)dc_qlookup[dequantization_index]; |
648 | 4.90M | else |
649 | 4.90M | dequantization_factor = (i16)ac_qlookup[dequantization_index]; |
650 | | |
651 | 5.24M | if (index.is_y2()) { |
652 | 83.1k | if (is_dc) |
653 | 7.18k | dequantization_factor *= 2; |
654 | 75.9k | else |
655 | 75.9k | dequantization_factor = max((dequantization_factor * 155) / 100, 8); |
656 | 83.1k | } |
657 | | |
658 | 5.24M | return dequantization_factor * value; |
659 | 5.24M | } |
660 | | |
661 | | // Reading macroblock coefficients requires needing to know if the block to the left and above the current macroblock |
662 | | // has non-zero coefficients. This stores that state. |
663 | | struct CoefficientReadingContext { |
664 | | // Store if each plane has nonzero coefficients in the block above and to the left of the current block. |
665 | | Vector<bool> y2_above; |
666 | | Vector<bool> y_above; |
667 | | Vector<bool> u_above; |
668 | | Vector<bool> v_above; |
669 | | |
670 | | bool y2_left {}; |
671 | | bool y_left[4] {}; |
672 | | bool u_left[2] {}; |
673 | | bool v_left[2] {}; |
674 | | |
675 | | ErrorOr<void> initialize(int macroblock_width) |
676 | 844 | { |
677 | 844 | TRY(y2_above.try_resize(macroblock_width)); |
678 | 844 | TRY(y_above.try_resize(macroblock_width * 4)); |
679 | 844 | TRY(u_above.try_resize(macroblock_width * 2)); |
680 | 844 | TRY(v_above.try_resize(macroblock_width * 2)); |
681 | 844 | return {}; |
682 | 844 | } |
683 | | |
684 | | void start_new_row() |
685 | 25.4k | { |
686 | 25.4k | y2_left = false; |
687 | 25.4k | for (bool& b : y_left) |
688 | 101k | b = false; |
689 | 25.4k | for (bool& b : u_left) |
690 | 50.9k | b = false; |
691 | 25.4k | for (bool& b : v_left) |
692 | 50.9k | b = false; |
693 | 25.4k | } |
694 | | |
695 | | bool& was_above_nonzero(CoefficientBlockIndex index, int mb_x) |
696 | 6.07M | { |
697 | 6.07M | if (index.is_y2()) |
698 | 105k | return y2_above[mb_x]; |
699 | 5.97M | if (index.is_u()) |
700 | 995k | return u_above[mb_x * 2 + index.sub_x()]; |
701 | 4.97M | if (index.is_v()) |
702 | 995k | return v_above[mb_x * 2 + index.sub_x()]; |
703 | 3.98M | return y_above[mb_x * 4 + index.sub_x()]; |
704 | 4.97M | } |
705 | 0 | bool was_above_nonzero(CoefficientBlockIndex index, int mb_x) const { return const_cast<CoefficientReadingContext&>(*this).was_above_nonzero(index, mb_x); } |
706 | | |
707 | | bool& was_left_nonzero(CoefficientBlockIndex index) |
708 | 6.07M | { |
709 | 6.07M | if (index.is_y2()) |
710 | 105k | return y2_left; |
711 | 5.97M | if (index.is_u()) |
712 | 995k | return u_left[index.sub_y()]; |
713 | 4.97M | if (index.is_v()) |
714 | 995k | return v_left[index.sub_y()]; |
715 | 3.98M | return y_left[index.sub_y()]; |
716 | 4.97M | } |
717 | 0 | bool was_left_nonzero(CoefficientBlockIndex index) const { return const_cast<CoefficientReadingContext&>(*this).was_left_nonzero(index); } |
718 | | |
719 | | void update(CoefficientBlockIndex index, int mb_x, bool subblock_has_nonzero_coefficients) |
720 | 3.40M | { |
721 | 3.40M | was_above_nonzero(index, mb_x) = subblock_has_nonzero_coefficients; |
722 | 3.40M | was_left_nonzero(index) = subblock_has_nonzero_coefficients; |
723 | 3.40M | } |
724 | | }; |
725 | | |
726 | | using Coefficients = i16[16]; |
727 | | |
728 | | // Returns if any non-zero coefficients were read. |
729 | | bool read_coefficent_block(BooleanDecoder& decoder, Coefficients out_coefficients, CoefficientBlockIndex block_index, CoefficientReadingContext& coefficient_reading_context, int mb_x, bool have_y2, int segment_id, FrameHeader const& header) |
730 | 2.67M | { |
731 | | // Corresponds to `residual_block()` in https://datatracker.ietf.org/doc/html/rfc6386#section-19.3, |
732 | | // but also does dequantization of the stored values. |
733 | | // "firstCoeff is 1 for luma blocks of macroblocks containing Y2 subblock; otherwise 0" |
734 | 2.67M | int firstCoeff = have_y2 && block_index.is_y() ? 1 : 0; |
735 | 2.67M | i16 last_decoded_value = num_dct_tokens; // Start with an invalid value |
736 | | |
737 | 2.67M | bool subblock_has_nonzero_coefficients = false; |
738 | | |
739 | 7.91M | for (int j = firstCoeff; j < 16; ++j) { |
740 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-13.2 "Coding of Individual Coefficient Values" |
741 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-13.3 "Token Probabilities" |
742 | | |
743 | | // "Working from the outside in, the outermost dimension is indexed by |
744 | | // the type of plane being decoded" |
745 | 7.60M | int plane = plane_index(block_index, have_y2); |
746 | | |
747 | | // "The next dimension is selected by the position of the coefficient |
748 | | // being decoded. That position, c, steps by ones up to 15, starting |
749 | | // from zero for block types 1, 2, or 3 and starting from one for block |
750 | | // type 0. The second array index is then" |
751 | | // "block type" here seems to refer to the "type of plane" in the previous paragraph. |
752 | 7.60M | static int constexpr coeff_bands[16] = { 0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7 }; |
753 | 7.60M | int band = coeff_bands[j]; |
754 | | |
755 | | // "The third dimension is the trickiest." |
756 | 7.60M | int tricky = 0; |
757 | | |
758 | | // "For the first coefficient (DC, unless the block type is 0), we |
759 | | // consider the (already encoded) blocks within the same plane (Y2, Y, |
760 | | // U, or V) above and to the left of the current block. The context |
761 | | // index is then the number (0, 1, or 2) of these blocks that had at |
762 | | // least one non-zero coefficient in their residue record. Specifically |
763 | | // for Y2, because macroblocks above and to the left may or may not have |
764 | | // a Y2 block, the block above is determined by the most recent |
765 | | // macroblock in the same column that has a Y2 block, and the block to |
766 | | // the left is determined by the most recent macroblock in the same row |
767 | | // that has a Y2 block. |
768 | | // [...] |
769 | | // As with other contexts used by VP8, the "neighboring block" context |
770 | | // described here needs a special definition for subblocks lying along |
771 | | // the top row or left edge of the frame. These "non-existent" |
772 | | // predictors above and to the left of the image are simply taken to be |
773 | | // empty -- that is, taken to contain no non-zero coefficients." |
774 | 7.60M | if (j == firstCoeff) { |
775 | 2.67M | bool was_left_nonzero = coefficient_reading_context.was_left_nonzero(block_index); |
776 | 2.67M | bool was_above_nonzero = coefficient_reading_context.was_above_nonzero(block_index, mb_x); |
777 | 2.67M | tricky = static_cast<int>(was_left_nonzero) + static_cast<int>(was_above_nonzero); |
778 | 2.67M | } |
779 | | // "Beyond the first coefficient, the context index is determined by the |
780 | | // absolute value of the most recently decoded coefficient (necessarily |
781 | | // within the current block) and is 0 if the last coefficient was a |
782 | | // zero, 1 if it was plus or minus one, and 2 if its absolute value |
783 | | // exceeded one." |
784 | 4.93M | else { |
785 | 4.93M | if (last_decoded_value == 0) |
786 | 129k | tricky = 0; |
787 | 4.80M | else if (last_decoded_value == 1 || last_decoded_value == -1) |
788 | 174k | tricky = 1; |
789 | 4.62M | else |
790 | 4.62M | tricky = 2; |
791 | 4.93M | } |
792 | | |
793 | | // "In general, all DCT coefficients are decoded using the same tree. |
794 | | // However, if the preceding coefficient is a DCT_0, decoding will skip |
795 | | // the first branch, since it is not possible for dct_eob to follow a |
796 | | // DCT_0." |
797 | 7.60M | u8 token = tree_decode(decoder, COEFFICIENT_TREE, header.coefficient_probabilities[plane][band][tricky], last_decoded_value == DCT_0 ? 2 : 0); |
798 | 7.60M | if (token == dct_eob) |
799 | 2.36M | break; |
800 | | |
801 | 5.24M | i16 v = coefficient_value_for_token(decoder, token); |
802 | | |
803 | 5.24M | if (v) { |
804 | | // Subblock has non-0 coefficients. Store that, so that `tricky` on the next subblock is initialized correctly. |
805 | 5.11M | subblock_has_nonzero_coefficients = true; |
806 | 5.11M | } |
807 | | |
808 | | // last_decoded_value is used for setting `tricky`. It needs to be set to the last decoded token, not to the last dequantized value. |
809 | 5.24M | last_decoded_value = v; |
810 | | |
811 | 5.24M | i16 dequantized_value = dequantize_value(v, j == 0, header.quantization_indices, header.segmentation, segment_id, block_index); |
812 | | |
813 | 5.24M | static int constexpr Zigzag[] = { 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 }; |
814 | 5.24M | out_coefficients[Zigzag[j]] = dequantized_value; |
815 | 5.24M | } |
816 | | |
817 | 2.67M | return subblock_has_nonzero_coefficients; |
818 | 2.67M | } |
819 | | |
820 | | struct MacroblockCoefficients { |
821 | | Coefficients y_coeffs[16] {}; |
822 | | Coefficients u_coeffs[4] {}; |
823 | | Coefficients v_coeffs[4] {}; |
824 | | }; |
825 | | |
826 | | MacroblockCoefficients read_macroblock_coefficients(BooleanDecoder& decoder, FrameHeader const& header, CoefficientReadingContext& coefficient_reading_context, MacroblockMetadata const& metadata, int mb_x) |
827 | 139k | { |
828 | | // Corresponds to `residual_data()` in https://datatracker.ietf.org/doc/html/rfc6386#section-19.3, |
829 | | // but also does the inverse walsh-hadamard transform if a Y2 block is present. |
830 | | |
831 | 139k | MacroblockCoefficients coefficients; |
832 | 139k | Coefficients y2_coeffs {}; |
833 | | |
834 | | // "firstCoeff is 1 for luma blocks of macroblocks containing Y2 subblock; otherwise 0" |
835 | | |
836 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-13 |
837 | | |
838 | | // "For all intra- and inter-prediction modes apart from B_PRED (intra: |
839 | | // whose Y subblocks are independently predicted) and SPLITMV (inter), |
840 | | // each macroblock's residue record begins with the Y2 component of the |
841 | | // residue, coded using a WHT. B_PRED and SPLITMV coded macroblocks |
842 | | // omit this WHT and specify the 0th DCT coefficient in each of the 16 Y |
843 | | // subblocks." |
844 | 139k | bool have_y2 = metadata.intra_y_mode != B_PRED; |
845 | | |
846 | | // "for Y2, because macroblocks above and to the left may or may not have |
847 | | // a Y2 block, the block above is determined by the most recent |
848 | | // macroblock in the same column that has a Y2 block, and the block to |
849 | | // the left is determined by the most recent macroblock in the same row |
850 | | // that has a Y2 block." |
851 | | // We only write to y2_above / y2_left when it's present, so we don't need to do any explicit work to get the right behavior. |
852 | | |
853 | | // "After the optional Y2 block, the residue record continues with 16 |
854 | | // DCTs for the Y subblocks, followed by 4 DCTs for the U subblocks, |
855 | | // ending with 4 DCTs for the V subblocks. The subblocks occur in the |
856 | | // usual order." |
857 | | |
858 | | /* (1 Y2)?, 16 Y, 4 U, 4 V */ |
859 | 3.54M | for (int i = have_y2 ? 0 : 1; i < 25; ++i) { |
860 | 3.40M | CoefficientBlockIndex block_index { i }; |
861 | | |
862 | 3.40M | bool subblock_has_nonzero_coefficients = false; |
863 | | |
864 | 3.40M | if (!metadata.skip_coefficients) { |
865 | 2.67M | i16* to_read; |
866 | 2.67M | if (block_index.is_y2()) |
867 | 41.8k | to_read = y2_coeffs; |
868 | 2.63M | else if (block_index.is_u()) |
869 | 438k | to_read = coefficients.u_coeffs[i - 17]; |
870 | 2.19M | else if (block_index.is_v()) |
871 | 438k | to_read = coefficients.v_coeffs[i - 21]; |
872 | 1.75M | else // Y |
873 | 1.75M | to_read = coefficients.y_coeffs[i - 1]; |
874 | 2.67M | subblock_has_nonzero_coefficients = read_coefficent_block(decoder, to_read, block_index, coefficient_reading_context, mb_x, have_y2, metadata.segment_id, header); |
875 | 2.67M | } |
876 | | |
877 | 3.40M | coefficient_reading_context.update(block_index, mb_x, subblock_has_nonzero_coefficients); |
878 | 3.40M | } |
879 | | |
880 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-14.2 "Inverse Transforms" |
881 | | // "If the Y2 residue block exists (i.e., the macroblock luma mode is not |
882 | | // SPLITMV or B_PRED), it is inverted first (using the inverse WHT) and |
883 | | // the element of the result at row i, column j is used as the 0th |
884 | | // coefficient of the Y subblock at position (i, j), that is, the Y |
885 | | // subblock whose index is (i * 4) + j." |
886 | 139k | if (have_y2) { |
887 | 63.2k | Coefficients wht_output; |
888 | 63.2k | vp8_short_inv_walsh4x4_c(y2_coeffs, wht_output); |
889 | 1.07M | for (size_t i = 0; i < 16; ++i) |
890 | 1.01M | coefficients.y_coeffs[i][0] = wht_output[i]; |
891 | 63.2k | } |
892 | | |
893 | 139k | return coefficients; |
894 | 139k | } |
895 | | |
896 | | template<int N> |
897 | | void predict_macroblock(Bytes prediction, IntraMacroblockMode mode, int mb_x, int mb_y, ReadonlyBytes left, ReadonlyBytes above, u8 truemotion_corner) |
898 | 341k | { |
899 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-12.2 "Chroma Prediction" |
900 | | // (Also used for the DC_PRED, H_PRED, V_PRED, TM_PRED for luma prediction.) |
901 | 341k | if (mode == DC_PRED) { |
902 | 170k | if (mb_x == 0 && mb_y == 0) { |
903 | 103k | for (size_t i = 0; i < N * N; ++i) |
904 | 102k | prediction[i] = 128; |
905 | 169k | } else { |
906 | 169k | int sum = 0, n = 0; |
907 | 169k | if (mb_x > 0) { |
908 | 1.38M | for (int i = 0; i < N; ++i) |
909 | 1.24M | sum += left[i]; |
910 | 143k | n += N; |
911 | 143k | } |
912 | 169k | if (mb_y > 0) { |
913 | 1.25M | for (int i = 0; i < N; ++i) |
914 | 1.12M | sum += above[mb_x * N + i]; |
915 | 130k | n += N; |
916 | 130k | } |
917 | 169k | u8 average = (sum + n / 2) / n; |
918 | 13.7M | for (size_t i = 0; i < N * N; ++i) |
919 | 13.6M | prediction[i] = average; |
920 | 169k | } |
921 | 170k | } else if (mode == H_PRED) { |
922 | 363k | for (int y = 0; y < N; ++y) |
923 | 3.83M | for (int x = 0; x < N; ++x) |
924 | 3.50M | prediction[y * N + x] = left[y]; |
925 | 136k | } else if (mode == V_PRED) { |
926 | 402k | for (int y = 0; y < N; ++y) |
927 | 4.32M | for (int x = 0; x < N; ++x) |
928 | 3.96M | prediction[y * N + x] = above[mb_x * N + x]; |
929 | 98.6k | } else { |
930 | 98.6k | VERIFY(mode == TM_PRED); |
931 | 1.15M | for (int y = 0; y < N; ++y) |
932 | 13.8M | for (int x = 0; x < N; ++x) |
933 | 12.8M | prediction[y * N + x] = clamp(left[y] + above[mb_x * N + x] - truemotion_corner, 0, 255); |
934 | 98.6k | } |
935 | 341k | } WebPLoaderLossy.cpp:void Gfx::(anonymous namespace)::predict_macroblock<16>(AK::Span<unsigned char>, Gfx::IntraMacroblockMode, int, int, AK::Span<unsigned char const>, AK::Span<unsigned char const>, unsigned char) Line | Count | Source | 898 | 63.2k | { | 899 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-12.2 "Chroma Prediction" | 900 | | // (Also used for the DC_PRED, H_PRED, V_PRED, TM_PRED for luma prediction.) | 901 | 63.2k | if (mode == DC_PRED) { | 902 | 14.3k | if (mb_x == 0 && mb_y == 0) { | 903 | 17.4k | for (size_t i = 0; i < N * N; ++i) | 904 | 17.4k | prediction[i] = 128; | 905 | 14.3k | } else { | 906 | 14.3k | int sum = 0, n = 0; | 907 | 14.3k | if (mb_x > 0) { | 908 | 199k | for (int i = 0; i < N; ++i) | 909 | 187k | sum += left[i]; | 910 | 11.7k | n += N; | 911 | 11.7k | } | 912 | 14.3k | if (mb_y > 0) { | 913 | 177k | for (int i = 0; i < N; ++i) | 914 | 167k | sum += above[mb_x * N + i]; | 915 | 10.4k | n += N; | 916 | 10.4k | } | 917 | 14.3k | u8 average = (sum + n / 2) / n; | 918 | 3.67M | for (size_t i = 0; i < N * N; ++i) | 919 | 3.66M | prediction[i] = average; | 920 | 14.3k | } | 921 | 48.8k | } else if (mode == H_PRED) { | 922 | 115k | for (int y = 0; y < N; ++y) | 923 | 1.84M | for (int x = 0; x < N; ++x) | 924 | 1.73M | prediction[y * N + x] = left[y]; | 925 | 42.0k | } else if (mode == V_PRED) { | 926 | 138k | for (int y = 0; y < N; ++y) | 927 | 2.22M | for (int x = 0; x < N; ++x) | 928 | 2.08M | prediction[y * N + x] = above[mb_x * N + x]; | 929 | 33.9k | } else { | 930 | 33.9k | VERIFY(mode == TM_PRED); | 931 | 576k | for (int y = 0; y < N; ++y) | 932 | 9.22M | for (int x = 0; x < N; ++x) | 933 | 8.68M | prediction[y * N + x] = clamp(left[y] + above[mb_x * N + x] - truemotion_corner, 0, 255); | 934 | 33.9k | } | 935 | 63.2k | } |
WebPLoaderLossy.cpp:void Gfx::(anonymous namespace)::predict_macroblock<8>(AK::Span<unsigned char>, Gfx::IntraMacroblockMode, int, int, AK::Span<unsigned char const>, AK::Span<unsigned char const>, unsigned char) Line | Count | Source | 898 | 278k | { | 899 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-12.2 "Chroma Prediction" | 900 | | // (Also used for the DC_PRED, H_PRED, V_PRED, TM_PRED for luma prediction.) | 901 | 278k | if (mode == DC_PRED) { | 902 | 156k | if (mb_x == 0 && mb_y == 0) { | 903 | 86.0k | for (size_t i = 0; i < N * N; ++i) | 904 | 84.7k | prediction[i] = 128; | 905 | 155k | } else { | 906 | 155k | int sum = 0, n = 0; | 907 | 155k | if (mb_x > 0) { | 908 | 1.18M | for (int i = 0; i < N; ++i) | 909 | 1.05M | sum += left[i]; | 910 | 132k | n += N; | 911 | 132k | } | 912 | 155k | if (mb_y > 0) { | 913 | 1.07M | for (int i = 0; i < N; ++i) | 914 | 958k | sum += above[mb_x * N + i]; | 915 | 119k | n += N; | 916 | 119k | } | 917 | 155k | u8 average = (sum + n / 2) / n; | 918 | 10.0M | for (size_t i = 0; i < N * N; ++i) | 919 | 9.93M | prediction[i] = average; | 920 | 155k | } | 921 | 156k | } else if (mode == H_PRED) { | 922 | 248k | for (int y = 0; y < N; ++y) | 923 | 1.98M | for (int x = 0; x < N; ++x) | 924 | 1.76M | prediction[y * N + x] = left[y]; | 925 | 94.0k | } else if (mode == V_PRED) { | 926 | 263k | for (int y = 0; y < N; ++y) | 927 | 2.10M | for (int x = 0; x < N; ++x) | 928 | 1.87M | prediction[y * N + x] = above[mb_x * N + x]; | 929 | 64.7k | } else { | 930 | 64.7k | VERIFY(mode == TM_PRED); | 931 | 583k | for (int y = 0; y < N; ++y) | 932 | 4.66M | for (int x = 0; x < N; ++x) | 933 | 4.14M | prediction[y * N + x] = clamp(left[y] + above[mb_x * N + x] - truemotion_corner, 0, 255); | 934 | 64.7k | } | 935 | 278k | } |
|
936 | | |
937 | | void predict_y_subblock(Bytes y_prediction, IntraBlockMode mode, int x, int y, ReadonlyBytes left, ReadonlyBytes above, u8 corner) |
938 | 1.21M | { |
939 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-12.3 "Luma Prediction" |
940 | | // Roughly corresponds to "subblock_intra_predict()" in the spec. |
941 | 1.37M | auto weighted_average = [](u8 x, u8 y, u8 z) { return (x + 2 * y + z + 2) / 4; }; |
942 | 1.21M | auto average = [](u8 x, u8 y) { return (x + y + 1) / 2; }; |
943 | | |
944 | 1.21M | auto at = [&y_prediction, y, x](int px, int py) -> u8& { return y_prediction[(4 * y + py) * 16 + 4 * x + px]; }; |
945 | | |
946 | 1.21M | if (mode == B_DC_PRED) { |
947 | | // The spec text says this is like DC_PRED, but predict_dc_nxn() in the sample implementation doesn't do the "oob isn't read" part. |
948 | 991k | int sum = 0, n = 8; |
949 | 4.95M | for (int i = 0; i < 4; ++i) |
950 | 3.96M | sum += left[i] + above[i]; |
951 | 991k | u8 average = (sum + n / 2) / n; |
952 | 4.95M | for (int py = 0; py < 4; ++py) |
953 | 19.8M | for (int px = 0; px < 4; ++px) |
954 | 15.8M | y_prediction[(4 * y + py) * 16 + 4 * x + px] = average; |
955 | 991k | } else if (mode == B_TM_PRED) { |
956 | 437k | for (int py = 0; py < 4; ++py) |
957 | 1.75M | for (int px = 0; px < 4; ++px) |
958 | 1.40M | y_prediction[(4 * y + py) * 16 + 4 * x + px] = clamp(left[py] + above[px] - corner, 0, 255); |
959 | 135k | } else if (mode == B_VE_PRED) { |
960 | | // The spec text says this is like V_PRED, but the sample implementation shows it does weighted averages (unlike V_PRED). |
961 | 165k | for (int py = 0; py < 4; ++py) |
962 | 662k | for (int px = 0; px < 4; ++px) { |
963 | 530k | auto top_left = (px > 0 ? above[px - 1] : corner); |
964 | 530k | y_prediction[(4 * y + py) * 16 + 4 * x + px] = weighted_average(top_left, above[px], above[px + 1]); |
965 | 530k | } |
966 | 102k | } else if (mode == B_HE_PRED) { |
967 | | // The spec text says this is like H_PRED, but the sample implementation shows it does weighted averages (unlike H_PRED). |
968 | 129k | for (int py = 0; py < 4; ++py) |
969 | 519k | for (int px = 0; px < 4; ++px) { |
970 | 415k | if (py == 0) { |
971 | 103k | y_prediction[(4 * y + py) * 16 + 4 * x + px] = weighted_average(corner, left[py], left[py + 1]); |
972 | 311k | } else if (py == 3) { |
973 | | /* Bottom row is exceptional because L[4] does not exist */ |
974 | 103k | y_prediction[(4 * y + py) * 16 + 4 * x + px] = weighted_average(left[2], left[3], left[3]); |
975 | 207k | } else { |
976 | 207k | y_prediction[(4 * y + py) * 16 + 4 * x + px] = weighted_average(left[py - 1], left[py], left[py + 1]); |
977 | 207k | } |
978 | 415k | } |
979 | 76.1k | } else if (mode == B_LD_PRED) { |
980 | | // this is 45-deg prediction from above, going left-down (i.e. isochromes on -1/+1 diags) |
981 | 11.9k | at(0, 0) = weighted_average(above[0], above[1], above[2]); |
982 | 11.9k | at(0, 1) = at(1, 0) = weighted_average(above[1], above[2], above[3]); |
983 | 11.9k | at(0, 2) = at(1, 1) = at(2, 0) = weighted_average(above[2], above[3], above[4]); |
984 | 11.9k | at(0, 3) = at(1, 2) = at(2, 1) = at(3, 0) = weighted_average(above[3], above[4], above[5]); |
985 | 11.9k | at(1, 3) = at(2, 2) = at(3, 1) = weighted_average(above[4], above[5], above[6]); |
986 | 11.9k | at(2, 3) = at(3, 2) = weighted_average(above[5], above[6], above[7]); |
987 | 11.9k | at(3, 3) = weighted_average(above[6], above[7], above[7]); // intentionally 6, 7, 7 |
988 | 64.2k | } else if (mode == B_RD_PRED) { |
989 | | // this is 45-deg prediction from above / left, going right-down (i.e. isochromes on +1/+1 diags) |
990 | 9.17k | at(0, 3) = weighted_average(left[3], left[2], left[1]); |
991 | 9.17k | at(0, 2) = at(1, 3) = weighted_average(left[2], left[1], left[0]); |
992 | 9.17k | at(0, 1) = at(1, 2) = at(2, 3) = weighted_average(left[1], left[0], corner); |
993 | 9.17k | at(0, 0) = at(1, 1) = at(2, 2) = at(3, 3) = weighted_average(left[0], corner, above[0]); |
994 | 9.17k | at(1, 0) = at(2, 1) = at(3, 2) = weighted_average(corner, above[0], above[1]); |
995 | 9.17k | at(2, 0) = at(3, 1) = weighted_average(above[0], above[1], above[2]); |
996 | 9.17k | at(3, 0) = weighted_average(above[1], above[2], above[3]); |
997 | 55.0k | } else if (mode == B_VR_PRED) { |
998 | | // this is 22.5-deg prediction |
999 | 12.7k | at(0, 3) = weighted_average(left[2], left[1], left[0]); |
1000 | 12.7k | at(0, 2) = weighted_average(left[1], left[0], corner); |
1001 | 12.7k | at(1, 3) = at(0, 1) = weighted_average(left[0], corner, above[0]); |
1002 | 12.7k | at(1, 2) = at(0, 0) = average(corner, above[0]); |
1003 | 12.7k | at(2, 3) = at(1, 1) = weighted_average(corner, above[0], above[1]); |
1004 | 12.7k | at(2, 2) = at(1, 0) = average(above[0], above[1]); |
1005 | 12.7k | at(3, 3) = at(2, 1) = weighted_average(above[0], above[1], above[2]); |
1006 | 12.7k | at(3, 2) = at(2, 0) = average(above[1], above[2]); |
1007 | 12.7k | at(3, 1) = weighted_average(above[1], above[2], above[3]); |
1008 | 12.7k | at(3, 0) = average(above[2], above[3]); |
1009 | 42.2k | } else if (mode == B_VL_PRED) { |
1010 | | // this is 22.5-deg prediction |
1011 | 13.8k | at(0, 0) = average(above[0], above[1]); |
1012 | 13.8k | at(0, 1) = weighted_average(above[0], above[1], above[2]); |
1013 | 13.8k | at(0, 2) = at(1, 0) = average(above[1], above[2]); |
1014 | 13.8k | at(1, 1) = at(0, 3) = weighted_average(above[1], above[2], above[3]); |
1015 | 13.8k | at(1, 2) = at(2, 0) = average(above[2], above[3]); |
1016 | 13.8k | at(1, 3) = at(2, 1) = weighted_average(above[2], above[3], above[4]); |
1017 | 13.8k | at(2, 2) = at(3, 0) = average(above[3], above[4]); |
1018 | 13.8k | at(2, 3) = at(3, 1) = weighted_average(above[3], above[4], above[5]); |
1019 | | /* Last two values do not strictly follow the pattern. */ |
1020 | 13.8k | at(3, 2) = weighted_average(above[4], above[5], above[6]); |
1021 | 13.8k | at(3, 3) = weighted_average(above[5], above[6], above[7]); |
1022 | 28.3k | } else if (mode == B_HD_PRED) { |
1023 | | // this is 22.5-deg prediction |
1024 | 10.8k | at(0, 3) = average(left[3], left[2]); |
1025 | 10.8k | at(1, 3) = weighted_average(left[3], left[2], left[1]); |
1026 | 10.8k | at(0, 2) = at(2, 3) = average(left[2], left[1]); |
1027 | 10.8k | at(1, 2) = at(3, 3) = weighted_average(left[2], left[1], left[0]); |
1028 | 10.8k | at(2, 2) = at(0, 1) = average(left[1], left[0]); |
1029 | 10.8k | at(3, 2) = at(1, 1) = weighted_average(left[1], left[0], corner); |
1030 | 10.8k | at(2, 1) = at(0, 0) = average(left[0], corner); |
1031 | 10.8k | at(3, 1) = at(1, 0) = weighted_average(left[0], corner, above[0]); |
1032 | 10.8k | at(2, 0) = weighted_average(corner, above[0], above[1]); |
1033 | 10.8k | at(3, 0) = weighted_average(above[0], above[1], above[2]); |
1034 | 17.5k | } else { |
1035 | 17.5k | VERIFY(mode == B_HU_PRED); |
1036 | | // this is 22.5-deg prediction |
1037 | 17.5k | at(0, 0) = average(left[0], left[1]); |
1038 | 17.5k | at(1, 0) = weighted_average(left[0], left[1], left[2]); |
1039 | 17.5k | at(2, 0) = at(0, 1) = average(left[1], left[2]); |
1040 | 17.5k | at(3, 0) = at(1, 1) = weighted_average(left[1], left[2], left[3]); |
1041 | 17.5k | at(2, 1) = at(0, 2) = average(left[2], left[3]); |
1042 | 17.5k | at(3, 1) = at(1, 2) = weighted_average(left[2], left[3], left[3]); // Intentionally 2, 3, 3 |
1043 | | /* Not possible to follow pattern for much of the bottom |
1044 | | row because no (nearby) already-constructed pixels lie |
1045 | | on the diagonals in question. */ |
1046 | 17.5k | at(2, 2) = at(3, 2) = at(0, 3) = at(1, 3) = at(2, 3) = at(3, 3) = left[3]; |
1047 | 17.5k | } |
1048 | 1.21M | } |
1049 | | |
1050 | | template<int N> |
1051 | | void add_idct_to_prediction(Bytes prediction, Coefficients coefficients, int x, int y) |
1052 | 3.33M | { |
1053 | 3.33M | Coefficients idct_output; |
1054 | 3.33M | short_idct4x4llm_c(coefficients, idct_output, 4 * sizeof(i16)); |
1055 | | |
1056 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-14.5 "Summation of Predictor and Residue" |
1057 | | // FIXME: Could omit the clamp() call if FrameHeader.clamping_type == ClampingSpecification::NoClampingNecessary. |
1058 | 16.6M | for (int py = 0; py < 4; ++py) { |
1059 | 66.7M | for (int px = 0; px < 4; ++px) { |
1060 | 53.4M | u8& p = prediction[(4 * y + py) * N + (4 * x + px)]; |
1061 | 53.4M | p = clamp(p + idct_output[py * 4 + px], 0, 255); |
1062 | 53.4M | } |
1063 | 13.3M | } |
1064 | 3.33M | } WebPLoaderLossy.cpp:void Gfx::(anonymous namespace)::add_idct_to_prediction<16>(AK::Span<unsigned char>, short*, int, int) Line | Count | Source | 1052 | 2.22M | { | 1053 | 2.22M | Coefficients idct_output; | 1054 | 2.22M | short_idct4x4llm_c(coefficients, idct_output, 4 * sizeof(i16)); | 1055 | | | 1056 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-14.5 "Summation of Predictor and Residue" | 1057 | | // FIXME: Could omit the clamp() call if FrameHeader.clamping_type == ClampingSpecification::NoClampingNecessary. | 1058 | 11.1M | for (int py = 0; py < 4; ++py) { | 1059 | 44.5M | for (int px = 0; px < 4; ++px) { | 1060 | 35.6M | u8& p = prediction[(4 * y + py) * N + (4 * x + px)]; | 1061 | 35.6M | p = clamp(p + idct_output[py * 4 + px], 0, 255); | 1062 | 35.6M | } | 1063 | 8.90M | } | 1064 | 2.22M | } |
WebPLoaderLossy.cpp:void Gfx::(anonymous namespace)::add_idct_to_prediction<8>(AK::Span<unsigned char>, short*, int, int) Line | Count | Source | 1052 | 1.11M | { | 1053 | 1.11M | Coefficients idct_output; | 1054 | 1.11M | short_idct4x4llm_c(coefficients, idct_output, 4 * sizeof(i16)); | 1055 | | | 1056 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-14.5 "Summation of Predictor and Residue" | 1057 | | // FIXME: Could omit the clamp() call if FrameHeader.clamping_type == ClampingSpecification::NoClampingNecessary. | 1058 | 5.56M | for (int py = 0; py < 4; ++py) { | 1059 | 22.2M | for (int px = 0; px < 4; ++px) { | 1060 | 17.8M | u8& p = prediction[(4 * y + py) * N + (4 * x + px)]; | 1061 | 17.8M | p = clamp(p + idct_output[py * 4 + px], 0, 255); | 1062 | 17.8M | } | 1063 | 4.45M | } | 1064 | 1.11M | } |
|
1065 | | |
1066 | | template<int N> |
1067 | | void process_macroblock(Bytes output, IntraMacroblockMode mode, int mb_x, int mb_y, ReadonlyBytes left, ReadonlyBytes above, u8 truemotion_corner, Coefficients coefficients_array[]) |
1068 | 341k | { |
1069 | 341k | predict_macroblock<4 * N>(output, mode, mb_x, mb_y, left, above, truemotion_corner); |
1070 | | |
1071 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-14.4 "Implementation of the DCT Inversion" |
1072 | | // Loop over the 4x4 subblocks |
1073 | 1.15M | for (int y = 0, i = 0; y < N; ++y) |
1074 | 2.93M | for (int x = 0; x < N; ++x, ++i) |
1075 | 2.12M | add_idct_to_prediction<4 * N>(output, coefficients_array[i], x, y); |
1076 | 341k | } WebPLoaderLossy.cpp:void Gfx::(anonymous namespace)::process_macroblock<4>(AK::Span<unsigned char>, Gfx::IntraMacroblockMode, int, int, AK::Span<unsigned char const>, AK::Span<unsigned char const>, unsigned char, short (*) [16]) Line | Count | Source | 1068 | 63.2k | { | 1069 | 63.2k | predict_macroblock<4 * N>(output, mode, mb_x, mb_y, left, above, truemotion_corner); | 1070 | | | 1071 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-14.4 "Implementation of the DCT Inversion" | 1072 | | // Loop over the 4x4 subblocks | 1073 | 316k | for (int y = 0, i = 0; y < N; ++y) | 1074 | 1.26M | for (int x = 0; x < N; ++x, ++i) | 1075 | 1.01M | add_idct_to_prediction<4 * N>(output, coefficients_array[i], x, y); | 1076 | 63.2k | } |
WebPLoaderLossy.cpp:void Gfx::(anonymous namespace)::process_macroblock<2>(AK::Span<unsigned char>, Gfx::IntraMacroblockMode, int, int, AK::Span<unsigned char const>, AK::Span<unsigned char const>, unsigned char, short (*) [16]) Line | Count | Source | 1068 | 278k | { | 1069 | 278k | predict_macroblock<4 * N>(output, mode, mb_x, mb_y, left, above, truemotion_corner); | 1070 | | | 1071 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-14.4 "Implementation of the DCT Inversion" | 1072 | | // Loop over the 4x4 subblocks | 1073 | 834k | for (int y = 0, i = 0; y < N; ++y) | 1074 | 1.66M | for (int x = 0; x < N; ++x, ++i) | 1075 | 1.11M | add_idct_to_prediction<4 * N>(output, coefficients_array[i], x, y); | 1076 | 278k | } |
|
1077 | | |
1078 | | void process_subblocks(Bytes y_output, MacroblockMetadata const& metadata, int mb_x, ReadonlyBytes predicted_y_left, ReadonlyBytes predicted_y_above, u8 y_truemotion_corner, Coefficients coefficients_array[], int macroblock_width) |
1079 | 75.9k | { |
1080 | | // Loop over the 4x4 subblocks |
1081 | 379k | for (int y = 0; y < 4; ++y) { |
1082 | 1.51M | for (int x = 0; x < 4; ++x) { |
1083 | 1.21M | u8 corner = y_truemotion_corner; |
1084 | 1.21M | if (x > 0 && y == 0) |
1085 | 227k | corner = predicted_y_above[mb_x * 16 + 4 * x - 1]; |
1086 | 986k | else if (x > 0 && y > 0) |
1087 | 683k | corner = y_output[(4 * y - 1) * 16 + 4 * x - 1]; |
1088 | 303k | else if (x == 0 && y > 0) |
1089 | 227k | corner = predicted_y_left[4 * y - 1]; |
1090 | | |
1091 | 1.21M | u8 left[4], above[8]; |
1092 | 6.07M | for (int i = 0; i < 4; ++i) { |
1093 | 4.85M | if (x == 0) |
1094 | 1.21M | left[i] = predicted_y_left[4 * y + i]; |
1095 | 3.64M | else |
1096 | 3.64M | left[i] = y_output[(4 * y + i) * 16 + 4 * x - 1]; |
1097 | 4.85M | } |
1098 | | // Subblock prediction can read 8 pixels above the block. |
1099 | | // For rightmost subblocks, the right 4 pixels there aren't initialized yet, so those get the 4 pixels to the right above the macroblock. |
1100 | | // For the rightmost macroblock, there's no macroblock to its right, so there they get the rightmost pixel above. |
1101 | | // But in the 0th row, there's no pixel above, so there they become 127. |
1102 | 10.9M | for (int i = 0; i < 8; ++i) { |
1103 | 9.71M | if (x == 3 && i >= 4) { // rightmost subblock, 4 right pixels? |
1104 | 1.21M | if (mb_x == macroblock_width - 1) { // rightmost macroblock |
1105 | | // predicted_y_above is initialized to 127 above the first row, so no need for an explicit branch for mb_y == 0. |
1106 | 183k | above[i] = predicted_y_above[mb_x * 16 + 4 * x + 3]; |
1107 | 1.03M | } else { |
1108 | 1.03M | above[i] = predicted_y_above[mb_x * 16 + 4 * x + i]; |
1109 | 1.03M | } |
1110 | 8.50M | } else if (y == 0) { |
1111 | 2.12M | above[i] = predicted_y_above[mb_x * 16 + 4 * x + i]; |
1112 | 6.37M | } else { |
1113 | 6.37M | above[i] = y_output[(4 * y - 1) * 16 + 4 * x + i]; |
1114 | 6.37M | } |
1115 | 9.71M | } |
1116 | | |
1117 | 1.21M | predict_y_subblock(y_output, metadata.intra_b_modes[y * 4 + x], x, y, left, above, corner); |
1118 | | |
1119 | | // Have to do IDCT summation here, since its results affect prediction of next subblock already. |
1120 | 1.21M | add_idct_to_prediction<16>(y_output, coefficients_array[4 * y + x], x, y); |
1121 | 1.21M | } |
1122 | 303k | } |
1123 | 75.9k | } |
1124 | | |
1125 | | void convert_yuv_to_rgb(Bitmap& bitmap, int mb_x, int mb_y, ReadonlyBytes y_data, ReadonlyBytes u_data, ReadonlyBytes v_data) |
1126 | 139k | { |
1127 | 2.36M | for (int y = 0; y < 16; ++y) { |
1128 | 37.8M | for (int x = 0; x < 16; ++x) { |
1129 | 35.6M | u8 Y = y_data[y * 16 + x]; |
1130 | | |
1131 | | // FIXME: Could do nicer upsampling than just nearest neighbor |
1132 | 35.6M | u8 U = u_data[(y / 2) * 8 + x / 2]; |
1133 | 35.6M | u8 V = v_data[(y / 2) * 8 + x / 2]; |
1134 | | |
1135 | | // XXX: These numbers are from the fixed-point values in libwebp's yuv.h. There's probably a better reference somewhere. |
1136 | 35.6M | int r = 1.1655 * Y + 1.596 * V - 222.4; |
1137 | 35.6M | int g = 1.1655 * Y - 0.3917 * U - 0.8129 * V + 136.0625; |
1138 | 35.6M | int b = 1.1655 * Y + 2.0172 * U - 276.33; |
1139 | | |
1140 | 35.6M | bitmap.scanline(mb_y * 16 + y)[mb_x * 16 + x] = Color(clamp(r, 0, 255), clamp(g, 0, 255), clamp(b, 0, 255)).value(); |
1141 | 35.6M | } |
1142 | 2.22M | } |
1143 | 139k | } |
1144 | | |
1145 | | ErrorOr<void> decode_VP8_image_data(Gfx::Bitmap& bitmap, FrameHeader const& header, Vector<ReadonlyBytes> data_partitions, int macroblock_width, int macroblock_height, Vector<MacroblockMetadata> const& macroblock_metadata) |
1146 | 852 | { |
1147 | | |
1148 | 852 | Vector<BooleanDecoder> streams; |
1149 | 944 | for (auto data : data_partitions) { |
1150 | 944 | auto decoder = TRY(BooleanDecoder::initialize(data)); |
1151 | 936 | TRY(streams.try_append(move(decoder))); |
1152 | 936 | } |
1153 | | |
1154 | 852 | CoefficientReadingContext coefficient_reading_context; |
1155 | 844 | TRY(coefficient_reading_context.initialize(macroblock_width)); |
1156 | | |
1157 | 844 | Vector<u8> predicted_y_above; |
1158 | 844 | TRY(predicted_y_above.try_resize(macroblock_width * 16)); |
1159 | 573k | for (size_t i = 0; i < predicted_y_above.size(); ++i) |
1160 | 572k | predicted_y_above[i] = 127; |
1161 | | |
1162 | 844 | Vector<u8> predicted_u_above; |
1163 | 844 | TRY(predicted_u_above.try_resize(macroblock_width * 8)); |
1164 | 287k | for (size_t i = 0; i < predicted_u_above.size(); ++i) |
1165 | 286k | predicted_u_above[i] = 127; |
1166 | | |
1167 | 844 | Vector<u8> predicted_v_above; |
1168 | 844 | TRY(predicted_v_above.try_resize(macroblock_width * 8)); |
1169 | 287k | for (size_t i = 0; i < predicted_v_above.size(); ++i) |
1170 | 286k | predicted_v_above[i] = 127; |
1171 | | |
1172 | 26.3k | for (int mb_y = 0, macroblock_index = 0; mb_y < macroblock_height; ++mb_y) { |
1173 | 25.4k | BooleanDecoder& decoder = streams[mb_y % streams.size()]; |
1174 | | |
1175 | 25.4k | coefficient_reading_context.start_new_row(); |
1176 | | |
1177 | 25.4k | u8 predicted_y_left[16] { 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129 }; |
1178 | 25.4k | u8 predicted_u_left[8] { 129, 129, 129, 129, 129, 129, 129, 129 }; |
1179 | 25.4k | u8 predicted_v_left[8] { 129, 129, 129, 129, 129, 129, 129, 129 }; |
1180 | | |
1181 | | // The spec doesn't say if this should be 127, 129, or something else. |
1182 | | // But ReconstructRow in frame_dec.c in libwebp suggests 129. |
1183 | 25.4k | u8 y_truemotion_corner = 129; |
1184 | 25.4k | u8 u_truemotion_corner = 129; |
1185 | 25.4k | u8 v_truemotion_corner = 129; |
1186 | | |
1187 | 164k | for (int mb_x = 0; mb_x < macroblock_width; ++mb_x, ++macroblock_index) { |
1188 | 139k | auto const& metadata = macroblock_metadata[macroblock_index]; |
1189 | | |
1190 | 139k | auto coefficients = read_macroblock_coefficients(decoder, header, coefficient_reading_context, metadata, mb_x); |
1191 | | |
1192 | 139k | u8 y_data[16 * 16] {}; |
1193 | 139k | if (metadata.intra_y_mode == B_PRED) |
1194 | 75.9k | process_subblocks(y_data, metadata, mb_x, predicted_y_left, predicted_y_above, y_truemotion_corner, coefficients.y_coeffs, macroblock_width); |
1195 | 63.2k | else |
1196 | 63.2k | process_macroblock<4>(y_data, metadata.intra_y_mode, mb_x, mb_y, predicted_y_left, predicted_y_above, y_truemotion_corner, coefficients.y_coeffs); |
1197 | | |
1198 | 139k | u8 u_data[8 * 8] {}; |
1199 | 139k | process_macroblock<2>(u_data, metadata.uv_mode, mb_x, mb_y, predicted_u_left, predicted_u_above, u_truemotion_corner, coefficients.u_coeffs); |
1200 | | |
1201 | 139k | u8 v_data[8 * 8] {}; |
1202 | 139k | process_macroblock<2>(v_data, metadata.uv_mode, mb_x, mb_y, predicted_v_left, predicted_v_above, v_truemotion_corner, coefficients.v_coeffs); |
1203 | | |
1204 | | // FIXME: insert loop filtering here |
1205 | | |
1206 | 139k | convert_yuv_to_rgb(bitmap, mb_x, mb_y, y_data, u_data, v_data); |
1207 | | |
1208 | 139k | y_truemotion_corner = predicted_y_above[mb_x * 16 + 15]; |
1209 | 2.36M | for (int i = 0; i < 16; ++i) |
1210 | 2.22M | predicted_y_left[i] = y_data[15 + i * 16]; |
1211 | 2.36M | for (int i = 0; i < 16; ++i) |
1212 | 2.22M | predicted_y_above[mb_x * 16 + i] = y_data[15 * 16 + i]; |
1213 | | |
1214 | 139k | u_truemotion_corner = predicted_u_above[mb_x * 8 + 7]; |
1215 | 1.25M | for (int i = 0; i < 8; ++i) |
1216 | 1.11M | predicted_u_left[i] = u_data[7 + i * 8]; |
1217 | 1.25M | for (int i = 0; i < 8; ++i) |
1218 | 1.11M | predicted_u_above[mb_x * 8 + i] = u_data[7 * 8 + i]; |
1219 | | |
1220 | 139k | v_truemotion_corner = predicted_v_above[mb_x * 8 + 7]; |
1221 | 1.25M | for (int i = 0; i < 8; ++i) |
1222 | 1.11M | predicted_v_left[i] = v_data[7 + i * 8]; |
1223 | 1.25M | for (int i = 0; i < 8; ++i) |
1224 | 1.11M | predicted_v_above[mb_x * 8 + i] = v_data[7 * 8 + i]; |
1225 | 139k | } |
1226 | 25.4k | } |
1227 | | |
1228 | 844 | for (auto& decoder : streams) |
1229 | 861 | TRY(decoder.finish_decode()); |
1230 | | |
1231 | 844 | return {}; |
1232 | 844 | } |
1233 | | |
1234 | | static ErrorOr<Vector<ReadonlyBytes>> split_data_partitions(ReadonlyBytes second_partition, u8 number_of_dct_partitions) |
1235 | 918 | { |
1236 | 918 | Vector<ReadonlyBytes> data_partitions; |
1237 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-9.5 "Token Partition and Partition Data Offsets" |
1238 | | // "If the number of data partitions is |
1239 | | // greater than 1, the size of each partition (except the last) is |
1240 | | // written in 3 bytes (24 bits). The size of the last partition is the |
1241 | | // remainder of the data not used by any of the previous partitions. |
1242 | | // The partitioned data are consecutive in the bitstream, so the size |
1243 | | // can also be used to calculate the offset of each partition." |
1244 | | // In practice, virtually all lossy webp files have a single data partition. |
1245 | 918 | VERIFY(number_of_dct_partitions >= 1); |
1246 | 918 | VERIFY(number_of_dct_partitions <= 8); |
1247 | | |
1248 | 918 | size_t sizes_size = (number_of_dct_partitions - 1) * 3; |
1249 | 918 | if (second_partition.size() < sizes_size) |
1250 | 5 | return Error::from_string_literal("WebPImageDecoderPlugin: not enough data for partition sizes"); |
1251 | | |
1252 | 913 | ReadonlyBytes sizes = second_partition.slice(0, sizes_size); |
1253 | 913 | ReadonlyBytes data = second_partition.slice(sizes_size); |
1254 | | |
1255 | 1.10k | for (int i = 0; i < number_of_dct_partitions - 1; ++i) { |
1256 | 253 | u32 partition_size = sizes[0] | (sizes[1] << 8) | (sizes[2] << 16); |
1257 | 253 | dbgln_if(WEBP_DEBUG, "partition_size {}", partition_size); |
1258 | 253 | sizes = sizes.slice(3); |
1259 | 253 | if (partition_size > data.size()) |
1260 | 61 | return Error::from_string_literal("WebPImageDecoderPlugin: not enough data for partition data"); |
1261 | 384 | TRY(data_partitions.try_append(data.slice(0, partition_size))); |
1262 | 384 | data = data.slice(partition_size); |
1263 | 384 | } |
1264 | 913 | TRY(data_partitions.try_append(data)); |
1265 | 852 | return data_partitions; |
1266 | 852 | } |
1267 | | |
1268 | | } |
1269 | | |
1270 | | ErrorOr<NonnullRefPtr<Bitmap>> decode_webp_chunk_VP8_contents(VP8Header const& vp8_header, bool include_alpha_channel) |
1271 | 1.03k | { |
1272 | 1.03k | VERIFY(vp8_header.keyframe_data.has_value()); |
1273 | | |
1274 | | // The first partition stores header, per-segment state, and macroblock metadata. |
1275 | 1.03k | auto decoder = TRY(BooleanDecoder::initialize(vp8_header.first_partition)); |
1276 | | |
1277 | 1.03k | auto header = TRY(decode_VP8_frame_header(decoder)); |
1278 | | |
1279 | | // https://datatracker.ietf.org/doc/html/rfc6386#section-2 "Format Overview" |
1280 | | // "Internally, VP8 decomposes each output frame into an array of |
1281 | | // macroblocks. A macroblock is a square array of pixels whose Y |
1282 | | // dimensions are 16x16 and whose U and V dimensions are 8x8." |
1283 | 1.03k | int macroblock_width = ceil_div(vp8_header.keyframe_data->width, 16); |
1284 | 1.03k | int macroblock_height = ceil_div(vp8_header.keyframe_data->height, 16); |
1285 | | |
1286 | 1.03k | auto macroblock_metadata = TRY(decode_VP8_macroblock_metadata(decoder, header, macroblock_width, macroblock_height)); |
1287 | | |
1288 | 1.03k | TRY(decoder.finish_decode()); |
1289 | | // Done with the first partition! |
1290 | | |
1291 | 937 | auto bitmap_format = include_alpha_channel ? BitmapFormat::BGRA8888 : BitmapFormat::BGRx8888; |
1292 | 937 | auto bitmap = TRY(Bitmap::create(bitmap_format, { macroblock_width * 16, macroblock_height * 16 })); |
1293 | | |
1294 | 918 | auto data_partitions = TRY(split_data_partitions(vp8_header.second_partition, header.number_of_dct_partitions)); |
1295 | 852 | TRY(decode_VP8_image_data(*bitmap, header, move(data_partitions), macroblock_width, macroblock_height, macroblock_metadata)); |
1296 | | |
1297 | 291 | auto width = static_cast<int>(vp8_header.keyframe_data->width); |
1298 | 291 | auto height = static_cast<int>(vp8_header.keyframe_data->height); |
1299 | 291 | if (bitmap->physical_size() == IntSize { width, height }) |
1300 | 10 | return bitmap; |
1301 | 281 | return bitmap->cropped({ 0, 0, width, height }); |
1302 | 291 | } |
1303 | | |
1304 | | } |