use clip_sys::{clip_image, clip_image_spec};
use failure::{err_msg, Error};
use image::{bmp::BMPEncoder, jpeg::JPEGEncoder, png::PNGEncoder, ColorType, ImageError};
use std::{io::Write, slice};
pub struct ClipImage {
clip_image: clip_image,
}
pub trait Encoder {
fn encode(
self,
data: &[u8],
width: u32,
height: u32,
color_type: ColorType,
) -> Result<(), ImageError>;
}
impl<W: Write> Encoder for PNGEncoder<W> {
fn encode(
self,
data: &[u8],
width: u32,
height: u32,
color_type: ColorType,
) -> Result<(), ImageError> {
PNGEncoder::encode(self, data, width, height, color_type)
}
}
impl<'a, W: 'a> Encoder for JPEGEncoder<'a, W>
where
W: Write,
{
fn encode(
mut self,
data: &[u8],
width: u32,
height: u32,
color_type: ColorType,
) -> Result<(), ImageError> {
JPEGEncoder::encode(&mut self, data, width, height, color_type)
}
}
impl<'a, W: 'a> Encoder for BMPEncoder<'a, W>
where
W: Write,
{
fn encode(
mut self,
data: &[u8],
width: u32,
height: u32,
color_type: ColorType,
) -> Result<(), ImageError> {
BMPEncoder::encode(&mut self, data, width, height, color_type)
}
}
impl ClipImage {
pub(crate) fn from_clip_image(clip_image: clip_image) -> Self {
Self { clip_image }
}
pub fn get_spec(&self) -> &clip_image_spec {
self.clip_image.spec()
}
pub fn get_data(&self) -> &[u8] {
let spec = self.get_spec();
let len: usize = spec.bytes_per_row as usize * spec.height as usize;
unsafe { slice::from_raw_parts(self.clip_image.data(), len) }
}
pub fn write_png<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
self.write_from_encoder(PNGEncoder::new(writer))
}
pub fn write_jpeg<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
self.write_from_encoder(JPEGEncoder::new(writer))
}
pub fn write_bmp<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
self.write_from_encoder(BMPEncoder::new(writer))
}
pub fn write_from_encoder<E: Encoder>(&self, encoder: E) -> Result<(), Error> {
let clip_spec = self.get_spec();
let clip_data = self.get_data();
let width = clip_spec.width as u32;
let height = clip_spec.height as u32;
let color_type = color_type_from_spec(&clip_spec)
.ok_or_else(|| err_msg("color_type_from_spec unhandled spec type"))?;
let bits_per_pixel = clip_spec.bits_per_pixel;
let bytes_in_pixel = (bits_per_pixel / 8) as usize;
match color_type {
ColorType::Bgra8 => {
let mut clip_data = clip_data.to_vec();
for i in (0..clip_data.len()).step_by(bytes_in_pixel) {
clip_data.swap(i, i + 2);
}
encoder.encode(&clip_data, width, height, ColorType::Rgba8)?;
}
ColorType::Bgr8 => {
let mut clip_data = clip_data.to_vec();
for i in (0..clip_data.len()).step_by(bytes_in_pixel) {
clip_data.swap(i, i + 2);
}
encoder.encode(&clip_data, width, height, ColorType::Rgb8)?;
}
_ => {
encoder.encode(&clip_data, width, height, color_type)?;
}
}
Ok(())
}
}
fn color_type_from_spec(spec: &clip_image_spec) -> Option<ColorType> {
Some(
match (
spec.red_shift,
spec.green_shift,
spec.blue_shift,
spec.alpha_shift,
) {
(0, 8, 16, 24) => match spec.bits_per_pixel / 4 {
8 => ColorType::Rgba8,
16 => ColorType::Rgba16,
_ => unreachable!(),
},
(0, 8, 16, 0) => match spec.bits_per_pixel / 3 {
8 => ColorType::Rgb8,
16 => ColorType::Rgb16,
_ => unreachable!(),
},
(16, 8, 0, 24) => match spec.bits_per_pixel / 4 {
8 => ColorType::Bgra8,
_ => unreachable!(),
},
(16, 8, 0, 0) => match spec.bits_per_pixel / 3 {
8 => ColorType::Bgr8,
_ => unreachable!(),
},
_ => return None,
},
)
}