| // Copyright 2020 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. |
| |
| pub status "#bad header" |
| pub status "#bad RLE compression" |
| pub status "#unsupported BMP file" |
| |
| pri status "@internal note: short read" |
| |
| pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE : base.u64 = 0 |
| |
| pri const COMPRESSION_NONE : base.u32 = 0 |
| pri const COMPRESSION_RLE8 : base.u32 = 1 |
| pri const COMPRESSION_RLE4 : base.u32 = 2 |
| pri const COMPRESSION_BITFIELDS : base.u32 = 3 |
| pri const COMPRESSION_JPEG : base.u32 = 4 |
| pri const COMPRESSION_PNG : base.u32 = 5 |
| pri const COMPRESSION_ALPHABITFIELDS : base.u32 = 6 |
| pri const COMPRESSION_LOW_BIT_DEPTH : base.u32 = 0x100 |
| |
| pub struct decoder? implements base.image_decoder( |
| width : base.u32[..= 0x7FFF_FFFF], |
| height : base.u32[..= 0x7FFF_FFFF], |
| |
| // Call sequence states: |
| // - 0x00: initial state. |
| // - 0x03: image config decoded. |
| // - 0x04: frame config decoded. |
| // - 0xFF: end-of-data, usually after (the non-animated) frame decoded. |
| // |
| // State transitions: |
| // |
| // - 0x00 -> 0x03: via DIC |
| // - 0x00 -> 0x04: via DFC with implicit DIC |
| // - 0x00 -> 0xFF: via DF with implicit DIC and DFC |
| // |
| // - 0x03 -> 0x04: via DFC |
| // - 0x03 -> 0xFF: via DF with implicit DFC |
| // |
| // - 0x04 -> 0xFF: via DFC |
| // - 0x04 -> 0xFF: via DF |
| // |
| // - ???? -> 0x03: via RF for ???? > 0x00 |
| // |
| // Where: |
| // - DF is decode_frame |
| // - DFC is decode_frame_config, implicit means nullptr args.dst |
| // - DIC is decode_image_config, implicit means nullptr args.dst |
| // - RF is restart_frame |
| call_sequence : base.u8, |
| |
| top_down : base.bool, |
| pad_per_row : base.u32[..= 3], |
| src_pixfmt : base.u32, |
| |
| io_redirect_fourcc : base.u32, |
| io_redirect_pos : base.u64, |
| |
| frame_config_io_position : base.u64, |
| |
| bitmap_info_len : base.u32, |
| padding : base.u32, |
| |
| bits_per_pixel : base.u32, |
| compression : base.u32, |
| |
| // channel_etc's indexes are: B, G, R, A. This is in Wuffs' default order, |
| // which isn't the RGBA order they're listed in the wire format. |
| channel_masks : array[4] base.u32, |
| channel_shifts : array[4] base.u8[..= 31], |
| channel_num_bits : array[4] base.u8[..= 32], |
| |
| dst_x : base.u32, |
| dst_y : base.u32, |
| dst_y_inc : base.u32, |
| |
| pending_pad : base.u32[..= 3], |
| |
| rle_state : base.u32, |
| rle_length : base.u32[..= 0xFF], |
| rle_delta_x : base.u8, |
| rle_padded : base.bool, |
| |
| swizzler : base.pixel_swizzler, |
| util : base.utility, |
| )( |
| // scratch is one of: |
| // - 2048 bytes = 256 * (8 bytes per BGRA_NONPREMUL_4X16LE). |
| // - 1024 bytes = 1024 * (1 byte per INDEXED__BGRA_BINARY), plus |
| // 1024 bytes = 256 * (4 bytes per dst_palette entry). |
| scratch : array[2048] base.u8, |
| |
| src_palette : array[4 * 256] base.u8, |
| ) |
| |
| pub func decoder.set_quirk_enabled!(quirk: base.u32, enabled: base.bool) { |
| } |
| |
| pub func decoder.decode_image_config?(dst: nptr base.image_config, src: base.io_reader) { |
| var magic : base.u32 |
| var width : base.u32 |
| var height : base.u32 |
| var planes : base.u32 |
| var dst_pixfmt : base.u32 |
| var byte_width : base.u32 |
| |
| if (this.call_sequence <> 0) or (this.io_redirect_fourcc == 1) { |
| return base."#bad call sequence" |
| } else if this.io_redirect_fourcc <> 0 { |
| return base."@I/O redirect" |
| } |
| |
| // Read the BITMAPFILEHEADER (14 bytes). |
| |
| magic = args.src.read_u16le_as_u32?() |
| if magic <> 'BM'le { |
| return "#bad header" |
| } |
| |
| args.src.skip_u32?(n: 8) |
| |
| this.padding = args.src.read_u32le?() |
| if this.padding < 14 { |
| return "#bad header" |
| } |
| this.padding -= 14 |
| this.io_redirect_pos = (this.padding as base.u64) ~sat+ args.src.position() |
| |
| // Read the BITMAPINFOHEADER: |
| // - OS/2 is 12, 16 or 64 bytes. |
| // - Windows is 40, 108 or 124 bytes. |
| |
| this.bitmap_info_len = args.src.read_u32le?() |
| if this.padding < this.bitmap_info_len { |
| return "#bad header" |
| } |
| this.padding -= this.bitmap_info_len |
| |
| if this.bitmap_info_len == 12 { |
| this.width = args.src.read_u16le_as_u32?() |
| this.height = args.src.read_u16le_as_u32?() |
| planes = args.src.read_u16le_as_u32?() |
| if planes <> 1 { |
| return "#unsupported BMP file" |
| } |
| this.bits_per_pixel = args.src.read_u16le_as_u32?() |
| |
| } else if this.bitmap_info_len == 16 { |
| width = args.src.read_u32le?() |
| if width >= 0x8000_0000 { |
| return "#bad header" |
| } |
| this.width = width |
| height = args.src.read_u32le?() |
| if height >= 0x8000_0000 { |
| return "#bad header" |
| } |
| this.height = height |
| planes = args.src.read_u16le_as_u32?() |
| if planes <> 1 { |
| return "#unsupported BMP file" |
| } |
| this.bits_per_pixel = args.src.read_u16le_as_u32?() |
| |
| } else { |
| width = args.src.read_u32le?() |
| if width >= 0x8000_0000 { |
| return "#bad header" |
| } |
| this.width = width |
| |
| height = args.src.read_u32le?() |
| if height == 0x8000_0000 { |
| return "#bad header" |
| } else if height >= 0x8000_0000 { |
| // The &0x7FFF_FFFF is redundant, but proves to the compiler that |
| // the result is within this.height's refined bounds. |
| this.height = (0 ~mod- height) & 0x7FFF_FFFF |
| this.top_down = true |
| } else { |
| this.height = height |
| } |
| |
| planes = args.src.read_u16le_as_u32?() |
| if planes <> 1 { |
| return "#unsupported BMP file" |
| } |
| |
| this.bits_per_pixel = args.src.read_u16le_as_u32?() |
| this.compression = args.src.read_u32le?() |
| if this.bits_per_pixel == 0 { |
| if this.compression == COMPRESSION_JPEG { |
| this.io_redirect_fourcc = 'JPEG'be |
| return base."@I/O redirect" |
| } else if this.compression == COMPRESSION_PNG { |
| this.io_redirect_fourcc = 'PNG 'be |
| return base."@I/O redirect" |
| } |
| return "#unsupported BMP file" |
| } |
| |
| // We've already read 20 bytes from the BITMAPINFOHEADER: size (4), |
| // width (4), height (4), planes (2), bpp (2), compression (4). Skip |
| // the rest of the version 3 BITMAPINFOHEADER (whose total size is 40). |
| args.src.skip_u32?(n: 40 - 20) |
| |
| if this.bitmap_info_len == 40 { |
| if this.bits_per_pixel >= 16 { |
| // It's poorly documented, but "length 40" data can be silently |
| // augmented with RGB or RGBA channel_masks (12 or 16 bytes). |
| if this.padding >= 16 { |
| this.bitmap_info_len = 56 |
| this.padding -= 16 |
| } else if this.padding >= 12 { |
| this.bitmap_info_len = 52 |
| this.padding -= 12 |
| } |
| } |
| } else if (this.bitmap_info_len <> 52) and |
| (this.bitmap_info_len <> 56) and |
| (this.bitmap_info_len <> 64) and |
| (this.bitmap_info_len <> 108) and |
| (this.bitmap_info_len <> 124) { |
| return "#unsupported BMP file" |
| } |
| |
| // Treat COMPRESSION_ALPHABITFIELDS the same as COMPRESSION_BITFIELDS. |
| if this.compression == COMPRESSION_ALPHABITFIELDS { |
| this.compression = COMPRESSION_BITFIELDS |
| } |
| |
| // Read the channel_masks when this.compression is |
| // COMPRESSION_BITFIELDS. |
| if this.compression == COMPRESSION_BITFIELDS { |
| if this.bitmap_info_len >= 52 { |
| this.channel_masks[2] = args.src.read_u32le?() |
| this.channel_masks[1] = args.src.read_u32le?() |
| this.channel_masks[0] = args.src.read_u32le?() |
| if this.bitmap_info_len >= 56 { |
| this.channel_masks[3] = args.src.read_u32le?() |
| // Skip the rest of the BITMAPINFOHEADER. |
| args.src.skip_u32?(n: this.bitmap_info_len ~mod- 56) |
| } |
| // If the explicit channel_masks are what the implicit ones |
| // would be for no compression, treat it as no compression. |
| if (this.channel_masks[0] == 0x0000_00FF) and |
| (this.channel_masks[1] == 0x0000_FF00) and |
| (this.channel_masks[2] == 0x00FF_0000) { |
| if this.bits_per_pixel == 24 { |
| this.compression = COMPRESSION_NONE |
| } else if this.bits_per_pixel == 32 { |
| if (this.channel_masks[3] == 0) or |
| (this.channel_masks[3] == 0xFF00_0000) { |
| this.compression = COMPRESSION_NONE |
| } |
| } |
| } |
| this.process_masks?() |
| } |
| } else if this.bitmap_info_len >= 40 { |
| // Skip the rest of the BITMAPINFOHEADER. |
| args.src.skip_u32?(n: this.bitmap_info_len - 40) |
| } else { |
| return "#unsupported BMP file" |
| } |
| } |
| |
| if this.compression <> COMPRESSION_BITFIELDS { |
| // The palette follows the BITMAPINFOHEADER. |
| if this.bits_per_pixel < 16 { |
| this.read_palette?(src: args.src) |
| } |
| } |
| |
| if this.compression == COMPRESSION_NONE { |
| if (this.bits_per_pixel == 1) or |
| (this.bits_per_pixel == 2) or |
| (this.bits_per_pixel == 4) { |
| this.src_pixfmt = base.PIXEL_FORMAT__INDEXED__BGRA_BINARY |
| this.compression = COMPRESSION_LOW_BIT_DEPTH |
| } else if this.bits_per_pixel == 8 { |
| this.src_pixfmt = base.PIXEL_FORMAT__INDEXED__BGRA_BINARY |
| } else if this.bits_per_pixel == 16 { |
| // BMP's 16-bit default is BGRX_5551. |
| this.compression = COMPRESSION_BITFIELDS |
| this.channel_masks[0] = 0x001F |
| this.channel_masks[1] = 0x03E0 |
| this.channel_masks[2] = 0x7C00 |
| this.channel_masks[3] = 0x0000 |
| this.process_masks?() |
| this.src_pixfmt = base.PIXEL_FORMAT__BGRA_NONPREMUL_4X16LE |
| } else if this.bits_per_pixel == 24 { |
| this.src_pixfmt = base.PIXEL_FORMAT__BGR |
| } else if this.bits_per_pixel == 32 { |
| if this.channel_masks[3] == 0 { |
| this.src_pixfmt = base.PIXEL_FORMAT__BGRX |
| } else { |
| this.src_pixfmt = base.PIXEL_FORMAT__BGRA_NONPREMUL |
| } |
| } else { |
| return "#unsupported BMP file" |
| } |
| |
| } else if this.compression == COMPRESSION_RLE8 { |
| if this.bits_per_pixel == 8 { |
| this.src_pixfmt = base.PIXEL_FORMAT__INDEXED__BGRA_BINARY |
| } else { |
| return "#unsupported BMP file" |
| } |
| |
| } else if this.compression == COMPRESSION_RLE4 { |
| if this.bits_per_pixel == 4 { |
| this.src_pixfmt = base.PIXEL_FORMAT__INDEXED__BGRA_BINARY |
| } else { |
| return "#unsupported BMP file" |
| } |
| |
| } else if this.compression == COMPRESSION_BITFIELDS { |
| if (this.bits_per_pixel == 16) or (this.bits_per_pixel == 32) { |
| this.src_pixfmt = base.PIXEL_FORMAT__BGRA_NONPREMUL_4X16LE |
| } else { |
| return "#unsupported BMP file" |
| } |
| |
| } else { |
| return "#unsupported BMP file" |
| } |
| |
| // OS/2 BMP files have fewer valid bits_per_pixel values. |
| if ((this.bitmap_info_len < 40) or (this.bitmap_info_len == 64)) and |
| (this.bits_per_pixel <> 1) and |
| (this.bits_per_pixel <> 4) and |
| (this.bits_per_pixel <> 8) and |
| (this.bits_per_pixel <> 24) { |
| return "#bad header" |
| } |
| |
| // The "((x + 3) >> 2) << 2" dance rounds x up to a multiple of 4. |
| if this.bits_per_pixel == 1 { |
| // byte_width is this.width divided by 8, rounding up. |
| byte_width = (this.width >> 3) + (((this.width & 7) + 7) >> 3) |
| this.pad_per_row = (4 - (byte_width & 3)) & 3 |
| } else if this.bits_per_pixel == 2 { |
| // byte_width is this.width divided by 4, rounding up. |
| byte_width = (this.width >> 2) + (((this.width & 3) + 3) >> 2) |
| this.pad_per_row = (4 - (byte_width & 3)) & 3 |
| } else if this.bits_per_pixel == 4 { |
| // byte_width is this.width divided by 2, rounding up. |
| byte_width = (this.width >> 1) + (this.width & 1) |
| this.pad_per_row = (4 - (byte_width & 3)) & 3 |
| } else if this.bits_per_pixel == 8 { |
| this.pad_per_row = (4 - (this.width & 3)) & 3 |
| } else if this.bits_per_pixel == 16 { |
| this.pad_per_row = (this.width & 1) * 2 |
| } else if this.bits_per_pixel == 24 { |
| this.pad_per_row = this.width & 3 |
| } else if this.bits_per_pixel == 32 { |
| this.pad_per_row = 0 |
| } |
| |
| this.frame_config_io_position = args.src.position() |
| |
| if args.dst <> nullptr { |
| dst_pixfmt = base.PIXEL_FORMAT__BGRA_NONPREMUL |
| if (this.channel_num_bits[0] > 8) or |
| (this.channel_num_bits[1] > 8) or |
| (this.channel_num_bits[2] > 8) or |
| (this.channel_num_bits[3] > 8) { |
| dst_pixfmt = base.PIXEL_FORMAT__BGRA_NONPREMUL_4X16LE |
| } |
| |
| args.dst.set!( |
| pixfmt: dst_pixfmt, |
| pixsub: 0, |
| width: this.width, |
| height: this.height, |
| first_frame_io_position: this.frame_config_io_position, |
| first_frame_is_opaque: this.channel_masks[3] == 0) |
| } |
| |
| this.call_sequence = 3 |
| } |
| |
| pub func decoder.decode_frame_config?(dst: nptr base.frame_config, src: base.io_reader) { |
| if this.call_sequence < 3 { |
| this.decode_image_config?(dst: nullptr, src: args.src) |
| } else if this.call_sequence == 3 { |
| if this.frame_config_io_position <> args.src.position() { |
| return base."#bad restart" |
| } |
| } else if this.call_sequence == 4 { |
| this.call_sequence = 0xFF |
| 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: this.frame_config_io_position, |
| disposal: 0, |
| opaque_within_bounds: true, |
| overwrite_instead_of_blend: false, |
| background_color: 0xFF00_0000) |
| } |
| |
| this.call_sequence = 4 |
| } |
| |
| 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 |
| |
| if this.call_sequence < 4 { |
| this.decode_frame_config?(dst: nullptr, src: args.src) |
| } else if this.call_sequence == 4 { |
| // No-op. |
| } else { |
| return base."@end of data" |
| } |
| |
| args.src.skip_u32?(n: this.padding) |
| |
| if (this.width > 0) and (this.height > 0) { |
| this.dst_x = 0 |
| if this.top_down { |
| this.dst_y = 0 |
| this.dst_y_inc = 1 |
| } else { |
| this.dst_y = this.height ~mod- 1 |
| this.dst_y_inc = 0xFFFF_FFFF // -1 as a base.u32. |
| } |
| |
| status = this.swizzler.prepare!( |
| dst_pixfmt: args.dst.pixel_format(), |
| dst_palette: args.dst.palette_or_else(fallback: this.scratch[1024 ..]), |
| src_pixfmt: this.util.make_pixel_format(repr: this.src_pixfmt), |
| src_palette: this.src_palette[..], |
| blend: args.blend) |
| if not status.is_ok() { |
| return status |
| } |
| |
| while true { |
| if this.compression == COMPRESSION_NONE { |
| status = this.swizzle_none!(dst: args.dst, src: args.src) |
| } else if this.compression < COMPRESSION_BITFIELDS { |
| status = this.swizzle_rle!(dst: args.dst, src: args.src) |
| } else if this.compression == COMPRESSION_BITFIELDS { |
| status = this.swizzle_bitfields!(dst: args.dst, src: args.src) |
| } else { |
| status = this.swizzle_low_bit_depth!(dst: args.dst, src: args.src) |
| } |
| |
| if status.is_ok() { |
| break |
| } else if status <> "@internal note: short read" { |
| return status |
| } |
| yield? base."$short read" |
| } endwhile |
| |
| args.src.skip_u32?(n: this.pending_pad) |
| this.pending_pad = 0 |
| } |
| |
| this.call_sequence = 0xFF |
| } |
| |
| pri func decoder.swizzle_none!(dst: ptr base.pixel_buffer, src: base.io_reader) 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 dst : slice base.u8 |
| var i : base.u64 |
| var n : base.u64 |
| |
| // 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.scratch[1024 ..]) |
| tab = args.dst.plane(p: 0) |
| |
| while.outer true { |
| while this.pending_pad > 0 { |
| if args.src.length() <= 0 { |
| return "@internal note: short read" |
| } |
| this.pending_pad -= 1 |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| } endwhile |
| |
| while.inner true { |
| if this.dst_x == this.width { |
| this.dst_x = 0 |
| this.dst_y ~mod+= this.dst_y_inc |
| if this.dst_y >= this.height { |
| if this.height > 0 { |
| this.pending_pad = this.pad_per_row |
| } |
| break.outer |
| } else if this.pad_per_row <> 0 { |
| this.pending_pad = this.pad_per_row |
| continue.outer |
| } |
| } |
| |
| dst = tab.row(y: this.dst_y) |
| if dst_bytes_per_row < dst.length() { |
| dst = dst[.. dst_bytes_per_row] |
| } |
| i = (this.dst_x as base.u64) * dst_bytes_per_pixel |
| if i >= dst.length() { |
| // TODO: advance args.src if the dst pixel_buffer bounds is |
| // smaller than this BMP's image bounds? |
| continue.inner |
| } |
| n = this.swizzler.swizzle_interleaved_from_reader!( |
| dst: dst[i ..], |
| dst_palette: dst_palette, |
| src: args.src) |
| if n == 0 { |
| return "@internal note: short read" |
| } |
| this.dst_x ~sat+= (n & 0xFFFF_FFFF) as base.u32 |
| } endwhile.inner |
| } endwhile.outer |
| |
| return ok |
| } |
| |
| pri const RLE_STATE_NEUTRAL : base.u32 = 0 |
| pri const RLE_STATE_RUN : base.u32 = 1 |
| pri const RLE_STATE_ESCAPE : base.u32 = 2 |
| pri const RLE_STATE_LITERAL : base.u32 = 3 |
| pri const RLE_STATE_DELTA_X : base.u32 = 4 |
| pri const RLE_STATE_DELTA_Y : base.u32 = 5 |
| |
| pri func decoder.swizzle_rle!(dst: ptr base.pixel_buffer, src: base.io_reader) 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 row : slice base.u8 |
| var dst : slice base.u8 |
| var i : base.u64 |
| var n : base.u64 |
| |
| var p0 : base.u32[..= 259] |
| var code : base.u8 |
| var indexes : array[2] base.u8 |
| |
| var rle_state : base.u32 |
| |
| var chunk_bits : base.u32 |
| var chunk_count : base.u32[..= 64] |
| |
| // 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.scratch[1024 ..]) |
| tab = args.dst.plane(p: 0) |
| |
| rle_state = this.rle_state |
| |
| while.outer true { |
| row = tab.row(y: this.dst_y) |
| if dst_bytes_per_row < row.length() { |
| row = row[.. dst_bytes_per_row] |
| } |
| |
| while.middle true { |
| i = (this.dst_x as base.u64) * dst_bytes_per_pixel |
| if i <= row.length() { |
| dst = row[i ..] |
| } else { |
| dst = this.util.empty_slice_u8() |
| } |
| |
| while.goto_suspend true {{ |
| while.inner true { |
| if rle_state == RLE_STATE_NEUTRAL { |
| if args.src.length() < 1 { |
| break.goto_suspend |
| } |
| code = args.src.peek_u8() |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| if code == 0 { |
| rle_state = RLE_STATE_ESCAPE |
| continue.inner |
| } |
| this.rle_length = code as base.u32 |
| rle_state = RLE_STATE_RUN |
| continue.inner |
| |
| } else if rle_state == RLE_STATE_RUN { |
| if args.src.length() < 1 { |
| break.goto_suspend |
| } |
| code = args.src.peek_u8() |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| if this.bits_per_pixel == 8 { |
| p0 = 0 |
| while p0 < this.rle_length { |
| assert p0 < 255 via "a < b: a < c; c <= b"(c: this.rle_length) |
| this.scratch[p0] = code |
| p0 += 1 |
| } endwhile |
| } else { |
| indexes[0] = (code >> 4) as base.u8 |
| indexes[1] = code & 0x0F |
| p0 = 0 |
| while p0 < this.rle_length { |
| assert p0 < 255 via "a < b: a < c; c <= b"(c: this.rle_length) |
| this.scratch[p0 + 0] = indexes[0] |
| this.scratch[p0 + 1] = indexes[1] |
| p0 += 2 |
| } endwhile |
| } |
| this.swizzler.swizzle_interleaved_from_slice!( |
| dst: dst, |
| dst_palette: dst_palette, |
| src: this.scratch[.. this.rle_length]) |
| this.dst_x ~sat+= this.rle_length |
| rle_state = RLE_STATE_NEUTRAL |
| continue.middle |
| |
| } else if rle_state == RLE_STATE_ESCAPE { |
| if args.src.length() < 1 { |
| break.goto_suspend |
| } |
| code = args.src.peek_u8() |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| if code < 2 { // 0=EOL, 1=EOF. |
| if (this.dst_y >= this.height) and (code == 0) { |
| return "#bad RLE compression" |
| } |
| this.swizzler.swizzle_interleaved_transparent_black!( |
| dst: dst, |
| dst_palette: dst_palette, |
| num_pixels: 0xFFFF_FFFF_FFFF_FFFF) |
| this.dst_x = 0 |
| this.dst_y ~mod+= this.dst_y_inc |
| if code > 0 { |
| break.outer |
| } |
| rle_state = RLE_STATE_NEUTRAL |
| continue.outer |
| } else if code == 2 { // 2=DELTA. |
| rle_state = RLE_STATE_DELTA_X |
| continue.inner |
| } |
| this.rle_length = code as base.u32 |
| this.rle_padded = (this.bits_per_pixel == 8) and ((code & 1) <> 0) |
| rle_state = RLE_STATE_LITERAL |
| continue.inner |
| |
| } else if rle_state == RLE_STATE_LITERAL { |
| if this.bits_per_pixel == 8 { |
| n = this.swizzler.limited_swizzle_u32_interleaved_from_reader!( |
| up_to_num_pixels: this.rle_length, |
| dst: dst, |
| dst_palette: dst_palette, |
| src: args.src) |
| this.dst_x ~sat+= (n & 0xFFFF_FFFF) as base.u32 |
| this.rle_length ~sat-= (n & 0xFFFF_FFFF) as base.u32 |
| } else { |
| // Calculate the remaining number of 16-bit chunks. At |
| // 4 bits per pixel there are 4 pixels per chunk. |
| // Division rounds up. |
| chunk_count = (this.rle_length + 3) / 4 |
| p0 = 0 |
| while (chunk_count > 0) and (args.src.length() >= 2) { |
| chunk_bits = args.src.peek_u16be_as_u32() |
| args.src.skip_u32_fast!(actual: 2, worst_case: 2) |
| this.scratch[p0 + 0x00] = (0x0F & (chunk_bits >> 0x0C)) as base.u8 |
| this.scratch[p0 + 0x01] = (0x0F & (chunk_bits >> 0x08)) as base.u8 |
| this.scratch[p0 + 0x02] = (0x0F & (chunk_bits >> 0x04)) as base.u8 |
| this.scratch[p0 + 0x03] = (0x0F & (chunk_bits >> 0x00)) as base.u8 |
| p0 = (p0 & 255) + 0x04 |
| chunk_count -= 1 |
| } endwhile |
| p0 = p0.min(a: this.rle_length) |
| this.swizzler.swizzle_interleaved_from_slice!( |
| dst: dst, |
| dst_palette: dst_palette, |
| src: this.scratch[.. p0]) |
| this.dst_x ~sat+= p0 |
| this.rle_length ~sat-= p0 |
| } |
| |
| if this.rle_length > 0 { |
| break.goto_suspend |
| } |
| if this.rle_padded { |
| if args.src.length() < 1 { |
| break.goto_suspend |
| } |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| this.rle_padded = false |
| } |
| rle_state = RLE_STATE_NEUTRAL |
| continue.middle |
| |
| } else if rle_state == RLE_STATE_DELTA_X { |
| if args.src.length() < 1 { |
| break.goto_suspend |
| } |
| this.rle_delta_x = args.src.peek_u8() |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| rle_state = RLE_STATE_DELTA_Y |
| continue.inner |
| |
| } // else (rle_state == RLE_STATE_DELTA_Y). |
| if args.src.length() < 1 { |
| break.goto_suspend |
| } |
| code = args.src.peek_u8() |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| if this.rle_delta_x > 0 { |
| this.swizzler.swizzle_interleaved_transparent_black!( |
| dst: dst, |
| dst_palette: dst_palette, |
| num_pixels: this.rle_delta_x as base.u64) |
| this.dst_x ~sat+= this.rle_delta_x as base.u32 |
| this.rle_delta_x = 0 |
| if this.dst_x > this.width { |
| return "#bad RLE compression" |
| } |
| } |
| |
| // Paint the skipped rows (and the partial final row) with |
| // transparent black. |
| if code > 0 { |
| code -= 1 |
| while true { |
| this.dst_y ~mod+= this.dst_y_inc |
| if this.dst_y >= this.height { |
| return "#bad RLE compression" |
| } |
| row = tab.row(y: this.dst_y) |
| if dst_bytes_per_row < row.length() { |
| row = row[.. dst_bytes_per_row] |
| } |
| if code <= 0 { |
| this.swizzler.swizzle_interleaved_transparent_black!( |
| dst: row, |
| dst_palette: dst_palette, |
| num_pixels: this.dst_x as base.u64) |
| break |
| } |
| this.swizzler.swizzle_interleaved_transparent_black!( |
| dst: row, |
| dst_palette: dst_palette, |
| num_pixels: 0xFFFF_FFFF_FFFF_FFFF) |
| code -= 1 |
| } endwhile |
| } |
| |
| rle_state = RLE_STATE_NEUTRAL |
| continue.middle |
| |
| } endwhile.inner |
| }} endwhile.goto_suspend |
| this.rle_state = rle_state |
| return "@internal note: short read" |
| } endwhile.middle |
| } endwhile.outer |
| |
| while this.dst_y < this.height { |
| row = tab.row(y: this.dst_y) |
| if dst_bytes_per_row < row.length() { |
| row = row[.. dst_bytes_per_row] |
| } |
| this.swizzler.swizzle_interleaved_transparent_black!( |
| dst: row, |
| dst_palette: dst_palette, |
| num_pixels: 0xFFFF_FFFF_FFFF_FFFF) |
| this.dst_y ~mod+= this.dst_y_inc |
| } endwhile |
| |
| return ok |
| } |
| |
| pri func decoder.swizzle_bitfields!(dst: ptr base.pixel_buffer, src: base.io_reader) 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 dst : slice base.u8 |
| var i : base.u64 |
| var n : base.u64 |
| |
| var p0 : base.u32[..= 256] |
| var p1 : base.u32[..= 256] |
| var p1_temp : base.u32 |
| |
| var num_bits : base.u32[..= 32] |
| var c : base.u32 |
| var c32 : base.u32 |
| var channel : base.u32[..= 4] |
| |
| // 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.scratch[1024 ..]) |
| tab = args.dst.plane(p: 0) |
| |
| while.outer true { |
| while this.pending_pad > 0 { |
| if args.src.length() <= 0 { |
| return "@internal note: short read" |
| } |
| this.pending_pad -= 1 |
| args.src.skip_u32_fast!(actual: 1, worst_case: 1) |
| } endwhile |
| |
| while.inner true { |
| if this.dst_x == this.width { |
| this.dst_x = 0 |
| this.dst_y ~mod+= this.dst_y_inc |
| if this.dst_y >= this.height { |
| if this.height > 0 { |
| this.pending_pad = this.pad_per_row |
| } |
| break.outer |
| } else if this.pad_per_row <> 0 { |
| this.pending_pad = this.pad_per_row |
| continue.outer |
| } |
| } |
| |
| // -------- BEGIN convert to PIXEL_FORMAT__BGRA_NONPREMUL_4X16LE. |
| p1_temp = this.width ~mod- this.dst_x |
| p1 = p1_temp.min(a: 256) |
| p0 = 0 |
| while p0 < p1 { |
| assert p0 < 256 via "a < b: a < c; c <= b"(c: p1) |
| if this.bits_per_pixel == 16 { |
| if args.src.length() < 2 { |
| break |
| } |
| c32 = args.src.peek_u16le_as_u32() |
| args.src.skip_u32_fast!(actual: 2, worst_case: 2) |
| } else { |
| if args.src.length() < 4 { |
| break |
| } |
| c32 = args.src.peek_u32le() |
| args.src.skip_u32_fast!(actual: 4, worst_case: 4) |
| } |
| |
| channel = 0 |
| while channel < 4, |
| inv p0 < 256, |
| { |
| if this.channel_num_bits[channel] == 0 { |
| this.scratch[(8 * p0) + (2 * channel) + 0] = 0xFF |
| this.scratch[(8 * p0) + (2 * channel) + 1] = 0xFF |
| } else { |
| c = (c32 & this.channel_masks[channel]) >> this.channel_shifts[channel] |
| num_bits = this.channel_num_bits[channel] as base.u32 |
| while num_bits < 16, |
| inv p0 < 256, |
| inv channel < 4, |
| post num_bits >= 16, |
| { |
| c |= c ~mod<< num_bits |
| num_bits *= 2 |
| } endwhile |
| c >>= num_bits - 16 |
| this.scratch[(8 * p0) + (2 * channel) + 0] = (0xFF & (c >> 0)) as base.u8 |
| this.scratch[(8 * p0) + (2 * channel) + 1] = (0xFF & (c >> 8)) as base.u8 |
| } |
| |
| channel += 1 |
| } endwhile |
| |
| p0 += 1 |
| } endwhile |
| // -------- END convert to PIXEL_FORMAT__BGRA_NONPREMUL_4X16LE. |
| |
| dst = tab.row(y: this.dst_y) |
| if dst_bytes_per_row < dst.length() { |
| dst = dst[.. dst_bytes_per_row] |
| } |
| i = (this.dst_x as base.u64) * dst_bytes_per_pixel |
| if i >= dst.length() { |
| // TODO: advance args.src if the dst pixel_buffer bounds is |
| // smaller than this BMP's image bounds? |
| continue.inner |
| } |
| |
| n = this.swizzler.swizzle_interleaved_from_slice!( |
| dst: dst[i ..], |
| dst_palette: dst_palette, |
| src: this.scratch[.. 8 * p0]) |
| if n == 0 { |
| return "@internal note: short read" |
| } |
| this.dst_x ~sat+= (n & 0xFFFF_FFFF) as base.u32 |
| } endwhile.inner |
| } endwhile.outer |
| |
| return ok |
| } |
| |
| pri func decoder.swizzle_low_bit_depth!(dst: ptr base.pixel_buffer, src: base.io_reader) 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 dst : slice base.u8 |
| var i : base.u64 |
| var n : base.u64 |
| |
| var p0 : base.u32[..= 543] |
| |
| var chunk_bits : base.u32 |
| var chunk_count : 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) 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.scratch[1024 ..]) |
| tab = args.dst.plane(p: 0) |
| |
| while.loop true { |
| if this.dst_x == this.width { |
| this.dst_x = 0 |
| this.dst_y ~mod+= this.dst_y_inc |
| if this.dst_y >= this.height { |
| break.loop |
| } |
| } |
| |
| dst = tab.row(y: this.dst_y) |
| if dst_bytes_per_row < dst.length() { |
| dst = dst[.. dst_bytes_per_row] |
| } |
| i = (this.dst_x as base.u64) * dst_bytes_per_pixel |
| if i >= dst.length() { |
| // TODO: advance args.src if the dst pixel_buffer bounds is |
| // smaller than this BMP's image bounds? |
| continue.loop |
| } |
| dst = dst[i ..] |
| p0 = 0 |
| |
| if this.bits_per_pixel == 1 { |
| // Calculate the remaining number of 32-bit chunks. At 1 bit per |
| // pixel there are 32 pixels per chunk. Division rounds up. |
| chunk_count = ((this.width ~sat- this.dst_x) + 31) / 32 |
| chunk_count = chunk_count.min(a: 16) // Keep p0 <= 512. |
| while (chunk_count > 0) and (args.src.length() >= 4) { |
| chunk_bits = args.src.peek_u32be() |
| args.src.skip_u32_fast!(actual: 4, worst_case: 4) |
| this.scratch[p0 + 0x00] = (0x01 & (chunk_bits >> 0x1F)) as base.u8 |
| this.scratch[p0 + 0x01] = (0x01 & (chunk_bits >> 0x1E)) as base.u8 |
| this.scratch[p0 + 0x02] = (0x01 & (chunk_bits >> 0x1D)) as base.u8 |
| this.scratch[p0 + 0x03] = (0x01 & (chunk_bits >> 0x1C)) as base.u8 |
| this.scratch[p0 + 0x04] = (0x01 & (chunk_bits >> 0x1B)) as base.u8 |
| this.scratch[p0 + 0x05] = (0x01 & (chunk_bits >> 0x1A)) as base.u8 |
| this.scratch[p0 + 0x06] = (0x01 & (chunk_bits >> 0x19)) as base.u8 |
| this.scratch[p0 + 0x07] = (0x01 & (chunk_bits >> 0x18)) as base.u8 |
| this.scratch[p0 + 0x08] = (0x01 & (chunk_bits >> 0x17)) as base.u8 |
| this.scratch[p0 + 0x09] = (0x01 & (chunk_bits >> 0x16)) as base.u8 |
| this.scratch[p0 + 0x0A] = (0x01 & (chunk_bits >> 0x15)) as base.u8 |
| this.scratch[p0 + 0x0B] = (0x01 & (chunk_bits >> 0x14)) as base.u8 |
| this.scratch[p0 + 0x0C] = (0x01 & (chunk_bits >> 0x13)) as base.u8 |
| this.scratch[p0 + 0x0D] = (0x01 & (chunk_bits >> 0x12)) as base.u8 |
| this.scratch[p0 + 0x0E] = (0x01 & (chunk_bits >> 0x11)) as base.u8 |
| this.scratch[p0 + 0x0F] = (0x01 & (chunk_bits >> 0x10)) as base.u8 |
| this.scratch[p0 + 0x10] = (0x01 & (chunk_bits >> 0x0F)) as base.u8 |
| this.scratch[p0 + 0x11] = (0x01 & (chunk_bits >> 0x0E)) as base.u8 |
| this.scratch[p0 + 0x12] = (0x01 & (chunk_bits >> 0x0D)) as base.u8 |
| this.scratch[p0 + 0x13] = (0x01 & (chunk_bits >> 0x0C)) as base.u8 |
| this.scratch[p0 + 0x14] = (0x01 & (chunk_bits >> 0x0B)) as base.u8 |
| this.scratch[p0 + 0x15] = (0x01 & (chunk_bits >> 0x0A)) as base.u8 |
| this.scratch[p0 + 0x16] = (0x01 & (chunk_bits >> 0x09)) as base.u8 |
| this.scratch[p0 + 0x17] = (0x01 & (chunk_bits >> 0x08)) as base.u8 |
| this.scratch[p0 + 0x18] = (0x01 & (chunk_bits >> 0x07)) as base.u8 |
| this.scratch[p0 + 0x19] = (0x01 & (chunk_bits >> 0x06)) as base.u8 |
| this.scratch[p0 + 0x1A] = (0x01 & (chunk_bits >> 0x05)) as base.u8 |
| this.scratch[p0 + 0x1B] = (0x01 & (chunk_bits >> 0x04)) as base.u8 |
| this.scratch[p0 + 0x1C] = (0x01 & (chunk_bits >> 0x03)) as base.u8 |
| this.scratch[p0 + 0x1D] = (0x01 & (chunk_bits >> 0x02)) as base.u8 |
| this.scratch[p0 + 0x1E] = (0x01 & (chunk_bits >> 0x01)) as base.u8 |
| this.scratch[p0 + 0x1F] = (0x01 & (chunk_bits >> 0x00)) as base.u8 |
| p0 = (p0 & 511) + 0x20 |
| chunk_count -= 1 |
| } endwhile |
| |
| } else if this.bits_per_pixel == 2 { |
| // Calculate the remaining number of 32-bit chunks. At 2 bits per |
| // pixel there are 16 pixels per chunk. Division rounds up. |
| chunk_count = ((this.width ~sat- this.dst_x) + 15) / 16 |
| chunk_count = chunk_count.min(a: 32) // Keep p0 <= 512. |
| while (chunk_count > 0) and (args.src.length() >= 4) { |
| chunk_bits = args.src.peek_u32be() |
| args.src.skip_u32_fast!(actual: 4, worst_case: 4) |
| this.scratch[p0 + 0x00] = (0x03 & (chunk_bits >> 0x1E)) as base.u8 |
| this.scratch[p0 + 0x01] = (0x03 & (chunk_bits >> 0x1C)) as base.u8 |
| this.scratch[p0 + 0x02] = (0x03 & (chunk_bits >> 0x1A)) as base.u8 |
| this.scratch[p0 + 0x03] = (0x03 & (chunk_bits >> 0x18)) as base.u8 |
| this.scratch[p0 + 0x04] = (0x03 & (chunk_bits >> 0x16)) as base.u8 |
| this.scratch[p0 + 0x05] = (0x03 & (chunk_bits >> 0x14)) as base.u8 |
| this.scratch[p0 + 0x06] = (0x03 & (chunk_bits >> 0x12)) as base.u8 |
| this.scratch[p0 + 0x07] = (0x03 & (chunk_bits >> 0x10)) as base.u8 |
| this.scratch[p0 + 0x08] = (0x03 & (chunk_bits >> 0x0E)) as base.u8 |
| this.scratch[p0 + 0x09] = (0x03 & (chunk_bits >> 0x0C)) as base.u8 |
| this.scratch[p0 + 0x0A] = (0x03 & (chunk_bits >> 0x0A)) as base.u8 |
| this.scratch[p0 + 0x0B] = (0x03 & (chunk_bits >> 0x08)) as base.u8 |
| this.scratch[p0 + 0x0C] = (0x03 & (chunk_bits >> 0x06)) as base.u8 |
| this.scratch[p0 + 0x0D] = (0x03 & (chunk_bits >> 0x04)) as base.u8 |
| this.scratch[p0 + 0x0E] = (0x03 & (chunk_bits >> 0x02)) as base.u8 |
| this.scratch[p0 + 0x0F] = (0x03 & (chunk_bits >> 0x00)) as base.u8 |
| p0 = (p0 & 511) + 0x10 |
| chunk_count -= 1 |
| } endwhile |
| |
| } else if this.bits_per_pixel == 4 { |
| // Calculate the remaining number of 32-bit chunks. At 4 bits per |
| // pixel there are 8 pixels per chunk. Division rounds up. |
| chunk_count = ((this.width ~sat- this.dst_x) + 7) / 8 |
| chunk_count = chunk_count.min(a: 64) // Keep p0 <= 512. |
| while (chunk_count > 0) and (args.src.length() >= 4) { |
| chunk_bits = args.src.peek_u32be() |
| args.src.skip_u32_fast!(actual: 4, worst_case: 4) |
| this.scratch[p0 + 0x00] = (0x0F & (chunk_bits >> 0x1C)) as base.u8 |
| this.scratch[p0 + 0x01] = (0x0F & (chunk_bits >> 0x18)) as base.u8 |
| this.scratch[p0 + 0x02] = (0x0F & (chunk_bits >> 0x14)) as base.u8 |
| this.scratch[p0 + 0x03] = (0x0F & (chunk_bits >> 0x10)) as base.u8 |
| this.scratch[p0 + 0x04] = (0x0F & (chunk_bits >> 0x0C)) as base.u8 |
| this.scratch[p0 + 0x05] = (0x0F & (chunk_bits >> 0x08)) as base.u8 |
| this.scratch[p0 + 0x06] = (0x0F & (chunk_bits >> 0x04)) as base.u8 |
| this.scratch[p0 + 0x07] = (0x0F & (chunk_bits >> 0x00)) as base.u8 |
| p0 = (p0 & 511) + 0x08 |
| chunk_count -= 1 |
| } endwhile |
| } |
| |
| p0 = p0.min(a: this.width ~sat- this.dst_x) |
| n = this.swizzler.swizzle_interleaved_from_slice!( |
| dst: dst, |
| dst_palette: dst_palette, |
| src: this.scratch[.. p0]) |
| if n == 0 { |
| return "@internal note: short read" |
| } |
| this.dst_x ~sat+= (n & 0xFFFF_FFFF) as base.u32 |
| } endwhile.loop |
| |
| 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 > 3 { |
| return 1 |
| } |
| return 0 |
| } |
| |
| pub func decoder.num_decoded_frames() base.u64 { |
| if this.call_sequence > 4 { |
| return 1 |
| } |
| return 0 |
| } |
| |
| pub func decoder.restart_frame!(index: base.u64, io_position: base.u64) base.status { |
| if this.call_sequence < 3 { |
| return base."#bad call sequence" |
| } |
| if args.index <> 0 { |
| return base."#bad argument" |
| } |
| this.call_sequence = 3 |
| this.frame_config_io_position = args.io_position |
| return ok |
| } |
| |
| pub func decoder.set_report_metadata!(fourcc: base.u32, report: base.bool) { |
| // No-op. BMP doesn't support metadata. |
| } |
| |
| pub func decoder.tell_me_more?(dst: base.io_writer, minfo: nptr base.more_information, src: base.io_reader) { |
| if this.io_redirect_fourcc <= 1 { |
| return base."#no more information" |
| } |
| if args.minfo <> nullptr { |
| args.minfo.set!( |
| flavor: 1, // WUFFS_BASE__MORE_INFORMATION__FLAVOR__IO_REDIRECT |
| w: this.io_redirect_fourcc, |
| x: 0, |
| y: this.io_redirect_pos, |
| z: 0xFFFF_FFFF_FFFF_FFFF) |
| } |
| // Setting io_redirect_fourcc to a placeholder value of 1 will cause future |
| // calls to return an error. |
| this.io_redirect_fourcc = 1 |
| } |
| |
| pub func decoder.workbuf_len() base.range_ii_u64 { |
| return this.util.make_range_ii_u64(min_incl: 0, max_incl: 0) |
| } |
| |
| pri func decoder.read_palette?(src: base.io_reader) { |
| var i : base.u32 |
| var argb : base.u32 |
| |
| if this.bitmap_info_len == 12 { |
| while (i < 256) and (this.padding >= 3) { |
| this.padding -= 3 |
| argb = args.src.read_u24le_as_u32?() |
| argb |= 0xFF00_0000 |
| this.src_palette[(4 * i) + 0] = ((argb >> 0) & 0xFF) as base.u8 |
| this.src_palette[(4 * i) + 1] = ((argb >> 8) & 0xFF) as base.u8 |
| this.src_palette[(4 * i) + 2] = ((argb >> 16) & 0xFF) as base.u8 |
| this.src_palette[(4 * i) + 3] = ((argb >> 24) & 0xFF) as base.u8 |
| i += 1 |
| } endwhile |
| } else { |
| while (i < 256) and (this.padding >= 4) { |
| this.padding -= 4 |
| argb = args.src.read_u32le?() |
| argb |= 0xFF00_0000 |
| this.src_palette[(4 * i) + 0] = ((argb >> 0) & 0xFF) as base.u8 |
| this.src_palette[(4 * i) + 1] = ((argb >> 8) & 0xFF) as base.u8 |
| this.src_palette[(4 * i) + 2] = ((argb >> 16) & 0xFF) as base.u8 |
| this.src_palette[(4 * i) + 3] = ((argb >> 24) & 0xFF) as base.u8 |
| i += 1 |
| } endwhile |
| } |
| |
| while i < 256 { |
| this.src_palette[(4 * i) + 0] = 0x00 |
| this.src_palette[(4 * i) + 1] = 0x00 |
| this.src_palette[(4 * i) + 2] = 0x00 |
| this.src_palette[(4 * i) + 3] = 0xFF |
| i += 1 |
| } endwhile |
| } |
| |
| pri func decoder.process_masks?() { |
| var i : base.u32 |
| var mask : base.u32 |
| var n : base.u32 |
| |
| while i < 4 { |
| mask = this.channel_masks[i] |
| if mask <> 0 { |
| n = 0 |
| while (mask & 1) == 0, |
| inv i < 4, |
| { |
| n ~mod+= 1 |
| mask >>= 1 |
| } endwhile |
| this.channel_shifts[i] = (n & 31) as base.u8 |
| |
| n = 0 |
| while (mask & 1) == 1, |
| inv i < 4, |
| { |
| n ~mod+= 1 |
| mask >>= 1 |
| } endwhile |
| if (mask <> 0) or (n > 32) { |
| return "#bad header" |
| } |
| this.channel_num_bits[i] = n as base.u8 |
| } else if i <> 3 { |
| return "#bad header" |
| } |
| |
| i += 1 |
| } endwhile |
| } |