use std::io::{Cursor, Write};
use png::{BitDepth, ColorType, Encoder, ScaledFloat};
use crate::{Canvas, EncoderError, Window};
const PNG_BUF_CAPACITY: usize = 0x10000;
#[allow(clippy::unnecessary_wraps)]
impl Canvas {
pub(super) fn export_png8bpp(&self, window: Window) -> Result<Vec<u8>, EncoderError> {
let mut pngbuf: Vec<u8> = Vec::with_capacity(PNG_BUF_CAPACITY);
let cursor = Cursor::new(&mut pngbuf);
let mut encoder = Encoder::new(cursor, window.w, window.h);
encoder.set_color(ColorType::Grayscale);
encoder.set_depth(BitDepth::Eight);
encoder.set_source_gamma(ScaledFloat::from_scaled(45455));
let mut writer = encoder.write_header().unwrap();
let mut stream = writer.stream_writer().unwrap();
let spans = self.window_spans(window).unwrap();
for span in spans {
for &p in span {
let gray8 = self.gamma_curve.transform(p);
stream.write_all(&[gray8]).unwrap();
}
}
stream.finish().unwrap();
writer.finish().unwrap();
Ok(pngbuf)
}
pub(super) fn export_png16bpp(&self, window: Window) -> Result<Vec<u8>, EncoderError> {
let mut pngbuf: Vec<u8> = Vec::with_capacity(PNG_BUF_CAPACITY);
let cursor = Cursor::new(&mut pngbuf);
let mut encoder = Encoder::new(cursor, window.w, window.h);
encoder.set_color(ColorType::Grayscale);
encoder.set_depth(BitDepth::Sixteen);
encoder.set_source_gamma(ScaledFloat::new(1.0));
let mut writer = encoder.write_header().unwrap();
let mut stream = writer.stream_writer().unwrap();
let spans = self.window_spans(window).unwrap();
for span in spans {
for p in span {
stream.write_all(&p.to_be_bytes()).unwrap();
}
}
stream.finish().unwrap();
writer.finish().unwrap();
Ok(pngbuf)
}
pub(super) fn export_sub_png8bpp(&self, factors: (u32, u32)) -> Result<Vec<u8>, EncoderError> {
let mut pngbuf: Vec<u8> = Vec::with_capacity(PNG_BUF_CAPACITY);
let width = self.width / factors.0;
let height = self.height / factors.1;
let cursor = Cursor::new(&mut pngbuf);
let mut encoder = Encoder::new(cursor, width, height);
encoder.set_color(ColorType::Grayscale);
encoder.set_depth(BitDepth::Eight);
encoder.set_source_gamma(ScaledFloat::from_scaled(45455));
let mut writer = encoder.write_header().unwrap();
let mut stream = writer.stream_writer().unwrap();
for i in 0..(self.height / factors.1) {
let loffset = (i * factors.1 * self.width) as usize;
for j in 0..(self.width / factors.0) {
let offset = loffset + (j * factors.0) as usize;
let gray8 = self.gamma_curve.transform(self.pixbuf[offset]);
stream.write_all(&[gray8]).unwrap();
}
}
stream.finish().unwrap();
writer.finish().unwrap();
Ok(pngbuf)
}
pub(super) fn export_sub_png16bpp(&self, factors: (u32, u32)) -> Result<Vec<u8>, EncoderError> {
let mut pngbuf: Vec<u8> = Vec::with_capacity(PNG_BUF_CAPACITY);
let width = self.width / factors.0;
let height = self.height / factors.1;
let cursor = Cursor::new(&mut pngbuf);
let mut encoder = Encoder::new(cursor, width, height);
encoder.set_color(ColorType::Grayscale);
encoder.set_depth(BitDepth::Sixteen);
encoder.set_source_gamma(ScaledFloat::new(1.0));
let mut writer = encoder.write_header().unwrap();
let mut stream = writer.stream_writer().unwrap();
for i in 0..(self.height / factors.1) {
let loffset = (i * factors.1 * self.width) as usize;
for j in 0..(self.width / factors.0) {
let offset = loffset + (j * factors.0) as usize;
let bytes = self.pixbuf[offset].to_be_bytes();
stream.write_all(&bytes).unwrap();
}
}
stream.finish().unwrap();
writer.finish().unwrap();
Ok(pngbuf)
}
}
#[cfg(test)]
mod tests {
use crate::{ImageFormat, SpotShape};
use super::*;
fn mkimage() -> Canvas {
let mut c = Canvas::new(256, 256);
c.set_background(1000);
let shape = SpotShape::default().scale(4.5);
let shape2 = shape.stretch(1.7, 0.7).rotate(45.0);
c.add_spot((100.6, 150.2), shape, 0.9);
c.add_spot((103.8, 146.5), shape2, 0.5);
c.draw();
c
}
#[test]
fn export_png8bpp() {
let img = mkimage().export_image(ImageFormat::PngGamma8Bpp).unwrap();
assert_eq!(img.len(), 1256);
}
#[test]
fn export_window_png8bpp() {
let wnd = Window::new(32, 16).at(90, 140);
let img = mkimage()
.export_window_image(wnd, ImageFormat::PngGamma8Bpp)
.unwrap();
assert_eq!(img.len(), 425);
}
#[test]
fn export_sub_png8bpp() {
let img = mkimage()
.export_subsampled_image((2, 2), ImageFormat::PngGamma8Bpp)
.unwrap();
assert_eq!(img.len(), 403);
}
#[test]
fn export_png16bpp() {
let img = mkimage().export_image(ImageFormat::PngLinear16Bpp).unwrap();
assert_eq!(img.len(), 2561);
}
#[test]
fn export_window_png16bpp() {
let wnd = Window::new(32, 16).at(90, 140);
let img = mkimage()
.export_window_image(wnd, ImageFormat::PngLinear16Bpp)
.unwrap();
assert_eq!(img.len(), 771);
}
#[test]
fn export_sub_png16bpp() {
let img = mkimage()
.export_subsampled_image((2, 2), ImageFormat::PngLinear16Bpp)
.unwrap();
assert_eq!(img.len(), 719);
}
}