【问题标题】:How to save a PNG image in Rust?如何在 Rust 中保存 PNG 图像?
【发布时间】:2016-08-11 01:51:19
【问题描述】:

给定一个 u8 字节的向量(每像素 4 字节 - RGBA),如何将其保存到 PNG 文件中?

【问题讨论】:

  • @Shepmaster,公平点,其实我问的目的是写我自己的答案(仍在计划中),它类似于这个问题,它有多个有用的答案 - stackoverflow.com/questions/902761(我是希望将我自己的答案从 Python 移植到 Rust)。
  • 您可以随时等到自己的答案准备好后再发布不太好的问题。
  • 是的,即便如此,不同意这是一个糟糕的问题 - 它是一个非常常见的操作,适用于多个有用的答案(使用包装器、访问 libpng 或直接写入数据并使用 zlib 压缩)。当一个新的 Rust 开发人员想要编写一个图像时,很容易得到这样的答案来展示一些不错的选择。
  • 展示一些不错的选择——除了这些问题最终只是过时的“这里有一个图书馆可以做到这一点”的目录,something that is explicitly off-topic
  • 我不想听起来我很想在这里获得声誉积分(因为我真的不是),但你的答案还在路上吗@ideasman42?

标签: image rust


【解决方案1】:

您可以use the image crate in the Piston repository 将原始缓冲区保存到磁盘。

页面底部的示例向您展示了如何执行此操作..:

extern crate image;

fn main() {

    let buffer: &[u8] = ...; // Generate the image data

    // Save the buffer as "image.png"
    image::save_buffer(&Path::new("image.png"), buffer, 800, 600, image::RGBA(8))
}

