blob: e64a2d9d0e4a8569c147b3e609d3f7195d878b90 [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.reconstruct!(
workbuf: slice base.u8,
mbx: base.u32[..= 0x3FF],
mby: base.u32[..= 0x3FF],
luma_mode: base.u32[..= 4],
chroma_mode: base.u32[..= 3]) {
var mode : base.u32[..= 12]
var b : base.u32
var mask : base.u32
this.prepare_yuv_cache!(workbuf: args.workbuf, mbx: args.mbx, mby: args.mby)
if args.luma_mode < 4 {
mode = this.substitute_dc_top_left(mode: args.luma_mode, mbx: args.mbx, mby: args.mby)
this.predict_y16!(mode: mode)
}
b = 0
while b < 16 {
if args.luma_mode >= 4 {
this.predict_y4!(b: b)
}
mask = (1 as base.u32) << (b + 0x00)
if (this.mb_ac_nz & mask) <> 0 {
this.inverse_dct_full!(
cachex: ((b & 0x3) * 4) + 0x08,
cachey: ((b & 0xC) * 1) + 0x01,
b: (b + 0x00))
} else if (this.mb_dc_nz & mask) <> 0 {
this.inverse_dct_dc_only!(
cachex: ((b & 0x3) * 4) + 0x08,
cachey: ((b & 0xC) * 1) + 0x01,
b: (b + 0x00))
}
b += 1
}
mode = this.substitute_dc_top_left(mode: args.chroma_mode, mbx: args.mbx, mby: args.mby)
this.predict_uv8!(b: 0, mode: mode)
if (this.mb_ac_nz & 0x0F_0000) <> 0 {
b = 0
while b < 4 {
this.inverse_dct_full!(
cachex: ((b & 0x1) * 4) + 0x08,
cachey: ((b & 0x2) * 2) + 0x12,
b: (b + 0x10))
b += 1
}
} else if (this.mb_dc_nz & 0x0F_0000) <> 0 {
b = 0
while b < 4 {
this.inverse_dct_dc_only!(
cachex: ((b & 0x1) * 4) + 0x08,
cachey: ((b & 0x2) * 2) + 0x12,
b: (b + 0x10))
b += 1
}
}
this.predict_uv8!(b: 1, mode: mode)
if (this.mb_ac_nz & 0xF0_0000) <> 0 {
b = 0
while b < 4 {
this.inverse_dct_full!(
cachex: ((b & 0x1) * 4) + 0x18,
cachey: ((b & 0x2) * 2) + 0x12,
b: (b + 0x14))
b += 1
}
} else if (this.mb_dc_nz & 0xF0_0000) <> 0 {
b = 0
while b < 4 {
this.inverse_dct_dc_only!(
cachex: ((b & 0x1) * 4) + 0x18,
cachey: ((b & 0x2) * 2) + 0x12,
b: (b + 0x14))
b += 1
}
}
}
pri func decoder.substitute_dc_top_left(
mode: base.u32[..= 3],
mbx: base.u32[..= 0x3FF],
mby: base.u32[..= 0x3FF]) base.u32[..= 12] {
if args.mode == 0 { // DC.
if args.mbx == 0 {
if args.mby == 0 {
return 12 // DCTopLeft.
} else {
return 11 // DCLeft.
}
} else if args.mby == 0 {
return 10 // DCTop.
}
}
return args.mode
}
pri func decoder.prepare_yuv_cache!(
workbuf: roslice base.u8,
mbx: base.u32[..= 0x3FF],
mby: base.u32[..= 0x3FF]) {
var i : base.u32
var j : base.u32
var offset : base.u64[..= 0x17FE_FFF8]
var o : base.u64
if args.mbx <= 0 {
j = 0x00
while j < 0x11 {
this.yuv_cache[j][0x07] = 0x81
j += 1
}
while j < 0x1A {
this.yuv_cache[j][0x07] = 0x81
this.yuv_cache[j][0x17] = 0x81
j += 1
}
} else {
j = 0x00
while j < 0x11 {
this.yuv_cache[j][0x07] = this.yuv_cache[j][0x17]
j += 1
}
while j < 0x1A {
this.yuv_cache[j][0x07] = this.yuv_cache[j][0x0F]
this.yuv_cache[j][0x17] = this.yuv_cache[j][0x1F]
j += 1
}
}
if args.mby <= 0 {
i = 0x00
while i < 0x20 {
this.yuv_cache[0x00][i] = 0x7F
i += 1
}
i = 0x00
while i < 0x20 {
this.yuv_cache[0x11][i] = 0x7F
i += 1
}
} else {
offset = 0 +
(((((args.mby * 16) - 1) * this.workbuf_yuv_y_stride) + (args.mbx * 16)) as base.u64)
i = 0x00
while i < 0x10,
inv args.mby > 0,
{
o = offset + (i as base.u64)
if o < args.workbuf.length() {
this.yuv_cache[0x00][0x08 + i] = args.workbuf[o]
}
i += 1
}
if (args.mbx + 1) < this.mbw {
while i < 0x14,
inv args.mby > 0,
{
o = offset + (i as base.u64)
if o < args.workbuf.length() {
this.yuv_cache[0x00][0x08 + i] = args.workbuf[o]
}
i += 1
}
} else {
while i < 0x14,
inv args.mby > 0,
{
o = offset + (0x0F as base.u64)
if o < args.workbuf.length() {
this.yuv_cache[0x00][0x08 + i] = args.workbuf[o]
}
i += 1
}
}
offset = this.workbuf_yuv_y_end +
(((((args.mby * 8) - 1) * this.workbuf_yuv_uv_stride) + (args.mbx * 8)) as base.u64)
i = 0x00
while i < 0x08,
inv args.mby > 0,
{
o = offset + (i as base.u64)
if o < args.workbuf.length() {
this.yuv_cache[0x11][0x08 + i] = args.workbuf[o]
}
i += 1
}
offset = this.workbuf_yuv_u_end +
(((((args.mby * 8) - 1) * this.workbuf_yuv_uv_stride) + (args.mbx * 8)) as base.u64)
i = 0x00
while i < 0x08,
inv args.mby > 0,
{
o = offset + (i as base.u64)
if o < args.workbuf.length() {
this.yuv_cache[0x11][0x18 + i] = args.workbuf[o]
}
i += 1
}
}
j = 0x04
while j < 0x10 {
this.yuv_cache[j][0x18] = this.yuv_cache[0][0x18]
this.yuv_cache[j][0x19] = this.yuv_cache[0][0x19]
this.yuv_cache[j][0x1A] = this.yuv_cache[0][0x1A]
this.yuv_cache[j][0x1B] = this.yuv_cache[0][0x1B]
j += 4
}
}