summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2023-09-04 00:32:49 -0400
committerpommicket <pommicket@gmail.com>2023-09-04 00:32:49 -0400
commit515bf125593f4259dc558baf0523402a1d212b9b (patch)
tree2340b3eb256b7431d165513de32c8b1d5f58095b
parentb7e31b24d5155303d704141bc85d9d846ebb87f4 (diff)
fixed huffman tables
-rw-r--r--examples/small.rs2
-rw-r--r--src/lib.rs87
-rw-r--r--test/earth0.png (renamed from examples/earth0.png)bin3151930 -> 3151930 bytes
-rw-r--r--test/earth9.png (renamed from examples/earth9.png)bin1473658 -> 1473658 bytes
-rw-r--r--test/earth_palette.png (renamed from examples/earth_palette.png)bin483244 -> 483244 bytes
-rw-r--r--test/small_rgb.pngbin0 -> 115 bytes
-rw-r--r--test/small_rgba.pngbin0 -> 104 bytes
7 files changed, 74 insertions, 15 deletions
diff --git a/examples/small.rs b/examples/small.rs
index e6af172..fd86cb6 100644
--- a/examples/small.rs
+++ b/examples/small.rs
@@ -1,5 +1,5 @@
fn main() {
- let mut data = &include_bytes!("small.png")[..];
+ let mut data = &include_bytes!("../test/small_rgb.png")[..];
let header = tiny_png::read_png_header(&mut data).unwrap();
let mut buf = vec![0; header.required_bytes()];
let result = tiny_png::read_png(&mut data, Some(&header), &mut buf);
diff --git a/src/lib.rs b/src/lib.rs
index 21438a2..2f2ff1a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,6 +1,6 @@
#![cfg_attr(not(feature = "std"), no_std)]
-use core::cmp::min;
+use core::cmp::{max, min};
use core::fmt::{self, Debug, Display};
pub trait IOError: Sized + Display + Debug {}
@@ -200,6 +200,15 @@ impl ColorType {
_ => return None,
})
}
+
+ fn channels(self) -> u8 {
+ match self {
+ Self::Gray | Self::Indexed => 1,
+ Self::GrayAlpha => 2,
+ Self::Rgb => 3,
+ Self::Rgba => 4,
+ }
+ }
}
impl ImageHeader {
@@ -219,6 +228,7 @@ impl ImageHeader {
let row_bytes = 1 + usize::try_from(self.width())
.ok()?
.checked_mul(usize::from(self.bit_depth() as u8))?
+ .checked_mul(usize::from(self.color_type().channels()))?
.checked_add(7)?
/ 8;
row_bytes.checked_mul(usize::try_from(self.height()).ok()?)
@@ -227,7 +237,10 @@ impl ImageHeader {
self.checked_required_bytes().unwrap()
}
pub fn bytes_per_scanline(&self) -> usize {
- (self.width() as usize * usize::from(self.bit_depth() as u8) + 7) / 8
+ (self.width() as usize
+ * usize::from(self.bit_depth() as u8)
+ * usize::from(self.color_type().channels())
+ + 7) / 8
}
fn data_size(&self) -> usize {
let scanline_bytes = self.bytes_per_scanline();
@@ -396,6 +409,16 @@ struct HuffmanTable {
subtables_used: u16,
}
+impl Default for HuffmanTable {
+ fn default() -> Self {
+ Self {
+ main_table: [0; HUFFMAN_MAIN_TABLE_SIZE],
+ subtables: [[0; HUFFMAN_SUBTABLE_SIZE]; HUFFMAN_MAX_CODES],
+ subtables_used: 0,
+ }
+ }
+}
+
impl HuffmanTable {
fn assign(&mut self, code: u16, length: u8, value: u16) {
if length == 0 {
@@ -439,11 +462,7 @@ impl HuffmanTable {
code = (code + bl_count[bits - 1]) << 1;
next_code[bits] = code;
}
- let mut table = HuffmanTable {
- main_table: [0; HUFFMAN_MAIN_TABLE_SIZE],
- subtables: [[0; HUFFMAN_SUBTABLE_SIZE]; HUFFMAN_MAX_CODES],
- subtables_used: 0,
- };
+ let mut table = HuffmanTable::default();
for (i, l) in code_lengths.iter().copied().enumerate() {
table.assign(next_code[usize::from(l)], l, i as u16);
next_code[usize::from(l)] += 1;
@@ -539,7 +558,26 @@ fn read_compressed_block<R: Read>(
literal_length_table = HuffmanTable::from_code_lengths(literal_length_code_lengths);
distance_table = HuffmanTable::from_code_lengths(distance_code_lengths);
} else {
- todo!()
+ let mut lit = HuffmanTable::default();
+ let mut dist = HuffmanTable::default();
+ for i in 0..=143 {
+ lit.assign(0b00110000 + i, 8, i);
+ }
+ for i in 144..=255 {
+ lit.assign(0b110010000 + (i - 144), 9, i);
+ }
+ for i in 256..=279 {
+ lit.assign(i - 256, 7, i);
+ }
+ for i in 280..=287 {
+ lit.assign(0b11000000 + (i - 280), 8, i);
+ }
+ for i in 0..30 {
+ dist.assign(i, 5, i);
+ }
+
+ literal_length_table = lit;
+ distance_table = dist;
}
loop {
let literal_length = literal_length_table.read_value(reader)?;
@@ -635,6 +673,10 @@ fn apply_filters<I: IOError>(header: &ImageHeader, data: &mut [u8]) -> Result<()
let mut s = 0;
let mut d = 0;
+ let x_byte_offset = max(
+ 1,
+ usize::from(header.bit_depth as u8) * usize::from(header.color_type.channels()) / 8,
+ );
let scanline_bytes = header.bytes_per_scanline();
for scanline in 0..header.height() {
let filter = data[s];
@@ -642,16 +684,20 @@ fn apply_filters<I: IOError>(header: &ImageHeader, data: &mut [u8]) -> Result<()
for i in 0..scanline_bytes {
let x = i32::from(data[s]);
- let a = i32::from(if i == 0 { 0 } else { data[d - 1] });
+ let a = i32::from(if i < x_byte_offset {
+ 0
+ } else {
+ data[d - x_byte_offset]
+ });
let b = i32::from(if scanline == 0 {
0
} else {
data[d - scanline_bytes]
});
- let c = i32::from(if scanline == 0 || i == 0 {
+ let c = i32::from(if scanline == 0 || i < x_byte_offset {
0
} else {
- data[d - 1 - scanline_bytes]
+ data[d - x_byte_offset - scanline_bytes]
});
fn paeth(a: i32, b: i32, c: i32) -> i32 {
@@ -668,10 +714,15 @@ fn apply_filters<I: IOError>(header: &ImageHeader, data: &mut [u8]) -> Result<()
}
}
data[d] = (match filter {
+ // none
0 => x,
+ // sub
1 => x + a,
+ // up
2 => x + b,
+ // average
3 => x + (a + b) / 2,
+ // paeth
4 => x + paeth(a, b, c),
_ => return Err(Error::BadFilter),
}) as u8;
@@ -839,15 +890,23 @@ mod tests {
test_both!("examples/small.png");
}
#[test]
+ fn test_small_rgb() {
+ test_both!("test/small_rgb.png");
+ }
+ #[test]
+ fn test_small_rgba() {
+ test_both!("test/small_rgba.png");
+ }
+ #[test]
fn test_earth0() {
- test_both!("examples/earth0.png");
+ test_both!("test/earth0.png");
}
#[test]
fn test_earth9() {
- test_both!("examples/earth9.png");
+ test_both!("test/earth9.png");
}
#[test]
fn test_earth_palette() {
- test_both!("examples/earth_palette.png");
+ test_both!("test/earth_palette.png");
}
}
diff --git a/examples/earth0.png b/test/earth0.png
index 8c1ab6a..8c1ab6a 100644
--- a/examples/earth0.png
+++ b/test/earth0.png
Binary files differ
diff --git a/examples/earth9.png b/test/earth9.png
index d2670b1..d2670b1 100644
--- a/examples/earth9.png
+++ b/test/earth9.png
Binary files differ
diff --git a/examples/earth_palette.png b/test/earth_palette.png
index af974e0..af974e0 100644
--- a/examples/earth_palette.png
+++ b/test/earth_palette.png
Binary files differ
diff --git a/test/small_rgb.png b/test/small_rgb.png
new file mode 100644
index 0000000..9cb9fac
--- /dev/null
+++ b/test/small_rgb.png
Binary files differ
diff --git a/test/small_rgba.png b/test/small_rgba.png
new file mode 100644
index 0000000..33d4c13
--- /dev/null
+++ b/test/small_rgba.png
Binary files differ