// Converting rows from PNG's own layout, the one the decoder writes, to
// RGBA and to the shape of the png crate's EXPAND. The arithmetic of a
// sample is `@convert`'s; here are the rows, the palette's table, and tRNS.
///|
/// Converts rows of one image (or frame: its rows have the image's layout)
/// to other layouts. Made from what the image says: its color type, bit
/// depth, palette and tRNS (`Decoder::converter`, or
/// `Converter::Converter`).
pub struct Converter {
priv color_type : Int32
priv bit_depth : Int32
// RGBA for each of the 256 indices: the palette's entries with tRNS's
// alpha, and opaque black for the indices past them.
priv table : FixedArray[Byte]
// tRNS for grayscale and truecolor: the level or color that is
// transparent.
priv key : FixedArray[Int32]
priv keyed : Bool
// Whether tRNS gave the palette's alpha.
priv alpha : Bool
}
///|
/// A converter for rows of `color_type` and `bit_depth`. `palette` is
/// PLTE's red, green and blue bytes (for indexed-color); `transparency` is
/// tRNS, as `Decoder::transparency` gives it. An index past the palette
/// converts to opaque black (13.1); every other value converts as it is.
pub fn Converter::Converter(
color_type : ColorType,
bit_depth : Int32,
palette? : ArrayView[Byte],
transparency? : Transparency,
) -> Converter {
let table = FixedArray::make(256 * 4, b'\x00')
for i in 0..<256 {
table[i * 4 + 3] = b'\xff'
}
if palette is Some(palette) {
for i in 0..<(palette.length() / 3).min(256) {
for c in 0..<3 {
table[i * 4 + c] = palette[i * 3 + c]
}
}
}
let key = FixedArray::make(3, 0)
let mut keyed = false
match transparency {
Some(Alpha(alpha)) =>
for i in 0.. {
key[0] = v
keyed = true
}
Some(Rgb(r, g, b)) => {
key[0] = r
key[1] = g
key[2] = b
keyed = true
}
None => ()
}
let color_type = match color_type {
Grayscale => 0
Truecolor => 2
IndexedColor => 3
GrayscaleAlpha => 4
TruecolorAlpha => 6
}
let alpha = transparency is Some(Alpha(_))
{ color_type, bit_depth, table, key, keyed, alpha, }
}
///|
/// A converter for the decoder's image: its color type, bit depth, palette
/// and tRNS. Valid from `Frame` until `reset`.
pub fn Decoder::converter(self : Decoder) -> Converter {
Converter::Converter(
self.color_type(),
self.bit_depth,
palette=self.palette(),
transparency?=self.transparency(),
)
}
///|
/// Sample `i` of `row`, of `bit_depth` bits.
fn sample(row : ArrayView[Byte], i : Int32, bit_depth : Int32) -> Int32 {
match bit_depth {
16 => (row[2 * i].to_int() << 8) | row[2 * i + 1].to_int()
8 => row[i].to_int()
_ => {
let per_byte = 8 / bit_depth
@convert.packed_sample(
row[i / per_byte].to_int(),
i % per_byte,
bit_depth,
)
}
}
}
///|
/// The channels a pixel of the converter's color type has in the row.
fn Converter::channels(self : Converter) -> Int32 {
@rules.channels(self.color_type)
}
///|
/// Whether pixel `p` of `row` is the color tRNS makes transparent.
fn Converter::transparent(
self : Converter,
row : ArrayView[Byte],
p : Int32,
) -> Bool {
guard self.keyed else { return false }
let n = self.channels()
for c in 0.. Unit {
let d = self.bit_depth
let n = self.channels()
let to8 = (v : Int32) => {
if d == 16 {
@convert.narrow(v)
} else {
@convert.widen(v, d)
}
}
for p in 0.. Unit {
let d = self.bit_depth
let n = self.channels()
let to16 = (v : Int32) => if d == 16 { v } else { @convert.widen(v, d) * 257 }
let put = (at : Int32, v : Int32) => {
into[at] = (v >> 8).to_byte()
into[at + 1] = v.to_byte()
}
for p in 0.. Int32 {
match self.color_type {
3 => if self.alpha { 4 } else { 3 }
0 | 2 => self.channels() + (if self.keyed { 1 } else { 0 })
_ => self.channels()
}
}
///|
/// The bits of a sample `expanded` writes: 16 for a 16-bit image, else 8.
pub fn Converter::expanded_depth(self : Converter) -> Int32 {
if self.bit_depth == 16 {
16
} else {
8
}
}
///|
/// Writes the first `pixels` pixels of `row` into `into` as the png crate's
/// EXPAND does, which libpng's `png_set_expand` matches: the palette applied,
/// to RGB, or RGBA with tRNS; gray of fewer than 8 bits widened to 8; tRNS
/// of grayscale and truecolor images turned into an alpha channel; 16-bit
/// samples kept, big-endian. `expanded_channels` and `expanded_depth` say
/// the layout; `into` holds at least their product, in bytes, times
/// `pixels`.
pub fn Converter::expanded(
self : Converter,
row : ArrayView[Byte],
pixels : Int32,
into : MutArrayView[Byte],
) -> Unit {
let d = self.bit_depth
let n = self.channels()
let out_channels = self.expanded_channels()
let mut at = 0
for p in 0..> 8).to_byte()
into[at + 1] = v.to_byte()
at += 2
} else {
into[at] = @convert.widen(v, d).to_byte()
at += 1
}
}
if out_channels > n {
let alpha = if self.transparent(row, p) { 0 } else { 0xFFFF }
if d == 16 {
into[at] = (alpha >> 8).to_byte()
into[at + 1] = alpha.to_byte()
at += 2
} else {
into[at] = alpha.to_byte()
at += 1
}
}
}
}