use std::convert::AsRef;
use std::error::Error;
use std::path::Path;
use image;
use crate::{
piet::{ImageFormat, InterpolationMode},
widget::common::FillStrat,
Affine, BoxConstraints, Data, Env, Event, EventCtx, LayoutCtx, LifeCycle, LifeCycleCtx,
PaintCtx, Rect, RenderContext, Size, UpdateCtx, Widget,
};
pub struct Image {
image_data: ImageData,
fill: FillStrat,
interpolation: InterpolationMode,
}
impl Image {
pub fn new(image_data: ImageData) -> Self {
Image {
image_data,
fill: FillStrat::default(),
interpolation: InterpolationMode::Bilinear,
}
}
pub fn fill_mode(mut self, mode: FillStrat) -> Self {
self.fill = mode;
self
}
pub fn set_fill_mode(&mut self, newfil: FillStrat) {
self.fill = newfil;
}
pub fn interpolation_mode(mut self, interpolation: InterpolationMode) -> Self {
self.interpolation = interpolation;
self
}
pub fn set_interpolation_mode(&mut self, interpolation: InterpolationMode) {
self.interpolation = interpolation;
}
}
impl<T: Data> Widget<T> for Image {
fn event(&mut self, _ctx: &mut EventCtx, _event: &Event, _data: &mut T, _env: &Env) {}
fn lifecycle(&mut self, _ctx: &mut LifeCycleCtx, _event: &LifeCycle, _data: &T, _env: &Env) {}
fn update(&mut self, _ctx: &mut UpdateCtx, _old_data: &T, _data: &T, _env: &Env) {}
fn layout(
&mut self,
_layout_ctx: &mut LayoutCtx,
bc: &BoxConstraints,
_data: &T,
_env: &Env,
) -> Size {
bc.debug_check("Image");
if bc.is_width_bounded() {
bc.max()
} else {
bc.constrain(self.image_data.get_size())
}
}
fn paint(&mut self, ctx: &mut PaintCtx, _data: &T, _env: &Env) {
let offset_matrix = self
.fill
.affine_to_fill(ctx.size(), self.image_data.get_size());
if self.fill != FillStrat::Contain {
let clip_rect = Rect::ZERO.with_size(ctx.size());
ctx.clip(clip_rect);
}
self.image_data
.to_piet(offset_matrix, ctx, self.interpolation);
}
}
#[derive(Clone)]
pub struct ImageData {
pixels: Vec<u8>,
x_pixels: u32,
y_pixels: u32,
format: ImageFormat,
}
impl ImageData {
pub fn empty() -> Self {
ImageData {
pixels: [].to_vec(),
x_pixels: 0,
y_pixels: 0,
format: ImageFormat::RgbaSeparate,
}
}
pub fn from_dynamic_image(image_data: image::DynamicImage) -> ImageData {
if has_alpha_channel(&image_data) {
Self::from_dynamic_image_with_alpha(image_data)
} else {
Self::from_dynamic_image_without_alpha(image_data)
}
}
pub fn from_dynamic_image_with_alpha(image_data: image::DynamicImage) -> ImageData {
let rgba_image = image_data.to_rgba();
let sizeofimage = rgba_image.dimensions();
ImageData {
pixels: rgba_image.to_vec(),
x_pixels: sizeofimage.0,
y_pixels: sizeofimage.1,
format: ImageFormat::RgbaSeparate,
}
}
pub fn from_dynamic_image_without_alpha(image_data: image::DynamicImage) -> ImageData {
let rgb_image = image_data.to_rgb();
let sizeofimage = rgb_image.dimensions();
ImageData {
pixels: rgb_image.to_vec(),
x_pixels: sizeofimage.0,
y_pixels: sizeofimage.1,
format: ImageFormat::Rgb,
}
}
pub fn from_data(raw_image: &[u8]) -> Result<Self, Box<dyn Error>> {
let image_data = image::load_from_memory(raw_image).map_err(|e| e)?;
Ok(ImageData::from_dynamic_image(image_data))
}
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, Box<dyn Error>> {
let image_data = image::open(path).map_err(|e| e)?;
Ok(ImageData::from_dynamic_image(image_data))
}
fn get_size(&self) -> Size {
Size::new(self.x_pixels as f64, self.y_pixels as f64)
}
fn to_piet(&self, offset_matrix: Affine, ctx: &mut PaintCtx, interpolation: InterpolationMode) {
ctx.with_save(|ctx| {
ctx.transform(offset_matrix);
let size = self.get_size();
let im = ctx
.make_image(
size.width as usize,
size.height as usize,
&self.pixels,
self.format,
)
.unwrap();
ctx.draw_image(&im, size.to_rect(), interpolation);
})
}
}
fn has_alpha_channel(image: &image::DynamicImage) -> bool {
use image::ColorType::*;
match image.color() {
La8 | Rgba8 | La16 | Rgba16 | Bgra8 => true,
_ => false,
}
}
impl Default for ImageData {
fn default() -> Self {
ImageData::empty()
}
}