Skip to content

Commit acc5b41

Browse files
authored
Add a few missing graphics readback methods (#216)
1 parent 76cc849 commit acc5b41

4 files changed

Lines changed: 147 additions & 0 deletions

File tree

crates/processing_ffi/src/color.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,11 @@ impl Color {
3030
space: ColorSpace::Linear as u8,
3131
}
3232
}
33+
34+
pub fn to_linear(self) -> LinearRgba {
35+
ColorSpace::from_u8(self.space)
36+
.unwrap_or(ColorSpace::Linear)
37+
.color(self.c1, self.c2, self.c3, self.a)
38+
.to_linear()
39+
}
3340
}

crates/processing_ffi/src/lib.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,23 @@ pub extern "C" fn processing_background_image(graphics_id: u64, image_id: u64) {
226226
});
227227
}
228228

229+
/// Clear the graphics surface to transparent.
230+
///
231+
/// SAFETY:
232+
/// - graphics_id is a valid ID returned from graphics_create.
233+
/// - This is called from the same thread as init.
234+
#[unsafe(no_mangle)]
235+
pub extern "C" fn processing_clear(graphics_id: u64) {
236+
error::clear_error();
237+
let graphics_entity = Entity::from_bits(graphics_id);
238+
error::check(|| {
239+
graphics_record_command(
240+
graphics_entity,
241+
DrawCommand::BackgroundColor(bevy::prelude::Color::NONE),
242+
)
243+
});
244+
}
245+
229246
/// Begins the draw for the given graphics context.
230247
///
231248
/// SAFETY:
@@ -1601,6 +1618,115 @@ pub unsafe extern "C" fn processing_image_readback(
16011618
});
16021619
}
16031620

