Skip to content

Commit

Permalink
git squash commit for palette-combined-rgba.
Browse files Browse the repository at this point in the history
1faf7bce034aca3c1eac2bfa381a91592a5ed363
git squash commit for palette-combined-rgba.

a68937908a68f273aacdf8d8516984dcab51f8be
git squash commit for palette-combined-rgba.

0c630e69e3161c3fd9553fc240dfab9b4e15f28a
git squash commit for palette-combined-rgba.

1f20d55e4f3fa11d9df6bf82a6f879190f133a0c
git squash commit for palette-combined-rgba.

dea910a3ee8004605508c6fb8258ae77aa279cfa
git squash commit for palette-combined-rgba.

eab1fdba4f1c3087c91109365e7a83ea5b120a72
.

65484b9fadd54e13fb77e7c70b2de508e7825c2e
.

0f301859d55a5db7dc3dfda7e6e2a36fc4897304
.

45928f3b3381352e205d49fd70bccafd83257139
.

65f372f2d9acd90d449281751a3db54ec2532d38
.

156c7724e7169c6f6c11f2ab473fdf1bad9f0f2e
.

2a4a8672434f5b8281e4ec6dc24238278cf08376
.

afc4a1aad99b0675eb593a81f05de6ad977a9909
.

f4ae84751dd99879abfb5896870767c6d83e83ef
.

dca51f8d7a72084bf829921c45aec8cfbdce0cc6
.
  • Loading branch information
