mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-20 03:18:06 +08:00
Rename Instances<T> to Table<T> and the "instance" terminology to "TableRow" and "element" (#2981)
* Instances -> Table * instances.rs -> table.rs * Rename occurrances of the word "instances" * .instance -> .element * Instance* -> TableRow* * Rename Table and TableRow methods to not say "instance" * Remove presumed unused serde defaults now that tables default to length 0 not 1 * Rename occurences of the word "instance" * Un-alias the RasterDataTable<Storage>, VectorDataTable, GraphicGroupTable, ArtboardGroupTable typedefs * Move artboard type and node code out of graphic_element.rs to a new artboard.rs * Organize the TaggedValues * Fix tests * Fix prior regression with Image Value node not upgrading
This commit is contained in:
@@ -10,8 +10,8 @@ impl Adjust<Color> for Color {
|
||||
}
|
||||
impl Adjust<Color> for Option<Color> {
|
||||
fn adjust(&mut self, map_fn: impl Fn(&Color) -> Color) {
|
||||
if let Some(v) = self {
|
||||
*v = map_fn(v)
|
||||
if let Some(color) = self {
|
||||
*color = map_fn(color)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,19 +20,21 @@ impl Adjust<Color> for Option<Color> {
|
||||
mod adjust_std {
|
||||
use super::*;
|
||||
use graphene_core::gradient::GradientStops;
|
||||
use graphene_core::raster_types::{CPU, RasterDataTable};
|
||||
use graphene_core::raster_types::{CPU, Raster};
|
||||
use graphene_core::table::Table;
|
||||
|
||||
impl Adjust<Color> for GradientStops {
|
||||
fn adjust(&mut self, map_fn: impl Fn(&Color) -> Color) {
|
||||
for (_pos, c) in self.iter_mut() {
|
||||
*c = map_fn(c);
|
||||
for (_, color) in self.iter_mut() {
|
||||
*color = map_fn(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Adjust<Color> for RasterDataTable<CPU> {
|
||||
impl Adjust<Color> for Table<Raster<CPU>> {
|
||||
fn adjust(&mut self, map_fn: impl Fn(&Color) -> Color) {
|
||||
for instance in self.instance_mut_iter() {
|
||||
for c in instance.instance.data_mut().data.iter_mut() {
|
||||
*c = map_fn(c);
|
||||
for row in self.iter_mut() {
|
||||
for color in row.element.data_mut().data.iter_mut() {
|
||||
*color = map_fn(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ use core::fmt::Debug;
|
||||
#[cfg(feature = "std")]
|
||||
use graphene_core::gradient::GradientStops;
|
||||
#[cfg(feature = "std")]
|
||||
use graphene_core::raster_types::{CPU, RasterDataTable};
|
||||
use graphene_core::raster_types::{CPU, Raster};
|
||||
use graphene_core::table::Table;
|
||||
use graphene_core_shaders::color::Color;
|
||||
use graphene_core_shaders::context::Ctx;
|
||||
use graphene_core_shaders::registry::types::{Angle, Percentage, SignedPercentage};
|
||||
@@ -44,7 +45,7 @@ fn luminance<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Color,
|
||||
RasterDataTable<CPU>,
|
||||
Table<Raster<CPU>>,
|
||||
GradientStops,
|
||||
)]
|
||||
mut input: T,
|
||||
@@ -68,7 +69,7 @@ fn gamma_correction<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Color,
|
||||
RasterDataTable<CPU>,
|
||||
Table<Raster<CPU>>,
|
||||
GradientStops,
|
||||
)]
|
||||
mut input: T,
|
||||
@@ -88,7 +89,7 @@ fn extract_channel<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Color,
|
||||
RasterDataTable<CPU>,
|
||||
Table<Raster<CPU>>,
|
||||
GradientStops,
|
||||
)]
|
||||
mut input: T,
|
||||
@@ -111,7 +112,7 @@ fn make_opaque<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Color,
|
||||
RasterDataTable<CPU>,
|
||||
Table<Raster<CPU>>,
|
||||
GradientStops,
|
||||
)]
|
||||
mut input: T,
|
||||
@@ -136,7 +137,7 @@ fn brightness_contrast<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Color,
|
||||
RasterDataTable<CPU>,
|
||||
Table<Raster<CPU>>,
|
||||
GradientStops,
|
||||
)]
|
||||
mut input: T,
|
||||
@@ -225,7 +226,7 @@ fn levels<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Color,
|
||||
RasterDataTable<CPU>,
|
||||
Table<Raster<CPU>>,
|
||||
GradientStops,
|
||||
)]
|
||||
mut image: T,
|
||||
@@ -292,7 +293,7 @@ async fn black_and_white<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Color,
|
||||
RasterDataTable<CPU>,
|
||||
Table<Raster<CPU>>,
|
||||
GradientStops,
|
||||
)]
|
||||
mut image: T,
|
||||
@@ -364,7 +365,7 @@ async fn hue_saturation<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Color,
|
||||
RasterDataTable<CPU>,
|
||||
Table<Raster<CPU>>,
|
||||
GradientStops,
|
||||
)]
|
||||
mut input: T,
|
||||
@@ -398,7 +399,7 @@ async fn invert<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Color,
|
||||
RasterDataTable<CPU>,
|
||||
Table<Raster<CPU>>,
|
||||
GradientStops,
|
||||
)]
|
||||
mut input: T,
|
||||
@@ -420,7 +421,7 @@ async fn threshold<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Color,
|
||||
RasterDataTable<CPU>,
|
||||
Table<Raster<CPU>>,
|
||||
GradientStops,
|
||||
)]
|
||||
mut image: T,
|
||||
@@ -465,7 +466,7 @@ async fn vibrance<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Color,
|
||||
RasterDataTable<CPU>,
|
||||
Table<Raster<CPU>>,
|
||||
GradientStops,
|
||||
)]
|
||||
mut image: T,
|
||||
@@ -630,7 +631,7 @@ async fn channel_mixer<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Color,
|
||||
RasterDataTable<CPU>,
|
||||
Table<Raster<CPU>>,
|
||||
GradientStops,
|
||||
)]
|
||||
mut image: T,
|
||||
@@ -758,7 +759,7 @@ async fn selective_color<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Color,
|
||||
RasterDataTable<CPU>,
|
||||
Table<Raster<CPU>>,
|
||||
GradientStops,
|
||||
)]
|
||||
mut image: T,
|
||||
@@ -900,7 +901,7 @@ async fn posterize<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Color,
|
||||
RasterDataTable<CPU>,
|
||||
Table<Raster<CPU>>,
|
||||
GradientStops,
|
||||
)]
|
||||
mut input: T,
|
||||
@@ -933,7 +934,7 @@ async fn exposure<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Color,
|
||||
RasterDataTable<CPU>,
|
||||
Table<Raster<CPU>>,
|
||||
GradientStops,
|
||||
)]
|
||||
mut input: T,
|
||||
|
||||
@@ -2,7 +2,8 @@ use crate::adjust::Adjust;
|
||||
#[cfg(feature = "std")]
|
||||
use graphene_core::gradient::GradientStops;
|
||||
#[cfg(feature = "std")]
|
||||
use graphene_core::raster_types::{CPU, RasterDataTable};
|
||||
use graphene_core::raster_types::{CPU, Raster};
|
||||
use graphene_core::table::Table;
|
||||
use graphene_core_shaders::Ctx;
|
||||
use graphene_core_shaders::blending::BlendMode;
|
||||
use graphene_core_shaders::color::{Color, Pixel};
|
||||
@@ -32,16 +33,18 @@ mod blend_std {
|
||||
use core::cmp::Ordering;
|
||||
use graphene_core::raster::Image;
|
||||
use graphene_core::raster_types::Raster;
|
||||
impl Blend<Color> for RasterDataTable<CPU> {
|
||||
use graphene_core::table::Table;
|
||||
|
||||
impl Blend<Color> for Table<Raster<CPU>> {
|
||||
fn blend(&self, under: &Self, blend_fn: impl Fn(Color, Color) -> Color) -> Self {
|
||||
let mut result_table = self.clone();
|
||||
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();
|
||||
for (over, under) in result_table.iter_mut().zip(under.iter_ref()) {
|
||||
let data = over.element.data.iter().zip(under.element.data.iter()).map(|(a, b)| blend_fn(*a, *b)).collect();
|
||||
|
||||
*over.instance = Raster::new_cpu(Image {
|
||||
*over.element = Raster::new_cpu(Image {
|
||||
data,
|
||||
width: over.instance.width,
|
||||
height: over.instance.height,
|
||||
width: over.element.width,
|
||||
height: over.element.height,
|
||||
base64_string: None,
|
||||
});
|
||||
}
|
||||
@@ -124,14 +127,14 @@ async fn blend<T: Blend<Color> + Send>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Color,
|
||||
RasterDataTable<CPU>,
|
||||
Table<Raster<CPU>>,
|
||||
GradientStops,
|
||||
)]
|
||||
over: T,
|
||||
#[expose]
|
||||
#[implementations(
|
||||
Color,
|
||||
RasterDataTable<CPU>,
|
||||
Table<Raster<CPU>>,
|
||||
GradientStops,
|
||||
)]
|
||||
under: T,
|
||||
@@ -146,7 +149,7 @@ fn color_overlay<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Color,
|
||||
RasterDataTable<CPU>,
|
||||
Table<Raster<CPU>>,
|
||||
GradientStops,
|
||||
)]
|
||||
mut image: T,
|
||||
@@ -185,7 +188,8 @@ mod test {
|
||||
use graphene_core::blending::BlendMode;
|
||||
use graphene_core::color::Color;
|
||||
use graphene_core::raster::image::Image;
|
||||
use graphene_core::raster_types::{Raster, RasterDataTable};
|
||||
use graphene_core::raster_types::Raster;
|
||||
use graphene_core::table::Table;
|
||||
|
||||
#[tokio::test]
|
||||
async fn color_overlay_multiply() {
|
||||
@@ -198,8 +202,8 @@ mod test {
|
||||
// 100% of the output should come from the multiplied value
|
||||
let opacity = 100_f64;
|
||||
|
||||
let result = super::color_overlay((), RasterDataTable::new(Raster::new_cpu(image.clone())), overlay_color, BlendMode::Multiply, opacity);
|
||||
let result = result.instance_ref_iter().next().unwrap().instance;
|
||||
let result = super::color_overlay((), Table::new_from_element(Raster::new_cpu(image.clone())), overlay_color, BlendMode::Multiply, opacity);
|
||||
let result = result.iter_ref().next().unwrap().element;
|
||||
|
||||
// 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.data[0], Color::from_rgbaf32_unchecked(0., image_color.g(), 0., image_color.a()));
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
use graphene_core::context::Ctx;
|
||||
use graphene_core::raster::image::Image;
|
||||
use graphene_core::raster_types::{CPU, Raster, RasterDataTable};
|
||||
use graphene_core::raster_types::{CPU, Raster};
|
||||
use graphene_core::registry::types::Percentage;
|
||||
use graphene_core::table::Table;
|
||||
use image::{DynamicImage, GenericImage, GenericImageView, GrayImage, ImageBuffer, Luma, Rgba, RgbaImage};
|
||||
use ndarray::{Array2, ArrayBase, Dim, OwnedRepr};
|
||||
use std::cmp::{max, min};
|
||||
|
||||
#[node_macro::node(category("Raster: Filter"))]
|
||||
async fn dehaze(_: impl Ctx, image_frame: RasterDataTable<CPU>, strength: Percentage) -> RasterDataTable<CPU> {
|
||||
async fn dehaze(_: impl Ctx, image_frame: Table<Raster<CPU>>, strength: Percentage) -> Table<Raster<CPU>> {
|
||||
image_frame
|
||||
.instance_iter()
|
||||
.map(|mut image_frame_instance| {
|
||||
let image = image_frame_instance.instance;
|
||||
.iter()
|
||||
.map(|mut row| {
|
||||
let image = row.element;
|
||||
// Prepare the image data for processing
|
||||
let image_data = bytemuck::cast_vec(image.data.clone());
|
||||
let image_buffer = image::Rgba32FImage::from_raw(image.width, image.height, image_data).expect("Failed to convert internal image format into image-rs data type.");
|
||||
@@ -30,8 +31,8 @@ async fn dehaze(_: impl Ctx, image_frame: RasterDataTable<CPU>, strength: Percen
|
||||
base64_string: None,
|
||||
};
|
||||
|
||||
image_frame_instance.instance = Raster::new_cpu(dehazed_image);
|
||||
image_frame_instance
|
||||
row.element = Raster::new_cpu(dehazed_image);
|
||||
row
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -2,15 +2,16 @@ use graphene_core::color::Color;
|
||||
use graphene_core::context::Ctx;
|
||||
use graphene_core::raster::image::Image;
|
||||
use graphene_core::raster::{Bitmap, BitmapMut};
|
||||
use graphene_core::raster_types::{CPU, Raster, RasterDataTable};
|
||||
use graphene_core::raster_types::{CPU, Raster};
|
||||
use graphene_core::registry::types::PixelLength;
|
||||
use graphene_core::table::Table;
|
||||
|
||||
/// Blurs the image with a Gaussian or blur kernel filter.
|
||||
#[node_macro::node(category("Raster: Filter"))]
|
||||
async fn blur(
|
||||
_: impl Ctx,
|
||||
/// The image to be blurred.
|
||||
image_frame: RasterDataTable<CPU>,
|
||||
image_frame: Table<Raster<CPU>>,
|
||||
/// The radius of the blur kernel.
|
||||
#[range((0., 100.))]
|
||||
#[hard_min(0.)]
|
||||
@@ -19,11 +20,11 @@ async fn blur(
|
||||
box_blur: bool,
|
||||
/// Opt to incorrectly apply the filter with color calculations in gamma space for compatibility with the results from other software.
|
||||
gamma: bool,
|
||||
) -> RasterDataTable<CPU> {
|
||||
) -> Table<Raster<CPU>> {
|
||||
image_frame
|
||||
.instance_iter()
|
||||
.map(|mut image_instance| {
|
||||
let image = image_instance.instance.clone();
|
||||
.iter()
|
||||
.map(|mut row| {
|
||||
let image = row.element.clone();
|
||||
|
||||
// Run blur algorithm
|
||||
let blurred_image = if radius < 0.1 {
|
||||
@@ -35,8 +36,8 @@ async fn blur(
|
||||
Raster::new_cpu(gaussian_blur_algorithm(image.into_data(), radius, gamma))
|
||||
};
|
||||
|
||||
image_instance.instance = blurred_image;
|
||||
image_instance
|
||||
row.element = blurred_image;
|
||||
row
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
use crate::adjust::Adjust;
|
||||
use graphene_core::gradient::GradientStops;
|
||||
use graphene_core::raster_types::{CPU, RasterDataTable};
|
||||
use graphene_core::raster_types::{CPU, Raster};
|
||||
use graphene_core::table::Table;
|
||||
use graphene_core::{Color, Ctx};
|
||||
|
||||
// Aims for interoperable compatibility with:
|
||||
@@ -13,7 +14,7 @@ async fn gradient_map<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Color,
|
||||
RasterDataTable<CPU>,
|
||||
Table<Raster<CPU>>,
|
||||
GradientStops,
|
||||
)]
|
||||
mut image: T,
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use graphene_core::color::Color;
|
||||
use graphene_core::context::Ctx;
|
||||
use graphene_core::raster_types::{CPU, RasterDataTable};
|
||||
use graphene_core::raster_types::{CPU, Raster};
|
||||
use graphene_core::table::Table;
|
||||
|
||||
#[node_macro::node(category("Color"))]
|
||||
async fn image_color_palette(
|
||||
_: impl Ctx,
|
||||
image: RasterDataTable<CPU>,
|
||||
image: Table<Raster<CPU>>,
|
||||
#[hard_min(1.)]
|
||||
#[soft_max(28.)]
|
||||
max_size: u32,
|
||||
@@ -17,8 +18,8 @@ async fn image_color_palette(
|
||||
let mut histogram: Vec<usize> = vec![0; (bins + 1.) as usize];
|
||||
let mut colors: Vec<Vec<Color>> = vec![vec![]; (bins + 1.) as usize];
|
||||
|
||||
for image_instance in image.instance_ref_iter() {
|
||||
for pixel in image_instance.instance.data.iter() {
|
||||
for row in image.iter_ref() {
|
||||
for pixel in row.element.data.iter() {
|
||||
let r = pixel.r() * GRID;
|
||||
let g = pixel.g() * GRID;
|
||||
let b = pixel.b() * GRID;
|
||||
@@ -66,13 +67,13 @@ async fn image_color_palette(
|
||||
mod test {
|
||||
use super::*;
|
||||
use graphene_core::raster::image::Image;
|
||||
use graphene_core::raster_types::{Raster, RasterDataTable};
|
||||
use graphene_core::raster_types::Raster;
|
||||
|
||||
#[test]
|
||||
fn test_image_color_palette() {
|
||||
let result = image_color_palette(
|
||||
(),
|
||||
RasterDataTable::new(Raster::new_cpu(Image {
|
||||
Table::new_from_element(Raster::new_cpu(Image {
|
||||
width: 100,
|
||||
height: 100,
|
||||
data: vec![Color::from_rgbaf32(0., 0., 0., 1.).unwrap(); 10000],
|
||||
|
||||
@@ -6,11 +6,11 @@ use graphene_core::blending::AlphaBlending;
|
||||
use graphene_core::color::Color;
|
||||
use graphene_core::color::{Alpha, AlphaMut, Channel, LinearChannel, Luminance, RGBMut};
|
||||
use graphene_core::context::{Ctx, ExtractFootprint};
|
||||
use graphene_core::instances::Instance;
|
||||
use graphene_core::math::bbox::Bbox;
|
||||
use graphene_core::raster::image::Image;
|
||||
use graphene_core::raster::{Bitmap, BitmapMut};
|
||||
use graphene_core::raster_types::{CPU, Raster, RasterDataTable};
|
||||
use graphene_core::raster_types::{CPU, Raster};
|
||||
use graphene_core::table::{Table, TableRow};
|
||||
use graphene_core::transform::Transform;
|
||||
use rand::prelude::*;
|
||||
use rand_chacha::ChaCha8Rng;
|
||||
@@ -30,12 +30,12 @@ impl From<std::io::Error> for Error {
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Debug: Raster"))]
|
||||
pub fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: RasterDataTable<CPU>) -> RasterDataTable<CPU> {
|
||||
pub fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: Table<Raster<CPU>>) -> Table<Raster<CPU>> {
|
||||
image_frame
|
||||
.instance_iter()
|
||||
.filter_map(|mut image_frame_instance| {
|
||||
let image_frame_transform = image_frame_instance.transform;
|
||||
let image = image_frame_instance.instance;
|
||||
.iter()
|
||||
.filter_map(|mut row| {
|
||||
let image_frame_transform = row.transform;
|
||||
let image = row.element;
|
||||
|
||||
// Resize the image using the image crate
|
||||
let data = bytemuck::cast_vec(image.data.clone());
|
||||
@@ -87,9 +87,9 @@ pub fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: Rast
|
||||
|
||||
let new_transform = image_frame_transform * DAffine2::from_translation(offset) * DAffine2::from_scale(size);
|
||||
|
||||
image_frame_instance.transform = new_transform;
|
||||
image_frame_instance.instance = Raster::new_cpu(image);
|
||||
Some(image_frame_instance)
|
||||
row.transform = new_transform;
|
||||
row.element = Raster::new_cpu(image);
|
||||
Some(row)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@@ -98,28 +98,28 @@ pub fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: Rast
|
||||
pub fn combine_channels(
|
||||
_: impl Ctx,
|
||||
_primary: (),
|
||||
#[expose] red: RasterDataTable<CPU>,
|
||||
#[expose] green: RasterDataTable<CPU>,
|
||||
#[expose] blue: RasterDataTable<CPU>,
|
||||
#[expose] alpha: RasterDataTable<CPU>,
|
||||
) -> RasterDataTable<CPU> {
|
||||
#[expose] red: Table<Raster<CPU>>,
|
||||
#[expose] green: Table<Raster<CPU>>,
|
||||
#[expose] blue: Table<Raster<CPU>>,
|
||||
#[expose] alpha: Table<Raster<CPU>>,
|
||||
) -> Table<Raster<CPU>> {
|
||||
let max_len = red.len().max(green.len()).max(blue.len()).max(alpha.len());
|
||||
let red = red.instance_iter().map(Some).chain(std::iter::repeat(None)).take(max_len);
|
||||
let green = green.instance_iter().map(Some).chain(std::iter::repeat(None)).take(max_len);
|
||||
let blue = blue.instance_iter().map(Some).chain(std::iter::repeat(None)).take(max_len);
|
||||
let alpha = alpha.instance_iter().map(Some).chain(std::iter::repeat(None)).take(max_len);
|
||||
let red = red.iter().map(Some).chain(std::iter::repeat(None)).take(max_len);
|
||||
let green = green.iter().map(Some).chain(std::iter::repeat(None)).take(max_len);
|
||||
let blue = blue.iter().map(Some).chain(std::iter::repeat(None)).take(max_len);
|
||||
let alpha = alpha.iter().map(Some).chain(std::iter::repeat(None)).take(max_len);
|
||||
|
||||
red.zip(green)
|
||||
.zip(blue)
|
||||
.zip(alpha)
|
||||
.filter_map(|(((red, green), blue), alpha)| {
|
||||
// Turn any default zero-sized image instances into None
|
||||
let red = red.filter(|i| i.instance.width > 0 && i.instance.height > 0);
|
||||
let green = green.filter(|i| i.instance.width > 0 && i.instance.height > 0);
|
||||
let blue = blue.filter(|i| i.instance.width > 0 && i.instance.height > 0);
|
||||
let alpha = alpha.filter(|i| i.instance.width > 0 && i.instance.height > 0);
|
||||
// Turn any default zero-sized image rows into None
|
||||
let red = red.filter(|i| i.element.width > 0 && i.element.height > 0);
|
||||
let green = green.filter(|i| i.element.width > 0 && i.element.height > 0);
|
||||
let blue = blue.filter(|i| i.element.width > 0 && i.element.height > 0);
|
||||
let alpha = alpha.filter(|i| i.element.width > 0 && i.element.height > 0);
|
||||
|
||||
// Get this instance's transform and alpha blending mode from the first non-empty channel
|
||||
// Get this row's transform and alpha blending mode from the first non-empty channel
|
||||
let (transform, alpha_blending, source_node_id) = [&red, &green, &blue, &alpha]
|
||||
.iter()
|
||||
.find_map(|i| i.as_ref())
|
||||
@@ -127,10 +127,10 @@ pub fn combine_channels(
|
||||
|
||||
// Get the common width and height of the channels, which must have equal dimensions
|
||||
let channel_dimensions = [
|
||||
red.as_ref().map(|r| (r.instance.width, r.instance.height)),
|
||||
green.as_ref().map(|g| (g.instance.width, g.instance.height)),
|
||||
blue.as_ref().map(|b| (b.instance.width, b.instance.height)),
|
||||
alpha.as_ref().map(|a| (a.instance.width, a.instance.height)),
|
||||
red.as_ref().map(|r| (r.element.width, r.element.height)),
|
||||
green.as_ref().map(|g| (g.element.width, g.element.height)),
|
||||
blue.as_ref().map(|b| (b.element.width, b.element.height)),
|
||||
alpha.as_ref().map(|a| (a.element.width, a.element.height)),
|
||||
];
|
||||
if channel_dimensions.iter().all(Option::is_none)
|
||||
|| channel_dimensions
|
||||
@@ -142,7 +142,7 @@ pub fn combine_channels(
|
||||
}
|
||||
let &(width, height) = channel_dimensions.iter().flatten().next()?;
|
||||
|
||||
// Create a new image for this instance output
|
||||
// Create a new image for the output element
|
||||
let mut image = Image::new(width, height, Color::TRANSPARENT);
|
||||
|
||||
// Iterate over all pixels in the image and set the color channels
|
||||
@@ -150,22 +150,22 @@ pub fn combine_channels(
|
||||
for x in 0..image.width() {
|
||||
let image_pixel = image.get_pixel_mut(x, y).unwrap();
|
||||
|
||||
if let Some(r) = red.as_ref().and_then(|r| r.instance.get_pixel(x, y)) {
|
||||
if let Some(r) = red.as_ref().and_then(|r| r.element.get_pixel(x, y)) {
|
||||
image_pixel.set_red(r.l().cast_linear_channel());
|
||||
} else {
|
||||
image_pixel.set_red(Channel::from_linear(0.));
|
||||
}
|
||||
if let Some(g) = green.as_ref().and_then(|g| g.instance.get_pixel(x, y)) {
|
||||
if let Some(g) = green.as_ref().and_then(|g| g.element.get_pixel(x, y)) {
|
||||
image_pixel.set_green(g.l().cast_linear_channel());
|
||||
} else {
|
||||
image_pixel.set_green(Channel::from_linear(0.));
|
||||
}
|
||||
if let Some(b) = blue.as_ref().and_then(|b| b.instance.get_pixel(x, y)) {
|
||||
if let Some(b) = blue.as_ref().and_then(|b| b.element.get_pixel(x, y)) {
|
||||
image_pixel.set_blue(b.l().cast_linear_channel());
|
||||
} else {
|
||||
image_pixel.set_blue(Channel::from_linear(0.));
|
||||
}
|
||||
if let Some(a) = alpha.as_ref().and_then(|a| a.instance.get_pixel(x, y)) {
|
||||
if let Some(a) = alpha.as_ref().and_then(|a| a.element.get_pixel(x, y)) {
|
||||
image_pixel.set_alpha(a.l().cast_linear_channel());
|
||||
} else {
|
||||
image_pixel.set_alpha(Channel::from_linear(1.));
|
||||
@@ -173,8 +173,8 @@ pub fn combine_channels(
|
||||
}
|
||||
}
|
||||
|
||||
Some(Instance {
|
||||
instance: Raster::new_cpu(image),
|
||||
Some(TableRow {
|
||||
element: Raster::new_cpu(image),
|
||||
transform,
|
||||
alpha_blending,
|
||||
source_node_id,
|
||||
@@ -187,70 +187,70 @@ pub fn combine_channels(
|
||||
pub fn mask(
|
||||
_: impl Ctx,
|
||||
/// The image to be masked.
|
||||
image: RasterDataTable<CPU>,
|
||||
image: Table<Raster<CPU>>,
|
||||
/// The stencil to be used for masking.
|
||||
#[expose]
|
||||
stencil: RasterDataTable<CPU>,
|
||||
) -> RasterDataTable<CPU> {
|
||||
// TODO: Support multiple stencil instances
|
||||
let Some(stencil_instance) = stencil.instance_iter().next() else {
|
||||
stencil: Table<Raster<CPU>>,
|
||||
) -> Table<Raster<CPU>> {
|
||||
// TODO: Figure out what it means to support multiple stencil rows?
|
||||
let Some(stencil) = stencil.iter().next() else {
|
||||
// No stencil provided so we return the original image
|
||||
return image;
|
||||
};
|
||||
let stencil_size = DVec2::new(stencil_instance.instance.width as f64, stencil_instance.instance.height as f64);
|
||||
let stencil_size = DVec2::new(stencil.element.width as f64, stencil.element.height as f64);
|
||||
|
||||
image
|
||||
.instance_iter()
|
||||
.filter_map(|mut image_instance| {
|
||||
let image_size = DVec2::new(image_instance.instance.width as f64, image_instance.instance.height as f64);
|
||||
let mask_size = stencil_instance.transform.decompose_scale();
|
||||
.iter()
|
||||
.filter_map(|mut row| {
|
||||
let image_size = DVec2::new(row.element.width as f64, row.element.height as f64);
|
||||
let mask_size = stencil.transform.decompose_scale();
|
||||
|
||||
if mask_size == DVec2::ZERO {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Transforms a point from the background image to the foreground image
|
||||
let bg_to_fg = image_instance.transform * DAffine2::from_scale(1. / image_size);
|
||||
let stencil_transform_inverse = stencil_instance.transform.inverse();
|
||||
let bg_to_fg = row.transform * DAffine2::from_scale(1. / image_size);
|
||||
let stencil_transform_inverse = stencil.transform.inverse();
|
||||
|
||||
for y in 0..image_instance.instance.height {
|
||||
for x in 0..image_instance.instance.width {
|
||||
for y in 0..row.element.height {
|
||||
for x in 0..row.element.width {
|
||||
let image_point = DVec2::new(x as f64, y as f64);
|
||||
let mask_point = bg_to_fg.transform_point2(image_point);
|
||||
let local_mask_point = stencil_transform_inverse.transform_point2(mask_point);
|
||||
let mask_point = stencil_instance.transform.transform_point2(local_mask_point.clamp(DVec2::ZERO, DVec2::ONE));
|
||||
let mask_point = (DAffine2::from_scale(stencil_size) * stencil_instance.transform.inverse()).transform_point2(mask_point);
|
||||
let mask_point = stencil.transform.transform_point2(local_mask_point.clamp(DVec2::ZERO, DVec2::ONE));
|
||||
let mask_point = (DAffine2::from_scale(stencil_size) * stencil.transform.inverse()).transform_point2(mask_point);
|
||||
|
||||
let image_pixel = image_instance.instance.data_mut().get_pixel_mut(x, y).unwrap();
|
||||
let mask_pixel = stencil_instance.instance.sample(mask_point);
|
||||
let image_pixel = row.element.data_mut().get_pixel_mut(x, y).unwrap();
|
||||
let mask_pixel = stencil.element.sample(mask_point);
|
||||
*image_pixel = image_pixel.multiplied_alpha(mask_pixel.l().cast_linear_channel());
|
||||
}
|
||||
}
|
||||
|
||||
Some(image_instance)
|
||||
Some(row)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[node_macro::node(category(""))]
|
||||
pub fn extend_image_to_bounds(_: impl Ctx, image: RasterDataTable<CPU>, bounds: DAffine2) -> RasterDataTable<CPU> {
|
||||
pub fn extend_image_to_bounds(_: impl Ctx, image: Table<Raster<CPU>>, bounds: DAffine2) -> Table<Raster<CPU>> {
|
||||
image
|
||||
.instance_iter()
|
||||
.map(|mut image_instance| {
|
||||
let image_aabb = Bbox::unit().affine_transform(image_instance.transform).to_axis_aligned_bbox();
|
||||
.iter()
|
||||
.map(|mut row| {
|
||||
let image_aabb = Bbox::unit().affine_transform(row.transform).to_axis_aligned_bbox();
|
||||
let bounds_aabb = Bbox::unit().affine_transform(bounds.transform()).to_axis_aligned_bbox();
|
||||
if image_aabb.contains(bounds_aabb.start) && image_aabb.contains(bounds_aabb.end) {
|
||||
return image_instance;
|
||||
return row;
|
||||
}
|
||||
|
||||
let image_data = &image_instance.instance.data;
|
||||
let (image_width, image_height) = (image_instance.instance.width, image_instance.instance.height);
|
||||
let image_data = &row.element.data;
|
||||
let (image_width, image_height) = (row.element.width, row.element.height);
|
||||
if image_width == 0 || image_height == 0 {
|
||||
return empty_image((), bounds, Color::TRANSPARENT).instance_iter().next().unwrap();
|
||||
return empty_image((), bounds, Color::TRANSPARENT).iter().next().unwrap();
|
||||
}
|
||||
|
||||
let orig_image_scale = DVec2::new(image_width as f64, image_height as f64);
|
||||
let layer_to_image_space = DAffine2::from_scale(orig_image_scale) * image_instance.transform.inverse();
|
||||
let layer_to_image_space = DAffine2::from_scale(orig_image_scale) * row.transform.inverse();
|
||||
let bounds_in_image_space = Bbox::unit().affine_transform(layer_to_image_space * bounds).to_axis_aligned_bbox();
|
||||
|
||||
let new_start = bounds_in_image_space.start.floor().min(DVec2::ZERO);
|
||||
@@ -270,26 +270,26 @@ pub fn extend_image_to_bounds(_: impl Ctx, image: RasterDataTable<CPU>, bounds:
|
||||
|
||||
// Compute new transform.
|
||||
// let layer_to_new_texture_space = (DAffine2::from_scale(1. / new_scale) * DAffine2::from_translation(new_start) * layer_to_image_space).inverse();
|
||||
let new_texture_to_layer_space = image_instance.transform * DAffine2::from_scale(1. / orig_image_scale) * DAffine2::from_translation(new_start) * DAffine2::from_scale(new_scale);
|
||||
let new_texture_to_layer_space = row.transform * DAffine2::from_scale(1. / orig_image_scale) * DAffine2::from_translation(new_start) * DAffine2::from_scale(new_scale);
|
||||
|
||||
image_instance.instance = Raster::new_cpu(new_image);
|
||||
image_instance.transform = new_texture_to_layer_space;
|
||||
image_instance
|
||||
row.element = Raster::new_cpu(new_image);
|
||||
row.transform = new_texture_to_layer_space;
|
||||
row
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Debug: Raster"))]
|
||||
pub fn empty_image(_: impl Ctx, transform: DAffine2, color: Color) -> RasterDataTable<CPU> {
|
||||
pub fn empty_image(_: impl Ctx, transform: DAffine2, color: Color) -> Table<Raster<CPU>> {
|
||||
let width = transform.transform_vector2(DVec2::new(1., 0.)).length() as u32;
|
||||
let height = transform.transform_vector2(DVec2::new(0., 1.)).length() as u32;
|
||||
|
||||
let image = Image::new(width, height, color);
|
||||
|
||||
let mut result_table = RasterDataTable::new(Raster::new_cpu(image));
|
||||
let image_instance = result_table.get_mut(0).unwrap();
|
||||
*image_instance.transform = transform;
|
||||
*image_instance.alpha_blending = AlphaBlending::default();
|
||||
let mut result_table = Table::new_from_element(Raster::new_cpu(image));
|
||||
let row = result_table.get_mut(0).unwrap();
|
||||
*row.transform = transform;
|
||||
*row.alpha_blending = AlphaBlending::default();
|
||||
|
||||
// Callers of empty_image can safely unwrap on returned table
|
||||
result_table
|
||||
@@ -297,7 +297,7 @@ pub fn empty_image(_: impl Ctx, transform: DAffine2, color: Color) -> RasterData
|
||||
|
||||
/// Constructs a raster image.
|
||||
#[node_macro::node(category(""))]
|
||||
pub fn image_value(_: impl Ctx, _primary: (), image: RasterDataTable<CPU>) -> RasterDataTable<CPU> {
|
||||
pub fn image_value(_: impl Ctx, _primary: (), image: Table<Raster<CPU>>) -> Table<Raster<CPU>> {
|
||||
image
|
||||
}
|
||||
|
||||
@@ -321,7 +321,7 @@ pub fn noise_pattern(
|
||||
cellular_distance_function: CellularDistanceFunction,
|
||||
cellular_return_type: CellularReturnType,
|
||||
cellular_jitter: f64,
|
||||
) -> RasterDataTable<CPU> {
|
||||
) -> Table<Raster<CPU>> {
|
||||
let footprint = ctx.footprint();
|
||||
let viewport_bounds = footprint.viewport_bounds_in_local_space();
|
||||
|
||||
@@ -339,7 +339,7 @@ pub fn noise_pattern(
|
||||
|
||||
// If the image would not be visible, return an empty image
|
||||
if size.x <= 0. || size.y <= 0. {
|
||||
return RasterDataTable::default();
|
||||
return Table::new();
|
||||
}
|
||||
|
||||
let footprint_scale = footprint.scale();
|
||||
@@ -383,8 +383,8 @@ pub fn noise_pattern(
|
||||
}
|
||||
}
|
||||
|
||||
return RasterDataTable::new_instance(Instance {
|
||||
instance: Raster::new_cpu(image),
|
||||
return Table::new_from_row(TableRow {
|
||||
element: Raster::new_cpu(image),
|
||||
transform: DAffine2::from_translation(offset) * DAffine2::from_scale(size),
|
||||
..Default::default()
|
||||
});
|
||||
@@ -445,15 +445,15 @@ pub fn noise_pattern(
|
||||
}
|
||||
}
|
||||
|
||||
RasterDataTable::new_instance(Instance {
|
||||
instance: Raster::new_cpu(image),
|
||||
Table::new_from_row(TableRow {
|
||||
element: Raster::new_cpu(image),
|
||||
transform: DAffine2::from_translation(offset) * DAffine2::from_scale(size),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Raster: Pattern"))]
|
||||
pub fn mandelbrot(ctx: impl ExtractFootprint + Send) -> RasterDataTable<CPU> {
|
||||
pub fn mandelbrot(ctx: impl ExtractFootprint + Send) -> Table<Raster<CPU>> {
|
||||
let footprint = ctx.footprint();
|
||||
let viewport_bounds = footprint.viewport_bounds_in_local_space();
|
||||
|
||||
@@ -465,7 +465,7 @@ pub fn mandelbrot(ctx: impl ExtractFootprint + Send) -> RasterDataTable<CPU> {
|
||||
|
||||
// If the image would not be visible, return an empty image
|
||||
if size.x <= 0. || size.y <= 0. {
|
||||
return RasterDataTable::default();
|
||||
return Table::new();
|
||||
}
|
||||
|
||||
let scale = footprint.scale();
|
||||
@@ -487,8 +487,8 @@ pub fn mandelbrot(ctx: impl ExtractFootprint + Send) -> RasterDataTable<CPU> {
|
||||
}
|
||||
}
|
||||
|
||||
RasterDataTable::new_instance(Instance {
|
||||
instance: Raster::new_cpu(Image {
|
||||
Table::new_from_row(TableRow {
|
||||
element: Raster::new_cpu(Image {
|
||||
width,
|
||||
height,
|
||||
data,
|
||||
|
||||
Reference in New Issue
Block a user