| // 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 |
| } |