image_encoder/png/
mod.rs

1mod zlib;
2
3use crc::{crc32, Hasher32};
4use std::io;
5use zlib::compress;
6
7pub struct Image<'a> {
8    pub width: u32,
9    pub height: u32,
10    data: &'a [u8],
11}
12
13impl<'a> Image<'a> {
14    pub fn new(data: &'a [u8], width: u32, height: u32) -> Image<'a> {
15        Image {
16            data,
17            width,
18            height,
19        }
20    }
21
22    pub fn encode_into<W: io::Write>(&self, buffer: &mut W) -> io::Result<()> {
23        write_header(buffer)?;
24        write_chunks(buffer, self.data, self.width, self.height)
25    }
26}
27
28pub fn write_header<W: io::Write>(buffer: &mut W) -> io::Result<()> {
29    buffer.write_all(b"\x89PNG\r\n\x1A\n")
30}
31
32pub fn write_chunks<W: io::Write>(
33    buffer: &mut W,
34    screen_data: &[u8],
35    width: u32,
36    height: u32,
37) -> io::Result<()> {
38    write_ihdr(buffer, width, height, 8u8, 6u8, 0u8, 0u8, 0u8)?;
39    write_idat(buffer, screen_data, width, height)?;
40    write_iend(buffer)?;
41
42    Ok(())
43}
44
45fn write_ihdr<W: io::Write>(
46    buffer: &mut W,
47    width: u32,
48    height: u32,
49    bit_depth: u8,
50    color_type: u8,
51    compression_method: u8,
52    filter_method: u8,
53    interlace_method: u8,
54) -> io::Result<()> {
55    let wb = width.to_be_bytes();
56    let hb = height.to_be_bytes();
57    let mut data = [
58        wb[0],
59        wb[1],
60        wb[2],
61        wb[3],
62        hb[0],
63        hb[1],
64        hb[2],
65        hb[3],
66        bit_depth,
67        color_type,
68        compression_method,
69        filter_method,
70        interlace_method,
71    ];
72    write_chunk(buffer, b"IHDR", &mut data)
73}
74
75fn write_idat<W: io::Write>(
76    buffer: &mut W,
77    screen_data: &[u8],
78    width: u32,
79    height: u32,
80) -> io::Result<()> {
81    let mut filtered = vec![];
82    for y in 0..height {
83        let start = (y * width * 4) as usize;
84        let end = start + (width as usize) * 4;
85        filtered.push(0);
86        let data = &screen_data[start..end];
87        filtered.extend_from_slice(data);
88    }
89    let compressed = compress(&filtered);
90
91    write_chunk(buffer, b"IDAT", &compressed)
92}
93
94fn write_iend<W: io::Write>(buffer: &mut W) -> io::Result<()> {
95    write_chunk(buffer, b"IEND", &[])
96}
97
98fn write_chunk<W: io::Write>(buffer: &mut W, kind: &[u8; 4], data: &[u8]) -> io::Result<()> {
99    buffer.write_all(&(data.len() as u32).to_be_bytes())?;
100    buffer.write_all(kind)?;
101    buffer.write_all(data)?;
102
103    let mut digest = crc32::Digest::new(crc32::IEEE);
104    digest.write(kind);
105    digest.write(data);
106    buffer.write_all(&digest.sum32().to_be_bytes())?;
107
108    Ok(())
109}