|
use super::Color; |
|
use crate::AlphaBlending; |
|
use crate::color::float_to_srgb_u8; |
|
use crate::instances::{Instance, Instances}; |
|
use crate::raster_types::Raster; |
|
use core::hash::{Hash, Hasher}; |
|
use dyn_any::{DynAny, StaticType}; |
|
use glam::{DAffine2, DVec2}; |
|
use std::vec::Vec; |
|
|
|
mod base64_serde { |
|
|
|
|
|
use super::super::Pixel; |
|
use base64::Engine; |
|
use serde::{Deserialize, Deserializer, Serialize, Serializer}; |
|
|
|
pub fn as_base64<S: Serializer, P: Pixel>(key: &[P], serializer: S) -> Result<S::Ok, S::Error> { |
|
let u8_data = bytemuck::cast_slice(key); |
|
let string = base64::engine::general_purpose::STANDARD.encode(u8_data); |
|
(key.len() as u64, string).serialize(serializer) |
|
} |
|
|
|
pub fn from_base64<'a, D: Deserializer<'a>, P: Pixel>(deserializer: D) -> Result<Vec<P>, D::Error> { |
|
use serde::de::Error; |
|
<(u64, &[u8])>::deserialize(deserializer) |
|
.and_then(|(len, str)| { |
|
let mut output: Vec<P> = vec![P::zeroed(); len as usize]; |
|
base64::engine::general_purpose::STANDARD |
|
.decode_slice(str, bytemuck::cast_slice_mut(output.as_mut_slice())) |
|
.map_err(|err| Error::custom(err.to_string()))?; |
|
|
|
Ok(output) |
|
}) |
|
.map_err(serde::de::Error::custom) |
|
} |
|
} |
|
|
|
#[derive(Clone, PartialEq, Default, specta::Type, serde::Serialize, serde::Deserialize)] |
|
pub struct Image<P: Pixel> { |
|
pub width: u32, |
|
pub height: u32, |
|
#[serde(serialize_with = "base64_serde::as_base64", deserialize_with = "base64_serde::from_base64")] |
|
pub data: Vec<P>, |
|
|
|
|
|
#[serde(skip)] |
|
pub base64_string: Option<String>, |
|
|
|
|
|
} |
|
|
|
impl<P: Pixel + Debug> Debug for Image<P> { |
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
|
let length = self.data.len(); |
|
f.debug_struct("Image") |
|
.field("width", &self.width) |
|
.field("height", &self.height) |
|
.field("data", if length < 100 { &self.data } else { &length }) |
|
.finish() |
|
} |
|
} |
|
|
|
unsafe impl<P> StaticType for Image<P> |
|
where |
|
P: dyn_any::StaticTypeSized + Pixel, |
|
P::Static: Pixel, |
|
{ |
|
type Static = Image<P::Static>; |
|
} |
|
|
|
impl<P: Copy + Pixel> Bitmap for Image<P> { |
|
type Pixel = P; |
|
#[inline(always)] |
|
fn get_pixel(&self, x: u32, y: u32) -> Option<P> { |
|
self.data.get((x + y * self.width) as usize).copied() |
|
} |
|
#[inline(always)] |
|
fn width(&self) -> u32 { |
|
self.width |
|
} |
|
#[inline(always)] |
|
fn height(&self) -> u32 { |
|
self.height |
|
} |
|
} |
|
|
|
impl<P: Copy + Pixel> BitmapMut for Image<P> { |
|
fn get_pixel_mut(&mut self, x: u32, y: u32) -> Option<&mut P> { |
|
self.data.get_mut((x + y * self.width) as usize) |
|
} |
|
} |
|
|
|
|
|
|
|
impl<P: Hash + Pixel> Hash for Image<P> { |
|
fn hash<H: Hasher>(&self, state: &mut H) { |
|
const HASH_SAMPLES: u64 = 1000; |
|
let data_length = self.data.len() as u64; |
|
self.width.hash(state); |
|
self.height.hash(state); |
|
for i in 0..HASH_SAMPLES.min(data_length) { |
|
self.data[(i * data_length / HASH_SAMPLES) as usize].hash(state); |
|
} |
|
} |
|
} |
|
|
|
impl<P: Pixel> Image<P> { |
|
pub fn new(width: u32, height: u32, color: P) -> Self { |
|
Self { |
|
width, |
|
height, |
|
data: vec![color; (width * height) as usize], |
|
base64_string: None, |
|
} |
|
} |
|
} |
|
|
|
impl Image<Color> { |
|
|
|
pub fn from_image_data(image_data: &[u8], width: u32, height: u32) -> Self { |
|
let data = image_data.chunks_exact(4).map(|v| Color::from_rgba8_srgb(v[0], v[1], v[2], v[3])).collect(); |
|
Image { |
|
width, |
|
height, |
|
data, |
|
base64_string: None, |
|
} |
|
} |
|
|
|
pub fn to_png(&self) -> Vec<u8> { |
|
use ::image::ImageEncoder; |
|
let (data, width, height) = self.to_flat_u8(); |
|
let mut png = Vec::new(); |
|
let encoder = ::image::codecs::png::PngEncoder::new(&mut png); |
|
encoder.write_image(&data, width, height, ::image::ExtendedColorType::Rgba8).expect("failed to encode image as png"); |
|
png |
|
} |
|
} |
|
|
|
use super::*; |
|
impl<P: Alpha + RGB + AssociatedAlpha> Image<P> |
|
where |
|
P::ColorChannel: Linear, |
|
<P as Alpha>::AlphaChannel: Linear, |
|
{ |
|
|
|
pub fn to_flat_u8(&self) -> (Vec<u8>, u32, u32) { |
|
let Image { width, height, data, .. } = self; |
|
assert_eq!(data.len(), *width as usize * *height as usize); |
|
|
|
|
|
let mut last_r = 0.; |
|
let mut last_r_srgb = 0u8; |
|
let mut last_g = 0.; |
|
let mut last_g_srgb = 0u8; |
|
let mut last_b = 0.; |
|
let mut last_b_srgb = 0u8; |
|
|
|
let mut result = vec![0; data.len() * 4]; |
|
let mut i = 0; |
|
for color in data { |
|
let a = color.a().to_f32(); |
|
|
|
|
|
if a >= 0.5 / 255. { |
|
let undo_premultiply = 1. / a; |
|
let r = color.r().to_f32() * undo_premultiply; |
|
let g = color.g().to_f32() * undo_premultiply; |
|
let b = color.b().to_f32() * undo_premultiply; |
|
|
|
|
|
if r != last_r { |
|
last_r = r; |
|
last_r_srgb = float_to_srgb_u8(r); |
|
} |
|
if g != last_g { |
|
last_g = g; |
|
last_g_srgb = float_to_srgb_u8(g); |
|
} |
|
if b != last_b { |
|
last_b = b; |
|
last_b_srgb = float_to_srgb_u8(b); |
|
} |
|
|
|
result[i] = last_r_srgb; |
|
result[i + 1] = last_g_srgb; |
|
result[i + 2] = last_b_srgb; |
|
result[i + 3] = (a * 255. + 0.5) as u8; |
|
} |
|
|
|
i += 4; |
|
} |
|
|
|
(result, *width, *height) |
|
} |
|
} |
|
|
|
impl<P: Pixel> IntoIterator for Image<P> { |
|
type Item = P; |
|
type IntoIter = std::vec::IntoIter<P>; |
|
fn into_iter(self) -> Self::IntoIter { |
|
self.data.into_iter() |
|
} |
|
} |
|
|
|
|
|
pub fn migrate_image_frame<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<RasterDataTable<CPU>, D::Error> { |
|
use serde::Deserialize; |
|
|
|
type ImageFrameTable<P> = Instances<Image<P>>; |
|
|
|
#[derive(Clone, Debug, Hash, PartialEq, DynAny)] |
|
enum RasterFrame { |
|
|
|
ImageFrame(ImageFrameTable<Color>), |
|
} |
|
impl<'de> serde::Deserialize<'de> for RasterFrame { |
|
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { |
|
Ok(RasterFrame::ImageFrame(ImageFrameTable::new(Image::deserialize(deserializer)?))) |
|
} |
|
} |
|
impl serde::Serialize for RasterFrame { |
|
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { |
|
match self { |
|
RasterFrame::ImageFrame(image_instances) => image_instances.serialize(serializer), |
|
} |
|
} |
|
} |
|
|
|
#[derive(Clone, Debug, Hash, PartialEq, DynAny, serde::Serialize, serde::Deserialize)] |
|
pub enum GraphicElement { |
|
|
|
GraphicGroup(GraphicGroupTable), |
|
|
|
VectorData(VectorDataTable), |
|
RasterFrame(RasterFrame), |
|
} |
|
|
|
#[derive(Clone, Default, Debug, PartialEq, specta::Type, serde::Serialize, serde::Deserialize)] |
|
pub struct ImageFrame<P: Pixel> { |
|
pub image: Image<P>, |
|
} |
|
impl From<ImageFrame<Color>> for GraphicElement { |
|
fn from(image_frame: ImageFrame<Color>) -> Self { |
|
GraphicElement::RasterFrame(RasterFrame::ImageFrame(ImageFrameTable::new(image_frame.image))) |
|
} |
|
} |
|
impl From<GraphicElement> for ImageFrame<Color> { |
|
fn from(element: GraphicElement) -> Self { |
|
match element { |
|
GraphicElement::RasterFrame(RasterFrame::ImageFrame(image)) => Self { |
|
image: image.instance_ref_iter().next().unwrap().instance.clone(), |
|
}, |
|
_ => panic!("Expected Image, found {:?}", element), |
|
} |
|
} |
|
} |
|
|
|
unsafe impl<P> StaticType for ImageFrame<P> |
|
where |
|
P: dyn_any::StaticTypeSized + Pixel, |
|
P::Static: Pixel, |
|
{ |
|
type Static = ImageFrame<P::Static>; |
|
} |
|
|
|
#[derive(Clone, Default, Debug, PartialEq, specta::Type, serde::Serialize, serde::Deserialize)] |
|
pub struct OldImageFrame<P: Pixel> { |
|
image: Image<P>, |
|
transform: DAffine2, |
|
alpha_blending: AlphaBlending, |
|
} |
|
|
|
#[derive(serde::Serialize, serde::Deserialize)] |
|
#[serde(untagged)] |
|
enum FormatVersions { |
|
Image(Image<Color>), |
|
OldImageFrame(OldImageFrame<Color>), |
|
ImageFrame(Instances<ImageFrame<Color>>), |
|
ImageFrameTable(ImageFrameTable<Color>), |
|
RasterDataTable(RasterDataTable<CPU>), |
|
} |
|
|
|
Ok(match FormatVersions::deserialize(deserializer)? { |
|
FormatVersions::Image(image) => RasterDataTable::new(Raster::new_cpu(image)), |
|
FormatVersions::OldImageFrame(image_frame_with_transform_and_blending) => { |
|
let OldImageFrame { image, transform, alpha_blending } = image_frame_with_transform_and_blending; |
|
let mut image_frame_table = RasterDataTable::new(Raster::new_cpu(image)); |
|
*image_frame_table.instance_mut_iter().next().unwrap().transform = transform; |
|
*image_frame_table.instance_mut_iter().next().unwrap().alpha_blending = alpha_blending; |
|
image_frame_table |
|
} |
|
FormatVersions::ImageFrame(image_frame) => RasterDataTable::new(Raster::new_cpu( |
|
image_frame |
|
.instance_ref_iter() |
|
.next() |
|
.unwrap_or(Instances::new(ImageFrame::default()).instance_ref_iter().next().unwrap()) |
|
.instance |
|
.image |
|
.clone(), |
|
)), |
|
FormatVersions::ImageFrameTable(image_frame_table) => RasterDataTable::new(Raster::new_cpu(image_frame_table.instance_ref_iter().next().unwrap().instance.clone())), |
|
FormatVersions::RasterDataTable(raster_data_table) => raster_data_table, |
|
}) |
|
} |
|
|
|
|
|
pub fn migrate_image_frame_instance<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Instance<Raster<CPU>>, D::Error> { |
|
use serde::Deserialize; |
|
|
|
type ImageFrameTable<P> = Instances<Image<P>>; |
|
|
|
#[derive(Clone, Debug, Hash, PartialEq, DynAny)] |
|
enum RasterFrame { |
|
|
|
ImageFrame(ImageFrameTable<Color>), |
|
} |
|
impl<'de> serde::Deserialize<'de> for RasterFrame { |
|
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { |
|
Ok(RasterFrame::ImageFrame(ImageFrameTable::new(Image::deserialize(deserializer)?))) |
|
} |
|
} |
|
impl serde::Serialize for RasterFrame { |
|
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { |
|
match self { |
|
RasterFrame::ImageFrame(image_instances) => image_instances.serialize(serializer), |
|
} |
|
} |
|
} |
|
|
|
#[derive(Clone, Debug, Hash, PartialEq, DynAny, serde::Serialize, serde::Deserialize)] |
|
pub enum GraphicElement { |
|
|
|
GraphicGroup(GraphicGroupTable), |
|
|
|
VectorData(VectorDataTable), |
|
RasterFrame(RasterFrame), |
|
} |
|
|
|
#[derive(Clone, Default, Debug, PartialEq, specta::Type, serde::Serialize, serde::Deserialize)] |
|
pub struct ImageFrame<P: Pixel> { |
|
pub image: Image<P>, |
|
} |
|
impl From<ImageFrame<Color>> for GraphicElement { |
|
fn from(image_frame: ImageFrame<Color>) -> Self { |
|
GraphicElement::RasterFrame(RasterFrame::ImageFrame(ImageFrameTable::new(image_frame.image))) |
|
} |
|
} |
|
impl From<GraphicElement> for ImageFrame<Color> { |
|
fn from(element: GraphicElement) -> Self { |
|
match element { |
|
GraphicElement::RasterFrame(RasterFrame::ImageFrame(image)) => Self { |
|
image: image.instance_ref_iter().next().unwrap().instance.clone(), |
|
}, |
|
_ => panic!("Expected Image, found {:?}", element), |
|
} |
|
} |
|
} |
|
|
|
unsafe impl<P> StaticType for ImageFrame<P> |
|
where |
|
P: dyn_any::StaticTypeSized + Pixel, |
|
P::Static: Pixel, |
|
{ |
|
type Static = ImageFrame<P::Static>; |
|
} |
|
|
|
#[derive(Clone, Default, Debug, PartialEq, specta::Type, serde::Serialize, serde::Deserialize)] |
|
pub struct OldImageFrame<P: Pixel> { |
|
image: Image<P>, |
|
transform: DAffine2, |
|
alpha_blending: AlphaBlending, |
|
} |
|
|
|
#[derive(serde::Serialize, serde::Deserialize)] |
|
#[serde(untagged)] |
|
enum FormatVersions { |
|
Image(Image<Color>), |
|
OldImageFrame(OldImageFrame<Color>), |
|
ImageFrame(Instances<ImageFrame<Color>>), |
|
RasterDataTable(RasterDataTable<CPU>), |
|
ImageInstance(Instance<Raster<CPU>>), |
|
} |
|
|
|
Ok(match FormatVersions::deserialize(deserializer)? { |
|
FormatVersions::Image(image) => Instance { |
|
instance: Raster::new_cpu(image), |
|
..Default::default() |
|
}, |
|
FormatVersions::OldImageFrame(image_frame_with_transform_and_blending) => Instance { |
|
instance: Raster::new_cpu(image_frame_with_transform_and_blending.image), |
|
transform: image_frame_with_transform_and_blending.transform, |
|
alpha_blending: image_frame_with_transform_and_blending.alpha_blending, |
|
source_node_id: None, |
|
}, |
|
FormatVersions::ImageFrame(image_frame) => Instance { |
|
instance: Raster::new_cpu(image_frame.instance_ref_iter().next().unwrap().instance.image.clone()), |
|
..Default::default() |
|
}, |
|
FormatVersions::RasterDataTable(image_frame_table) => image_frame_table.instance_iter().next().unwrap_or_default(), |
|
FormatVersions::ImageInstance(image_instance) => image_instance, |
|
}) |
|
} |
|
|
|
|
|
|
|
impl<P: Debug + Copy + Pixel> Sample for Image<P> { |
|
type Pixel = P; |
|
|
|
|
|
#[inline(always)] |
|
fn sample(&self, pos: DVec2, _area: DVec2) -> Option<Self::Pixel> { |
|
let image_size = DVec2::new(self.width() as f64, self.height() as f64); |
|
if pos.x < 0. || pos.y < 0. || pos.x >= image_size.x || pos.y >= image_size.y { |
|
return None; |
|
} |
|
self.get_pixel(pos.x as u32, pos.y as u32) |
|
} |
|
} |
|
|
|
impl<P: Copy + Pixel> Image<P> { |
|
pub fn get_mut(&mut self, x: usize, y: usize) -> &mut P { |
|
&mut self.data[y * (self.width as usize) + x] |
|
} |
|
|
|
|
|
pub fn sample(&self, position: DVec2) -> P { |
|
let x = position.x.clamp(0., self.width as f64 - 1.) as usize; |
|
let y = position.y.clamp(0., self.height as f64 - 1.) as usize; |
|
|
|
self.data[x + y * self.width as usize] |
|
} |
|
} |
|
|
|
impl<P: Pixel> AsRef<Image<P>> for Image<P> { |
|
fn as_ref(&self) -> &Image<P> { |
|
self |
|
} |
|
} |
|
|
|
impl From<Image<Color>> for Image<SRGBA8> { |
|
fn from(image: Image<Color>) -> Self { |
|
let data = image.data.into_iter().map(|x| x.into()).collect(); |
|
Self { |
|
data, |
|
width: image.width, |
|
height: image.height, |
|
base64_string: None, |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl From<Image<SRGBA8>> for Image<Color> { |
|
fn from(image: Image<SRGBA8>) -> Self { |
|
let data = image.data.into_iter().map(|x| x.into()).collect(); |
|
Self { |
|
data, |
|
width: image.width, |
|
height: image.height, |
|
base64_string: None, |
|
} |
|
} |
|
} |
|
|
|
#[cfg(test)] |
|
mod test { |
|
#[test] |
|
fn test_image_serialization_roundtrip() { |
|
use super::*; |
|
use crate::Color; |
|
let image = Image { |
|
width: 2, |
|
height: 2, |
|
data: vec![Color::WHITE, Color::BLACK, Color::RED, Color::GREEN], |
|
base64_string: None, |
|
}; |
|
|
|
let serialized = serde_json::to_string(&image).unwrap(); |
|
println!("{}", serialized); |
|
let deserialized: Image<Color> = serde_json::from_str(&serialized).unwrap(); |
|
println!("{:?}", deserialized); |
|
|
|
assert_eq!(image, deserialized); |
|
} |
|
} |
|
|