Skip to content

Commit 3d837f0

Browse files
committed
Ffi image fns.
1 parent f81c48f commit 3d837f0

File tree

4 files changed

+138
-2
lines changed

4 files changed

+138
-2
lines changed

crates/processing_ffi/src/color.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use bevy::color::LinearRgba;
2+
13
/// A sRGB (?) color
24
#[repr(C)]
35
#[derive(Debug, Clone, Copy)]
@@ -13,3 +15,10 @@ impl From<Color> for bevy::color::Color {
1315
bevy::color::Color::srgba(color.r, color.g, color.b, color.a)
1416
}
1517
}
18+
19+
impl From<LinearRgba> for Color {
20+
fn from(lin: LinearRgba) -> Self {
21+
let srgb = bevy::color::Color::srgba(lin.red, lin.green, lin.blue, lin.alpha);
22+
srgb.into()
23+
}
24+
}

crates/processing_ffi/src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::{
44
panic,
55
};
66

7-
use processing::prelude::error::ProcessingError;
7+
pub(crate) use processing::prelude::error::ProcessingError;
88

99
thread_local! {
1010
static LAST_ERROR: RefCell<Option<CString>> = const { RefCell::new(None) };

crates/processing_ffi/src/lib.rs

Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use bevy::prelude::Entity;
2+
use bevy::render::render_resource::{Extent3d, TextureFormat};
23
use processing::prelude::*;
34

45
use crate::color::Color;
@@ -73,7 +74,21 @@ pub extern "C" fn processing_surface_resize(window_id: u64, width: u32, height:
7374
pub extern "C" fn processing_background_color(window_id: u64, color: Color) {
7475
error::clear_error();
7576
let window_entity = Entity::from_bits(window_id);
76-
error::check(|| background_color(window_entity, color.into()));
77+
error::check(|| record_command(window_entity, DrawCommand::BackgroundColor(color.into())));
78+
}
79+
80+
/// Set the background image for the given window.
81+
///
82+
/// SAFETY:
83+
/// - This is called from the same thread as init.
84+
/// - image_id is a valid ID returned from processing_image_create.
85+
/// - The image has been fully uploaded.
86+
#[unsafe(no_mangle)]
87+
pub extern "C" fn processing_background_image(window_id: u64, image_id: u64) {
88+
error::clear_error();
89+
let window_entity = Entity::from_bits(window_id);
90+
let image_entity = Entity::from_bits(image_id);
91+
error::check(|| record_command(window_entity, DrawCommand::BackgroundImage(image_entity)));
7792
}
7893

7994
/// Begins the draw for the given window.
@@ -223,3 +238,113 @@ pub extern "C" fn processing_rect(
223238
)
224239
});
225240
}
241+
242+
/// Create an image from raw pixel data.
243+
///
244+
/// SAFETY:
245+
/// - Init has been called.
246+
/// - data is a valid pointer to data_len bytes of RGBA pixel data.
247+
/// - This is called from the same thread as init.
248+
#[unsafe(no_mangle)]
249+
pub extern "C" fn processing_image_create(
250+
width: u32,
251+
height: u32,
252+
data: *const u8,
253+
data_len: usize,
254+
) -> u64 {
255+
error::clear_error();
256+
// SAFETY: Caller must ensure that `data` is valid for `data_len` bytes.
257+
let data = unsafe { std::slice::from_raw_parts(data, data_len) };
258+
error::check(|| {
259+
let size = Extent3d {
260+
width,
261+
height,
262+
depth_or_array_layers: 1,
263+
};
264+
image_create(size, data.to_vec(), TextureFormat::Rgba8UnormSrgb)
265+
})
266+
.map(|entity| entity.to_bits())
267+
.unwrap_or(0)
268+
}
269+
270+
/// Load an image from a file path.
271+
///
272+
/// SAFETY:
273+
/// - Init has been called.
274+
/// - path is a valid null-terminated C string.
275+
/// - This is called from the same thread as init.
276+
///
277+
/// Note: This function is currently synchronous but Bevy's asset loading is async.
278+
/// The image may not be immediately available. This needs to be improved.
279+
#[unsafe(no_mangle)]
280+
pub extern "C" fn processing_image_load(path: *const std::ffi::c_char) -> u64 {
281+
error::clear_error();
282+
283+
// SAFETY: Caller guarantees path is a valid C string
284+
let c_str = unsafe { std::ffi::CStr::from_ptr(path) };
285+
let path_str = match c_str.to_str() {
286+
Ok(s) => s,
287+
Err(_) => {
288+
error::set_error("Invalid UTF-8 in image path");
289+
return 0;
290+
}
291+
};
292+
293+
error::check(|| image_load(path_str))
294+
.map(|entity| entity.to_bits())
295+
.unwrap_or(0)
296+
}
297+
298+
#[unsafe(no_mangle)]
299+
pub extern "C" fn processing_image_resize(image_id: u64, new_width: u32, new_height: u32) {
300+
error::clear_error();
301+
let image_entity = Entity::from_bits(image_id);
302+
let new_size = Extent3d {
303+
width: new_width,
304+
height: new_height,
305+
depth_or_array_layers: 1,
306+
};
307+
error::check(|| image_resize(image_entity, new_size));
308+
}
309+
310+
/// Load pixels from an image into a caller-provided buffer.
311+
///
312+
/// SAFETY:
313+
/// - Init and image_create have been called.
314+
/// - image_id is a valid ID returned from image_create.
315+
/// - buffer is a valid pointer to at least buffer_len Color elements.
316+
/// - buffer_len must equal width * height of the image.
317+
/// - This is called from the same thread as init.
318+
#[unsafe(no_mangle)]
319+
pub extern "C" fn processing_image_load_pixels(
320+
image_id: u64,
321+
buffer: *mut Color,
322+
buffer_len: usize,
323+
) {
324+
error::clear_error();
325+
let image_entity = Entity::from_bits(image_id);
326+
error::check(|| {
327+
let colors = image_load_pixels(image_entity)?;
328+
329+
// Validate buffer size
330+
if colors.len() != buffer_len {
331+
let error_msg = format!(
332+
"Buffer size mismatch: expected {}, got {}",
333+
colors.len(),
334+
buffer_len
335+
);
336+
error::set_error(&error_msg);
337+
return Err(error::ProcessingError::InvalidArgument(error_msg));
338+
}
339+
340+
// SAFETY: Caller guarantees buffer is valid for buffer_len elements
341+
unsafe {
342+
let buffer_slice = std::slice::from_raw_parts_mut(buffer, buffer_len);
343+
for (i, color) in colors.iter().enumerate() {
344+
buffer_slice[i] = Color::from(*color);
345+
}
346+
}
347+
348+
Ok(())
349+
});
350+
}

crates/processing_render/src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,6 @@ pub enum ProcessingError {
1818
ImageNotFound,
1919
#[error("Unsupported texture format")]
2020
UnsupportedTextureFormat,
21+
#[error("Invalid argument: {0}")]
22+
InvalidArgument(String),
2123
}

0 commit comments

Comments
 (0)