blob: f08c010843a992c2aa4ea1709791d543b1cd698c [file] [log] [blame]
// Copyright 2023 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_mcu_progressive_ac_low_bit!(workbuf: slice base.u8, mx: base.u32[..= 0x1FFF], my: base.u32[..= 0x1FFF]) base.u32 {
var ret : base.u32
var bits : base.u64
var n_bits : base.u32
var one_lshift_scan_al : base.u16[..= 0x2000]
var r : base.io_reader
var pos : base.u32
var ac_huff_table_fast : nptr roarray[256] base.u16
var ac_h : base.u8[..= 7]
var ac_symbol : base.u32[..= 0xFF]
var ac_ht_fast : base.u32
var ac_bl : base.u32
var ac_code : base.u32
var ac_blm1 : base.u32[..= 15]
var ac_ht_slow : base.u32
var ac_value : base.u16
var ac_rrrr : base.u32[..= 15]
var ac_ssss : base.u32[..= 15]
var unzig : base.u8[..= 63]
var bit : base.bool
bits = this.bitstream_bits
n_bits = this.bitstream_n_bits
one_lshift_scan_al = (1 as base.u16) << this.scan_al
if this.bitstream_ri > this.bitstream_wi {
return 2 // Internal error.
}
io_bind (io: r, data: this.bitstream_buffer[this.bitstream_ri .. this.bitstream_wi], history_position: this.bitstream_ri as base.u64) {
while.goto_done true {{
while.block true {
// Ensure that we have enough bits for this iteration of the
// while.block loop body. Worst case, there are 64 components and
// each one needs (16 + 15) bits (round that up to 4 bytes), so we
// need (64 * 4) = 256 bytes available. 8 more bytes of slack means
// that we can always call peek_u64be.
if r.length() < 264 {
ret = 1 // Request another fill_bitstream call.
break.goto_done
}
while.goto_do_eob true {{
if this.eob_run > 0 {
break.goto_do_eob
}
ac_h = this.mcu_blocks_ac_hselector[0]
ac_huff_table_fast = this.huff_tables_fast[ac_h][..] as ptr array[256] base.u16
while.ac_components true,
inv ac_huff_table_fast <> nullptr,
{
// Load at least 56 bits.
if r.length() < 8 {
ret = 2 // Internal error.
break.goto_done
}
bits |= r.peek_u64be() >> (n_bits & 63)
r.skip_u32_fast!(actual: (63 - (n_bits & 63)) >> 3, worst_case: 8)
n_bits |= 56
// Read the Huffman-encoded ac_symbol, up to 16 bits long.
ac_ht_fast = ac_huff_table_fast[bits >> 56] as base.u32
ac_bl = ac_ht_fast >> 8
if n_bits >= ac_bl {
ac_symbol = 0xFF & ac_ht_fast
bits ~mod<<= (ac_bl & 63)
n_bits -= ac_bl
} else {
ac_code = (bits >> 55) as base.u32
ac_blm1 = 8
bits ~mod<<= 9
n_bits ~mod-= 9
while true,
inv ac_huff_table_fast <> nullptr,
{
ac_ht_slow = this.huff_tables_slow[ac_h][ac_blm1]
if ac_code < (ac_ht_slow >> 8) {
ac_symbol = this.huff_tables_symbols[ac_h][0xFF & (ac_code ~mod+ ac_ht_slow)] as base.u32
break
}
ac_code = (ac_code ~mod<< 1) | ((bits >> 63) as base.u32)
bits ~mod<<= 1
n_bits ~mod-= 1
ac_blm1 = (ac_blm1 + 1) & 15
if ac_blm1 == 0 {
ac_symbol = 0
break
}
} endwhile
}
// Split the 8-bit ac_symbol into two 4-bit halves, per section
// F.2.2.2 "Decoding procedure for AC coefficients".
ac_rrrr = ac_symbol >> 4
ac_ssss = ac_symbol & 15
// Process the ac_value in the next ac_ssss (up to 15) bits or
// the eob_run in the next ac_rrrr (up to 15) bits.
ac_value = 0
if ac_ssss > 0 {
ac_value = (0x0001 as base.u16) << this.scan_al
if (bits >> 63) == 0 {
ac_value = (0xFFFF as base.u16) ~mod<< this.scan_al
}
bits ~mod<<= 1
n_bits ~mod-= 1
} else if ac_rrrr < 15 {
this.eob_run = (1 as base.u16) << ac_rrrr
if ac_rrrr > 0 {
this.eob_run ~mod+= ((bits >> (64 - ac_rrrr)) & 0xFFFF) as base.u16
bits ~mod<<= ac_rrrr
n_bits ~mod-= ac_rrrr
}
break.goto_do_eob
}
// Consume 1 bit per non-zero AC coefficient, up to 63 bits.
while.refine_non_zeroes true,
inv ac_huff_table_fast <> nullptr,
{
unzig = UNZIG[1 + this.mcu_zig_index]
if this.mcu_blocks[0][unzig] <> 0 {
if n_bits == 0 {
if r.length() < 8 {
ret = 2 // Internal error.
break.goto_done
}
bits |= r.peek_u64be() >> (n_bits & 63)
r.skip_u32_fast!(actual: (63 - (n_bits & 63)) >> 3, worst_case: 8)
n_bits |= 56
}
bit = (bits >> 63) > 0
bits ~mod<<= 1
n_bits ~mod-= 1
if bit {
if this.mcu_blocks[0][unzig] < 0x8000 {
this.mcu_blocks[0][unzig] += one_lshift_scan_al
} else {
this.mcu_blocks[0][unzig] -= one_lshift_scan_al
}
}
} else if ac_rrrr <= 0 {
break.refine_non_zeroes
} else {
ac_rrrr -= 1
}
if this.mcu_zig_index >= (this.scan_se as base.u32) {
break.refine_non_zeroes
}
assert this.mcu_zig_index < 63 via "a < b: a < c; c <= b"(c: (this.scan_se as base.u32))
this.mcu_zig_index += 1
} endwhile.refine_non_zeroes
if ac_value <> 0 {
this.mcu_blocks[0][UNZIG[1 + this.mcu_zig_index]] = ac_value
}
if this.mcu_zig_index >= (this.scan_se as base.u32) {
break.ac_components
}
assert this.mcu_zig_index < 63 via "a < b: a < c; c <= b"(c: (this.scan_se as base.u32))
this.mcu_zig_index += 1
} endwhile.ac_components
break.block
}} endwhile.goto_do_eob
if this.eob_run <= 0 {
ret = 2 // Internal error.
break.goto_done
}
while.refine_eob_non_zeroes true,
inv this.eob_run > 0,
{
unzig = UNZIG[1 + this.mcu_zig_index]
if this.mcu_blocks[0][unzig] <> 0 {
if n_bits == 0 {
// Load at least 56 bits.
if r.length() < 8 {
ret = 2 // Internal error.
break.goto_done
}
bits |= r.peek_u64be() >> (n_bits & 63)
r.skip_u32_fast!(actual: (63 - (n_bits & 63)) >> 3, worst_case: 8)
n_bits |= 56
}
bit = (bits >> 63) > 0
bits ~mod<<= 1
n_bits ~mod-= 1
if bit {
if this.mcu_blocks[0][unzig] < 0x8000 {
this.mcu_blocks[0][unzig] += one_lshift_scan_al
} else {
this.mcu_blocks[0][unzig] -= one_lshift_scan_al
}
}
}
if this.mcu_zig_index >= (this.scan_se as base.u32) {
break.refine_eob_non_zeroes
}
assert this.mcu_zig_index < 63 via "a < b: a < c; c <= b"(c: (this.scan_se as base.u32))
this.mcu_zig_index += 1
} endwhile.refine_eob_non_zeroes
this.eob_run -= 1
break.block
} endwhile.block
break.goto_done
}} endwhile.goto_done
pos = (r.position() & 0xFFFF_FFFF) as base.u32
if pos > this.bitstream_wi {
ret = 2 // Internal error.
} else {
assert pos <= 0x800 via "a <= b: a <= c; c <= b"(c: this.bitstream_wi)
this.bitstream_ri = pos
}
}
this.bitstream_bits = bits
this.bitstream_n_bits = n_bits
return ret
}