mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-03 21:08:18 +00:00
Instance tables refactor part 3: flatten ImageFrame<P> in lieu of Image<P> (#2256)
* Remove ImageFrame<T> by flattening it into Image<T> * Rename TextureFrame to ImageTexture * Fix tests
This commit is contained in:
parent
f1160e1ca6
commit
2f6c6e28f0
19 changed files with 235 additions and 269 deletions
|
@ -64,17 +64,18 @@ impl Size for web_sys::HtmlCanvasElement {
|
|||
}
|
||||
}
|
||||
|
||||
pub type TextureFrameTable = Instances<TextureFrame>;
|
||||
// TODO: Rename to ImageTextureTable
|
||||
pub type TextureFrameTable = Instances<ImageTexture>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TextureFrame {
|
||||
pub struct ImageTexture {
|
||||
#[cfg(feature = "wgpu")]
|
||||
pub texture: Arc<wgpu::Texture>,
|
||||
#[cfg(not(feature = "wgpu"))]
|
||||
pub texture: (),
|
||||
}
|
||||
|
||||
impl Hash for TextureFrame {
|
||||
impl Hash for ImageTexture {
|
||||
#[cfg(feature = "wgpu")]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.texture.hash(state);
|
||||
|
@ -83,18 +84,18 @@ impl Hash for TextureFrame {
|
|||
fn hash<H: Hasher>(&self, _state: &mut H) {}
|
||||
}
|
||||
|
||||
impl PartialEq for TextureFrame {
|
||||
impl PartialEq for ImageTexture {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.texture == other.texture
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl StaticType for TextureFrame {
|
||||
type Static = TextureFrame;
|
||||
unsafe impl StaticType for ImageTexture {
|
||||
type Static = ImageTexture;
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgpu")]
|
||||
impl Size for TextureFrame {
|
||||
impl Size for ImageTexture {
|
||||
fn size(&self) -> UVec2 {
|
||||
UVec2::new(self.texture.width(), self.texture.height())
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::application_io::{TextureFrame, TextureFrameTable};
|
||||
use crate::application_io::{ImageTexture, TextureFrameTable};
|
||||
use crate::instances::Instances;
|
||||
use crate::raster::image::{ImageFrame, ImageFrameTable};
|
||||
use crate::raster::image::{Image, ImageFrameTable};
|
||||
use crate::raster::BlendMode;
|
||||
use crate::transform::{Transform, TransformMut};
|
||||
use crate::uuid::NodeId;
|
||||
|
@ -111,9 +111,9 @@ impl From<VectorDataTable> for GraphicGroupTable {
|
|||
Self::new(GraphicGroup::new(vec![GraphicElement::VectorData(vector_data)]))
|
||||
}
|
||||
}
|
||||
impl From<ImageFrame<Color>> for GraphicGroupTable {
|
||||
fn from(image_frame: ImageFrame<Color>) -> Self {
|
||||
Self::new(GraphicGroup::new(vec![GraphicElement::RasterFrame(RasterFrame::ImageFrame(ImageFrameTable::new(image_frame)))]))
|
||||
impl From<Image<Color>> for GraphicGroupTable {
|
||||
fn from(image: Image<Color>) -> Self {
|
||||
Self::new(GraphicGroup::new(vec![GraphicElement::RasterFrame(RasterFrame::ImageFrame(ImageFrameTable::new(image)))]))
|
||||
}
|
||||
}
|
||||
impl From<ImageFrameTable<Color>> for GraphicGroupTable {
|
||||
|
@ -121,9 +121,9 @@ impl From<ImageFrameTable<Color>> for GraphicGroupTable {
|
|||
Self::new(GraphicGroup::new(vec![GraphicElement::RasterFrame(RasterFrame::ImageFrame(image_frame))]))
|
||||
}
|
||||
}
|
||||
impl From<TextureFrame> for GraphicGroupTable {
|
||||
fn from(texture_frame: TextureFrame) -> Self {
|
||||
Self::new(GraphicGroup::new(vec![GraphicElement::RasterFrame(RasterFrame::TextureFrame(TextureFrameTable::new(texture_frame)))]))
|
||||
impl From<ImageTexture> for GraphicGroupTable {
|
||||
fn from(image_texture: ImageTexture) -> Self {
|
||||
Self::new(GraphicGroup::new(vec![GraphicElement::RasterFrame(RasterFrame::TextureFrame(TextureFrameTable::new(image_texture)))]))
|
||||
}
|
||||
}
|
||||
impl From<TextureFrameTable> for GraphicGroupTable {
|
||||
|
@ -194,11 +194,14 @@ impl GraphicElement {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Rename to Raster
|
||||
#[derive(Clone, Debug, Hash, PartialEq, DynAny)]
|
||||
pub enum RasterFrame {
|
||||
/// A CPU-based bitmap image with a finite position and extent, equivalent to the SVG <image> tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image
|
||||
// TODO: Rename to ImageTable
|
||||
ImageFrame(ImageFrameTable<Color>),
|
||||
/// A GPU texture with a finite position and extent
|
||||
// TODO: Rename to ImageTextureTable
|
||||
TextureFrame(TextureFrameTable),
|
||||
}
|
||||
|
||||
|
@ -207,7 +210,7 @@ impl<'de> serde::Deserialize<'de> for RasterFrame {
|
|||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
Ok(RasterFrame::ImageFrame(ImageFrameTable::new(ImageFrame::deserialize(deserializer)?)))
|
||||
Ok(RasterFrame::ImageFrame(ImageFrameTable::new(Image::deserialize(deserializer)?)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,7 +242,7 @@ impl Artboard {
|
|||
pub fn new(location: IVec2, dimensions: IVec2) -> Self {
|
||||
Self {
|
||||
graphic_group: GraphicGroupTable::default(),
|
||||
label: String::from("Artboard"),
|
||||
label: "Artboard".to_string(),
|
||||
location: location.min(location + dimensions),
|
||||
dimensions: dimensions.abs(),
|
||||
background: Color::WHITE,
|
||||
|
@ -382,16 +385,13 @@ async fn to_artboard<Data: Into<GraphicGroupTable> + 'n>(
|
|||
}
|
||||
|
||||
#[node_macro::node(category(""))]
|
||||
async fn append_artboard(ctx: impl Ctx, mut artboards: ArtboardGroup, artboard: Artboard, node_path: Vec<NodeId>) -> ArtboardGroup {
|
||||
async fn append_artboard(_ctx: impl Ctx, mut artboards: ArtboardGroup, artboard: Artboard, node_path: Vec<NodeId>) -> ArtboardGroup {
|
||||
// let mut artboards = artboards.eval(ctx.clone()).await;
|
||||
// let artboard = artboard.eval(ctx).await;
|
||||
// let foot = ctx.footprint();
|
||||
// log::debug!("{:?}", foot);
|
||||
// Get the penultimate element of the node path, or None if the path is too short
|
||||
|
||||
// TODO: Delete this line
|
||||
let _ctx = ctx;
|
||||
|
||||
// Get the penultimate element of the node path, or None if the path is too short.
|
||||
// This is used to get the ID of the user-facing "Artboard" node (which encapsulates this internal "Append Artboard" node).
|
||||
let encapsulating_node_id = node_path.get(node_path.len().wrapping_sub(2)).copied();
|
||||
artboards.append_artboard(artboard, encapsulating_node_id);
|
||||
|
||||
|
@ -399,8 +399,8 @@ async fn append_artboard(ctx: impl Ctx, mut artboards: ArtboardGroup, artboard:
|
|||
}
|
||||
|
||||
// TODO: Remove this one
|
||||
impl From<ImageFrame<Color>> for GraphicElement {
|
||||
fn from(image_frame: ImageFrame<Color>) -> Self {
|
||||
impl From<Image<Color>> for GraphicElement {
|
||||
fn from(image_frame: Image<Color>) -> Self {
|
||||
GraphicElement::RasterFrame(RasterFrame::ImageFrame(ImageFrameTable::new(image_frame)))
|
||||
}
|
||||
}
|
||||
|
@ -410,8 +410,8 @@ impl From<ImageFrameTable<Color>> for GraphicElement {
|
|||
}
|
||||
}
|
||||
// TODO: Remove this one
|
||||
impl From<TextureFrame> for GraphicElement {
|
||||
fn from(texture: TextureFrame) -> Self {
|
||||
impl From<ImageTexture> for GraphicElement {
|
||||
fn from(texture: ImageTexture) -> Self {
|
||||
GraphicElement::RasterFrame(RasterFrame::TextureFrame(TextureFrameTable::new(texture)))
|
||||
}
|
||||
}
|
||||
|
@ -462,7 +462,7 @@ trait ToGraphicElement: Into<GraphicElement> {}
|
|||
|
||||
impl ToGraphicElement for VectorDataTable {}
|
||||
impl ToGraphicElement for ImageFrameTable<Color> {}
|
||||
impl ToGraphicElement for TextureFrame {}
|
||||
impl ToGraphicElement for ImageTexture {}
|
||||
|
||||
impl<T> From<T> for GraphicGroup
|
||||
where
|
||||
|
|
|
@ -276,6 +276,7 @@ pub struct RenderMetadata {
|
|||
pub clip_targets: HashSet<NodeId>,
|
||||
}
|
||||
|
||||
// TODO: Rename to "Graphical"
|
||||
pub trait GraphicElementRendered {
|
||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams);
|
||||
|
||||
|
@ -831,7 +832,7 @@ impl GraphicElementRendered for ImageFrameTable<Color> {
|
|||
|
||||
match render_params.image_render_mode {
|
||||
ImageRenderMode::Base64 => {
|
||||
let image = &instance.instance.image;
|
||||
let image = &instance.instance;
|
||||
if image.data.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
@ -894,7 +895,7 @@ impl GraphicElementRendered for ImageFrameTable<Color> {
|
|||
use vello::peniko;
|
||||
|
||||
for instance in self.instances() {
|
||||
let image = &instance.instance.image;
|
||||
let image = &instance.instance;
|
||||
if image.data.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
@ -918,7 +919,7 @@ impl GraphicElementRendered for RasterFrame {
|
|||
};
|
||||
|
||||
for instance in image.instances() {
|
||||
let (image, blending) = (&instance.instance.image, instance.alpha_blending);
|
||||
let (image, blending) = (&instance.instance, instance.alpha_blending);
|
||||
if image.data.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
@ -992,9 +993,9 @@ impl GraphicElementRendered for RasterFrame {
|
|||
};
|
||||
|
||||
match self {
|
||||
RasterFrame::ImageFrame(image_frame) => {
|
||||
for instance in image_frame.instances() {
|
||||
let image = &instance.instance.image;
|
||||
RasterFrame::ImageFrame(image) => {
|
||||
for instance in image.instances() {
|
||||
let image = &instance.instance;
|
||||
if image.data.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
@ -1004,8 +1005,8 @@ impl GraphicElementRendered for RasterFrame {
|
|||
render_stuff(image, *instance.alpha_blending);
|
||||
}
|
||||
}
|
||||
RasterFrame::TextureFrame(texture) => {
|
||||
for instance in texture.instances() {
|
||||
RasterFrame::TextureFrame(image_texture) => {
|
||||
for instance in image_texture.instances() {
|
||||
let image =
|
||||
vello::peniko::Image::new(vec![].into(), peniko::Format::Rgba8, instance.instance.texture.width(), instance.instance.texture.height()).with_extend(peniko::Extend::Repeat);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::application_io::{TextureFrame, TextureFrameTable};
|
||||
use crate::raster::image::{ImageFrame, ImageFrameTable};
|
||||
use crate::application_io::{ImageTexture, TextureFrameTable};
|
||||
use crate::raster::image::{Image, ImageFrameTable};
|
||||
use crate::raster::Pixel;
|
||||
use crate::transform::{Transform, TransformMut};
|
||||
use crate::vector::{InstanceId, VectorData, VectorDataTable};
|
||||
|
@ -180,18 +180,18 @@ impl TransformMut for GraphicGroupTable {
|
|||
}
|
||||
}
|
||||
|
||||
// TEXTURE FRAME
|
||||
impl Transform for Instance<'_, TextureFrame> {
|
||||
// IMAGE TEXTURE
|
||||
impl Transform for Instance<'_, ImageTexture> {
|
||||
fn transform(&self) -> DAffine2 {
|
||||
*self.transform
|
||||
}
|
||||
}
|
||||
impl Transform for InstanceMut<'_, TextureFrame> {
|
||||
impl Transform for InstanceMut<'_, ImageTexture> {
|
||||
fn transform(&self) -> DAffine2 {
|
||||
*self.transform
|
||||
}
|
||||
}
|
||||
impl TransformMut for InstanceMut<'_, TextureFrame> {
|
||||
impl TransformMut for InstanceMut<'_, ImageTexture> {
|
||||
fn transform_mut(&mut self) -> &mut DAffine2 {
|
||||
self.transform
|
||||
}
|
||||
|
@ -209,8 +209,8 @@ impl TransformMut for TextureFrameTable {
|
|||
}
|
||||
}
|
||||
|
||||
// IMAGE FRAME
|
||||
impl<P: Pixel> Transform for Instance<'_, ImageFrame<P>> {
|
||||
// IMAGE
|
||||
impl<P: Pixel> Transform for Instance<'_, Image<P>> {
|
||||
fn transform(&self) -> DAffine2 {
|
||||
*self.transform
|
||||
}
|
||||
|
@ -218,7 +218,7 @@ impl<P: Pixel> Transform for Instance<'_, ImageFrame<P>> {
|
|||
self.transform.transform_point2(pivot)
|
||||
}
|
||||
}
|
||||
impl<P: Pixel> Transform for InstanceMut<'_, ImageFrame<P>> {
|
||||
impl<P: Pixel> Transform for InstanceMut<'_, Image<P>> {
|
||||
fn transform(&self) -> DAffine2 {
|
||||
*self.transform
|
||||
}
|
||||
|
@ -226,7 +226,7 @@ impl<P: Pixel> Transform for InstanceMut<'_, ImageFrame<P>> {
|
|||
self.transform.transform_point2(pivot)
|
||||
}
|
||||
}
|
||||
impl<P: Pixel> TransformMut for InstanceMut<'_, ImageFrame<P>> {
|
||||
impl<P: Pixel> TransformMut for InstanceMut<'_, Image<P>> {
|
||||
fn transform_mut(&mut self) -> &mut DAffine2 {
|
||||
self.transform
|
||||
}
|
||||
|
@ -237,7 +237,7 @@ impl<P: Pixel> Transform for ImageFrameTable<P>
|
|||
where
|
||||
P: dyn_any::StaticType,
|
||||
P::Static: Pixel,
|
||||
GraphicElement: From<ImageFrame<P>>,
|
||||
GraphicElement: From<Image<P>>,
|
||||
{
|
||||
fn transform(&self) -> DAffine2 {
|
||||
self.one_instance().transform()
|
||||
|
@ -247,7 +247,7 @@ impl<P: Pixel> TransformMut for ImageFrameTable<P>
|
|||
where
|
||||
P: dyn_any::StaticType,
|
||||
P::Static: Pixel,
|
||||
GraphicElement: From<ImageFrame<P>>,
|
||||
GraphicElement: From<Image<P>>,
|
||||
{
|
||||
fn transform_mut(&mut self) -> &mut DAffine2 {
|
||||
self.transform.first_mut().unwrap_or_else(|| panic!("ONE INSTANCE EXPECTED"))
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#[cfg(feature = "alloc")]
|
||||
use crate::raster::curve::{Curve, CurveManipulatorGroup, ValueMapperNode};
|
||||
#[cfg(feature = "alloc")]
|
||||
use crate::raster::image::{ImageFrame, ImageFrameTable};
|
||||
use crate::raster::image::{Image, ImageFrameTable};
|
||||
use crate::raster::{Channel, Color, Pixel};
|
||||
use crate::registry::types::{Angle, Percentage, SignedPercentage};
|
||||
use crate::vector::style::GradientStops;
|
||||
|
@ -605,15 +605,13 @@ impl Blend<Color> for ImageFrameTable<Color> {
|
|||
let mut result = self.clone();
|
||||
|
||||
for (over, under) in result.instances_mut().zip(under.instances()) {
|
||||
let data = over.instance.image.data.iter().zip(under.instance.image.data.iter()).map(|(a, b)| blend_fn(*a, *b)).collect();
|
||||
let data = over.instance.data.iter().zip(under.instance.data.iter()).map(|(a, b)| blend_fn(*a, *b)).collect();
|
||||
|
||||
*over.instance = ImageFrame {
|
||||
image: super::Image {
|
||||
data,
|
||||
width: over.instance.image.width,
|
||||
height: over.instance.image.height,
|
||||
base64_string: None,
|
||||
},
|
||||
*over.instance = Image {
|
||||
data,
|
||||
width: over.instance.width,
|
||||
height: over.instance.height,
|
||||
base64_string: None,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -738,11 +736,11 @@ impl<P: Pixel> Adjust<P> for ImageFrameTable<P>
|
|||
where
|
||||
P: dyn_any::StaticType,
|
||||
P::Static: Pixel,
|
||||
GraphicElement: From<ImageFrame<P>>,
|
||||
GraphicElement: From<Image<P>>,
|
||||
{
|
||||
fn adjust(&mut self, map_fn: impl Fn(&P) -> P) {
|
||||
for instance in self.instances_mut() {
|
||||
for c in instance.instance.image.data.iter_mut() {
|
||||
for c in instance.instance.data.iter_mut() {
|
||||
*c = map_fn(c);
|
||||
}
|
||||
}
|
||||
|
@ -1386,7 +1384,7 @@ impl<P: Pixel> MultiplyAlpha for ImageFrameTable<P>
|
|||
where
|
||||
P: dyn_any::StaticType,
|
||||
P::Static: Pixel,
|
||||
GraphicElement: From<ImageFrame<P>>,
|
||||
GraphicElement: From<Image<P>>,
|
||||
{
|
||||
fn multiply_alpha(&mut self, factor: f64) {
|
||||
for instance in self.instances_mut() {
|
||||
|
@ -1539,13 +1537,13 @@ fn color_overlay<T: Adjust<Color>>(
|
|||
|
||||
// #[cfg(feature = "alloc")]
|
||||
// mod index_node {
|
||||
// use crate::raster::{Color, ImageFrame};
|
||||
// use crate::raster::{Color, Image};
|
||||
// use crate::Ctx;
|
||||
|
||||
// #[node_macro::node(category(""))]
|
||||
// pub fn index<T: Default + Clone>(
|
||||
// _: impl Ctx,
|
||||
// #[implementations(Vec<ImageFrame<Color>>, Vec<Color>)]
|
||||
// #[implementations(Vec<Image<Color>>, Vec<Color>)]
|
||||
// #[widget(ParsedWidgetOverride::Hidden)]
|
||||
// input: Vec<T>,
|
||||
// index: u32,
|
||||
|
@ -1561,8 +1559,8 @@ fn color_overlay<T: Adjust<Color>>(
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::raster::image::{ImageFrame, ImageFrameTable};
|
||||
use crate::raster::{BlendMode, Image};
|
||||
use crate::raster::adjustments::BlendMode;
|
||||
use crate::raster::image::{Image, ImageFrameTable};
|
||||
use crate::{Color, Node};
|
||||
use std::pin::Pin;
|
||||
|
||||
|
@ -1580,7 +1578,7 @@ mod test {
|
|||
#[tokio::test]
|
||||
async fn color_overlay_multiply() {
|
||||
let image_color = Color::from_rgbaf32_unchecked(0.7, 0.6, 0.5, 0.4);
|
||||
let image = ImageFrame { image: Image::new(1, 1, image_color) };
|
||||
let image = Image::new(1, 1, image_color);
|
||||
|
||||
// Color { red: 0., green: 1., blue: 0., alpha: 1. }
|
||||
let overlay_color = Color::GREEN;
|
||||
|
@ -1592,6 +1590,6 @@ mod test {
|
|||
let result = result.one_instance().instance;
|
||||
|
||||
// The output should just be the original green and alpha channels (as we multiply them by 1 and other channels by 0)
|
||||
assert_eq!(result.image.data[0], Color::from_rgbaf32_unchecked(0., image_color.g(), 0., image_color.a()));
|
||||
assert_eq!(result.data[0], Color::from_rgbaf32_unchecked(0., image_color.g(), 0., image_color.a()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ struct BrushCacheImpl {
|
|||
impl BrushCacheImpl {
|
||||
fn compute_brush_plan(&mut self, mut background: ImageFrameTable<Color>, input: &[BrushStroke]) -> BrushPlan {
|
||||
// Do background invalidation.
|
||||
if background.one_instance().instance.image != self.background.one_instance().instance.image {
|
||||
if background.one_instance().instance != self.background.one_instance().instance {
|
||||
self.background = background.clone();
|
||||
return BrushPlan {
|
||||
strokes: input.to_vec(),
|
||||
|
|
|
@ -53,6 +53,8 @@ pub struct Image<P: Pixel> {
|
|||
/// to an svg string. This is used as a cache in order to not have to encode the data on every graph evaluation.
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
pub base64_string: Option<String>,
|
||||
// TODO: Add an `origin` field to store where in the local space the image is anchored.
|
||||
// TODO: Currently it is always anchored at the top left corner at (0, 0). The bottom right corner of the new origin field would correspond to (1, 1).
|
||||
}
|
||||
|
||||
impl<P: Pixel + Debug> Debug for Image<P> {
|
||||
|
@ -66,8 +68,9 @@ impl<P: Pixel + Debug> Debug for Image<P> {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe impl<P: dyn_any::StaticTypeSized + Pixel> StaticType for Image<P>
|
||||
unsafe impl<P> StaticType for Image<P>
|
||||
where
|
||||
P: dyn_any::StaticTypeSized + Pixel,
|
||||
P::Static: Pixel,
|
||||
{
|
||||
type Static = Image<P::Static>;
|
||||
|
@ -212,6 +215,34 @@ impl<P: Pixel> IntoIterator for Image<P> {
|
|||
pub fn migrate_image_frame<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<ImageFrameTable<Color>, D::Error> {
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Clone, Default, Debug, PartialEq, specta::Type)]
|
||||
#[cfg_attr(feature = "serde", derive(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(crate::RasterFrame::ImageFrame(ImageFrameTable::new(image_frame.image)))
|
||||
}
|
||||
}
|
||||
impl From<GraphicElement> for ImageFrame<Color> {
|
||||
fn from(element: GraphicElement) -> Self {
|
||||
match element {
|
||||
GraphicElement::RasterFrame(crate::RasterFrame::ImageFrame(image)) => Self {
|
||||
image: image.one_instance().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)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct OldImageFrame<P: Pixel> {
|
||||
|
@ -222,60 +253,58 @@ pub fn migrate_image_frame<'de, D: serde::Deserializer<'de>>(deserializer: D) ->
|
|||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum EitherFormat {
|
||||
ImageFrame(ImageFrame<Color>),
|
||||
enum FormatVersions {
|
||||
Image(Image<Color>),
|
||||
OldImageFrame(OldImageFrame<Color>),
|
||||
ImageFrame(Instances<ImageFrame<Color>>),
|
||||
ImageFrameTable(ImageFrameTable<Color>),
|
||||
}
|
||||
|
||||
Ok(match EitherFormat::deserialize(deserializer)? {
|
||||
EitherFormat::ImageFrame(image_frame) => ImageFrameTable::<Color>::new(image_frame),
|
||||
EitherFormat::OldImageFrame(image_frame_with_transform_and_blending) => {
|
||||
Ok(match FormatVersions::deserialize(deserializer)? {
|
||||
FormatVersions::Image(image) => ImageFrameTable::new(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 = ImageFrameTable::new(ImageFrame { image });
|
||||
let mut image_frame_table = ImageFrameTable::new(image);
|
||||
*image_frame_table.one_instance_mut().transform = transform;
|
||||
*image_frame_table.one_instance_mut().alpha_blending = alpha_blending;
|
||||
image_frame_table
|
||||
}
|
||||
EitherFormat::ImageFrameTable(image_frame_table) => image_frame_table,
|
||||
FormatVersions::ImageFrame(image_frame) => ImageFrameTable::new(image_frame.one_instance().instance.image.clone()),
|
||||
FormatVersions::ImageFrameTable(image_frame_table) => image_frame_table,
|
||||
})
|
||||
}
|
||||
|
||||
pub type ImageFrameTable<P> = Instances<ImageFrame<P>>;
|
||||
// TODO: Rename to ImageTable
|
||||
pub type ImageFrameTable<P> = Instances<Image<P>>;
|
||||
|
||||
/// Construct a 0x0 image frame table. This is useful because ImageFrameTable::default() will return a 1x1 image frame table.
|
||||
impl ImageFrameTable<Color> {
|
||||
pub fn empty() -> Self {
|
||||
let mut result = Self::new(ImageFrame::default());
|
||||
let mut result = Self::new(Image::default());
|
||||
*result.transform_mut() = DAffine2::ZERO;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, PartialEq, specta::Type)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct ImageFrame<P: Pixel> {
|
||||
pub image: Image<P>,
|
||||
}
|
||||
|
||||
impl<P: Debug + Copy + Pixel> Sample for ImageFrame<P> {
|
||||
impl<P: Debug + Copy + Pixel> Sample for Image<P> {
|
||||
type Pixel = P;
|
||||
|
||||
// TODO: Improve sampling logic
|
||||
#[inline(always)]
|
||||
fn sample(&self, pos: DVec2, _area: DVec2) -> Option<Self::Pixel> {
|
||||
let image_size = DVec2::new(self.image.width() as f64, self.image.height() as f64);
|
||||
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.image.get_pixel(pos.x as u32, pos.y as u32)
|
||||
self.get_pixel(pos.x as u32, pos.y as u32)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Debug + Copy + Pixel + dyn_any::StaticType> Sample for ImageFrameTable<P>
|
||||
impl<P> Sample for ImageFrameTable<P>
|
||||
where
|
||||
GraphicElement: From<ImageFrame<P>>,
|
||||
P: Debug + Copy + Pixel + dyn_any::StaticType,
|
||||
P::Static: Pixel,
|
||||
GraphicElement: From<Image<P>>,
|
||||
{
|
||||
type Pixel = P;
|
||||
|
||||
|
@ -292,26 +321,11 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<P: Copy + Pixel> Bitmap for ImageFrame<P> {
|
||||
type Pixel = P;
|
||||
|
||||
fn width(&self) -> u32 {
|
||||
self.image.width()
|
||||
}
|
||||
|
||||
fn height(&self) -> u32 {
|
||||
self.image.height()
|
||||
}
|
||||
|
||||
fn get_pixel(&self, x: u32, y: u32) -> Option<Self::Pixel> {
|
||||
self.image.get_pixel(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Copy + Pixel + dyn_any::StaticType> Bitmap for ImageFrameTable<P>
|
||||
impl<P> Bitmap for ImageFrameTable<P>
|
||||
where
|
||||
P: Copy + Pixel + dyn_any::StaticType,
|
||||
P::Static: Pixel,
|
||||
GraphicElement: From<ImageFrame<P>>,
|
||||
GraphicElement: From<Image<P>>,
|
||||
{
|
||||
type Pixel = P;
|
||||
|
||||
|
@ -334,95 +348,57 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<P: Copy + Pixel> BitmapMut for ImageFrame<P> {
|
||||
impl<P> BitmapMut for ImageFrameTable<P>
|
||||
where
|
||||
P: Copy + Pixel + dyn_any::StaticType,
|
||||
P::Static: Pixel,
|
||||
GraphicElement: From<Image<P>>,
|
||||
{
|
||||
fn get_pixel_mut(&mut self, x: u32, y: u32) -> Option<&mut Self::Pixel> {
|
||||
self.image.get_pixel_mut(x, y)
|
||||
self.one_instance_mut().instance.get_pixel_mut(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Copy + Pixel + dyn_any::StaticType> BitmapMut for ImageFrameTable<P>
|
||||
where
|
||||
GraphicElement: From<ImageFrame<P>>,
|
||||
P::Static: Pixel,
|
||||
{
|
||||
fn get_pixel_mut(&mut self, x: u32, y: u32) -> Option<&mut Self::Pixel> {
|
||||
let image = self.one_instance_mut().instance;
|
||||
|
||||
BitmapMut::get_pixel_mut(image, x, y)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<P: dyn_any::StaticTypeSized + Pixel> StaticType for ImageFrame<P>
|
||||
where
|
||||
P::Static: Pixel,
|
||||
{
|
||||
type Static = ImageFrame<P::Static>;
|
||||
}
|
||||
|
||||
impl<P: Copy + Pixel> ImageFrame<P> {
|
||||
impl<P: Copy + Pixel> Image<P> {
|
||||
pub fn get_mut(&mut self, x: usize, y: usize) -> &mut P {
|
||||
&mut self.image.data[y * (self.image.width as usize) + x]
|
||||
&mut self.data[y * (self.width as usize) + x]
|
||||
}
|
||||
|
||||
/// Clamps the provided point to ((0, 0), (ImageSize.x, ImageSize.y)) and returns the closest pixel
|
||||
pub fn sample(&self, position: DVec2) -> P {
|
||||
let x = position.x.clamp(0., self.image.width as f64 - 1.) as usize;
|
||||
let y = position.y.clamp(0., self.image.height as f64 - 1.) as usize;
|
||||
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.image.data[x + y * self.image.width as usize]
|
||||
self.data[x + y * self.width as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> AsRef<ImageFrame<P>> for ImageFrame<P> {
|
||||
fn as_ref(&self) -> &ImageFrame<P> {
|
||||
impl<P: Pixel> AsRef<Image<P>> for Image<P> {
|
||||
fn as_ref(&self) -> &Image<P> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Hash + Pixel> Hash for ImageFrame<P> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
0.hash(state);
|
||||
self.image.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
/* This does not work because of missing specialization
|
||||
* so we have to manually implement this for now
|
||||
impl<S: Into<P> + Pixel, P: Pixel> From<Image<S>> for Image<P> {
|
||||
fn from(image: Image<S>) -> 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,
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
impl From<ImageFrame<Color>> for ImageFrame<SRGBA8> {
|
||||
fn from(image: ImageFrame<Color>) -> Self {
|
||||
let data = image.image.data.into_iter().map(|x| x.into()).collect();
|
||||
Self {
|
||||
image: Image {
|
||||
data,
|
||||
width: image.image.width,
|
||||
height: image.image.height,
|
||||
base64_string: None,
|
||||
},
|
||||
base64_string: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ImageFrame<SRGBA8>> for ImageFrame<Color> {
|
||||
fn from(image: ImageFrame<SRGBA8>) -> Self {
|
||||
let data = image.image.data.into_iter().map(|x| x.into()).collect();
|
||||
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 {
|
||||
image: Image {
|
||||
data,
|
||||
width: image.image.width,
|
||||
height: image.image.height,
|
||||
base64_string: None,
|
||||
},
|
||||
data,
|
||||
width: image.width,
|
||||
height: image.height,
|
||||
base64_string: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,9 +150,14 @@ fn migrate_type_descriptor_names<'de, D: serde::Deserializer<'de>>(deserializer:
|
|||
let name = match name.as_str() {
|
||||
"f32" => "f64".to_string(),
|
||||
"graphene_core::transform::Footprint" => "core::option::Option<alloc::sync::Arc<graphene_core::context::OwnedContextImpl>>".to_string(),
|
||||
"graphene_core::graphic_element::GraphicGroup" => "graphene_core::graphic_element::Instances<graphene_core::graphic_element::GraphicGroup>".to_string(),
|
||||
"graphene_core::vector::vector_data::VectorData" => "graphene_core::graphic_element::Instances<graphene_core::vector::vector_data::VectorData>".to_string(),
|
||||
"graphene_core::raster::image::ImageFrame<Color>" => "graphene_core::graphic_element::Instances<graphene_core::raster::image::ImageFrame<Color>>".to_string(),
|
||||
"graphene_core::graphic_element::GraphicGroup" => "graphene_core::instances::Instances<graphene_core::graphic_element::GraphicGroup>".to_string(),
|
||||
"graphene_core::vector::vector_data::VectorData" => "graphene_core::instances::Instances<graphene_core::vector::vector_data::VectorData>".to_string(),
|
||||
"graphene_core::raster::image::ImageFrame<Color>"
|
||||
| "graphene_core::raster::image::ImageFrame<graphene_core::raster::color::Color>"
|
||||
| "graphene_core::instances::Instances<graphene_core::raster::image::ImageFrame<Color>>"
|
||||
| "graphene_core::instances::Instances<graphene_core::raster::image::ImageFrame<graphene_core::raster::color::Color>>" => {
|
||||
"graphene_core::instances::Instances<graphene_core::raster::image::Image<graphene_core::raster::color::Color>>".to_string()
|
||||
}
|
||||
_ => name,
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue