blob: df806c8058b51e7d4e06719bdbead34c887348cc [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.copy_partitions_to_workbuf?(src: base.io_reader, workbuf: slice base.u8) {
var i : base.u64
var j : base.u64
var n : base.u32
i = this.workbuf_yuv_v_end
j = this.workbuf_yuv_v_end + (this.partitioned_data_length as base.u64)
while i < j,
inv j <= 0x20FF_FFF7,
{
if j > args.workbuf.length() {
return base."#bad workbuf length"
}
assert j > i via "a > b: b < a"()
n = args.src.limited_copy_u32_to_slice!(
up_to: ((j - i) & 0xFFFF_FFFF) as base.u32,
s: args.workbuf[i .. j])
i ~mod+= n as base.u64
if i < j {
yield? base."$short read"
}
}
// Write eight 0x00 bytes to the end of the workbuf, past what we copy from
// args.src. Later on, we can then always peek_u64le from the workbuf.
j += 8
while i < j {
assert i < 0xFFFF_FFFF_FFFF_FFFF via "a < b: a < c; c <= b"(c: j)
if i >= args.workbuf.length() {
return base."#bad workbuf length"
}
args.workbuf[i] = 0x00
i += 1
}
}
pri func decoder.decode_first_partition!(workbuf: roslice base.u8) base.status {
var v1 : base.u32[..= 1]
var v32 : base.u32
// RFC 6386 Section 9.2. Color Space and Pixel Type (Key Frames Only).
if this.is_key_frame {
this.read_bit!(workbuf: args.workbuf, part: 0, prob: 128)
this.read_bit!(workbuf: args.workbuf, part: 0, prob: 128)
}
// RFC 6386 Section 9.3. Segment-Based Adjustments.
this.decode_segment_header!(workbuf: args.workbuf)
// RFC 6386 Section 9.4. Loop Filter Type and Levels.
this.decode_filter_header!(workbuf: args.workbuf)
// RFC 6386 Section 9.5. Token Partition and Partition Data Offsets.
v32 = this.read_unsigned!(workbuf: args.workbuf, part: 0, n: 2)
this.num_other_partitions_m1 = ((1 as base.u32) << (v32 & 3)) - 1
// RFC 6386 Section 9.6. Dequantization Indices.
v32 = this.read_unsigned!(workbuf: args.workbuf, part: 0, n: 7)
this.quant_yac_qi = v32 & 127
this.quant_ydc_delta = this.read_signed!(workbuf: args.workbuf, part: 0, n: 4)
this.quant_y2dc_delta = this.read_signed!(workbuf: args.workbuf, part: 0, n: 4)
this.quant_y2ac_delta = this.read_signed!(workbuf: args.workbuf, part: 0, n: 4)
this.quant_uvdc_delta = this.read_signed!(workbuf: args.workbuf, part: 0, n: 4)
this.quant_uvac_delta = this.read_signed!(workbuf: args.workbuf, part: 0, n: 4)
this.calculate_dequants!()
// RFC 6386 Section 9.7. Refresh Golden Frame and Altref Frame.
if not this.is_key_frame {
return "#unsupported VP8 file"
}
// RFC 6386 Section 9.8. Refresh Last Frame Buffer.
this.read_bit!(workbuf: args.workbuf, part: 0, prob: 128)
// RFC 6386 Section 9.9. DCT Coefficient Probability Update.
this.decode_coeff_probs!(workbuf: args.workbuf)
if not this.is_key_frame {
// RFC 6386 Section 9.10. Remaining Frame Header Data (Non-Key Frame).
return "#unsupported VP8 file"
} else {
// RFC 6386 Section 9.11. Remaining Frame Header Data (Key Frame).
v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: 128)
if v1 <> 0 {
v32 = this.read_unsigned!(workbuf: args.workbuf, part: 0, n: 8)
this.prob_skip = (v32 & 0xFF) | 0x100
}
}
return ok
}
pri func decoder.decode_segment_header!(workbuf: roslice base.u8) {
var v1 : base.u32[..= 1]
var v32 : base.u32
var i : base.u32
v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: 128)
this.seg_enabled = v1 <> 0
if not this.seg_enabled {
this.seg_update_map = false
return nothing
}
v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: 128)
this.seg_update_map = v1 <> 0
v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: 128)
if v1 <> 0 {
v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: 128)
this.seg_absolute = v1 <> 0
i = 0
while i < 4 {
v32 = this.read_signed!(workbuf: args.workbuf, part: 0, n: 7)
this.seg_quants[i] = (v32 & 0xFF) as base.u8
i += 1
}
i = 0
while i < 4 {
v32 = this.read_signed!(workbuf: args.workbuf, part: 0, n: 6)
this.seg_strengths[i] = (v32 & 0xFF) as base.u8
i += 1
}
}
if this.seg_update_map {
i = 0
while i < 3 {
v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: 128)
if v1 <> 0 {
v32 = this.read_unsigned!(workbuf: args.workbuf, part: 0, n: 8)
this.seg_probs[i] = (v32 & 0xFF) as base.u8
} else {
this.seg_probs[i] = 0xFF
}
i += 1
}
}
}
pri func decoder.decode_filter_header!(workbuf: roslice base.u8) {
var v1 : base.u32[..= 1]
var v32 : base.u32
var i : base.u32
v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: 128)
this.filt_simple = v1 <> 0
v32 = this.read_unsigned!(workbuf: args.workbuf, part: 0, n: 6)
this.filt_level = (v32 & 0x3F) as base.u8
v32 = this.read_unsigned!(workbuf: args.workbuf, part: 0, n: 3)
this.filt_sharpness = (v32 & 0x07) as base.u8
v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: 128)
this.filt_deltas_enabled = v1 <> 0
if not this.filt_deltas_enabled {
return nothing
}
v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: 128)
if v1 == 0 {
return nothing
}
i = 0
while i < 8 {
v32 = this.read_signed!(workbuf: args.workbuf, part: 0, n: 6)
this.filt_deltas[i >> 2][i & 3] = (v32 & 0xFF) as base.u8
i += 1
}
}
pri func decoder.decode_coeff_probs!(workbuf: roslice base.u8) {
var v1 : base.u32[..= 1]
var v32 : base.u32
var i : base.u32
i = 0
while i < 1056 {
v1 = this.read_bit!(workbuf: args.workbuf, part: 0, prob: COEFF_UPDATE_PROBS[i] as base.u32)
if v1 <> 0 {
v32 = this.read_unsigned!(workbuf: args.workbuf, part: 0, n: 8)
this.coeff_probs[i] = (v32 & 0xFF) as base.u8
} else {
this.coeff_probs[i] = DEFAULT_COEFF_PROBS[i]
}
i += 1
}
}
pri func decoder.calculate_dequants!() {
var seg : base.u32
var q : base.u32
seg = 0
while seg < 4 {
q = this.quant_yac_qi
if not this.seg_enabled {
// No-op.
} else if this.seg_absolute {
q = this.util.sign_extend_convert_u8_u32(a: this.seg_quants[seg])
} else {
// The (q as base.u32) is redundant, equivalent to q, but avoids a
// -Werror=sign-conversion false positive with gcc 12.2.0.
q = (q as base.u32) ~mod+ this.util.sign_extend_convert_u8_u32(a: this.seg_quants[seg])
}
this.dequants[seg][0][0] = DEQUANTS[0][this.clip_127(a: q ~mod+ this.quant_ydc_delta)]
this.dequants[seg][0][1] = DEQUANTS[1][this.clip_127(a: q)]
this.dequants[seg][1][0] = DEQUANTS[0][this.clip_127(a: q ~mod+ this.quant_y2dc_delta)] * 2
this.dequants[seg][1][1] = (DEQUANTS[1][this.clip_127(a: q ~mod+ this.quant_y2ac_delta)] * 155) / 100
if this.dequants[seg][1][1] < 8 {
this.dequants[seg][1][1] = 8
}
this.dequants[seg][2][0] = DEQUANTS[0][this.clip_127(a: q ~mod+ this.quant_uvdc_delta)]
if this.dequants[seg][2][0] > 132 {
this.dequants[seg][2][0] = 132
}
this.dequants[seg][2][1] = DEQUANTS[1][this.clip_127(a: q ~mod+ this.quant_uvac_delta)]
seg += 1
}
}
pri func decoder.clip_127(a: base.u32) base.u32[..= 127] {
if args.a >= 0x8000_0000 {
return 0
} else if args.a >= 127 {
return 127
}
return args.a
}
pri func decoder.decode_other_partition_lengths!(workbuf: roslice base.u8) base.status {
var v32 : base.u32
var remainder : base.u32
var i : base.u32
var o : base.u64
remainder = this.partitioned_data_length ~sat- this.part_lens[0]
remainder ~sat-= 3 * this.num_other_partitions_m1
o = this.workbuf_yuv_v_end + (this.part_lens[0] as base.u64)
i = 0
while i < this.num_other_partitions_m1 {
assert i < 7 via "a < b: a < c; c <= b"(c: this.num_other_partitions_m1)
v32 = 0
if o >= args.workbuf.length() {
return "#bad header"
}
assert o < 0xFFFF_FFFF_FFFF_FFFF via "a < b: a < c; c <= b"(c: args.workbuf.length())
v32 |= (args.workbuf[o] as base.u32) << 0
o += 1
if o >= args.workbuf.length() {
return "#bad header"
}
assert o < 0xFFFF_FFFF_FFFF_FFFF via "a < b: a < c; c <= b"(c: args.workbuf.length())
v32 |= (args.workbuf[o] as base.u32) << 8
o += 1
if o >= args.workbuf.length() {
return "#bad header"
}
assert o < 0xFFFF_FFFF_FFFF_FFFF via "a < b: a < c; c <= b"(c: args.workbuf.length())
v32 |= (args.workbuf[o] as base.u32) << 16
o += 1
if remainder < v32 {
return "#bad header"
}
remainder -= v32
this.part_lens[1 + i] = v32
i += 1
}
if remainder > 0xFF_FFFF {
return "#unsupported VP8 file"
}
this.part_lens[1 + this.num_other_partitions_m1] = remainder
i = 0
while i < (1 + this.num_other_partitions_m1) {
assert i < 8 via "a < b: a < c; c <= b"(c: (1 + this.num_other_partitions_m1))
this.part_workbuf_ris[1 + i] = o
o ~mod+= this.part_lens[1 + i] as base.u64
if o > args.workbuf.length() {
return "#bad header"
}
i += 1
}
return ok
}