1621+
/// Load pixels from the graphics surface into a caller-provided buffer.
1622+
///
1623+
/// # Safety
1624+
/// - Init and graphics_create have been called.
1625+
/// - graphics_id is a valid ID returned from graphics_create.
1626+
/// - buffer is a valid pointer to at least buffer_len Color elements.
1627+
/// - buffer_len must equal width * height of the graphics surface.
1628+
/// - This is called from the same thread as init.
1629+
#[unsafe(no_mangle)]
1630+
pub unsafe extern "C" fn processing_graphics_readback(
1631+
graphics_id: u64,
1632+
buffer: *mut Color,
1633+
buffer_len: usize,
1634+
) {
1635+
error::clear_error();
1636+
let graphics_entity = Entity::from_bits(graphics_id);
1637+
error::check(|| {
1638+
let colors = graphics_readback(graphics_entity)?;
1639+
1640+
if colors.len() != buffer_len {
1641+
let error_msg = format!(
1642+
"Buffer size mismatch: expected {}, got {}",
1643+
colors.len(),
1644+
buffer_len
1645+
);
1646+
error::set_error(&error_msg);
1647+
return Err(error::ProcessingError::InvalidArgument(error_msg));
1648+
}
1649+
1650+
// SAFETY: Caller guarantees buffer is valid for buffer_len elements
1651+
unsafe {
1652+
let buffer_slice = std::slice::from_raw_parts_mut(buffer, buffer_len);
1653+
for (i, color) in colors.iter().enumerate() {
1654+
buffer_slice[i] = Color::from_linear(*color);
1655+
}
1656+
}
1657+
1658+
Ok(())
1659+
});
1660+
}
1661+
1662+
/// Write a caller-provided pixel buffer back onto the graphics surface.
1663+
///
1664+
/// # Safety
1665+
/// - Init and graphics_create have been called.
1666+
/// - graphics_id is a valid ID returned from graphics_create.
1667+
/// - buffer is a valid pointer to at least buffer_len Color elements.
1668+
/// - buffer_len must equal width * height of the graphics surface.
1669+
/// - This is called from the same thread as init.
1670+
#[unsafe(no_mangle)]
1671+
pub unsafe extern "C" fn processing_graphics_update(
1672+
graphics_id: u64,
1673+
buffer: *const Color,
1674+
buffer_len: usize,
1675+
) {
1676+
error::clear_error();
1677+
let graphics_entity = Entity::from_bits(graphics_id);
1678+
error::check(|| {
1679+
// SAFETY: Caller guarantees buffer is valid for buffer_len elements
1680+
let pixels: Vec<_> = unsafe { std::slice::from_raw_parts(buffer, buffer_len) }
1681+
.iter()
1682+
.map(|color| color.to_linear())
1683+
.collect();
1684+
graphics_update(graphics_entity, &pixels)
1685+
});
1686+
}
1687+
1688+
/// Write a caller-provided pixel buffer onto a rectangular region of the surface.
1689+
///
1690+
/// # Safety
1691+
/// - Init and graphics_create have been called.
1692+
/// - graphics_id is a valid ID returned from graphics_create.
1693+
/// - buffer is a valid pointer to at least buffer_len Color elements.
1694+
/// - buffer_len must equal width * height.
1695+
/// - This is called from the same thread as init.
1696+
#[unsafe(no_mangle)]
1697+
pub unsafe extern "C" fn processing_graphics_update_region(
1698+
graphics_id: u64,
1699+
x: u32,
1700+
y: u32,
1701+
width: u32,
1702+
height: u32,
1703+
buffer: *const Color,
1704+
buffer_len: usize,
1705+
) {
1706+
error::clear_error();
1707+
let graphics_entity = Entity::from_bits(graphics_id);
1708+
error::check(|| {
1709+
// SAFETY: Caller guarantees buffer is valid for buffer_len elements
1710+
let pixels: Vec<_> = unsafe { std::slice::from_raw_parts(buffer, buffer_len) }
1711+
.iter()
1712+
.map(|color| color.to_linear())
1713+
.collect();
1714+
graphics_update_region(graphics_entity, x, y, width, height, &pixels)
1715+
});
1716+
}
1717+
1718+
/// Set a single pixel on the graphics surface.
1719+
///
1720+
/// SAFETY:
1721+
/// - graphics_id is a valid ID returned from graphics_create.
1722+
/// - This is called from the same thread as init.
1723+
#[unsafe(no_mangle)]
1724+
pub extern "C" fn processing_graphics_set(graphics_id: u64, x: u32, y: u32, color: Color) {
1725+
error::clear_error();
1726+
let graphics_entity = Entity::from_bits(graphics_id);
1727+
error::check(|| graphics_update_region(graphics_entity, x, y, 1, 1, &[color.to_linear()]));
1728+
}
1729+
16041730
/// Set the tint color applied to images.
16051731
///
16061732
/// SAFETY:

crates/processing_pyo3/src/graphics.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,14 @@ impl Graphics {
664664
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
665665
}
666666

667+
pub fn clear(&self) -> PyResult<()> {
668+
graphics_record_command(
669+
self.entity,
670+
DrawCommand::BackgroundColor(bevy::prelude::Color::NONE),
671+
)
672+
.map_err(|e| PyRuntimeError::new_err(format!("{e}")))
673+
}
674+
667675
#[pyo3(signature = (*args))]
668676
pub fn fill(&self, args: &Bound<'_, PyTuple>) -> PyResult<()> {
669677
if args.len() == 1

crates/processing_pyo3/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,6 +1133,12 @@ mod mewnala {
11331133
}
11341134
}
11351135

1136+
#[pyfunction]
1137+
#[pyo3(pass_module)]
1138+
fn clear(module: &Bound<'_, PyModule>) -> PyResult<()> {
1139+
graphics!(module).clear()
1140+
}
1141+
11361142
#[pyfunction]
11371143
#[pyo3(pass_module, signature = (mode, max1=None, max2=None, max3=None, max_alpha=None))]
11381144
fn color_mode<'py>(

0 commit comments

Comments
 (0)