mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-17 07:18:04 +08:00
Instance tables refactor part 6: unwrap VectorData and ImageFrame from single-row to multi-row tables (#2684)
* Start refactoring the boolean operations code * Switch to iterators in the boolean operations code * Make boolean operations work on rows of a table, not Vecs of single-row tables * Remove more .transform() * Simplify brush code * Attempt to remove .transform() by using Instance<Image<Color>> in brush code, but a regression is introduced * Improve blend_image_closure * Simplify * Remove leading underscore from type arguments * Remove .transform() from ImageFrameTable<P> and fix Mask node behavior on stencils not fully overlapping its target image * Remove more .one_instance_ref() * Fully remove .one_instance_ref() and improve the 'Combine Channels' node robustness * Fully remove .once_instance_mut() * Fix tests * Remove .one_empty_image() * Make Instances<T>::default() return an empty table for images, but still not yet vector --------- Co-authored-by: hypercube <0hypercube@gmail.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
use crate::instances::Instances;
|
||||
use crate::text::FontCache;
|
||||
use crate::transform::{Footprint, Transform, TransformMut};
|
||||
use crate::transform::Footprint;
|
||||
use crate::vector::style::ViewMode;
|
||||
use alloc::sync::Arc;
|
||||
use core::fmt::Debug;
|
||||
@@ -37,17 +37,6 @@ impl Hash for SurfaceFrame {
|
||||
}
|
||||
}
|
||||
|
||||
impl Transform for SurfaceFrame {
|
||||
fn transform(&self) -> DAffine2 {
|
||||
self.transform
|
||||
}
|
||||
}
|
||||
impl TransformMut for SurfaceFrame {
|
||||
fn transform_mut(&mut self) -> &mut DAffine2 {
|
||||
&mut self.transform
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "dyn-any")]
|
||||
unsafe impl StaticType for SurfaceFrame {
|
||||
type Static = SurfaceFrame;
|
||||
@@ -152,18 +141,6 @@ unsafe impl<T: 'static> StaticType for SurfaceHandleFrame<T> {
|
||||
type Static = SurfaceHandleFrame<T>;
|
||||
}
|
||||
|
||||
impl<T> Transform for SurfaceHandleFrame<T> {
|
||||
fn transform(&self) -> DAffine2 {
|
||||
self.transform
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TransformMut for SurfaceHandleFrame<T> {
|
||||
fn transform_mut(&mut self) -> &mut DAffine2 {
|
||||
&mut self.transform
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: think about how to automatically clean up memory
|
||||
/*
|
||||
impl<'a, Surface> Drop for SurfaceHandle<'a, Surface> {
|
||||
|
||||
@@ -277,13 +277,13 @@ pub trait GraphicElementRendered {
|
||||
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, _render_params: &RenderParams);
|
||||
fn bounding_box(&self, transform: DAffine2, include_stroke: bool) -> Option<[DVec2; 2]>;
|
||||
|
||||
// The upstream click targets for each layer are collected during the render so that they do not have to be calculated for each click detection
|
||||
/// The upstream click targets for each layer are collected during the render so that they do not have to be calculated for each click detection.
|
||||
fn add_upstream_click_targets(&self, _click_targets: &mut Vec<ClickTarget>) {}
|
||||
|
||||
// TODO: Store all click targets in a vec which contains the AABB, click target, and path
|
||||
// fn add_click_targets(&self, click_targets: &mut Vec<([DVec2; 2], ClickTarget, Vec<NodeId>)>, current_path: Option<NodeId>) {}
|
||||
|
||||
// Recursively iterate over data in the render (including groups upstream from vector data in the case of a boolean operation) to collect the footprints, click targets, and vector modify
|
||||
/// Recursively iterate over data in the render (including groups upstream from vector data in the case of a boolean operation) to collect the footprints, click targets, and vector modify.
|
||||
fn collect_metadata(&self, _metadata: &mut RenderMetadata, _footprint: Footprint, _element_id: Option<NodeId>) {}
|
||||
|
||||
fn contains_artboard(&self) -> bool {
|
||||
@@ -639,9 +639,10 @@ impl GraphicElementRendered for VectorDataTable {
|
||||
}
|
||||
|
||||
fn collect_metadata(&self, metadata: &mut RenderMetadata, mut footprint: Footprint, element_id: Option<NodeId>) {
|
||||
let instance_transform = self.transform();
|
||||
for instance in self.instance_ref_iter() {
|
||||
let instance_transform = *instance.transform;
|
||||
let instance = instance.instance;
|
||||
|
||||
for instance in self.instance_ref_iter().map(|instance| instance.instance) {
|
||||
if let Some(element_id) = element_id {
|
||||
let stroke_width = instance.style.stroke().as_ref().map_or(0., Stroke::weight);
|
||||
let filled = instance.style.fill() != &Fill::None;
|
||||
@@ -905,14 +906,15 @@ impl GraphicElementRendered for ImageFrameTable<Color> {
|
||||
}
|
||||
|
||||
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, element_id: Option<NodeId>) {
|
||||
let instance_transform = self.transform();
|
||||
|
||||
let Some(element_id) = element_id else { return };
|
||||
let subpath = Subpath::new_rect(DVec2::ZERO, DVec2::ONE);
|
||||
|
||||
metadata.click_targets.insert(element_id, vec![ClickTarget::new(subpath, 0.)]);
|
||||
metadata.upstream_footprints.insert(element_id, footprint);
|
||||
metadata.local_transforms.insert(element_id, instance_transform);
|
||||
// TODO: Find a way to handle more than one row of the graphical data table
|
||||
if let Some(image) = self.instance_ref_iter().next() {
|
||||
metadata.local_transforms.insert(element_id, *image.transform);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
||||
@@ -933,8 +935,8 @@ impl GraphicElementRendered for RasterFrame {
|
||||
fn render_to_vello(&self, scene: &mut Scene, transform: DAffine2, context: &mut RenderContext, _render_params: &RenderParams) {
|
||||
use vello::peniko;
|
||||
|
||||
let mut render_stuff = |image: vello::peniko::Image, blend_mode: crate::AlphaBlending| {
|
||||
let image_transform = transform * self.transform() * DAffine2::from_scale(1. / DVec2::new(image.width as f64, image.height as f64));
|
||||
let mut render_stuff = |image: vello::peniko::Image, instance_transform: DAffine2, blend_mode: crate::AlphaBlending| {
|
||||
let image_transform = transform * instance_transform * DAffine2::from_scale(1. / DVec2::new(image.width as f64, image.height as f64));
|
||||
let layer = blend_mode != Default::default();
|
||||
|
||||
let Some(bounds) = self.bounding_box(transform, true) else { return };
|
||||
@@ -960,7 +962,7 @@ impl GraphicElementRendered for RasterFrame {
|
||||
|
||||
let image = vello::peniko::Image::new(image.to_flat_u8().0.into(), peniko::Format::Rgba8, image.width, image.height).with_extend(peniko::Extend::Repeat);
|
||||
|
||||
render_stuff(image, *instance.alpha_blending);
|
||||
render_stuff(image, *instance.transform, *instance.alpha_blending);
|
||||
}
|
||||
}
|
||||
RasterFrame::TextureFrame(image_texture) => {
|
||||
@@ -971,15 +973,22 @@ impl GraphicElementRendered for RasterFrame {
|
||||
let id = image.data.id();
|
||||
context.resource_overrides.insert(id, instance.instance.texture.clone());
|
||||
|
||||
render_stuff(image, *instance.alpha_blending);
|
||||
render_stuff(image, *instance.transform, *instance.alpha_blending);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn bounding_box(&self, transform: DAffine2, _include_stroke: bool) -> Option<[DVec2; 2]> {
|
||||
let transform = transform * self.transform();
|
||||
(transform.matrix2.determinant() != 0.).then(|| (transform * Quad::from_box([DVec2::ZERO, DVec2::ONE])).bounding_box())
|
||||
let calculate_transform = |instance_transform| {
|
||||
let transform: DAffine2 = transform * instance_transform;
|
||||
(transform.matrix2.determinant() != 0.).then(|| (transform * Quad::from_box([DVec2::ZERO, DVec2::ONE])).bounding_box())
|
||||
};
|
||||
|
||||
match self {
|
||||
RasterFrame::ImageFrame(instances) => instances.instance_ref_iter().flat_map(|instance| calculate_transform(*instance.transform)).reduce(Quad::combine_bounds),
|
||||
RasterFrame::TextureFrame(instances) => instances.instance_ref_iter().flat_map(|instance| calculate_transform(*instance.transform)).reduce(Quad::combine_bounds),
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_metadata(&self, metadata: &mut RenderMetadata, footprint: Footprint, element_id: Option<NodeId>) {
|
||||
@@ -988,7 +997,21 @@ impl GraphicElementRendered for RasterFrame {
|
||||
let subpath = Subpath::new_rect(DVec2::ZERO, DVec2::ONE);
|
||||
metadata.click_targets.insert(element_id, vec![ClickTarget::new(subpath, 0.)]);
|
||||
metadata.upstream_footprints.insert(element_id, footprint);
|
||||
metadata.local_transforms.insert(element_id, self.transform());
|
||||
|
||||
match self {
|
||||
RasterFrame::ImageFrame(instances) => {
|
||||
// TODO: Find a way to handle more than one row of the graphical data table
|
||||
if let Some(image) = instances.instance_ref_iter().next() {
|
||||
metadata.local_transforms.insert(element_id, *image.transform);
|
||||
}
|
||||
}
|
||||
RasterFrame::TextureFrame(instances) => {
|
||||
// TODO: Find a way to handle more than one row of the graphical data table
|
||||
if let Some(image_texture) = instances.instance_ref_iter().next() {
|
||||
metadata.local_transforms.insert(element_id, *image_texture.transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_upstream_click_targets(&self, click_targets: &mut Vec<ClickTarget>) {
|
||||
@@ -1031,11 +1054,27 @@ impl GraphicElementRendered for GraphicElement {
|
||||
}
|
||||
GraphicElement::VectorData(vector_data) => {
|
||||
metadata.upstream_footprints.insert(element_id, footprint);
|
||||
metadata.local_transforms.insert(element_id, vector_data.transform());
|
||||
// TODO: Find a way to handle more than one row of the graphical data table
|
||||
if let Some(vector_data) = vector_data.instance_ref_iter().next() {
|
||||
metadata.local_transforms.insert(element_id, *vector_data.transform);
|
||||
}
|
||||
}
|
||||
GraphicElement::RasterFrame(raster_frame) => {
|
||||
metadata.upstream_footprints.insert(element_id, footprint);
|
||||
metadata.local_transforms.insert(element_id, raster_frame.transform());
|
||||
match raster_frame {
|
||||
RasterFrame::ImageFrame(instances) => {
|
||||
// TODO: Find a way to handle more than one row of images
|
||||
if let Some(image) = instances.instance_ref_iter().next() {
|
||||
metadata.local_transforms.insert(element_id, *image.transform);
|
||||
}
|
||||
}
|
||||
RasterFrame::TextureFrame(instances) => {
|
||||
// TODO: Find a way to handle more than one row of image textures
|
||||
if let Some(image_texture) = instances.instance_ref_iter().next() {
|
||||
metadata.local_transforms.insert(element_id, *image_texture.transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
use crate::application_io::TextureFrameTable;
|
||||
use crate::raster::Pixel;
|
||||
use crate::raster::image::{Image, ImageFrameTable};
|
||||
use crate::transform::{Transform, TransformMut};
|
||||
use crate::AlphaBlending;
|
||||
use crate::uuid::NodeId;
|
||||
use crate::vector::VectorDataTable;
|
||||
use crate::{AlphaBlending, GraphicElement, RasterFrame};
|
||||
use dyn_any::StaticType;
|
||||
use glam::DAffine2;
|
||||
use std::hash::Hash;
|
||||
@@ -47,26 +42,6 @@ impl<T> Instances<T> {
|
||||
self.source_node_id.push(instance.source_node_id);
|
||||
}
|
||||
|
||||
pub fn one_instance_ref(&self) -> InstanceRef<T> {
|
||||
InstanceRef {
|
||||
instance: self.instance.first().unwrap_or_else(|| panic!("ONE INSTANCE EXPECTED, FOUND {}", self.instance.len())),
|
||||
transform: self.transform.first().unwrap_or_else(|| panic!("ONE INSTANCE EXPECTED, FOUND {}", self.instance.len())),
|
||||
alpha_blending: self.alpha_blending.first().unwrap_or_else(|| panic!("ONE INSTANCE EXPECTED, FOUND {}", self.instance.len())),
|
||||
source_node_id: self.source_node_id.first().unwrap_or_else(|| panic!("ONE INSTANCE EXPECTED, FOUND {}", self.instance.len())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn one_instance_mut(&mut self) -> InstanceMut<T> {
|
||||
let length = self.instance.len();
|
||||
|
||||
InstanceMut {
|
||||
instance: self.instance.first_mut().unwrap_or_else(|| panic!("ONE INSTANCE EXPECTED, FOUND {}", length)),
|
||||
transform: self.transform.first_mut().unwrap_or_else(|| panic!("ONE INSTANCE EXPECTED, FOUND {}", length)),
|
||||
alpha_blending: self.alpha_blending.first_mut().unwrap_or_else(|| panic!("ONE INSTANCE EXPECTED, FOUND {}", length)),
|
||||
source_node_id: self.source_node_id.first_mut().unwrap_or_else(|| panic!("ONE INSTANCE EXPECTED, FOUND {}", length)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn instance_iter(self) -> impl DoubleEndedIterator<Item = Instance<T>> {
|
||||
self.instance
|
||||
.into_iter()
|
||||
@@ -81,7 +56,7 @@ impl<T> Instances<T> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn instance_ref_iter(&self) -> impl DoubleEndedIterator<Item = InstanceRef<T>> {
|
||||
pub fn instance_ref_iter(&self) -> impl DoubleEndedIterator<Item = InstanceRef<T>> + Clone {
|
||||
self.instance
|
||||
.iter()
|
||||
.zip(self.transform.iter())
|
||||
@@ -146,11 +121,8 @@ impl<T> Instances<T> {
|
||||
|
||||
impl<T: Default + Hash + 'static> Default for Instances<T> {
|
||||
fn default() -> Self {
|
||||
// TODO: Remove once all types have been converted to tables
|
||||
let converted_to_tables = [TypeId::of::<crate::Artboard>(), TypeId::of::<crate::GraphicElement>()];
|
||||
|
||||
use core::any::TypeId;
|
||||
if converted_to_tables.contains(&TypeId::of::<T>()) {
|
||||
if TypeId::of::<T>() != TypeId::of::<crate::vector::VectorData>() {
|
||||
// TODO: Remove the 'static trait bound when this special casing is removed by making all types return empty
|
||||
Self::empty()
|
||||
} else {
|
||||
@@ -188,7 +160,7 @@ fn one_source_node_id_default() -> Vec<Option<NodeId>> {
|
||||
vec![None]
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct InstanceRef<'a, T> {
|
||||
pub instance: &'a T,
|
||||
pub transform: &'a DAffine2,
|
||||
@@ -196,6 +168,20 @@ pub struct InstanceRef<'a, T> {
|
||||
pub source_node_id: &'a Option<NodeId>,
|
||||
}
|
||||
|
||||
impl<T> InstanceRef<'_, T> {
|
||||
pub fn to_instance_cloned(self) -> Instance<T>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
Instance {
|
||||
instance: self.instance.clone(),
|
||||
transform: *self.transform,
|
||||
alpha_blending: *self.alpha_blending,
|
||||
source_node_id: *self.source_node_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InstanceMut<'a, T> {
|
||||
pub instance: &'a mut T,
|
||||
@@ -204,7 +190,7 @@ pub struct InstanceMut<'a, T> {
|
||||
pub source_node_id: &'a mut Option<NodeId>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
#[derive(Copy, Clone, Default, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Instance<T> {
|
||||
pub instance: T,
|
||||
pub transform: DAffine2,
|
||||
@@ -224,64 +210,31 @@ impl<T> Instance<T> {
|
||||
source_node_id: self.source_node_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// VECTOR DATA TABLE
|
||||
impl Transform for VectorDataTable {
|
||||
fn transform(&self) -> DAffine2 {
|
||||
*self.one_instance_ref().transform
|
||||
pub fn to_instance_ref(&self) -> InstanceRef<T> {
|
||||
InstanceRef {
|
||||
instance: &self.instance,
|
||||
transform: &self.transform,
|
||||
alpha_blending: &self.alpha_blending,
|
||||
source_node_id: &self.source_node_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl TransformMut for VectorDataTable {
|
||||
fn transform_mut(&mut self) -> &mut DAffine2 {
|
||||
self.one_instance_mut().transform
|
||||
}
|
||||
}
|
||||
|
||||
// TEXTURE FRAME TABLE
|
||||
impl Transform for TextureFrameTable {
|
||||
fn transform(&self) -> DAffine2 {
|
||||
*self.one_instance_ref().transform
|
||||
pub fn to_instance_mut(&mut self) -> InstanceMut<T> {
|
||||
InstanceMut {
|
||||
instance: &mut self.instance,
|
||||
transform: &mut self.transform,
|
||||
alpha_blending: &mut self.alpha_blending,
|
||||
source_node_id: &mut self.source_node_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl TransformMut for TextureFrameTable {
|
||||
fn transform_mut(&mut self) -> &mut DAffine2 {
|
||||
self.one_instance_mut().transform
|
||||
}
|
||||
}
|
||||
|
||||
// IMAGE FRAME TABLE
|
||||
impl<P: Pixel> Transform for ImageFrameTable<P>
|
||||
where
|
||||
GraphicElement: From<Image<P>>,
|
||||
{
|
||||
fn transform(&self) -> DAffine2 {
|
||||
*self.one_instance_ref().transform
|
||||
}
|
||||
}
|
||||
impl<P: Pixel> TransformMut for ImageFrameTable<P>
|
||||
where
|
||||
GraphicElement: From<Image<P>>,
|
||||
{
|
||||
fn transform_mut(&mut self) -> &mut DAffine2 {
|
||||
self.one_instance_mut().transform
|
||||
}
|
||||
}
|
||||
|
||||
// RASTER FRAME
|
||||
impl Transform for RasterFrame {
|
||||
fn transform(&self) -> DAffine2 {
|
||||
match self {
|
||||
RasterFrame::ImageFrame(image_frame) => image_frame.transform(),
|
||||
RasterFrame::TextureFrame(texture_frame) => texture_frame.transform(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl TransformMut for RasterFrame {
|
||||
fn transform_mut(&mut self) -> &mut DAffine2 {
|
||||
match self {
|
||||
RasterFrame::ImageFrame(image_frame) => image_frame.transform_mut(),
|
||||
RasterFrame::TextureFrame(texture_frame) => texture_frame.transform_mut(),
|
||||
pub fn to_table(self) -> Instances<T> {
|
||||
Instances {
|
||||
instance: vec![self.instance],
|
||||
transform: vec![self.transform],
|
||||
alpha_blending: vec![self.alpha_blending],
|
||||
source_node_id: vec![self.source_node_id],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use glam::{DAffine2, DVec2};
|
||||
fn log_to_console<T: core::fmt::Debug>(_: impl Ctx, #[implementations(String, bool, f64, u32, u64, DVec2, VectorDataTable, DAffine2, Color, Option<Color>)] value: T) -> T {
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
// KEEP THIS `debug!()` - It acts as the output for the debug node itself
|
||||
debug!("{:#?}", value);
|
||||
log::debug!("{:#?}", value);
|
||||
value
|
||||
}
|
||||
|
||||
|
||||
@@ -586,22 +586,22 @@ impl<'i, N: for<'a> Node<'a, I> + Copy, I: 'i> Copy for TypeNode<N, I, <N as Nod
|
||||
|
||||
// Into
|
||||
pub struct IntoNode<O>(PhantomData<O>);
|
||||
impl<_O> IntoNode<_O> {
|
||||
impl<O> IntoNode<O> {
|
||||
#[cfg(feature = "alloc")]
|
||||
pub const fn new() -> Self {
|
||||
Self(core::marker::PhantomData)
|
||||
}
|
||||
}
|
||||
impl<_O> Default for IntoNode<_O> {
|
||||
impl<O> Default for IntoNode<O> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
impl<'input, I: 'input, _O: 'input> Node<'input, I> for IntoNode<_O>
|
||||
impl<'input, I: 'input, O: 'input> Node<'input, I> for IntoNode<O>
|
||||
where
|
||||
I: Into<_O> + Sync + Send,
|
||||
I: Into<O> + Sync + Send,
|
||||
{
|
||||
type Output = ::dyn_any::DynFuture<'input, _O>;
|
||||
type Output = ::dyn_any::DynFuture<'input, O>;
|
||||
|
||||
#[inline]
|
||||
fn eval(&'input self, input: I) -> Self::Output {
|
||||
|
||||
@@ -665,9 +665,9 @@ impl Blend<Color> for Option<Color> {
|
||||
}
|
||||
impl Blend<Color> for ImageFrameTable<Color> {
|
||||
fn blend(&self, under: &Self, blend_fn: impl Fn(Color, Color) -> Color) -> Self {
|
||||
let mut result = self.clone();
|
||||
let mut result_table = self.clone();
|
||||
|
||||
for (over, under) in result.instance_mut_iter().zip(under.instance_ref_iter()) {
|
||||
for (over, under) in result_table.instance_mut_iter().zip(under.instance_ref_iter()) {
|
||||
let data = over.instance.data.iter().zip(under.instance.data.iter()).map(|(a, b)| blend_fn(*a, *b)).collect();
|
||||
|
||||
*over.instance = Image {
|
||||
@@ -678,7 +678,7 @@ impl Blend<Color> for ImageFrameTable<Color> {
|
||||
};
|
||||
}
|
||||
|
||||
result
|
||||
result_table
|
||||
}
|
||||
}
|
||||
impl Blend<Color> for GradientStops {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::Color;
|
||||
use crate::graphene_core::raster::image::ImageFrameTable;
|
||||
use crate::instances::Instance;
|
||||
use crate::raster::Image;
|
||||
use crate::vector::brush_stroke::BrushStroke;
|
||||
use crate::vector::brush_stroke::BrushStyle;
|
||||
@@ -16,12 +16,12 @@ struct BrushCacheImpl {
|
||||
prev_input: Vec<BrushStroke>,
|
||||
|
||||
// The strokes that have been fully processed and blended into the background.
|
||||
#[cfg_attr(feature = "serde", serde(deserialize_with = "crate::graphene_core::raster::image::migrate_image_frame"))]
|
||||
background: ImageFrameTable<Color>,
|
||||
#[cfg_attr(feature = "serde", serde(deserialize_with = "crate::graphene_core::raster::image::migrate_image_frame"))]
|
||||
blended_image: ImageFrameTable<Color>,
|
||||
#[cfg_attr(feature = "serde", serde(deserialize_with = "crate::graphene_core::raster::image::migrate_image_frame"))]
|
||||
last_stroke_texture: ImageFrameTable<Color>,
|
||||
#[cfg_attr(feature = "serde", serde(deserialize_with = "crate::graphene_core::raster::image::migrate_image_frame_instance"))]
|
||||
background: Instance<Image<Color>>,
|
||||
#[cfg_attr(feature = "serde", serde(deserialize_with = "crate::graphene_core::raster::image::migrate_image_frame_instance"))]
|
||||
blended_image: Instance<Image<Color>>,
|
||||
#[cfg_attr(feature = "serde", serde(deserialize_with = "crate::graphene_core::raster::image::migrate_image_frame_instance"))]
|
||||
last_stroke_texture: Instance<Image<Color>>,
|
||||
|
||||
// A cache for brush textures.
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
@@ -29,9 +29,9 @@ struct BrushCacheImpl {
|
||||
}
|
||||
|
||||
impl BrushCacheImpl {
|
||||
fn compute_brush_plan(&mut self, mut background: ImageFrameTable<Color>, input: &[BrushStroke]) -> BrushPlan {
|
||||
fn compute_brush_plan(&mut self, mut background: Instance<Image<Color>>, input: &[BrushStroke]) -> BrushPlan {
|
||||
// Do background invalidation.
|
||||
if background.one_instance_ref().instance != self.background.one_instance_ref().instance {
|
||||
if background != self.background {
|
||||
self.background = background.clone();
|
||||
return BrushPlan {
|
||||
strokes: input.to_vec(),
|
||||
@@ -56,7 +56,11 @@ impl BrushCacheImpl {
|
||||
background = core::mem::take(&mut self.blended_image);
|
||||
|
||||
// Check if the first non-blended stroke is an extension of the last one.
|
||||
let mut first_stroke_texture = ImageFrameTable::one_empty_image();
|
||||
let mut first_stroke_texture = Instance {
|
||||
instance: Image::default(),
|
||||
transform: glam::DAffine2::ZERO,
|
||||
..Default::default()
|
||||
};
|
||||
let mut first_stroke_point_skip = 0;
|
||||
let strokes = input[num_blended_strokes..].to_vec();
|
||||
if !strokes.is_empty() && self.prev_input.len() > num_blended_strokes {
|
||||
@@ -80,7 +84,7 @@ impl BrushCacheImpl {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cache_results(&mut self, input: Vec<BrushStroke>, blended_image: ImageFrameTable<Color>, last_stroke_texture: ImageFrameTable<Color>) {
|
||||
pub fn cache_results(&mut self, input: Vec<BrushStroke>, blended_image: Instance<Image<Color>>, last_stroke_texture: Instance<Image<Color>>) {
|
||||
self.prev_input = input;
|
||||
self.blended_image = blended_image;
|
||||
self.last_stroke_texture = last_stroke_texture;
|
||||
@@ -95,8 +99,8 @@ impl Hash for BrushCacheImpl {
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct BrushPlan {
|
||||
pub strokes: Vec<BrushStroke>,
|
||||
pub background: ImageFrameTable<Color>,
|
||||
pub first_stroke_texture: ImageFrameTable<Color>,
|
||||
pub background: Instance<Image<Color>>,
|
||||
pub first_stroke_texture: Instance<Image<Color>>,
|
||||
pub first_stroke_point_skip: usize,
|
||||
}
|
||||
|
||||
@@ -160,12 +164,12 @@ impl BrushCache {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_brush_plan(&self, background: ImageFrameTable<Color>, input: &[BrushStroke]) -> BrushPlan {
|
||||
pub fn compute_brush_plan(&self, background: Instance<Image<Color>>, input: &[BrushStroke]) -> BrushPlan {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
inner.compute_brush_plan(background, input)
|
||||
}
|
||||
|
||||
pub fn cache_results(&self, input: Vec<BrushStroke>, blended_image: ImageFrameTable<Color>, last_stroke_texture: ImageFrameTable<Color>) {
|
||||
pub fn cache_results(&self, input: Vec<BrushStroke>, blended_image: Instance<Image<Color>>, last_stroke_texture: Instance<Image<Color>>) {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
inner.cache_results(input, blended_image, last_stroke_texture)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ use super::discrete_srgb::float_to_srgb_u8;
|
||||
use crate::AlphaBlending;
|
||||
use crate::GraphicElement;
|
||||
use crate::instances::{Instance, Instances};
|
||||
use crate::transform::TransformMut;
|
||||
use alloc::vec::Vec;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use dyn_any::StaticType;
|
||||
@@ -232,7 +231,7 @@ pub fn migrate_image_frame<'de, D: serde::Deserializer<'de>>(deserializer: D) ->
|
||||
fn from(element: GraphicElement) -> Self {
|
||||
match element {
|
||||
GraphicElement::RasterFrame(crate::RasterFrame::ImageFrame(image)) => Self {
|
||||
image: image.one_instance_ref().instance.clone(),
|
||||
image: image.instance_ref_iter().next().unwrap().instance.clone(),
|
||||
},
|
||||
_ => panic!("Expected Image, found {:?}", element),
|
||||
}
|
||||
@@ -270,27 +269,90 @@ pub fn migrate_image_frame<'de, D: serde::Deserializer<'de>>(deserializer: D) ->
|
||||
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(image);
|
||||
*image_frame_table.one_instance_mut().transform = transform;
|
||||
*image_frame_table.one_instance_mut().alpha_blending = alpha_blending;
|
||||
*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) => ImageFrameTable::new(image_frame.one_instance_ref().instance.image.clone()),
|
||||
FormatVersions::ImageFrame(image_frame) => ImageFrameTable::new(image_frame.instance_ref_iter().next().unwrap().instance.image.clone()),
|
||||
FormatVersions::ImageFrameTable(image_frame_table) => image_frame_table,
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Eventually remove this migration document upgrade code
|
||||
pub fn migrate_image_frame_instance<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Instance<Image<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.instance_ref_iter().next().unwrap().instance.clone(),
|
||||
},
|
||||
_ => panic!("Expected Image, found {:?}", element),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "dyn-any")]
|
||||
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> {
|
||||
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>),
|
||||
ImageInstance(Instance<Image<Color>>),
|
||||
}
|
||||
|
||||
Ok(match FormatVersions::deserialize(deserializer)? {
|
||||
FormatVersions::Image(image) => Instance {
|
||||
instance: image,
|
||||
..Default::default()
|
||||
},
|
||||
FormatVersions::OldImageFrame(image_frame_with_transform_and_blending) => Instance {
|
||||
instance: 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: image_frame.instance_ref_iter().next().unwrap().instance.image.clone(),
|
||||
..Default::default()
|
||||
},
|
||||
FormatVersions::ImageFrameTable(image_frame_table) => image_frame_table.instance_iter().next().unwrap_or_default(),
|
||||
FormatVersions::ImageInstance(image_instance) => image_instance,
|
||||
})
|
||||
}
|
||||
|
||||
// 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 one_empty_image() -> Self {
|
||||
let mut result = Self::new(Image::default());
|
||||
*result.transform_mut() = DAffine2::ZERO;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Debug + Copy + Pixel> Sample for Image<P> {
|
||||
type Pixel = P;
|
||||
|
||||
@@ -305,62 +367,6 @@ impl<P: Debug + Copy + Pixel> Sample for Image<P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Sample for ImageFrameTable<P>
|
||||
where
|
||||
P: Debug + Copy + Pixel,
|
||||
GraphicElement: From<Image<P>>,
|
||||
{
|
||||
type Pixel = P;
|
||||
|
||||
// TODO: Improve sampling logic
|
||||
#[inline(always)]
|
||||
fn sample(&self, pos: DVec2, area: DVec2) -> Option<Self::Pixel> {
|
||||
let image_transform = self.one_instance_ref().transform;
|
||||
let image = self.one_instance_ref().instance;
|
||||
|
||||
let image_size = DVec2::new(image.width() as f64, image.height() as f64);
|
||||
let pos = (DAffine2::from_scale(image_size) * image_transform.inverse()).transform_point2(pos);
|
||||
|
||||
Sample::sample(image, pos, area)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Bitmap for ImageFrameTable<P>
|
||||
where
|
||||
P: Copy + Pixel,
|
||||
GraphicElement: From<Image<P>>,
|
||||
{
|
||||
type Pixel = P;
|
||||
|
||||
fn width(&self) -> u32 {
|
||||
let image = self.one_instance_ref().instance;
|
||||
|
||||
image.width()
|
||||
}
|
||||
|
||||
fn height(&self) -> u32 {
|
||||
let image = self.one_instance_ref().instance;
|
||||
|
||||
image.height()
|
||||
}
|
||||
|
||||
fn get_pixel(&self, x: u32, y: u32) -> Option<Self::Pixel> {
|
||||
let image = self.one_instance_ref().instance;
|
||||
|
||||
image.get_pixel(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> BitmapMut for ImageFrameTable<P>
|
||||
where
|
||||
P: Copy + Pixel,
|
||||
GraphicElement: From<Image<P>>,
|
||||
{
|
||||
fn get_pixel_mut(&mut self, x: u32, y: u32) -> Option<&mut Self::Pixel> {
|
||||
self.one_instance_mut().instance.get_pixel_mut(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
@@ -266,11 +266,11 @@ pub struct DynAnyNode<I, O, Node> {
|
||||
_o: PhantomData<O>,
|
||||
}
|
||||
|
||||
impl<'input, _I, _O, N> Node<'input, Any<'input>> for DynAnyNode<_I, _O, N>
|
||||
impl<'input, I, O, N> Node<'input, Any<'input>> for DynAnyNode<I, O, N>
|
||||
where
|
||||
_I: 'input + dyn_any::StaticType + WasmNotSend,
|
||||
_O: 'input + dyn_any::StaticType + WasmNotSend,
|
||||
N: 'input + Node<'input, _I, Output = DynFuture<'input, _O>>,
|
||||
I: 'input + dyn_any::StaticType + WasmNotSend,
|
||||
O: 'input + dyn_any::StaticType + WasmNotSend,
|
||||
N: 'input + Node<'input, I, Output = DynFuture<'input, O>>,
|
||||
{
|
||||
type Output = FutureAny<'input>;
|
||||
#[inline]
|
||||
@@ -294,11 +294,11 @@ where
|
||||
self.node.serialize()
|
||||
}
|
||||
}
|
||||
impl<'input, _I, _O, N> DynAnyNode<_I, _O, N>
|
||||
impl<'input, I, O, N> DynAnyNode<I, O, N>
|
||||
where
|
||||
_I: 'input + dyn_any::StaticType,
|
||||
_O: 'input + dyn_any::StaticType,
|
||||
N: 'input + Node<'input, _I, Output = DynFuture<'input, _O>>,
|
||||
I: 'input + dyn_any::StaticType,
|
||||
O: 'input + dyn_any::StaticType,
|
||||
N: 'input + Node<'input, I, Output = DynFuture<'input, O>>,
|
||||
{
|
||||
pub const fn new(node: N) -> Self {
|
||||
Self {
|
||||
|
||||
@@ -129,7 +129,9 @@ mod test {
|
||||
.instance
|
||||
.as_vector_data()
|
||||
.unwrap()
|
||||
.one_instance_ref()
|
||||
.instance_ref_iter()
|
||||
.next()
|
||||
.unwrap()
|
||||
.instance
|
||||
.bounding_box_with_transform(*instanced.transform)
|
||||
.unwrap();
|
||||
|
||||
@@ -253,9 +253,9 @@ fn isometric_grid_test() {
|
||||
|
||||
// Works properly
|
||||
let grid = grid((), (), GridType::Isometric, 10., (30., 30.).into(), 5, 5);
|
||||
assert_eq!(grid.one_instance_ref().instance.point_domain.ids().len(), 5 * 5);
|
||||
assert_eq!(grid.one_instance_ref().instance.segment_bezier_iter().count(), 4 * 5 + 4 * 9);
|
||||
for (_, bezier, _, _) in grid.one_instance_ref().instance.segment_bezier_iter() {
|
||||
assert_eq!(grid.instance_ref_iter().next().unwrap().instance.point_domain.ids().len(), 5 * 5);
|
||||
assert_eq!(grid.instance_ref_iter().next().unwrap().instance.segment_bezier_iter().count(), 4 * 5 + 4 * 9);
|
||||
for (_, bezier, _, _) in grid.instance_ref_iter().next().unwrap().instance.segment_bezier_iter() {
|
||||
assert_eq!(bezier.handles, bezier_rs::BezierHandles::Linear);
|
||||
assert!(
|
||||
((bezier.start - bezier.end).length() - 10.).abs() < 1e-5,
|
||||
@@ -268,9 +268,9 @@ fn isometric_grid_test() {
|
||||
#[test]
|
||||
fn skew_isometric_grid_test() {
|
||||
let grid = grid((), (), GridType::Isometric, 10., (40., 30.).into(), 5, 5);
|
||||
assert_eq!(grid.one_instance_ref().instance.point_domain.ids().len(), 5 * 5);
|
||||
assert_eq!(grid.one_instance_ref().instance.segment_bezier_iter().count(), 4 * 5 + 4 * 9);
|
||||
for (_, bezier, _, _) in grid.one_instance_ref().instance.segment_bezier_iter() {
|
||||
assert_eq!(grid.instance_ref_iter().next().unwrap().instance.point_domain.ids().len(), 5 * 5);
|
||||
assert_eq!(grid.instance_ref_iter().next().unwrap().instance.segment_bezier_iter().count(), 4 * 5 + 4 * 9);
|
||||
for (_, bezier, _, _) in grid.instance_ref_iter().next().unwrap().instance.segment_bezier_iter() {
|
||||
assert_eq!(bezier.handles, bezier_rs::BezierHandles::Linear);
|
||||
let vector = bezier.start - bezier.end;
|
||||
let angle = (vector.angle_to(DVec2::X).to_degrees() + 180.) % 180.;
|
||||
|
||||
@@ -60,8 +60,8 @@ pub fn migrate_vector_data<'de, D: serde::Deserializer<'de>>(deserializer: D) ->
|
||||
region_domain: old.region_domain,
|
||||
upstream_graphic_group: old.upstream_graphic_group,
|
||||
});
|
||||
*vector_data_table.one_instance_mut().transform = old.transform;
|
||||
*vector_data_table.one_instance_mut().alpha_blending = old.alpha_blending;
|
||||
*vector_data_table.instance_mut_iter().next().unwrap().transform = old.transform;
|
||||
*vector_data_table.instance_mut_iter().next().unwrap().alpha_blending = old.alpha_blending;
|
||||
vector_data_table
|
||||
}
|
||||
EitherFormat::VectorDataTable(vector_data_table) => vector_data_table,
|
||||
|
||||
@@ -1193,11 +1193,16 @@ async fn flatten_vector_elements(_: impl Ctx, graphic_group_input: GraphicGroupT
|
||||
}
|
||||
}
|
||||
|
||||
let mut output_table = VectorDataTable::default();
|
||||
let Some(mut output) = output_table.instance_mut_iter().next() else { return output_table };
|
||||
// Create a table with one instance of an empty VectorData, then get a mutable reference to it which we append flattened subpaths to
|
||||
let mut output_table = VectorDataTable::new(VectorData::default());
|
||||
let Some(mut output) = output_table.instance_mut_iter().next() else {
|
||||
return output_table;
|
||||
};
|
||||
|
||||
// Flatten the graphic group input into the output VectorData instance
|
||||
flatten_group(&graphic_group_input, &mut output);
|
||||
|
||||
// Return the single-row VectorDataTable containing the flattened VectorData subpaths
|
||||
output_table
|
||||
}
|
||||
|
||||
@@ -1217,6 +1222,8 @@ async fn sample_points(_: impl Ctx, vector_data: VectorDataTable, spacing: f64,
|
||||
style: std::mem::take(&mut vector_data_instance.instance.style),
|
||||
upstream_graphic_group: std::mem::take(&mut vector_data_instance.instance.upstream_graphic_group),
|
||||
};
|
||||
// Transfer the stroke transform from the input vector data to the result.
|
||||
result.style.set_stroke_transform(vector_data_instance.transform);
|
||||
|
||||
// Using `stroke_bezpath_iter` so that the `subpath_segment_lengths` is aligned to the segments of each bezpath.
|
||||
// So we can index into `subpath_segment_lengths` to get the length of the segments.
|
||||
@@ -1249,10 +1256,6 @@ async fn sample_points(_: impl Ctx, vector_data: VectorDataTable, spacing: f64,
|
||||
result.append_bezpath(sample_bezpath);
|
||||
}
|
||||
|
||||
// Transfer the style from the input vector data to the result.
|
||||
result.style = vector_data_instance.instance.style;
|
||||
result.style.set_stroke_transform(vector_data_instance.transform);
|
||||
|
||||
vector_data_instance.instance = result;
|
||||
result_table.push(vector_data_instance);
|
||||
}
|
||||
@@ -1459,11 +1462,6 @@ async fn spline(_: impl Ctx, vector_data: VectorDataTable) -> VectorDataTable {
|
||||
result_table.push(vector_data_instance);
|
||||
}
|
||||
|
||||
// TODO: remove after pt6 of instance table refactor
|
||||
if result_table.is_empty() {
|
||||
return VectorDataTable::new(VectorData::empty());
|
||||
}
|
||||
|
||||
result_table
|
||||
}
|
||||
|
||||
@@ -1732,14 +1730,14 @@ fn bevel(_: impl Ctx, source: VectorDataTable, #[default(10.)] distance: Length)
|
||||
|
||||
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
||||
fn close_path(_: impl Ctx, source: VectorDataTable) -> VectorDataTable {
|
||||
let mut new_table = VectorDataTable::empty();
|
||||
let mut result_table = VectorDataTable::empty();
|
||||
|
||||
for mut source_instance in source.instance_iter() {
|
||||
source_instance.instance.close_subpaths();
|
||||
new_table.push(source_instance);
|
||||
result_table.push(source_instance);
|
||||
}
|
||||
|
||||
new_table
|
||||
result_table
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
|
||||
|
||||
Reference in New Issue
Block a user