|
1 | 1 | use bevy::prelude::Entity; |
| 2 | +use bevy::render::render_resource::{Extent3d, TextureFormat}; |
2 | 3 | use processing::prelude::*; |
3 | 4 |
|
4 | 5 | use crate::color::Color; |
@@ -73,7 +74,21 @@ pub extern "C" fn processing_surface_resize(window_id: u64, width: u32, height: |
73 | 74 | pub extern "C" fn processing_background_color(window_id: u64, color: Color) { |
74 | 75 | error::clear_error(); |
75 | 76 | 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))); |
77 | 92 | } |
78 | 93 |
|
79 | 94 | /// Begins the draw for the given window. |
@@ -223,3 +238,113 @@ pub extern "C" fn processing_rect( |
223 | 238 | ) |
224 | 239 | }); |
225 | 240 | } |
| 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 | +} |
0 commit comments