blob: b8cbbb1e0b75204a8d8959bb016ee56d452829be [file] [log] [blame]
// Copyright 2024 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
// --------
// ETC1 / ETC2 is a lossy, fixed-rate texture compression format. ETC1 uses 8
// bytes per 4×4 pixel block, 0.5 bytes per pixel (after rounding image
// dimensions up to a multiple of 4). The ETC1 specification is at:
// http://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt
//
// That's the raw format for uploading to GPUs. The iPACKMAN file format (*.pkm
// files) prepends a 16-byte header, stating width, height and pixel format.
//
// Android v2.2 Froyo (2010) and its OpenGL ES shipped with ETC1 / PKM support.
// AOSP has a software implementation, used by its etc1tool program:
// https://cs.android.com/android/platform/superproject/main/+/main:frameworks/native/opengl/libs/ETC1/etc1.cpp;l=200;drc=efb735f4d5a2f04550e33e8aa9485f906018fe4e
//
// ETC2 (mandatory in OpenGL ES 3.0) extends ETC1 in a backwards-compatible way
// by re-purposing bit patterns that well-behaved encoders wouldn't emit.
//
// https://www.khronos.org/assets/uploads/developers/library/2012-siggraph-opengl-es-bof/Ericsson-ETC2-SIGGRAPH_Aug12.pdf
// https://www.graphicshardware.org/previous/www_2007/presentations/strom-etc2-gh07.pdf
// https://github.com/Ericsson/ETCPACK
// https://en.wikipedia.org/wiki/Ericsson_Texture_Compression
pub status "#bad header"
pub status "#truncated input"
pub status "#unsupported ETC2 file"
pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE : base.u64 = 0
pub struct decoder? implements base.image_decoder(
pixfmt : base.u32,
width : base.u32[..= 0xFFFF],
height : base.u32[..= 0xFFFF],
remaining_blocks : base.u32[..= 0x1000_0000],
// The call sequence state machine is discussed in
// (/doc/std/image-decoders-call-sequence.md).
call_sequence : base.u8,
srgb : base.bool,
// buffer_index is measured in blocks.
buffer_index : base.u32[..= 64],
dst_x : base.u32,
dst_y : base.u32,
swizzler : base.pixel_swizzler,
util : base.utility,
) + (
// buffer holds 64 blocks. Each block is 4×4 pixels, 64 bytes.
buffer : array[4096] base.u8,
)
pub func decoder.get_quirk(key: base.u32) base.u64 {
return 0
}
pub func decoder.set_quirk!(key: base.u32, value: base.u64) base.status {
return base."#unsupported option"
}
pub func decoder.decode_image_config?(dst: nptr base.image_config, src: base.io_reader) {
var status : base.status
while true {
status =? this.do_decode_image_config?(dst: args.dst, src: args.src)
if (status == base."$short read") and args.src.is_closed() {
return "#truncated input"
}
yield? status
}
}
pri func decoder.do_decode_image_config?(dst: nptr base.image_config, src: base.io_reader) {
var c32 : base.u32
var rounded_up_width : base.u16
var rounded_up_height : base.u16
if this.call_sequence <> 0x00 {
return base."#bad call sequence"
}
c32 = args.src.read_u32le?()
if c32 <> 'PKM 'le {
return "#bad header"
}
c32 = args.src.read_u32le?()
if (c32 == '10\x00\x00'le) or // ETC1.
(c32 == '20\x00\x01'le) or // ETC2 RGB.
(c32 == '20\x00\x09'le) { // ETC2 sRGB.
this.pixfmt = base.PIXEL_FORMAT__BGRX
} else if (c32 & 0xFFFF) == '20'le {
return "#unsupported ETC2 file"
} else {
return "#bad header"
}
this.srgb = (c32 >> 24) >= 0x09
rounded_up_width = args.src.read_u16be?()
rounded_up_height = args.src.read_u16be?()
c32 = args.src.read_u16be_as_u32?()
if ((c32 + 3) & 0xFFFF_FFFC) <> (rounded_up_width as base.u32) {
return "#bad header"
}
this.width = c32
c32 = args.src.read_u16be_as_u32?()
if ((c32 + 3) & 0xFFFF_FFFC) <> (rounded_up_height as base.u32) {
return "#bad header"
}
this.height = c32
if args.dst <> nullptr {
args.dst.set!(
pixfmt: this.pixfmt,
pixsub: 0,
width: this.width,
height: this.height,
first_frame_io_position: 16,
first_frame_is_opaque: this.pixfmt == base.PIXEL_FORMAT__BGRX)
}
this.call_sequence = 0x20
}
pub func decoder.decode_frame_config?(dst: nptr base.frame_config, src: base.io_reader) {
var status : base.status
while true {
status =? this.do_decode_frame_config?(dst: args.dst, src: args.src)
if (status == base."$short read") and args.src.is_closed() {
return "#truncated input"
}
yield? status
}
}
pri func decoder.do_decode_frame_config?(dst: nptr base.frame_config, src: base.io_reader) {
if this.call_sequence == 0x20 {
// No-op.
} else if this.call_sequence < 0x20 {
this.do_decode_image_config?(dst: nullptr, src: args.src)
} else if this.call_sequence == 0x28 {
if 16 <> args.src.position() {
return base."#bad restart"
}
} else if this.call_sequence == 0x40 {
this.call_sequence = 0x60
return base."@end of data"
} else {
return base."@end of data"
}
if args.dst <> nullptr {
args.dst.set!(bounds: this.util.make_rect_ie_u32(
min_incl_x: 0,
min_incl_y: 0,
max_excl_x: this.width,
max_excl_y: this.height),
duration: 0,
index: 0,
io_position: 16,
disposal: 0,
opaque_within_bounds: this.pixfmt == base.PIXEL_FORMAT__BGRX,
overwrite_instead_of_blend: false,
background_color: 0xFF00_0000)
}
this.call_sequence = 0x40
}
pub func decoder.decode_frame?(dst: ptr base.pixel_buffer, src: base.io_reader, blend: base.pixel_blend, workbuf: slice base.u8, opts: nptr base.decode_frame_options) {
var status : base.status
while true {
status =? this.do_decode_frame?(dst: args.dst, src: args.src, blend: args.blend, workbuf: args.workbuf, opts: args.opts)
if (status == base."$short read") and args.src.is_closed() {
return "#truncated input"
}
yield? status
}
}
pri func decoder.do_decode_frame?(dst: ptr base.pixel_buffer, src: base.io_reader, blend: base.pixel_blend, workbuf: slice base.u8, opts: nptr base.decode_frame_options) {
var status : base.status
if this.call_sequence == 0x40 {
// No-op.
} else if this.call_sequence < 0x40 {
this.do_decode_frame_config?(dst: nullptr, src: args.src)
} else {
return base."@end of data"
}
status = this.swizzler.prepare!(
dst_pixfmt: args.dst.pixel_format(),
dst_palette: args.dst.palette(),
src_pixfmt: this.util.make_pixel_format(repr: this.pixfmt),
src_palette: this.util.empty_slice_u8(),
blend: args.blend)
if not status.is_ok() {
return status
}
this.dst_x = 0
this.dst_y = 0
this.buffer[.. 4096].bulk_memset!(byte_value: 0xFF)
this.remaining_blocks = ((this.width + 3) / 4) * ((this.height + 3) / 4)
while this.remaining_blocks > 0 {
this.from_src_to_buffer?(src: args.src)
if this.remaining_blocks < this.buffer_index {
return base."#too much data"
}
this.remaining_blocks -= this.buffer_index
status = this.from_buffer_to_dst!(dst: args.dst)
if not status.is_ok() {
return status
}
}
this.call_sequence = 0x60
}
pri func decoder.from_src_to_buffer?(src: base.io_reader) {
var bi : base.u32[..= 64]
var bj : base.u32[..= 64]
var c64 : base.u64
var r0 : base.u32
var r1 : base.u32
var g0 : base.u32
var g1 : base.u32
var b0 : base.u32
var b1 : base.u32
var flip : base.bool
bj = this.remaining_blocks.min(no_more_than: 64)
while bi < bj {
assert bi < 64 via "a < b: a < c; c <= b"(c: bj)
c64 = args.src.read_u64be?()
if (c64 & 0x2_0000_0000) == 0 {
// The high 32 bits of c64:
//
// 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 dec
// 3F 3E 3D 3C 3B 3A 39 38 37 36 35 34 33 32 31 30 2F 2E 2D 2C 2B 2A 29 28 27 26 25 24 23 22 21 20 hex
// R0 r0 r0 r0 R1 r1 r1 r1 G0 g0 g0 g0 G1 g1 g1 g1 B0 b0 b0 b0 B1 b1 b1 b1 W0 w0 w0 W1 w1 w1 00 Fl
r0 = (0x0F & (c64 >> 0x3C)) as base.u32
r0 = (r0 << 4) | r0
r1 = (0x0F & (c64 >> 0x38)) as base.u32
r1 = (r1 << 4) | r1
g0 = (0x0F & (c64 >> 0x34)) as base.u32
g0 = (g0 << 4) | g0
g1 = (0x0F & (c64 >> 0x30)) as base.u32
g1 = (g1 << 4) | g1
b0 = (0x0F & (c64 >> 0x2C)) as base.u32
b0 = (b0 << 4) | b0
b1 = (0x0F & (c64 >> 0x28)) as base.u32
b1 = (b1 << 4) | b1
} else {
// The high 32 bits of c64 (unless ETC2's T / H / Planar modes):
//
// 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 dec
// 3F 3E 3D 3C 3B 3A 39 38 37 36 35 34 33 32 31 30 2F 2E 2D 2C 2B 2A 29 28 27 26 25 24 23 22 21 20 hex
// R0 r0 r0 r0 r0 R1 r1 r1 G0 g0 g0 g0 g0 G1 g1 g1 B0 b0 b0 b0 b0 B1 b1 b1 W0 w0 w0 W1 w1 w1 01 Fl
r0 = (0x1F & (c64 >> 0x3B)) as base.u32
r1 = r0 ~mod+ DIFFS[0x07 & (c64 >> 0x38)]
if (r1 >> 5) <> 0 {
this.decode_t_mode!(
bits: c64,
offset: 16 * bi)
bi += 1
continue
}
r0 = (r0 ~mod<< 3) | (r0 >> 2)
r1 = (r1 ~mod<< 3) | (r1 >> 2)
g0 = (0x1F & (c64 >> 0x33)) as base.u32
g1 = g0 ~mod+ DIFFS[0x07 & (c64 >> 0x30)]
if (g1 >> 5) <> 0 {
this.decode_h_mode!(
bits: c64,
offset: 16 * bi)
bi += 1
continue
}
g0 = (g0 ~mod<< 3) | (g0 >> 2)
g1 = (g1 ~mod<< 3) | (g1 >> 2)
b0 = (0x1F & (c64 >> 0x2B)) as base.u32
b1 = b0 ~mod+ DIFFS[0x07 & (c64 >> 0x28)]
if (b1 >> 5) <> 0 {
this.decode_planar_mode!(
bits: c64,
offset: 16 * bi)
bi += 1
continue
}
b0 = (b0 ~mod<< 3) | (b0 >> 2)
b1 = (b1 ~mod<< 3) | (b1 >> 2)
}
flip = (c64 & 0x1_0000_0000) <> 0
this.decode_half_block!(
bits: (c64 & 0xFFFF_FFFF) as base.u32,
offset: 16 * bi,
which: ((c64 >> 0x25) & 7) as base.u32,
r: r0,
g: g0,
b: b0,
flip: flip,
second: false)
this.decode_half_block!(
bits: (c64 & 0xFFFF_FFFF) as base.u32,
offset: 16 * bi,
which: ((c64 >> 0x22) & 7) as base.u32,
r: r1,
g: g1,
b: b1,
flip: flip,
second: true)
bi += 1
}
this.buffer_index = bi
}
pri func decoder.decode_t_mode!(
bits: base.u64,
offset: base.u32[..= 1008]) {
var r : array[4] base.u8
var g : array[4] base.u8
var b : array[4] base.u8
var which : base.u32[..= 7]
var delta : base.u32
var y : base.u32
var x : base.u32
var x4y : base.u32[..= 15]
var i : base.u32[..= 3]
var o : base.u32[..= 4092]
// Unjumble T-mode's 59 bits and convert from RGB444 to RGB888.
//
// 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 dec
// 3F 3E 3D 3C 3B 3A 39 38 37 36 35 34 33 32 31 30 2F 2E 2D 2C 2B 2A 29 28 27 26 25 24 23 22 21 20 hex
// .. .. .. R0 r0 .. r0 r0 G0 g0 g0 g0 B0 b0 b0 b0 R2 r2 r2 r2 G2 g2 g2 g2 B2 b2 b2 b2 Wh wh .. wh
//
// The low 32 bits hold "2 bits per pixel" indexes into the r, g, b arrays.
r[0] = ((0x0C & (args.bits >> 0x39)) as base.u8) |
((0x03 & (args.bits >> 0x38)) as base.u8)
r[0] = (r[0] << 4) | r[0]
g[0] = (0x0F & (args.bits >> 0x34)) as base.u8
g[0] = (g[0] << 4) | g[0]
b[0] = (0x0F & (args.bits >> 0x30)) as base.u8
b[0] = (b[0] << 4) | b[0]
r[2] = (0x0F & (args.bits >> 0x2C)) as base.u8
r[2] = (r[2] << 4) | r[2]
g[2] = (0x0F & (args.bits >> 0x28)) as base.u8
g[2] = (g[2] << 4) | g[2]
b[2] = (0x0F & (args.bits >> 0x24)) as base.u8
b[2] = (b[2] << 4) | b[2]
// Modulate.
which = ((0x06 & (args.bits >> 0x21)) as base.u32) |
((0x01 & (args.bits >> 0x20)) as base.u32)
delta = T_H_MODIFIERS[which] as base.u32
r[1] = CLAMP[((r[2] as base.u32) ~mod+ delta) & 1023]
g[1] = CLAMP[((g[2] as base.u32) ~mod+ delta) & 1023]
b[1] = CLAMP[((b[2] as base.u32) ~mod+ delta) & 1023]
r[3] = CLAMP[((r[2] as base.u32) ~mod- delta) & 1023]
g[3] = CLAMP[((g[2] as base.u32) ~mod- delta) & 1023]
b[3] = CLAMP[((b[2] as base.u32) ~mod- delta) & 1023]
// Paint the 4×4 block.
while y < 4 {
x = 0
while x < 4,
inv y < 4,
{
x4y = (x * 4) | y
i = (((args.bits >> x4y) & 1) as base.u32) |
(((args.bits >> (x4y + 15)) & 2) as base.u32)
o = args.offset + (x * 4) + (y * 1024)
this.buffer[o + 0] = b[i]
this.buffer[o + 1] = g[i]
this.buffer[o + 2] = r[i]
x += 1
}
y += 1
}
}
pri func decoder.decode_h_mode!(
bits: base.u64,
offset: base.u32[..= 1008]) {
var r : array[4] base.u8
var g : array[4] base.u8
var b : array[4] base.u8
var rgb0 : base.u32
var rgb2 : base.u32
var which : base.u32[..= 7]
var delta : base.u32
var y : base.u32
var x : base.u32
var x4y : base.u32[..= 15]
var i : base.u32[..= 3]
var o : base.u32[..= 4092]
// Unjumble H-mode's 58 bits (one of the 'which' bits is implied by order)
// and convert from RGB444 to RGB888.
//
// 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 dec
// 3F 3E 3D 3C 3B 3A 39 38 37 36 35 34 33 32 31 30 2F 2E 2D 2C 2B 2A 29 28 27 26 25 24 23 22 21 20 hex
// .. R0 r0 r0 r0 G0 g0 g0 .. .. .. g0 B0 .. b0 b0 b0 R2 r2 r2 r2 G2 g2 g2 g2 B2 b2 b2 b2 Wh .. wh
//
// The low 32 bits hold "2 bits per pixel" indexes into the r, g, b arrays.
r[0] = (0x0F & (args.bits >> 0x3B)) as base.u8
r[0] = (r[0] << 4) | r[0]
g[0] = ((0x0E & (args.bits >> 0x37)) as base.u8) |
((0x01 & (args.bits >> 0x34)) as base.u8)
g[0] = (g[0] << 4) | g[0]
b[0] = ((0x08 & (args.bits >> 0x30)) as base.u8) |
((0x07 & (args.bits >> 0x2F)) as base.u8)
b[0] = (b[0] << 4) | b[0]
r[2] = (0x0F & (args.bits >> 0x2B)) as base.u8
r[2] = (r[2] << 4) | r[2]
g[2] = (0x0F & (args.bits >> 0x27)) as base.u8
g[2] = (g[2] << 4) | g[2]
b[2] = (0x0F & (args.bits >> 0x23)) as base.u8
b[2] = (b[2] << 4) | b[2]
// Modulate.
rgb0 = ((r[0] as base.u32) << 0x10) |
((g[0] as base.u32) << 0x08) |
((b[0] as base.u32) << 0x00)
rgb2 = ((r[2] as base.u32) << 0x10) |
((g[2] as base.u32) << 0x08) |
((b[2] as base.u32) << 0x00)
which = ((0x04 & (args.bits >> 0x20)) as base.u32) |
((0x02 & (args.bits >> 0x1F)) as base.u32)
if rgb0 >= rgb2 {
which |= 1
}
delta = T_H_MODIFIERS[which] as base.u32
r[1] = CLAMP[((r[0] as base.u32) ~mod- delta) & 1023]
g[1] = CLAMP[((g[0] as base.u32) ~mod- delta) & 1023]
b[1] = CLAMP[((b[0] as base.u32) ~mod- delta) & 1023]
r[0] = CLAMP[((r[0] as base.u32) ~mod+ delta) & 1023]
g[0] = CLAMP[((g[0] as base.u32) ~mod+ delta) & 1023]
b[0] = CLAMP[((b[0] as base.u32) ~mod+ delta) & 1023]
r[3] = CLAMP[((r[2] as base.u32) ~mod- delta) & 1023]
g[3] = CLAMP[((g[2] as base.u32) ~mod- delta) & 1023]
b[3] = CLAMP[((b[2] as base.u32) ~mod- delta) & 1023]
r[2] = CLAMP[((r[2] as base.u32) ~mod+ delta) & 1023]
g[2] = CLAMP[((g[2] as base.u32) ~mod+ delta) & 1023]
b[2] = CLAMP[((b[2] as base.u32) ~mod+ delta) & 1023]
// Paint the 4×4 block.
while y < 4 {
x = 0
while x < 4,
inv y < 4,
{
x4y = (x * 4) | y
i = (((args.bits >> x4y) & 1) as base.u32) |
(((args.bits >> (x4y + 15)) & 2) as base.u32)
o = args.offset + (x * 4) + (y * 1024)
this.buffer[o + 0] = b[i]
this.buffer[o + 1] = g[i]
this.buffer[o + 2] = r[i]
x += 1
}
y += 1
}
}
pri func decoder.decode_planar_mode!(
bits: base.u64,
offset: base.u32[..= 1008]) {
var ro : base.u32
var go : base.u32
var bo : base.u32
var rh : base.u32
var gh : base.u32
var bh : base.u32
var rv : base.u32
var gv : base.u32
var bv : base.u32
var y : base.u32
var x : base.u32
var o : base.u32[..= 4092]
var rp : base.u32
var gp : base.u32
var bp : base.u32
// Unjumble Planar-mode's 57 bits and convert from RGB676 to RGB888.
//
// 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 dec
// 3F 3E 3D 3C 3B 3A 39 38 37 36 35 34 33 32 31 30 2F 2E 2D 2C 2B 2A 29 28 27 26 25 24 23 22 21 20 hex
// .. Ro ro ro ro ro ro Go .. go go go go go go Bo .. .. .. bo bo .. bo bo bo Rh rh rh rh rh .. rh
//
// 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 dec
// 1F 1E 1D 1C 1B 1A 19 18 17 16 15 14 13 12 11 10 0F 0E 0D 0C 0B 0A 09 08 07 06 05 04 03 02 01 00 hex
// Gh gh gh gh gh gh gh Bh bh bh bh bh bh Rv rv rv rv rv rv Gv gv gv gv gv gv gv Bv bv bv bv bv bv
ro = (0x3F & (args.bits >> 0x39)) as base.u32
ro = (ro ~mod<< 2) | (ro >> 4)
go = ((0x40 & (args.bits >> 0x32)) as base.u32) |
((0x3F & (args.bits >> 0x31)) as base.u32)
go = (go ~mod<< 1) | (go >> 6)
bo = ((0x20 & (args.bits >> 0x2B)) as base.u32) |
((0x18 & (args.bits >> 0x28)) as base.u32) |
((0x07 & (args.bits >> 0x27)) as base.u32)
bo = (bo ~mod<< 2) | (bo >> 4)
rh = ((0x3E & (args.bits >> 0x21)) as base.u32) |
((0x01 & (args.bits >> 0x20)) as base.u32)
rh = (rh ~mod<< 2) | (rh >> 4)
gh = (0x7F & (args.bits >> 0x19)) as base.u32
gh = (gh ~mod<< 1) | (gh >> 6)
bh = (0x3F & (args.bits >> 0x13)) as base.u32
bh = (bh ~mod<< 2) | (bh >> 4)
rv = (0x3F & (args.bits >> 0x0D)) as base.u32
rv = (rv ~mod<< 2) | (rv >> 4)
gv = (0x7F & (args.bits >> 0x06)) as base.u32
gv = (gv ~mod<< 1) | (gv >> 6)
bv = (0x3F & (args.bits >> 0x00)) as base.u32
bv = (bv ~mod<< 2) | (bv >> 4)
// Convert h and v colors from absolute to relative.
rh ~mod-= ro
gh ~mod-= go
bh ~mod-= bo
rv ~mod-= ro
gv ~mod-= go
bv ~mod-= bo
// Prepare to linearly interpolate (which involves dividing by 4).
ro ~mod*= 4
go ~mod*= 4
bo ~mod*= 4
// Paint the 4×4 block.
while y < 4 {
x = 0
while x < 4,
inv y < 4,
{
o = args.offset + (x * 4) + (y * 1024)
bp = ((x ~mod* bh) ~mod+ (y ~mod* bv)) ~mod+ bo
this.buffer[o + 0] = CLAMP[((bp ~mod+ 2) / 4) & 1023]
gp = ((x ~mod* gh) ~mod+ (y ~mod* gv)) ~mod+ go
this.buffer[o + 1] = CLAMP[((gp ~mod+ 2) / 4) & 1023]
rp = ((x ~mod* rh) ~mod+ (y ~mod* rv)) ~mod+ ro
this.buffer[o + 2] = CLAMP[((rp ~mod+ 2) / 4) & 1023]
x += 1
}
y += 1
}
}
pri func decoder.decode_half_block!(
bits: base.u32,
offset: base.u32[..= 1008],
which: base.u32[..= 7],
r: base.u32,
g: base.u32,
b: base.u32,
flip: base.bool,
second: base.bool) {
var x0 : base.u32[..= 2]
var y0 : base.u32[..= 2]
var x : base.u32[..= 3]
var y : base.u32[..= 3]
var i : base.u32
var x4y : base.u32[..= 15]
var modif : base.u32
var o : base.u32[..= 4092]
if not args.second {
// No-op.
} else if args.flip {
y0 = 2
} else {
x0 = 2
}
while i < 8 {
if args.flip {
x = (x0 + (i / 2)) & 3
y = (y0 + (i & 1))
} else {
x = (x0 + (i / 4))
y = (y0 + i) & 3
}
x4y = (x * 4) | y
modif = MODIFIERS[args.which][
((args.bits >> x4y) & 1) |
((args.bits >> (x4y + 15)) & 2)]
o = args.offset + (x * 4) + (y * 1024)
this.buffer[o + 0] = CLAMP[(args.b ~mod+ modif) & 1023]
this.buffer[o + 1] = CLAMP[(args.g ~mod+ modif) & 1023]
this.buffer[o + 2] = CLAMP[(args.r ~mod+ modif) & 1023]
i += 1
}
}
pri func decoder.from_buffer_to_dst!(dst: ptr base.pixel_buffer) base.status {
var dst_pixfmt : base.pixel_format
var dst_bits_per_pixel : base.u32[..= 256]
var dst_bytes_per_pixel : base.u32[..= 32]
var dst_bytes_per_row : base.u64
var tab : table base.u8
var bi : base.u32
var rem_x : base.u32[..= 0xFFFF]
var dy : base.u32[..= 4]
var dst : slice base.u8
var src : slice base.u8
var si : base.u32[..= 4080]
var sj : base.u32[..= 4096]
var i : base.u64
var num_src_pixels : base.u32
// 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
dst_bytes_per_row = (this.width * dst_bytes_per_pixel) as base.u64
tab = args.dst.plane(p: 0)
while bi < this.buffer_index {
assert bi < 64 via "a < b: a < c; c <= b"(c: this.buffer_index)
if this.width <= this.dst_x {
this.dst_x = 0
this.dst_y ~mod+= 4
if this.dst_y >= this.height {
break
}
rem_x = this.width
} else {
rem_x = this.width - this.dst_x
}
dy = 0
while dy < 4,
inv bi < 64,
{
si = (1024 * dy) + (16 * bi)
sj = (1024 * dy) + (16 * 64)
if si < sj {
src = this.buffer[si .. sj]
}
if ((4 * rem_x) as base.u64) < src.length() {
src = src[.. (4 * rem_x) as base.u64]
}
if (this.dst_y ~mod+ dy) >= this.height {
break
}
dst = tab.row_u32(y: this.dst_y ~mod+ dy)
if dst_bytes_per_row < dst.length() {
dst = dst[.. dst_bytes_per_row]
}
i = (this.dst_x as base.u64) * (dst_bytes_per_pixel as base.u64)
if i < dst.length() {
this.swizzler.swizzle_interleaved_from_slice!(
dst: dst[i ..],
dst_palette: args.dst.palette(),
src: src)
}
dy += 1
}
num_src_pixels = ((src.length() & 0xFFFF_FFFF) as base.u32) / 4
this.dst_x ~mod+= num_src_pixels
bi ~mod+= (num_src_pixels + 3) / 4
}
return ok
}
pub func decoder.frame_dirty_rect() base.rect_ie_u32 {
return this.util.make_rect_ie_u32(
min_incl_x: 0,
min_incl_y: 0,
max_excl_x: this.width,
max_excl_y: this.height)
}
pub func decoder.num_animation_loops() base.u32 {
return 0
}
pub func decoder.num_decoded_frame_configs() base.u64 {
if this.call_sequence > 0x20 {
return 1
}
return 0
}
pub func decoder.num_decoded_frames() base.u64 {
if this.call_sequence > 0x40 {
return 1
}
return 0
}
pub func decoder.restart_frame!(index: base.u64, io_position: base.u64) base.status {
if this.call_sequence < 0x20 {
return base."#bad call sequence"
}
if (args.index <> 0) or (args.io_position <> 16) {
return base."#bad argument"
}
this.call_sequence = 0x28
return ok
}
pub func decoder.set_report_metadata!(fourcc: base.u32, report: base.bool) {
// No-op. ETC2 doesn't support metadata.
}
pub func decoder.tell_me_more?(dst: base.io_writer, minfo: nptr base.more_information, src: base.io_reader) {
return base."#no more information"
}
pub func decoder.workbuf_len() base.range_ii_u64 {
return this.util.make_range_ii_u64(min_incl: 0, max_incl: 0)
}
pri const DIFFS : roarray[8] base.u32 = [
0x0000_0000,
0x0000_0001,
0x0000_0002,
0x0000_0003,
0xFFFF_FFFC,
0xFFFF_FFFD,
0xFFFF_FFFE,
0xFFFF_FFFF,
]
pri const MODIFIERS : roarray[8] roarray[4] base.u32 = [
[0x0000_0002, 0x0000_0008, 0xFFFF_FFFE, 0xFFFF_FFF8], // [ -8, -2, 2, 8] re-ordered.
[0x0000_0005, 0x0000_0011, 0xFFFF_FFFB, 0xFFFF_FFEF], // [ -17, -5, 5, 17] re-ordered.
[0x0000_0009, 0x0000_001D, 0xFFFF_FFF7, 0xFFFF_FFE3], // [ -29, -9, 9, 29] re-ordered.
[0x0000_000D, 0x0000_002A, 0xFFFF_FFF3, 0xFFFF_FFD6], // [ -42, -13, 13, 42] re-ordered.
[0x0000_0012, 0x0000_003C, 0xFFFF_FFEE, 0xFFFF_FFC4], // [ -60, -18, 18, 60] re-ordered.
[0x0000_0018, 0x0000_0050, 0xFFFF_FFE8, 0xFFFF_FFB0], // [ -80, -24, 24, 80] re-ordered.
[0x0000_0021, 0x0000_006A, 0xFFFF_FFDF, 0xFFFF_FF96], // [-106, -33, 33, 106] re-ordered.
[0x0000_002F, 0x0000_00B7, 0xFFFF_FFD1, 0xFFFF_FF49], // [-183, -47, 47, 183] re-ordered.
]
pri const T_H_MODIFIERS : roarray[8] base.u8 = [3, 6, 11, 16, 23, 32, 41, 64]
pri const CLAMP : roarray[1024] base.u8 = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]