Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 19 additions & 21 deletions crates/processing_ffi/src/color.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,33 @@
use bevy::color::{LinearRgba, Srgba};
use processing::prelude::color::{ColorMode, ColorSpace};

/// A sRGB (?) color
/// A color with 4 float components and its color space.
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct Color {
pub r: f32,
pub g: f32,
pub b: f32,
pub c1: f32,
pub c2: f32,
pub c3: f32,
pub a: f32,
pub space: u8,
}

impl From<Color> for bevy::color::Color {
fn from(color: Color) -> Self {
bevy::color::Color::srgba(color.r, color.g, color.b, color.a)
impl Color {
pub fn resolve(self, mode: &ColorMode) -> bevy::color::Color {
let c1 = mode.scale(self.c1, 0);
let c2 = mode.scale(self.c2, 1);
let c3 = mode.scale(self.c3, 2);
let ca = mode.scale(self.a, 3);
mode.space.color(c1, c2, c3, ca)
}
}

impl From<LinearRgba> for Color {
fn from(lin: LinearRgba) -> Self {
let srgb: Srgba = lin.into();
srgb.into()
}
}

impl From<Srgba> for Color {
fn from(srgb: Srgba) -> Self {
Comment on lines -19 to -27
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was surprising to me! Isn't From<T> the way to handle generally?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mmm, probably a more stylistic thing but before we were saying a color is ALWAYS a srgb repr and now it's more just like, this is one way you can construct a color? Since all the specific color spaces impl Into<bevy::Color> we could probably impl From<bevy::Color> for our color and it would probably "just work"

pub fn from_linear(lin: LinearRgba) -> Self {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the idea we would implement this for every color space?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a specific helper because some operations like GPU readback return raw bytes in a linear format.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahh i see!

Color {
r: srgb.red,
g: srgb.green,
b: srgb.blue,
a: srgb.alpha,
c1: lin.red,
c2: lin.green,
c3: lin.blue,
a: lin.alpha,
space: ColorSpace::Linear as u8,
}
}
}
79 changes: 64 additions & 15 deletions crates/processing_ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,9 @@ pub extern "C" fn processing_background_color(graphics_id: u64, color: Color) {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| {
graphics_record_command(graphics_entity, DrawCommand::BackgroundColor(color.into()))
let mode = graphics_get_color_mode(graphics_entity)?;
let color = color.resolve(&mode);
graphics_record_command(graphics_entity, DrawCommand::BackgroundColor(color))
});
}

Expand Down Expand Up @@ -244,17 +246,46 @@ pub extern "C" fn processing_exit(exit_code: u8) {
error::check(|| exit(exit_code));
}

/// Set the color mode for a graphics context.
///
/// SAFETY:
/// - graphics_id is a valid ID returned from graphics_create.
/// - This is called from the same thread as init.
#[unsafe(no_mangle)]
pub extern "C" fn processing_color_mode(
graphics_id: u64,
space: u8,
max1: f32,
max2: f32,
max3: f32,
max_alpha: f32,
) {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| {
let space = processing::prelude::color::ColorSpace::from_u8(space).ok_or_else(|| {
processing::prelude::error::ProcessingError::InvalidArgument(format!(
"unknown color space: {space}"
))
})?;
let mode = processing::prelude::color::ColorMode::new(space, max1, max2, max3, max_alpha);
graphics_set_color_mode(graphics_entity, mode)
});
}

/// Set the fill color.
///
/// SAFETY:
/// - graphics_id is a valid ID returned from graphics_create.
/// - This is called from the same thread as init.
#[unsafe(no_mangle)]
pub extern "C" fn processing_set_fill(graphics_id: u64, r: f32, g: f32, b: f32, a: f32) {
pub extern "C" fn processing_set_fill(graphics_id: u64, color: Color) {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
let color = bevy::color::Color::srgba(r, g, b, a);
error::check(|| graphics_record_command(graphics_entity, DrawCommand::Fill(color)));
error::check(|| {
let mode = graphics_get_color_mode(graphics_entity)?;
graphics_record_command(graphics_entity, DrawCommand::Fill(color.resolve(&mode)))
});
}

/// Set the stroke color.
Expand All @@ -263,11 +294,16 @@ pub extern "C" fn processing_set_fill(graphics_id: u64, r: f32, g: f32, b: f32,
/// - graphics_id is a valid ID returned from graphics_create.
/// - This is called from the same thread as init.
#[unsafe(no_mangle)]
pub extern "C" fn processing_set_stroke_color(graphics_id: u64, r: f32, g: f32, b: f32, a: f32) {
pub extern "C" fn processing_set_stroke_color(graphics_id: u64, color: Color) {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
let color = bevy::color::Color::srgba(r, g, b, a);
error::check(|| graphics_record_command(graphics_entity, DrawCommand::StrokeColor(color)));
error::check(|| {
let mode = graphics_get_color_mode(graphics_entity)?;
graphics_record_command(
graphics_entity,
DrawCommand::StrokeColor(color.resolve(&mode)),
)
});
}

/// Set the stroke weight.
Expand Down Expand Up @@ -565,7 +601,7 @@ pub unsafe extern "C" fn processing_image_readback(
unsafe {
let buffer_slice = std::slice::from_raw_parts_mut(buffer, buffer_len);
for (i, color) in colors.iter().enumerate() {
buffer_slice[i] = Color::from(*color);
buffer_slice[i] = Color::from_linear(*color);
}
}

Expand Down Expand Up @@ -1154,9 +1190,12 @@ pub extern "C" fn processing_light_create_directional(
) -> u64 {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| light_create_directional(graphics_entity, color.into(), illuminance))
.map(|e| e.to_bits())
.unwrap_or(0)
error::check(|| {
let mode = graphics_get_color_mode(graphics_entity)?;
light_create_directional(graphics_entity, color.resolve(&mode), illuminance)
})
.map(|e| e.to_bits())
.unwrap_or(0)
}

#[unsafe(no_mangle)]
Expand All @@ -1169,9 +1208,18 @@ pub extern "C" fn processing_light_create_point(
) -> u64 {
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| light_create_point(graphics_entity, color.into(), intensity, range, radius))
.map(|e| e.to_bits())
.unwrap_or(0)
error::check(|| {
let mode = graphics_get_color_mode(graphics_entity)?;
light_create_point(
graphics_entity,
color.resolve(&mode),
intensity,
range,
radius,
)
})
.map(|e| e.to_bits())
.unwrap_or(0)
}

#[unsafe(no_mangle)]
Expand All @@ -1187,9 +1235,10 @@ pub extern "C" fn processing_light_create_spot(
error::clear_error();
let graphics_entity = Entity::from_bits(graphics_id);
error::check(|| {
let mode = graphics_get_color_mode(graphics_entity)?;
light_create_spot(
graphics_entity,
color.into(),
color.resolve(&mode),
intensity,
range,
radius,
Expand Down
Loading
Loading