blob: 7fbb43406272d7c0642450f5adb854da71d594eb [file]
// 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.
}