【讨论】:

    【解决方案2】:

    这是一个独立的、纯 Rust 实现的 PNG 图像写入函数。

    它基于我的 Python 答案here

    注意事项:

    • 由于 Rust 没有公开 zlib,这比原始 Python 代码要大很多。
    • 这通过写入未压缩的图像来避免使用 zlib。
    • crc32adler32 校验和实现作为模块包含在内。
    • 要检查的关键函数是write,它采用任何可写类型(通常是文件)。

    例子:

    mod crc32 {
        // https://github.com/ledbettj/crc32/blob/master/rust/src/crc32.rs
        pub struct Crc32 {
            table: [u32; 256],
            value: u32,
        }
    
        const CRC32_INITIAL: u32 = 0xedb88320;
    
        impl Crc32 {
            pub fn new() -> Crc32 {
                let mut c = Crc32 {
                    table: [0; 256],
                    value: 0xffffffff,
                };
                for i in 0..256 {
                    let mut v = i as u32;
                    for _ in 0..8 {
                        v = if v & 1 != 0 {
                            CRC32_INITIAL ^ (v >> 1)
                        } else {
                            v >> 1
                        }
                    }
                    c.table[i] = v;
                }
                return c;
            }
    
            pub fn start(&mut self) {
                self.value = 0xffffffff;
            }
    
            pub fn update(&mut self, buf: &[u8]) {
                for &i in buf {
                    self.value = self.table[((self.value ^ (i as u32)) & 0xff) as usize] ^
                                (self.value >> 8);
                }
            }
    
            pub fn finalize(&mut self) -> u32 {
                self.value ^ 0xffffffff_u32
            }
    
            #[allow(dead_code)]
            pub fn crc(&mut self, buf: &[u8]) -> u32 {
                self.start();
                self.update(buf);
                self.finalize()
            }
        }
    }
    
    mod adler32 {
        // https://en.wikipedia.org/wiki/Adler-32
    
        pub struct Adler32 {
            a: u32,
            b: u32,
        }
    
        const MOD_ADLER: u32 = 65521;
    
        impl Adler32 {
            pub fn new() -> Adler32 {
                Adler32 { a: 1, b: 0 }
            }
    
            pub fn start(&mut self) {
                self.a = 1;
                self.b = 0;
            }
    
            pub fn update(&mut self, buf: &[u8]) {
                for &i in buf {
                    self.a = (self.a + i as u32) % MOD_ADLER;
                    self.b = (self.a + self.b) % MOD_ADLER;
                }
            }
    
            pub fn finalize(&self) -> u32 {
                return (self.b << 16) | self.a;
            }
    
            #[allow(dead_code)]
            pub fn crc(&mut self, buf: &[u8]) -> u32 {
                self.start();
                self.update(buf);
                self.finalize()
            }
        }
    }
    
    // big endian
    #[inline]
    fn u32_to_u8_be(v: u32) -> [u8; 4] {
        [(v >> 24) as u8, (v >> 16) as u8, (v >> 8) as u8, v as u8]
    }
    
    mod fake_zlib {
        use super::adler32;
        use super::u32_to_u8_be;
    
        // Use 'none' compression
        pub fn compress(data: &[u8]) -> Vec<u8> {
            const CHUNK_SIZE: usize = 65530;
    
            let final_len =
                // header
                2 +
                // every chunk adds 5 bytes [1:type, 4:size].
                (5 * {
                    let n = data.len() / CHUNK_SIZE;
                    // include an extra chunk when we don't fit exactly into CHUNK_SIZE
                    (n + {if data.len() == n * CHUNK_SIZE && data.len() != 0 { 0 } else { 1 }})
                }) +
                // data
                data.len() +
                // crc
                4
            ;
    
            let mut raw_data = Vec::with_capacity(final_len);
            // header
            raw_data.extend(&[120, 1]);
            let mut pos_curr = 0_usize;
            let mut crc = adler32::Adler32::new();
            loop {
                let pos_next = ::std::cmp::min(data.len(), pos_curr + CHUNK_SIZE);
                let chunk_len = (pos_next - pos_curr) as u32;
                let is_last = pos_next == data.len();
                raw_data.extend(&[
                    // type
                    if is_last { 1 } else { 0 },
    
                    // size
                    (chunk_len & 0xff) as u8,
                    ((chunk_len >> 8) & 0xff) as u8,
                    (0xff - (chunk_len & 0xff)) as u8,
                    (0xff - ((chunk_len >> 8) & 0xff)) as u8,
                ]);
    
                raw_data.extend(&data[pos_curr..pos_next]);
    
                crc.update(&data[pos_curr..pos_next]);
    
                if is_last {
                    break;
                }
                pos_curr = pos_next;
            }
    
            raw_data.extend(&u32_to_u8_be(crc.finalize()));
    
            assert_eq!(final_len, raw_data.len());
            return raw_data;
        }
    }
    
    ///
    /// Write RGBA pixels to uncompressed PNG.
    ///
    pub fn write<W: ::std::io::Write>(
        file: &mut W,
        image: &[u8],
        w: u32,
        h: u32,
    ) -> Result<(), ::std::io::Error> {
    
        assert!(w as usize * h as usize * 4 == image.len());
    
        fn png_pack<W: ::std::io::Write>(
            file: &mut W,
            png_tag: &[u8; 4],
            data: &[u8],
        ) -> Result<(), ::std::io::Error> {
            file.write(&u32_to_u8_be(data.len() as u32))?;
            file.write(png_tag)?;
            file.write(data)?;
            {
                let mut crc = crc32::Crc32::new();
                crc.start();
                crc.update(png_tag);
                crc.update(data);
                file.write(&u32_to_u8_be(crc.finalize()))?;
            }
            Ok(())
        }
    
        file.write(b"\x89PNG\r\n\x1a\n")?;
        {
            let wb = u32_to_u8_be(w);
            let hb = u32_to_u8_be(h);
            let data = [wb[0], wb[1], wb[2], wb[3],
                        hb[0], hb[1], hb[2], hb[3],
                        8, 6, 0, 0, 0];
            png_pack(file, b"IHDR", &data)?;
        }
    
        {
            let width_byte_4 = w * 4;
            let final_len = (width_byte_4 + 1) * h;
            let mut raw_data = Vec::with_capacity(final_len as usize);
            let mut span: u32 = (h - 1) * width_byte_4;
            loop {
                raw_data.push(0);
                raw_data.extend(&image[(span as usize)..(span + width_byte_4) as usize]);
                if span == 0 {
                    break;
                }
                span -= width_byte_4;
            }
            assert!(final_len == (raw_data.len() as u32));
    
            png_pack(file, b"IDAT", &fake_zlib::compress(&raw_data))?;
        }
    
        png_pack(file, b"IEND", &[])?;
    
        Ok(())
    }
    
    
    fn main() {
        let mut f = std::fs::File::create("test.png").unwrap();
    
        // image from bottom to top 3x2
        let image_width = 3;
        let image_height = 2;
        let image = vec!(
            // R     G     B     A
            0xff, 0x00, 0x00, 0xff,
            0x00, 0xff, 0x00, 0xff,
            0x00, 0x00, 0xff, 0xff,
    
            0x80, 0x00, 0x00, 0xff,
            0x00, 0x80, 0x00, 0xff,
            0x00, 0x00, 0x80, 0xff,
        );
    
        match write(&mut f, &image, image_width, image_height) {
            Ok(_) => println!("Written image!"),
            Err(e) => println!("Error {:?}", e),
        }
    }
    

    这个后来做成了png_encode_mini crate,和这个sn-p基本一样。

    【讨论】:

    • 超级有帮助。小注释:它会生成有关不必要括号的警告
    猜你喜欢
    • 2012-09-21
    • 2011-01-19
    • 2018-11-04
    • 2011-06-02
    • 1970-01-01
    • 2021-12-27
    • 2019-06-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多