blob: 44dd36c12007dd99606273ce202a40bc25e56288 [file] [log] [blame]
// Copyright 2021 The Wuffs Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
pri func decoder.filter_and_swizzle_tricky!(dst: ptr base.pixel_buffer, workbuf: slice base.u8) base.status {
var dst_pixfmt : base.pixel_format
var dst_bits_per_pixel : base.u32[..= 256]
var dst_bytes_per_pixel : base.u64[..= 32]
var dst_bytes_per_row : base.u64
var dst_palette : slice base.u8
var tab : table base.u8
var src_bytes_per_pixel : base.u64[..= 8]
var x : base.u32
var y : base.u32
var i : base.u64[..= 0x1FFF_FFC0]
var dst : slice base.u8
var filter : base.u8
var s : slice base.u8
var curr_row : slice base.u8
var prev_row : slice base.u8
var bits_unpacked : array[8] base.u8
var bits_packed : base.u8
var packs_remaining : base.u8
var multiplier : base.u8
var shift : base.u8[..= 7]
// TODO: the dst_pixfmt variable shouldn't be necessary. We should be able
// to chain the two calls: "args.dst.pixel_format().bits_per_pixel()".
dst_pixfmt = args.dst.pixel_format()
dst_bits_per_pixel = dst_pixfmt.bits_per_pixel()
if (dst_bits_per_pixel & 7) <> 0 {
return base."#unsupported option"
}
dst_bytes_per_pixel = (dst_bits_per_pixel / 8) as base.u64
dst_bytes_per_row = (this.width as base.u64) * dst_bytes_per_pixel
dst_palette = args.dst.palette_or_else(fallback: this.dst_palette[..])
tab = args.dst.plane(p: 0)
src_bytes_per_pixel = 1
if this.depth >= 8 {
src_bytes_per_pixel = (NUM_CHANNELS[this.color_type] as base.u64) *
((this.depth >> 3) as base.u64)
}
bits_unpacked[0] = 0xFF
bits_unpacked[1] = 0xFF
bits_unpacked[2] = 0xFF
bits_unpacked[3] = 0xFF
bits_unpacked[4] = 0xFF
bits_unpacked[5] = 0xFF
bits_unpacked[6] = 0xFF
bits_unpacked[7] = 0xFF
y = INTERLACING[this.interlace_pass][5] as base.u32
while y < this.height {
assert y < 0x00FF_FFFF via "a < b: a < c; c <= b"(c: this.height)
dst = tab.row(y: y)
if dst_bytes_per_row < dst.length() {
dst = dst[.. dst_bytes_per_row]
}
if 1 > args.workbuf.length() {
return "#internal error: inconsistent workbuf length"
}
filter = args.workbuf[0]
args.workbuf = args.workbuf[1 ..]
if this.pass_bytes_per_row > args.workbuf.length() {
return "#internal error: inconsistent workbuf length"
}
curr_row = args.workbuf[.. this.pass_bytes_per_row]
args.workbuf = args.workbuf[this.pass_bytes_per_row ..]
if filter == 0 {
// No-op.
} else if filter == 1 {
this.filter_1!(curr: curr_row)
} else if filter == 2 {
this.filter_2!(curr: curr_row, prev: prev_row)
} else if filter == 3 {
this.filter_3!(curr: curr_row, prev: prev_row)
} else if filter == 4 {
this.filter_4!(curr: curr_row, prev: prev_row)
} else {
return "#bad filter"
}
s = curr_row
x = INTERLACING[this.interlace_pass][2] as base.u32
if this.depth == 8 {
while x < this.width,
inv y < 0x00FF_FFFF,
{
assert x < 0x00FF_FFFF via "a < b: a < c; c <= b"(c: this.width)
i = (x as base.u64) * dst_bytes_per_pixel
if i <= dst.length() {
if this.color_type == 4 {
if 2 <= s.length() {
bits_unpacked[0] = s[0]
bits_unpacked[1] = s[0]
bits_unpacked[2] = s[0]
bits_unpacked[3] = s[1]
s = s[2 ..]
this.swizzler.swizzle_interleaved_from_slice!(
dst: dst[i ..],
dst_palette: dst_palette,
src: bits_unpacked[.. 4])
}
} else if src_bytes_per_pixel <= s.length() {
this.swizzler.swizzle_interleaved_from_slice!(
dst: dst[i ..],
dst_palette: dst_palette,
src: s[.. src_bytes_per_pixel])
s = s[src_bytes_per_pixel ..]
}
}
x += (1 as base.u32) << INTERLACING[this.interlace_pass][0]
} endwhile
} else if this.depth < 8 {
multiplier = 1
if this.color_type == 0 { // Color type 0 means base.PIXEL_FORMAT__Y.
multiplier = LOW_BIT_DEPTH_MULTIPLIERS[this.depth]
}
shift = (8 - this.depth) & 7
packs_remaining = 0
while x < this.width,
inv y < 0x00FF_FFFF,
inv this.depth < 8,
{
assert x < 0x00FF_FFFF via "a < b: a < c; c <= b"(c: this.width)
i = (x as base.u64) * dst_bytes_per_pixel
if i <= dst.length() {
if (packs_remaining == 0) and (1 <= s.length()) {
packs_remaining = LOW_BIT_DEPTH_NUM_PACKS[this.depth]
bits_packed = s[0]
s = s[1 ..]
}
bits_unpacked[0] = (bits_packed >> shift) ~mod* multiplier
bits_packed = bits_packed ~mod<< this.depth
packs_remaining = packs_remaining ~mod- 1
this.swizzler.swizzle_interleaved_from_slice!(
dst: dst[i ..],
dst_palette: dst_palette,
src: bits_unpacked[.. 1])
}
x += (1 as base.u32) << INTERLACING[this.interlace_pass][0]
} endwhile
} else {
while x < this.width,
inv y < 0x00FF_FFFF,
{
assert x < 0x00FF_FFFF via "a < b: a < c; c <= b"(c: this.width)
i = (x as base.u64) * dst_bytes_per_pixel
if i <= dst.length() {
if this.color_type == 0 {
if 2 <= s.length() {
bits_unpacked[0] = s[1]
bits_unpacked[1] = s[0]
bits_unpacked[2] = s[1]
bits_unpacked[3] = s[0]
bits_unpacked[4] = s[1]
bits_unpacked[5] = s[0]
s = s[2 ..]
}
} else if this.color_type == 2 {
if 6 <= s.length() {
bits_unpacked[0] = s[5]
bits_unpacked[1] = s[4]
bits_unpacked[2] = s[3]
bits_unpacked[3] = s[2]
bits_unpacked[4] = s[1]
bits_unpacked[5] = s[0]
s = s[6 ..]
}
} else if this.color_type == 4 {
if 4 <= s.length() {
bits_unpacked[0] = s[1]
bits_unpacked[1] = s[0]
bits_unpacked[2] = s[1]
bits_unpacked[3] = s[0]
bits_unpacked[4] = s[1]
bits_unpacked[5] = s[0]
bits_unpacked[6] = s[3]
bits_unpacked[7] = s[2]
s = s[4 ..]
}
} else {
if 8 <= s.length() {
bits_unpacked[0] = s[5]
bits_unpacked[1] = s[4]
bits_unpacked[2] = s[3]
bits_unpacked[3] = s[2]
bits_unpacked[4] = s[1]
bits_unpacked[5] = s[0]
bits_unpacked[6] = s[7]
bits_unpacked[7] = s[6]
s = s[8 ..]
}
}
this.swizzler.swizzle_interleaved_from_slice!(
dst: dst[i ..],
dst_palette: dst_palette,
src: bits_unpacked[.. 8])
}
x += (1 as base.u32) << INTERLACING[this.interlace_pass][0]
} endwhile
}
prev_row = curr_row
y += (1 as base.u32) << INTERLACING[this.interlace_pass][3]
} endwhile
return ok
}