WebPLoaderLossy.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. /*
  2. * Copyright (c) 2023, Nico Weber <thakis@chromium.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Debug.h>
  7. #include <AK/Endian.h>
  8. #include <AK/Format.h>
  9. #include <AK/MemoryStream.h>
  10. #include <AK/Vector.h>
  11. #include <LibGfx/ImageFormats/BooleanDecoder.h>
  12. #include <LibGfx/ImageFormats/WebPLoaderLossy.h>
  13. #include <LibGfx/ImageFormats/WebPLoaderLossyTables.h>
  14. // Lossy format: https://datatracker.ietf.org/doc/html/rfc6386
  15. // Summary:
  16. // A lossy webp image is a VP8 keyframe.
  17. // A VP8 keyframe consists of 16x16 pixel tiles called macroblocks. Each macroblock is subdivided into 4x4 pixel tiles called subblocks.
  18. // 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.
  19. // This means one macroblock is covered by 4x4 Y subblocks and 2x2 U and V subblocks each.
  20. // VP8 data consists of:
  21. // * A tiny bit of uncompressed data, storing image dimensions and the size of the first compressed chunk of data, called the first partition
  22. // * The first partition, which is a entropy-coded bitstream storing:
  23. // 1. A fixed-size header.
  24. // The main piece of data this stores is a probability distribution for how pixel values of each metablock are predicted from previously decoded data.
  25. // 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).
  26. // 2. For each metablock, it stores how that metablock's pixel values are predicted from previously decoded data (and some more per-metablock metadata).
  27. // There are independent prediction modes for Y, U, V.
  28. // U and V store a single prediction mode per macroblock.
  29. // Y can store a single prediction mode per macroblock, or it can store one subblock prediction mode for each of the 4x4 luma subblocks.
  30. // * One or more additional entropy-coded bitstreams ("partitions") that store the discrete cosine transform ("DCT") coefficients for the actual pixel data for each metablock.
  31. // Each metablock is subdivided into 4x4 tiles called "subblocks". A 16x16 pixel metablock consists of:
  32. // 0. If the metablock 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,
  33. // as coefficients of an inverse Walsh-Hadamard Transform (WHT).
  34. // 1. 4x4 luma subblocks
  35. // 2. 2x2 U chrome subblocks
  36. // 3. 2x2 U chrome subblocks
  37. // That is, each metablock stores 24 or 25 sets of coefficients.
  38. // Each set of coefficients stores 16 numbers, using a combination of a custom prefix tree and dequantization.
  39. // The inverse DCT output is added to the output of the prediction.
  40. namespace Gfx {
  41. // https://developers.google.com/speed/webp/docs/riff_container#simple_file_format_lossy
  42. // https://datatracker.ietf.org/doc/html/rfc6386#section-19 "Annex A: Bitstream Syntax"
  43. ErrorOr<VP8Header> decode_webp_chunk_VP8_header(ReadonlyBytes vp8_data)
  44. {
  45. if (vp8_data.size() < 10)
  46. return Error::from_string_literal("WebPImageDecoderPlugin: 'VP8 ' chunk too small");
  47. // FIXME: Eventually, this should probably call into LibVideo/VP8,
  48. // and image decoders should move into LibImageDecoders which depends on both LibGfx and LibVideo.
  49. // (LibVideo depends on LibGfx, so LibGfx can't depend on LibVideo itself.)
  50. // https://datatracker.ietf.org/doc/html/rfc6386#section-4 "Overview of Compressed Data Format"
  51. // "The decoder is simply presented with a sequence of compressed frames [...]
  52. // The first frame presented to the decompressor is [...] a key frame. [...]
  53. // [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"
  54. u8 const* data = vp8_data.data();
  55. // https://datatracker.ietf.org/doc/html/rfc6386#section-9.1 "Uncompressed Data Chunk"
  56. u32 frame_tag = data[0] | (data[1] << 8) | (data[2] << 16);
  57. bool is_key_frame = (frame_tag & 1) == 0; // https://www.rfc-editor.org/errata/eid5534
  58. u8 version = (frame_tag & 0xe) >> 1;
  59. bool show_frame = (frame_tag & 0x10) != 0;
  60. u32 size_of_first_partition = frame_tag >> 5;
  61. if (!is_key_frame)
  62. return Error::from_string_literal("WebPImageDecoderPlugin: 'VP8 ' chunk not a key frame");
  63. if (!show_frame)
  64. return Error::from_string_literal("WebPImageDecoderPlugin: 'VP8 ' chunk has invalid visibility for webp image");
  65. if (version > 3)
  66. return Error::from_string_literal("WebPImageDecoderPlugin: unknown version number in 'VP8 ' chunk");
  67. u32 start_code = data[3] | (data[4] << 8) | (data[5] << 16);
  68. if (start_code != 0x2a019d) // https://www.rfc-editor.org/errata/eid7370
  69. return Error::from_string_literal("WebPImageDecoderPlugin: 'VP8 ' chunk invalid start_code");
  70. // "The scaling specifications for each dimension are encoded as follows.
  71. // 0 | No upscaling (the most common case).
  72. // 1 | Upscale by 5/4.
  73. // 2 | Upscale by 5/3.
  74. // 3 | Upscale by 2."
  75. // This is a display-time operation and doesn't affect decoding."
  76. u16 width_and_horizontal_scale = data[6] | (data[7] << 8);
  77. u16 width = width_and_horizontal_scale & 0x3fff;
  78. u8 horizontal_scale = width_and_horizontal_scale >> 14;
  79. u16 heigth_and_vertical_scale = data[8] | (data[9] << 8);
  80. u16 height = heigth_and_vertical_scale & 0x3fff;
  81. u8 vertical_scale = heigth_and_vertical_scale >> 14;
  82. dbgln_if(WEBP_DEBUG, "version {}, show_frame {}, size_of_first_partition {}, width {}, horizontal_scale {}, height {}, vertical_scale {}",
  83. version, show_frame, size_of_first_partition, width, horizontal_scale, height, vertical_scale);
  84. return VP8Header { version, show_frame, size_of_first_partition, width, horizontal_scale, height, vertical_scale, vp8_data.slice(10) };
  85. }
  86. namespace {
  87. // Reads n bits followed by a sign bit (0: positive, 1: negative).
  88. ErrorOr<i8> read_signed_literal(BooleanDecoder& decoder, u8 n)
  89. {
  90. VERIFY(n <= 7);
  91. i8 i = TRY(decoder.read_literal(n));
  92. if (TRY(decoder.read_literal(1)))
  93. i = -i;
  94. return i;
  95. }
  96. // https://datatracker.ietf.org/doc/html/rfc6386#section-19 "Annex A: Bitstream Syntax"
  97. #define L(n) decoder.read_literal(n)
  98. #define B(prob) decoder.read_bool(prob)
  99. #define L_signed(n) read_signed_literal(decoder, n)
  100. // https://datatracker.ietf.org/doc/html/rfc6386#section-9.2 "Color Space and Pixel Type (Key Frames Only)"
  101. enum class ColorSpaceAndPixelType {
  102. YUV = 0,
  103. ReservedForFutureUse = 1,
  104. };
  105. enum class ClampingSpecification {
  106. DecoderMustClampTo0To255 = 0,
  107. NoClampingNecessary = 1,
  108. };
  109. // https://datatracker.ietf.org/doc/html/rfc6386#section-9.3 Segment-Based Adjustments"
  110. // https://datatracker.ietf.org/doc/html/rfc6386#section-19.2 "Frame Header"
  111. enum class SegmentFeatureMode {
  112. // Spec 19.2 says 0 is delta, 1 absolute; spec 9.3 has it the other way round. 19.2 is correct.
  113. // https://www.rfc-editor.org/errata/eid7519
  114. DeltaValueMode = 0,
  115. AbsoluteValueMode = 1,
  116. };
  117. struct Segmentation {
  118. bool update_metablock_segmentation_map { false };
  119. SegmentFeatureMode segment_feature_mode { SegmentFeatureMode::DeltaValueMode };
  120. i8 quantizer_update_value[4] {};
  121. i8 loop_filter_update_value[4] {};
  122. u8 metablock_segment_tree_probabilities[3] = { 255, 255, 255 };
  123. };
  124. ErrorOr<Segmentation> decode_VP8_frame_header_segmentation(BooleanDecoder&);
  125. // Also https://datatracker.ietf.org/doc/html/rfc6386#section-9.6 "Dequantization Indices"
  126. struct QuantizationIndices {
  127. u8 y_ac { 0 };
  128. i8 y_dc_delta { 0 };
  129. i8 y2_dc_delta { 0 };
  130. i8 y2_ac_delta { 0 };
  131. i8 uv_dc_delta { 0 };
  132. i8 uv_ac_delta { 0 };
  133. };
  134. ErrorOr<QuantizationIndices> decode_VP8_frame_header_quantization_indices(BooleanDecoder&);
  135. struct LoopFilterAdjustment {
  136. bool enable_loop_filter_adjustment { false };
  137. i8 ref_frame_delta[4] {};
  138. i8 mb_mode_delta[4] {};
  139. };
  140. ErrorOr<LoopFilterAdjustment> decode_VP8_frame_header_loop_filter_adjustment(BooleanDecoder&);
  141. using CoefficientProbabilities = Prob[4][8][3][num_dct_tokens - 1];
  142. ErrorOr<void> decode_VP8_frame_header_coefficient_probabilities(BooleanDecoder&, CoefficientProbabilities);
  143. // https://datatracker.ietf.org/doc/html/rfc6386#section-15 "Loop Filter"
  144. // "The first is a flag (filter_type) selecting the type of filter (normal or simple)"
  145. enum class FilterType {
  146. Normal = 0,
  147. Simple = 1,
  148. };
  149. // https://datatracker.ietf.org/doc/html/rfc6386#section-19.2 "Frame Header"
  150. struct FrameHeader {
  151. ColorSpaceAndPixelType color_space {};
  152. ClampingSpecification clamping_type {};
  153. bool is_segmentation_enabled {};
  154. Segmentation segmentation {};
  155. FilterType filter_type {};
  156. u8 loop_filter_level {};
  157. u8 sharpness_level {};
  158. LoopFilterAdjustment loop_filter_adjustment {};
  159. u8 number_of_dct_partitions {};
  160. QuantizationIndices quantization_indices {};
  161. CoefficientProbabilities coefficient_probabilities;
  162. bool enable_skipping_of_metablocks_containing_only_zero_coefficients {};
  163. u8 probability_skip_false;
  164. };
  165. ErrorOr<FrameHeader> decode_VP8_frame_header(BooleanDecoder& decoder)
  166. {
  167. // https://datatracker.ietf.org/doc/html/rfc6386#section-19.2 "Frame Header"
  168. FrameHeader header;
  169. // In the VP8 spec, this is in an `if (key_frames)`, but webp files only have key frames.
  170. header.color_space = ColorSpaceAndPixelType { TRY(L(1)) };
  171. header.clamping_type = ClampingSpecification { TRY(L(1)) };
  172. dbgln_if(WEBP_DEBUG, "color_space {} clamping_type {}", (int)header.color_space, (int)header.clamping_type);
  173. // https://datatracker.ietf.org/doc/html/rfc6386#section-9.3 "Segment-Based Adjustments"
  174. header.is_segmentation_enabled = TRY(L(1));
  175. dbgln_if(WEBP_DEBUG, "segmentation_enabled {}", header.is_segmentation_enabled);
  176. if (header.is_segmentation_enabled)
  177. header.segmentation = TRY(decode_VP8_frame_header_segmentation(decoder));
  178. header.filter_type = FilterType { TRY(L(1)) };
  179. header.loop_filter_level = TRY(L(6));
  180. header.sharpness_level = TRY(L(3));
  181. dbgln_if(WEBP_DEBUG, "filter_type {} loop_filter_level {} sharpness_level {}", (int)header.filter_type, header.loop_filter_level, header.sharpness_level);
  182. header.loop_filter_adjustment = TRY(decode_VP8_frame_header_loop_filter_adjustment(decoder));
  183. u8 log2_nbr_of_dct_partitions = TRY(L(2));
  184. dbgln_if(WEBP_DEBUG, "log2_nbr_of_dct_partitions {}", log2_nbr_of_dct_partitions);
  185. header.number_of_dct_partitions = 1 << log2_nbr_of_dct_partitions;
  186. header.quantization_indices = TRY(decode_VP8_frame_header_quantization_indices(decoder));
  187. // In the VP8 spec, this is in an `if (key_frames)` followed by a lengthy `else`, but webp files only have key frames.
  188. u8 refresh_entropy_probs = TRY(L(1)); // Has no effect in webp files.
  189. dbgln_if(WEBP_DEBUG, "refresh_entropy_probs {}", refresh_entropy_probs);
  190. memcpy(header.coefficient_probabilities, default_coeff_probs, sizeof(header.coefficient_probabilities));
  191. TRY(decode_VP8_frame_header_coefficient_probabilities(decoder, header.coefficient_probabilities));
  192. // https://datatracker.ietf.org/doc/html/rfc6386#section-9.11 "Remaining Frame Header Data (Key Frame)"
  193. header.enable_skipping_of_metablocks_containing_only_zero_coefficients = TRY(L(1));
  194. dbgln_if(WEBP_DEBUG, "mb_no_skip_coeff {}", header.enable_skipping_of_metablocks_containing_only_zero_coefficients);
  195. if (header.enable_skipping_of_metablocks_containing_only_zero_coefficients) {
  196. header.probability_skip_false = TRY(L(8));
  197. dbgln_if(WEBP_DEBUG, "prob_skip_false {}", header.probability_skip_false);
  198. }
  199. // In the VP8 spec, there is a length `if (!key_frames)` here, but webp files only have key frames.
  200. return header;
  201. }
  202. ErrorOr<Segmentation> decode_VP8_frame_header_segmentation(BooleanDecoder& decoder)
  203. {
  204. // Corresponds to "update_segmentation()" in section 19.2 of the spec.
  205. Segmentation segmentation;
  206. segmentation.update_metablock_segmentation_map = TRY(L(1));
  207. u8 update_segment_feature_data = TRY(L(1));
  208. dbgln_if(WEBP_DEBUG, "update_mb_segmentation_map {} update_segment_feature_data {}",
  209. segmentation.update_metablock_segmentation_map, update_segment_feature_data);
  210. if (update_segment_feature_data) {
  211. segmentation.segment_feature_mode = static_cast<SegmentFeatureMode>(TRY(L(1)));
  212. dbgln_if(WEBP_DEBUG, "segment_feature_mode {}", (int)segmentation.segment_feature_mode);
  213. for (int i = 0; i < 4; ++i) {
  214. u8 quantizer_update = TRY(L(1));
  215. dbgln_if(WEBP_DEBUG, "quantizer_update {}", quantizer_update);
  216. if (quantizer_update) {
  217. i8 quantizer_update_value = TRY(L_signed(7));
  218. dbgln_if(WEBP_DEBUG, "quantizer_update_value {}", quantizer_update_value);
  219. segmentation.quantizer_update_value[i] = quantizer_update_value;
  220. }
  221. }
  222. for (int i = 0; i < 4; ++i) {
  223. u8 loop_filter_update = TRY(L(1));
  224. dbgln_if(WEBP_DEBUG, "loop_filter_update {}", loop_filter_update);
  225. if (loop_filter_update) {
  226. i8 loop_filter_update_value = TRY(L_signed(6));
  227. dbgln_if(WEBP_DEBUG, "loop_filter_update_value {}", loop_filter_update_value);
  228. segmentation.loop_filter_update_value[i] = loop_filter_update_value;
  229. }
  230. }
  231. }
  232. if (segmentation.update_metablock_segmentation_map) {
  233. // This reads mb_segment_tree_probs for https://datatracker.ietf.org/doc/html/rfc6386#section-10.
  234. for (int i = 0; i < 3; ++i) {
  235. u8 segment_prob_update = TRY(L(1));
  236. dbgln_if(WEBP_DEBUG, "segment_prob_update {}", segment_prob_update);
  237. if (segment_prob_update) {
  238. u8 segment_prob = TRY(L(8));
  239. dbgln_if(WEBP_DEBUG, "segment_prob {}", segment_prob);
  240. segmentation.metablock_segment_tree_probabilities[i] = segment_prob;
  241. }
  242. }
  243. }
  244. return segmentation;
  245. }
  246. ErrorOr<QuantizationIndices> decode_VP8_frame_header_quantization_indices(BooleanDecoder& decoder)
  247. {
  248. // Corresponds to "quant_indices()" in section 19.2 of the spec.
  249. QuantizationIndices quantization_indices;
  250. // "The first 7-bit index gives the dequantization table index for
  251. // Y-plane AC coefficients, called yac_qi. It is always coded and acts
  252. // as a baseline for the other 5 quantization indices, each of which is
  253. // represented by a delta from this baseline index."
  254. quantization_indices.y_ac = TRY(L(7));
  255. dbgln_if(WEBP_DEBUG, "y_ac_qi {}", quantization_indices.y_ac);
  256. auto read_delta = [&decoder](StringView name, i8* destination) -> ErrorOr<void> {
  257. u8 is_present = TRY(L(1));
  258. dbgln_if(WEBP_DEBUG, "{}_present {}", name, is_present);
  259. if (is_present) {
  260. i8 delta = TRY(L_signed(4));
  261. dbgln_if(WEBP_DEBUG, "{} {}", name, delta);
  262. *destination = delta;
  263. }
  264. return {};
  265. };
  266. TRY(read_delta("y_dc_delta"sv, &quantization_indices.y_dc_delta));
  267. TRY(read_delta("y2_dc_delta"sv, &quantization_indices.y2_dc_delta));
  268. TRY(read_delta("y2_ac_delta"sv, &quantization_indices.y2_ac_delta));
  269. TRY(read_delta("uv_dc_delta"sv, &quantization_indices.uv_dc_delta));
  270. TRY(read_delta("uv_ac_delta"sv, &quantization_indices.uv_ac_delta));
  271. return quantization_indices;
  272. }
  273. ErrorOr<LoopFilterAdjustment> decode_VP8_frame_header_loop_filter_adjustment(BooleanDecoder& decoder)
  274. {
  275. // Corresponds to "mb_lf_adjustments()" in section 19.2 of the spec.
  276. LoopFilterAdjustment adjustment;
  277. adjustment.enable_loop_filter_adjustment = TRY(L(1));
  278. if (adjustment.enable_loop_filter_adjustment) {
  279. u8 mode_ref_lf_delta_update = TRY(L(1));
  280. dbgln_if(WEBP_DEBUG, "mode_ref_lf_delta_update {}", mode_ref_lf_delta_update);
  281. if (mode_ref_lf_delta_update) {
  282. for (int i = 0; i < 4; ++i) {
  283. u8 ref_frame_delta_update_flag = TRY(L(1));
  284. dbgln_if(WEBP_DEBUG, "ref_frame_delta_update_flag {}", ref_frame_delta_update_flag);
  285. if (ref_frame_delta_update_flag) {
  286. i8 delta = TRY(L_signed(6));
  287. dbgln_if(WEBP_DEBUG, "delta {}", delta);
  288. adjustment.ref_frame_delta[i] = delta;
  289. }
  290. }
  291. for (int i = 0; i < 4; ++i) {
  292. u8 mb_mode_delta_update_flag = TRY(L(1));
  293. dbgln_if(WEBP_DEBUG, "mb_mode_delta_update_flag {}", mb_mode_delta_update_flag);
  294. if (mb_mode_delta_update_flag) {
  295. i8 delta = TRY(L_signed(6));
  296. dbgln_if(WEBP_DEBUG, "delta {}", delta);
  297. adjustment.mb_mode_delta[i] = delta;
  298. }
  299. }
  300. }
  301. }
  302. return adjustment;
  303. }
  304. ErrorOr<void> decode_VP8_frame_header_coefficient_probabilities(BooleanDecoder& decoder, CoefficientProbabilities coefficient_probabilities)
  305. {
  306. // Corresponds to "token_prob_update()" in section 19.2 of the spec.
  307. for (int i = 0; i < 4; i++) {
  308. for (int j = 0; j < 8; j++) {
  309. for (int k = 0; k < 3; k++) {
  310. for (int l = 0; l < 11; l++) {
  311. // token_prob_update() says L(1) and L(8), but it's actually B(p) and L(8).
  312. // https://datatracker.ietf.org/doc/html/rfc6386#section-13.4 "Token Probability Updates" describes it correctly.
  313. if (TRY(B(coeff_update_probs[i][j][k][l])))
  314. coefficient_probabilities[i][j][k][l] = TRY(L(8));
  315. }
  316. }
  317. }
  318. }
  319. return {};
  320. }
  321. // https://datatracker.ietf.org/doc/html/rfc6386#section-8.1 "Tree Coding Implementation"
  322. ErrorOr<u8> tree_decode(BooleanDecoder& decoder, ReadonlySpan<TreeIndex> tree, ReadonlyBytes probabilities, TreeIndex initial_i = 0)
  323. {
  324. TreeIndex i = initial_i;
  325. while (true) {
  326. u8 b = TRY(B(probabilities[i >> 1]));
  327. i = tree[i + b];
  328. if (i <= 0)
  329. return -i;
  330. }
  331. }
  332. // Similar to BlockContext in LibVideo/VP9/Context.h
  333. struct MacroblockMetadata {
  334. // https://datatracker.ietf.org/doc/html/rfc6386#section-10 "Segment-Based Feature Adjustments"
  335. // Read only if `update_mb_segmentation_map` is set.
  336. int segment_id { 0 }; // 0, 1, 2, or 3. Fits in two bits.
  337. // https://datatracker.ietf.org/doc/html/rfc6386#section-11.1 "mb_skip_coeff"
  338. bool skip_coefficients { false };
  339. IntraMetablockMode intra_y_mode;
  340. IntraMetablockMode uv_mode;
  341. IntraBlockMode intra_b_modes[16];
  342. };
  343. ErrorOr<Vector<MacroblockMetadata>> decode_VP8_macroblock_metadata(BooleanDecoder& decoder, FrameHeader const& header, int macroblock_width, int macroblock_height)
  344. {
  345. // https://datatracker.ietf.org/doc/html/rfc6386#section-19.3
  346. // Corresponds to "macroblock_header()" in section 19.3 of the spec.
  347. Vector<MacroblockMetadata> macroblock_metadata;
  348. // Key frames must use intra prediction, that is new macroblocks are predicted from old macroblocks in the same frame.
  349. // (Inter prediction on the other hand predicts new macroblocks from the corresponding macroblock in the previous frame.)
  350. // https://datatracker.ietf.org/doc/html/rfc6386#section-11.3 "Subblock Mode Contexts"
  351. // "For macroblocks on the top row or left edge of the image, some of
  352. // the predictors will be non-existent. Such predictors are taken
  353. // to have had the value B_DC_PRED, which, perhaps conveniently,
  354. // takes the value 0 in the enumeration above.
  355. // A simple management scheme for these contexts might maintain a row
  356. // of above predictors and four left predictors. Before decoding the
  357. // frame, the entire row is initialized to B_DC_PRED; before decoding
  358. // each row of macroblocks, the four left predictors are also set to
  359. // B_DC_PRED. After decoding a macroblock, the bottom four subblock
  360. // modes are copied into the row predictor (at the current position,
  361. // which then advances to be above the next macroblock), and the
  362. // right four subblock modes are copied into the left predictor."
  363. Vector<IntraBlockMode> above;
  364. TRY(above.try_resize(macroblock_width * 4)); // One per 4x4 subblock.
  365. for (int mb_y = 0; mb_y < macroblock_height; ++mb_y) {
  366. IntraBlockMode left[4] {};
  367. for (int mb_x = 0; mb_x < macroblock_width; ++mb_x) {
  368. MacroblockMetadata metadata;
  369. if (header.segmentation.update_metablock_segmentation_map)
  370. metadata.segment_id = TRY(tree_decode(decoder, METABLOCK_SEGMENT_TREE, header.segmentation.metablock_segment_tree_probabilities));
  371. if (header.enable_skipping_of_metablocks_containing_only_zero_coefficients)
  372. metadata.skip_coefficients = TRY(B(header.probability_skip_false));
  373. int intra_y_mode = TRY(tree_decode(decoder, KEYFRAME_YMODE_TREE, KEYFRAME_YMODE_PROBABILITIES));
  374. metadata.intra_y_mode = (IntraMetablockMode)intra_y_mode;
  375. // "If the Ymode is B_PRED, it is followed by a (tree-coded) mode for each of the 16 Y subblocks."
  376. if (intra_y_mode == B_PRED) {
  377. for (int y = 0; y < 4; ++y) {
  378. for (int x = 0; x < 4; ++x) {
  379. // "The outer two dimensions of this array are indexed by the already-
  380. // coded subblock modes above and to the left of the current block,
  381. // respectively."
  382. int A = above[mb_x * 4 + x];
  383. int L = left[y];
  384. auto intra_b_mode = static_cast<IntraBlockMode>(TRY(tree_decode(decoder, BLOCK_MODE_TREE, KEYFRAME_BLOCK_MODE_PROBABILITIES[A][L])));
  385. metadata.intra_b_modes[y * 4 + x] = intra_b_mode;
  386. above[mb_x * 4 + x] = intra_b_mode;
  387. left[y] = intra_b_mode;
  388. }
  389. }
  390. } else {
  391. VERIFY(intra_y_mode < B_PRED);
  392. constexpr IntraBlockMode b_mode_from_y_mode[] = { B_DC_PRED, B_VE_PRED, B_HE_PRED, B_TM_PRED };
  393. IntraBlockMode intra_b_mode = b_mode_from_y_mode[intra_y_mode];
  394. for (int i = 0; i < 4; ++i) {
  395. above[mb_x * 4 + i] = intra_b_mode;
  396. left[i] = intra_b_mode;
  397. }
  398. }
  399. metadata.uv_mode = (IntraMetablockMode)TRY(tree_decode(decoder, UV_MODE_TREE, KEYFRAME_UV_MODE_PROBABILITIES));
  400. TRY(macroblock_metadata.try_append(metadata));
  401. }
  402. }
  403. return macroblock_metadata;
  404. }
  405. }
  406. ErrorOr<NonnullRefPtr<Bitmap>> decode_webp_chunk_VP8_contents(VP8Header const& vp8_header, bool include_alpha_channel)
  407. {
  408. // The first partition stores header, per-segment state, and macroblock metadata.
  409. FixedMemoryStream memory_stream { vp8_header.lossy_data };
  410. BigEndianInputBitStream bit_stream { MaybeOwned<Stream>(memory_stream) };
  411. auto decoder = TRY(BooleanDecoder::initialize(MaybeOwned { bit_stream }, vp8_header.lossy_data.size() * 8));
  412. auto header = TRY(decode_VP8_frame_header(decoder));
  413. // https://datatracker.ietf.org/doc/html/rfc6386#section-2 "Format Overview"
  414. // "Internally, VP8 decomposes each output frame into an array of
  415. // macroblocks. A macroblock is a square array of pixels whose Y
  416. // dimensions are 16x16 and whose U and V dimensions are 8x8."
  417. int macroblock_width = ceil_div(vp8_header.width, 16);
  418. int macroblock_height = ceil_div(vp8_header.height, 16);
  419. auto macroblock_metadata = TRY(decode_VP8_macroblock_metadata(decoder, header, macroblock_width, macroblock_height));
  420. (void)macroblock_metadata;
  421. if (header.number_of_dct_partitions > 1)
  422. return Error::from_string_literal("WebPImageDecoderPlugin: decoding lossy webps with more than one dct partition not yet implemented");
  423. auto bitmap_format = include_alpha_channel ? BitmapFormat::BGRA8888 : BitmapFormat::BGRx8888;
  424. // Uncomment this to test ALPH decoding for WebP-lossy-with-alpha images while lossy decoding isn't implemented yet.
  425. #if 0
  426. return Bitmap::create(bitmap_format, { vp8_header.width, vp8_header.height });
  427. #else
  428. // FIXME: Implement webp lossy decoding.
  429. (void)bitmap_format;
  430. return Error::from_string_literal("WebPImageDecoderPlugin: decoding lossy webps not yet implemented");
  431. #endif
  432. }
  433. }