| // Copyright 2026 The Wuffs Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your |
| // option. This file may not be copied, modified, or distributed |
| // except according to those terms. |
| // |
| // SPDX-License-Identifier: Apache-2.0 OR MIT |
| |
| pri func decoder.decode_macroblocks!(workbuf: slice base.u8) base.status { |
| var mby : base.u32 |
| var mbx : base.u32 |
| |
| mbx = 0 |
| while mbx < this.mbw { |
| assert mbx < 0x400 via "a < b: a < c; c <= b"(c: this.mbw) |
| |
| this.mb_states_top[mbx] = 0 |
| |
| mbx += 1 |
| } |
| |
| mby = 0 |
| while mby < this.mbh { |
| assert mby < 0x400 via "a < b: a < c; c <= b"(c: this.mbh) |
| |
| this.mb_states_left = 0 |
| |
| mbx = 0 |
| while mbx < this.mbw, |
| inv mby < 0x400, |
| { |
| assert mbx < 0x400 via "a < b: a < c; c <= b"(c: this.mbw) |
| |
| this.decode_one_macroblock!(workbuf: args.workbuf, mbx: mbx, mby: mby) |
| |
| mbx += 1 |
| } |
| |
| mby += 1 |
| } |
| return ok |
| } |
| |
| pri func decoder.decode_one_macroblock!(workbuf: slice base.u8, mbx: base.u32[..= 0x3FF], mby: base.u32[..= 0x3FF]) { |
| var v1 : base.u32[..= 1] |
| |
| var seg : base.u32[..= 3] |
| var skip : base.bool |
| var luma_mode : base.u32[..= 4] |
| var chroma_mode : base.u32[..= 3] |
| |
| seg = 0 |
| if this.seg_update_map { |
| v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: this.seg_probs[0] as base.u32) |
| if v1 == 0 { |
| v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: this.seg_probs[1] as base.u32) |
| seg = v1 |
| } else { |
| v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: this.seg_probs[2] as base.u32) |
| seg = v1 + 2 |
| } |
| } |
| |
| skip = false |
| if (this.prob_skip & 0x100) <> 0 { |
| v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: this.prob_skip & 0xFF) |
| skip = v1 <> 0 |
| } |
| |
| luma_mode = this.decode_luma_mode!(workbuf: args.workbuf) |
| this.decode_subblock_modes!(workbuf: args.workbuf, mbx: args.mbx, luma_mode: luma_mode) |
| |
| chroma_mode = this.decode_chroma_mode!(workbuf: args.workbuf) |
| |
| if skip { |
| // Clear the HNZC bits. |
| this.mb_states_left &= 0x0000_FFFF |
| this.mb_states_top[args.mbx] &= 0x0000_FFFF |
| |
| } else { |
| this.decode_coefficients!( |
| workbuf: args.workbuf, mbx: args.mbx, mby: args.mby, |
| seg: seg, luma_mode: luma_mode) |
| } |
| |
| this.reconstruct!( |
| workbuf: args.workbuf, mbx: args.mbx, mby: args.mby, |
| luma_mode: luma_mode, chroma_mode: chroma_mode) |
| } |
| |
| // RFC 6386 Section 11.2. Luma Modes. |
| // |
| // 0 = B_DC_PRED = predict DC using row above and column to the left. |
| // 1 = B_TM_PRED = propagate second differences a la "True Motion". |
| // 2 = B_VE_PRED = predict rows using row above. |
| // 3 = B_HE_PRED = predict columns using column to the left. |
| // 4 = B_PRED = each Y subblock is independently predicted. |
| // |
| // Returning (as luma_mode) at least 4 (equivalently, returning B_PRED) means |
| // that we use "intra4x4 prediction" (as opposed to "intra16x16 prediction"). |
| // (luma_mode >= 4) here is equivalent to libwebp's "is_i4x4 == true" and |
| // golang.org/x/image/vp8's "usePredY16 == false". |
| // |
| // The RFC gives an enum in (DC_PRED, V_PRED, H_PRED, TM_PRED, B_PRED) order |
| // but the libwebp implementation uses a different one, (B_DC_PRED, B_TM_PRED, |
| // B_VE_PRED, B_HE_PRED, B_PRED). Modulo the B_ prefixes, it's DC,V,H,TM versus |
| // DC,TM,VE,HE. At some point, the internal enum values were re-assigned after |
| // the RFC was written. |
| // |
| // libwebp also uses B_PRED=10 instead of the RFC's B_PRED=4. We use 4. |
| pri func decoder.decode_luma_mode!(workbuf: slice base.u8) base.u32[..= 4] { |
| var v1 : base.u32[..= 1] |
| |
| v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: 145) |
| if v1 == 0 { |
| return 4 // 4=B_PRED. is_i4x4 == true. usePredY16 == false. |
| } |
| |
| // Otherwise, is_i4x4 == false. usePredY16 == true. |
| |
| v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: 156) |
| if v1 == 0 { |
| v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: 163) |
| return 2 * v1 // 0=B_DC_PRED or 2=B_VE_PRED. |
| } |
| |
| v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: 128) |
| return 3 - (2 * v1) // 3=B_HE_PRED or 1=B_TM_PRED. |
| } |
| |
| // RFC 6386 Section 11.4. Chroma Modes. |
| // |
| // 0 = B_DC_PRED, etc, the same as the Luma Modes, except there's no B_PRED. |
| pri func decoder.decode_chroma_mode!(workbuf: slice base.u8) base.u32[..= 3] { |
| var v1 : base.u32[..= 1] |
| |
| v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: 142) |
| if v1 == 0 { |
| return 0 // 0=B_DC_PRED. |
| } |
| |
| v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: 114) |
| if v1 == 0 { |
| return 2 // 2=B_VE_PRED. |
| } |
| |
| v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: 183) |
| return 3 - (2 * v1) // 3=B_HE_PRED or 1=B_TM_PRED. |
| } |
| |
| // RFC 6386 Section 11.2. Luma Modes. |
| pri func decoder.decode_subblock_modes!( |
| workbuf: slice base.u8, |
| mbx: base.u32[..= 0x3FF], |
| luma_mode: base.u32[..= 4]) { |
| var i : base.u32 |
| var top_mode : base.u8[..= 9] |
| var left_mode : base.u8[..= 9] |
| var mode : base.u8[..= 9] |
| |
| if args.luma_mode < 4 { |
| this.mb_states_top[args.mbx] = (mode as base.u32) * 0x1111 |
| this.mb_states_left = (mode as base.u32) * 0x1111 |
| return nothing |
| } |
| |
| i = 0 |
| while i < 16 { |
| if i < 4 { |
| top_mode = CLAMP_NO_MORE_THAN_9[15 & (this.mb_states_top[args.mbx] >> (i * 4))] |
| } else { |
| top_mode = this.mb_subblock_modes[i - 4] |
| } |
| |
| if (i & 3) == 0 { |
| left_mode = CLAMP_NO_MORE_THAN_9[15 & (this.mb_states_left >> (i & 12))] |
| } else { |
| left_mode = this.mb_subblock_modes[(i ~mod+ 15) & 15] |
| } |
| |
| mode = this.decode_one_subblock_mode!(workbuf: args.workbuf, top_mode: top_mode, left_mode: left_mode) |
| this.mb_subblock_modes[i] = mode |
| |
| i += 1 |
| } |
| |
| this.mb_states_top[args.mbx] = (this.mb_states_top[args.mbx] & 0xFFFF_0000) | |
| ((this.mb_subblock_modes[0xC] as base.u32) << 0x0) | |
| ((this.mb_subblock_modes[0xD] as base.u32) << 0x4) | |
| ((this.mb_subblock_modes[0xE] as base.u32) << 0x8) | |
| ((this.mb_subblock_modes[0xF] as base.u32) << 0xC) |
| this.mb_states_left = (this.mb_states_left & 0xFFFF_0000) | |
| ((this.mb_subblock_modes[0x3] as base.u32) << 0x0) | |
| ((this.mb_subblock_modes[0x7] as base.u32) << 0x4) | |
| ((this.mb_subblock_modes[0xB] as base.u32) << 0x8) | |
| ((this.mb_subblock_modes[0xF] as base.u32) << 0xC) |
| } |
| |
| // RFC 6386 Section 11.3. Subblock Mode Contexts. |
| pri func decoder.decode_one_subblock_mode!( |
| workbuf: slice base.u8, |
| top_mode: base.u8[..= 9], |
| left_mode: base.u8[..= 9]) base.u8[..= 9] { |
| var v1 : base.u32[..= 1] |
| var tm : base.u8[..= 9] |
| var lm : base.u8[..= 9] |
| |
| tm = args.top_mode |
| lm = args.left_mode |
| |
| v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: SUBBLOCK_MODE_PROBS[tm][lm][0] as base.u32) |
| if v1 == 0 { |
| return 0 // 0=B_DC_PRED. |
| } |
| |
| v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: SUBBLOCK_MODE_PROBS[tm][lm][1] as base.u32) |
| if v1 == 0 { |
| return 1 // 1=B_TM_PRED. |
| } |
| |
| v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: SUBBLOCK_MODE_PROBS[tm][lm][2] as base.u32) |
| if v1 == 0 { |
| return 2 // 2=B_VE_PRED. |
| } |
| |
| v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: SUBBLOCK_MODE_PROBS[tm][lm][3] as base.u32) |
| if v1 == 0 { |
| v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: SUBBLOCK_MODE_PROBS[tm][lm][4] as base.u32) |
| if v1 == 0 { |
| return 3 // 3=B_HE_PRED. |
| } |
| |
| v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: SUBBLOCK_MODE_PROBS[tm][lm][5] as base.u32) |
| if v1 == 0 { |
| return 4 // 4=B_RD_PRED. |
| } |
| |
| return 5 // 5=B_VR_PRED. |
| } |
| |
| v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: SUBBLOCK_MODE_PROBS[tm][lm][6] as base.u32) |
| if v1 == 0 { |
| return 6 // 6=B_LD_PRED. |
| } |
| |
| v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: SUBBLOCK_MODE_PROBS[tm][lm][7] as base.u32) |
| if v1 == 0 { |
| return 7 // 7=B_VL_PRED. |
| } |
| |
| v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: SUBBLOCK_MODE_PROBS[tm][lm][8] as base.u32) |
| if v1 == 0 { |
| return 8 // 8=B_HD_PRED. |
| } |
| |
| return 9 // 9=B_HU_PRED. |
| } |