anforowicz committed Sep 28, 2023
1 parent 9ae8dc9 commit a1ee0c1
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 37 deletions.
2 changes: 2 additions & 0 deletions benches/expand_palette.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,15 @@ fn bench_expand_palette(
&input,
|b, input| {
let mut output = vec![0; input.output_size_in_bytes()];
let mut memoized_rgba_palette = None;
b.iter(|| {
expand_palette(
input.src.as_slice(),
input.src_bit_depth,
output.as_mut_slice(),
input.palette.as_slice(),
input.trns.as_ref().map(|trns| trns.as_slice()),
&mut memoized_rgba_palette,
)
});
},
Expand Down
10 changes: 9 additions & 1 deletion src/benchable_apis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ pub fn expand_palette(
dst: &mut [u8],
palette: &[u8],
trns: Option<&[u8]>,
memoized_rgba_palette: &mut Option<[[u8; 4]; 256]>,
) {
crate::decoder::palette::expand(src, src_bit_depth, dst, palette, trns);
crate::decoder::palette::expand(
src,
src_bit_depth,
dst,
palette,
trns,
memoized_rgba_palette,
);
}
21 changes: 19 additions & 2 deletions src/decoder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ impl<R: Read> Decoder<R> {
transform: self.transform,
scratch_buffer: Vec::new(),
limits: self.limits,
memoized_rgba_palette: None,
};

// Check if the decoding buffer of a single raw line has a valid size.
Expand Down Expand Up @@ -362,6 +363,8 @@ pub struct Reader<R: Read> {
scratch_buffer: Vec<u8>,
/// How resources we can spend (for example, on allocation).
limits: Limits,
/// Fixed-length RGBA palette (based on potentially shorter PLTE and an optional tRNS chunks).
memoized_rgba_palette: Option<[[u8; 4]; 256]>,
}

/// The subframe specific information.
Expand Down Expand Up @@ -620,7 +623,13 @@ impl<R: Read> Reader<R> {
};
match (color_type, trns) {
(ColorType::Indexed, _) if expand => {
expand_paletted(row, output_buffer, info, trns)?;
expand_paletted(
row,
output_buffer,
info,
trns,
&mut self.memoized_rgba_palette,
)?;
}
(ColorType::Grayscale | ColorType::GrayscaleAlpha, _) if bit_depth < 8 && expand => {
expand_gray_u8(row, output_buffer, info, trns)
Expand Down Expand Up @@ -814,6 +823,7 @@ fn expand_paletted(
buffer: &mut [u8],
info: &Info,
trns: Option<Option<&[u8]>>,
memoized_rgba_palette: &mut Option<[[u8; 4]; 256]>,
) -> Result<(), DecodingError> {
if let Some(palette) = info.palette.as_ref() {
if let BitDepth::Sixteen = info.bit_depth {
Expand All @@ -827,7 +837,14 @@ fn expand_paletted(
))
} else {
let trns = trns.map(|trns| trns.unwrap_or(&[]));
palette::expand(row, info.bit_depth as u8, buffer, palette, trns);
palette::expand(
row,
info.bit_depth as u8,
buffer,
palette,
trns,
memoized_rgba_palette,
);
Ok(())
}
} else {
Expand Down
88 changes: 54 additions & 34 deletions src/decoder/palette.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,39 @@

use crate::utils;

/// Combines RGB and `trns` (alpha) palettes into a single RGBA palette
/// that always contains 256 entries (falling back to black, opaque pixels
/// when `palette` and/or `trns` have less than 256 entries).
fn compute_rgba_palette(palette: &[u8], trns: &[u8]) -> [[u8; 4]; 256] {
// Default to black, opaque entries.
let mut rgba_palette = [[0, 0, 0, 0xFF]; 256];

// > The tRNS chunk shall not contain more alpha values than there are palette
// entries, but a tRNS chunk may contain fewer values than there are palette
// entries. In this case, the alpha value for all remaining palette entries is
// assumed to be 255.
//
// It seems, accepted reading is to fully *ignore* an invalid tRNS as if it were
// completely empty / all pixels are non-transparent.
let trns = if trns.len() <= palette.len() / 3 {
trns
} else {
&[]
};

for (rgba, rgb) in rgba_palette.iter_mut().zip(palette.chunks_exact(3)) {
rgba[0] = rgb[0];
rgba[1] = rgb[1];
rgba[2] = rgb[2];
}

for (rgba, &alpha) in rgba_palette.iter_mut().zip(trns.iter()) {
rgba[3] = alpha;
}

rgba_palette
}

/// Treats samples in `src` (one sample per `src_bit_depth`) as indices into the `palette` and/or
/// `trsn` tables. Expands each `src` sample into RGB or RGBA format and writes the result into
/// `dst`. If `trns` is present then `dst` will be in RGBA format (each byte from `src` will
Expand All @@ -13,52 +46,31 @@ use crate::utils;
///
/// This function will gracefully handle cases where `palette` and/or `trns` don't contain all 256
/// values. If `src` contains a missing value, then it will be expanded into a black, opaque pixel.
///
/// Empty vs missing `trns` have subtly different meaning:
/// - `None` means that the output should be RGB
/// - `Some(&[])` means that the output should be RGBA, but the `tRNS` data may have been
/// malformed.
pub(crate) fn expand(
src: &[u8],
src_bit_depth: u8,
dst: &mut [u8],
palette: &[u8],
trns: Option<&[u8]>,
memoized_rgba_palette: &mut Option<[[u8; 4]; 256]>,
) {
debug_assert!(src_bit_depth <= 8);

let trns = trns.map(|trns| {
// > The tRNS chunk shall not contain more alpha values than there are palette
// entries, but a tRNS chunk may contain fewer values than there are palette
// entries. In this case, the alpha value for all remaining palette entries is
// assumed to be 255.
//
// It seems, accepted reading is to fully *ignore* an invalid tRNS as if it were
// completely empty / all pixels are non-transparent.
if trns.len() <= palette.len() / 3 {
trns
} else {
&[]
}
});

let black = [0, 0, 0];
if let Some(trns) = trns {
let rgba_palette: &[[u8; 4]; 256] = memoized_rgba_palette
.get_or_insert_with(|| compute_rgba_palette(palette, trns.unwrap_or(&[])));

if trns.is_some() {
utils::unpack_bits(src, dst, 4, src_bit_depth as u8, |i, chunk| {
let (rgb, a) = (
palette
.get(3 * i as usize..3 * i as usize + 3)
.unwrap_or(&black),
*trns.get(i as usize).unwrap_or(&0xFF),
);
chunk[0] = rgb[0];
chunk[1] = rgb[1];
chunk[2] = rgb[2];
chunk[3] = a;
chunk.copy_from_slice(&rgba_palette[i as usize]);
});
} else {
utils::unpack_bits(src, dst, 3, src_bit_depth as u8, |i, chunk| {
let rgb = palette
.get(3 * i as usize..3 * i as usize + 3)
.unwrap_or(&black);
chunk[0] = rgb[0];
chunk[1] = rgb[1];
chunk[2] = rgb[2];
chunk.copy_from_slice(&rgba_palette[i as usize][..3]);
})
}
}
Expand All @@ -73,7 +85,15 @@ mod test {
let samples_count_per_byte = (8 / src_bit_depth) as usize;
let samples_count = src.len() * samples_count_per_byte;
let mut dst = vec![0; samples_count * output_bytes_per_input_sample];
super::expand(src, src_bit_depth, &mut dst, palette, trns);
let mut memoized_rgba_palette = None;
super::expand(
src,
src_bit_depth,
&mut dst,
palette,
trns,
&mut memoized_rgba_palette,
);
dst
}

Expand Down

0 comments on commit a1ee0c1

Please sign in to comment.