use byteorder::WriteBytesExt;
use std::fs::File;
use std::io;
use std::io::Write;
use std::path::Path;
use crate::low_level::header;
use crate::low_level::rle::Compressor;
use crate::low_level::PALETTE_START;
use crate::user_error;
#[derive(Clone, Debug)]
pub struct WriterRgb<W: io::Write> {
stream: W,
num_rows_left: u16,
width: u16,
}
#[derive(Clone, Debug)]
pub struct WriterPaletted<W: io::Write> {
stream: W,
num_rows_left: u16,
width: u16,
}
impl WriterRgb<io::BufWriter<File>> {
pub fn create_file<P: AsRef<Path>>(
path: P,
image_size: (u16, u16),
dpi: (u16, u16),
) -> io::Result<Self> {
let file = File::create(path)?;
Self::new(io::BufWriter::new(file), image_size, dpi)
}
}
impl WriterPaletted<io::BufWriter<File>> {
pub fn create_file<P: AsRef<Path>>(
path: P,
image_size: (u16, u16),
dpi: (u16, u16),
) -> io::Result<Self> {
let file = File::create(path)?;
Self::new(io::BufWriter::new(file), image_size, dpi)
}
}
impl<W: io::Write> WriterRgb<W> {
pub fn new(mut stream: W, image_size: (u16, u16), dpi: (u16, u16)) -> io::Result<Self> {
header::write(&mut stream, false, image_size, dpi)?;
Ok(WriterRgb {
stream,
width: image_size.0,
num_rows_left: image_size.1,
})
}
pub fn write_row_from_separate(&mut self, r: &[u8], g: &[u8], b: &[u8]) -> io::Result<()> {
if self.num_rows_left == 0 {
return user_error(
"pcx::WriterRgb::write_row_from_separate: all rows were already written",
);
}
let width = self.width as usize;
if r.len() != width || g.len() != width || b.len() != width {
return user_error("pcx::WriterRgb::write_row_from_separate: buffer lengths must be equal to the width of the image");
}
compress(&mut self.stream, r)?;
compress(&mut self.stream, g)?;
compress(&mut self.stream, b)?;
self.num_rows_left -= 1;
Ok(())
}
pub fn write_row(&mut self, rgb: &[u8]) -> io::Result<()> {
if self.num_rows_left == 0 {
return user_error("pcx::WriterRgb::write_row: all rows were already written");
}
if rgb.len() != (self.width as usize) * 3 {
return user_error("pcx::WriterRgb::write_row: buffer length must be equal to the width of the image multiplied by 3");
}
let lane_length = self.width + (self.width & 1); for color in 0..3 {
let mut compressor = Compressor::new(&mut self.stream, lane_length);
for x in 0..(self.width as usize) {
compressor.write_u8(rgb[x * 3 + color])?;
}
compressor.pad()?;
compressor.finish()?;
}
self.num_rows_left -= 1;
Ok(())
}
pub fn finish(mut self) -> io::Result<()> {
if self.num_rows_left != 0 {
return user_error("pcx::WriterRgb::finish: not all rows written");
}
self.stream.flush()
}
}
impl<W: io::Write> Drop for WriterRgb<W> {
fn drop(&mut self) {
let _r = self.stream.flush();
}
}
impl<W: io::Write> WriterPaletted<W> {
pub fn new(mut stream: W, image_size: (u16, u16), dpi: (u16, u16)) -> io::Result<Self> {
header::write(&mut stream, true, image_size, dpi)?;
Ok(WriterPaletted {
stream,
width: image_size.0,
num_rows_left: image_size.1,
})
}
pub fn write_row(&mut self, row: &[u8]) -> io::Result<()> {
if self.num_rows_left == 0 {
return user_error("pcx::WriterPaletted::write_row: all rows were already written");
}
if row.len() != self.width as usize {
return user_error("pcx::WriterPaletted::write_row: buffer length must be equal to the width of the image");
}
compress(&mut self.stream, row)?;
self.num_rows_left -= 1;
Ok(())
}
pub fn write_palette(mut self, palette: &[u8]) -> io::Result<()> {
if self.num_rows_left != 0 {
return user_error("pcx::WriterPaletted::write_palette: not all rows written");
}
if palette.len() > 256 * 3 || palette.len() % 3 != 0 {
return user_error("pcx::WriterPaletted::write_palette: incorrect palette length");
}
self.stream.write_u8(PALETTE_START)?;
self.stream.write_all(palette)?;
for _ in 0..(256 * 3 - palette.len()) {
self.stream.write_u8(0)?;
}
Ok(())
}
}
fn compress(stream: impl io::Write, row: &[u8]) -> io::Result<()> {
let lane_length = row.len() + (row.len() & 1); let mut compressor = Compressor::new(stream, lane_length as u16);
compressor.write_all(row)?;
compressor.pad()?;
compressor.finish()?;
Ok(())
}