blob: 2287c52b5784886202f1183c3bc8e6ac9c2de84f [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_alph?(src: base.io_reader, workbuf: slice base.u8) {
var r_mark : base.u64
var status : base.status
while true {
io_limit (io: args.src, limit: this.sub_chunk_length as base.u64) {
r_mark = args.src.mark()
status =? this.decode_alph_limited?(src: args.src, workbuf: args.workbuf)
this.sub_chunk_length ~sat-=
(args.src.count_since(mark: r_mark) & 0xFFFF_FFFF) as base.u32
}
if status.is_ok() {
break
} else if not status.is_suspension() {
return status
} else if (status == base."$short read") and (this.sub_chunk_length == 0) {
return "#short chunk"
}
yield? status
}
}
pri func decoder.decode_alph_limited?(src: base.io_reader, workbuf: slice base.u8) {
var n : base.u64
this.vp8x_alph_flags = args.src.read_u8?()
if (this.vp8x_alph_flags & 3) == 0 {
args.src.limited_copy_u32_to_slice!(up_to: this.width * this.height, s: args.workbuf)
} else if (this.vp8x_alph_flags & 3) == 1 {
this.do_decode_frame_vp8l?(
dst: nullptr,
src: args.src,
blend: this.util.make_pixel_blend(repr: base.PIXEL_BLEND__SRC),
workbuf: args.workbuf,
opts: nullptr)
n = (this.width * this.height * 4) as base.u64
if n <= args.workbuf.length() {
this.compact_alph!(s: args.workbuf[.. n])
}
} else {
return "#bad VP8X chunk"
}
this.vp8x_alph_flags |= 2
n = (this.width * this.height) as base.u64
if n <= args.workbuf.length() {
if (this.vp8x_alph_flags & 0x0C) == 0x04 {
this.filter_alph_horizontal!(s: args.workbuf[.. n])
} else if (this.vp8x_alph_flags & 0x0C) == 0x08 {
this.filter_alph_vertical!(s: args.workbuf[.. n])
} else if (this.vp8x_alph_flags & 0x0C) == 0x0C {
this.filter_alph_gradient!(s: args.workbuf[.. n])
}
}
}
pri func decoder.compact_alph!(s: slice base.u8) {
var i : base.u64[..= 0x4000_0004]
var j : base.u64[..= 0x4000_0004]
if args.s.length() > 0x4000_0000 {
return nothing
}
i = 0
j = 1
while (i < args.s.length()) and (j < args.s.length()),
inv args.s.length() <= 0x4000_0000,
{
assert i <= 0x4000_0000 via "a <= b: a <= c; c <= b"(c: args.s.length())
assert j <= 0x4000_0000 via "a <= b: a <= c; c <= b"(c: args.s.length())
args.s[i] = args.s[j]
i += 1
j += 4
}
}
// libwebp calls this HorizontalUnfilter_C.
pri func decoder.filter_alph_horizontal!(s: slice base.u8) {
var y : base.u32
var x : base.u32
var i : base.u64
var pred : base.u8
y = 0
while y < this.height {
assert y < 0x4000 via "a < b: a < c; c <= b"(c: this.height)
pred = 0
if y > 0 {
i = ((y - 1) * this.width) as base.u64
if i >= args.s.length() {
break
}
pred = args.s[i]
}
x = 0
while x < this.width,
inv y < 0x4000,
{
assert x < 0x4000 via "a < b: a < c; c <= b"(c: this.width)
i = ((y * this.width) + x) as base.u64
if i >= args.s.length() {
break
}
args.s[i] ~mod+= pred
pred = args.s[i]
x += 1
}
y += 1
}
}
// libwebp calls this VerticalUnfilter_C.
pri func decoder.filter_alph_vertical!(s: slice base.u8) {
var y : base.u32
var x : base.u32
var i : base.u64
var pred : base.u8
x = 0
while x < this.width {
assert x < 0x4000 via "a < b: a < c; c <= b"(c: this.width)
pred = 0
if x > 0 {
i = (x - 1) as base.u64
if i >= args.s.length() {
break
}
pred = args.s[i]
}
y = 0
while y < this.height,
inv x < 0x4000,
{
assert y < 0x4000 via "a < b: a < c; c <= b"(c: this.height)
i = ((y * this.width) + x) as base.u64
if i >= args.s.length() {
break
}
args.s[i] ~mod+= pred
pred = args.s[i]
y += 1
}
x += 1
}
}
// libwebp calls this GradientUnfilter_C.
pri func decoder.filter_alph_gradient!(s: slice base.u8) {
var y : base.u32
var x : base.u32
var i : base.u64
var pred : base.u8
var grad : base.u32
var l : base.u8 // Left sample.
var t : base.u8 // Top sample.
var tl : base.u8 // Top-Left sample.
// First row.
x = 0
while x < this.width {
assert x < 0x4000 via "a < b: a < c; c <= b"(c: this.width)
i = x as base.u64
if i >= args.s.length() {
break
}
args.s[i] ~mod+= pred
pred = args.s[i]
x += 1
}
// Other rows.
y = 1
while y < this.height,
inv y >= 1,
{
assert y < 0x4000 via "a < b: a < c; c <= b"(c: this.height)
i = (((y - 1) * this.width) + 0) as base.u64
if i >= args.s.length() {
break
}
t = args.s[i]
tl = t
l = t
x = 0
while x < this.width,
inv y >= 1,
inv y < 0x4000,
{
assert x < 0x4000 via "a < b: a < c; c <= b"(c: this.width)
i = (((y - 1) * this.width) + x) as base.u64
if i >= args.s.length() {
break
}
t = args.s[i]
i = ((y * this.width) + x) as base.u64
if i >= args.s.length() {
break
}
grad = ((l as base.u32) ~mod+ (t as base.u32)) ~mod- (tl as base.u32)
if grad > 0x7FFF_FFFF {
grad = 0x00
} else if grad > 0xFF {
grad = 0xFF
}
l = args.s[i] ~mod+ ((grad & 0xFF) as base.u8)
args.s[i] = l
tl = t
x += 1
}
y += 1
}
}