Skip to content
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ name = "renderer-target"
[[example]]
name = "events"

[[example]]
name = "render-geometry"

[[example]]
name = "renderer-texture"

Expand Down
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ when upgrading from a version of rust-sdl2 to another.

### Next

[PR #1472](https://github.com/Rust-SDL2/rust-sdl2/pull/1472) Add `Canvas::render_geometry`, `Canvas::render_geometry_raw` and an example that uses them both. Both these bindings use `SDL_RenderGeometryRaw`.

[PR #1473](https://github.com/Rust-SDL2/rust-sdl2/pull/1473) **BREAKING CHANGE** Fix UB in `ToColor` implementations and remove implementation for `isize`.

[PR #1414](https://github.com/Rust-SDL2/rust-sdl2/pull/1414) Use `TTF_GlyphIsProvided32` and `TTF_GlyphMetrics32` instead of the 16 bit ones.
Expand Down
146 changes: 146 additions & 0 deletions examples/render-geometry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
extern crate sdl2;

use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::pixels::Color;
use sdl2::rect::FPoint;
use sdl2::render::{RenderGeometryTextureParams, Vertex, VertexIndices};
use std::mem::offset_of;
use std::thread;
use std::time::Duration;

fn main() {
let sdl_context = sdl2::init().unwrap();
let video_subsystem = sdl_context.video().unwrap();

let window = video_subsystem
.window("Rust SDL2 render_geometry custom struct example", 800, 600)
.position_centered()
.opengl()
.build()
.unwrap();

let mut canvas = window.into_canvas().build().unwrap();

let mut event_pump = sdl_context.event_pump().unwrap();
let mut running = true;

while running {
for event in event_pump.poll_iter() {
match event {
Event::Quit { .. }
| Event::KeyDown {
keycode: Some(Keycode::Escape),
..
} => {
running = false;
}
_ => {}
}
}

// black background
canvas.set_draw_color(Color::BLACK);
canvas.clear();

// First, draw a triangle using `render_geometry`. The `tex_coord` fields are unused but
// must be provided, `render_geometry` only supports `sdl2::render::Vertex`.
let vertices = [
Vertex {
position: FPoint::new(100.0, 200.0),
color: Color::RED,
tex_coord: FPoint::new(0.0, 0.0),
},
Vertex {
position: FPoint::new(200.0, 200.0),
color: Color::GREEN,
tex_coord: FPoint::new(0.0, 0.0),
},
Vertex {
position: FPoint::new(150.0, 100.0),
color: Color::BLUE,
tex_coord: FPoint::new(0.0, 0.0),
},
];
canvas
.render_geometry(&vertices, None, VertexIndices::Sequential)
.expect("render_geometry failed (probably unsupported, see error message)");

// `render_geometry_raw` supports any custom struct as long as it contains the needed data
// (or other layout compatible of the needed data).
// The struct does not need to be `repr(C)` or `Copy` for example.
struct MyVertex {
// For demonstration purposes color is `[u8; 4]` here. `[u8; 4]` is layout-compatible
// with `sdl2::pixels::Color`
color: [u8; 4],
// The struct may contain data not needed by SDL.
#[expect(dead_code)]
foo: Vec<u8>,
// When defining your own vertex struct, using `FPoint` for position and tex_coord
// (and `Color` for color) is the easiest way. These are obviously layout-compatible
// with `FPoint` and `Color`, respectively.
pos: FPoint,
}

// Define the vertices of a square
let vertices = [
MyVertex {
color: [0, 0, 0, 0xff],
foo: b"some".to_vec(),
pos: FPoint::new(300.0, 100.0),
},
MyVertex {
color: [0, 0xff, 0, 0xff],
foo: b"unrelated".to_vec(),
pos: FPoint::new(400.0, 100.0),
},
MyVertex {
color: [0xff, 0, 0, 0xff],
foo: b"data".to_vec(),
pos: FPoint::new(300.0, 200.0),
},
MyVertex {
color: [0xff, 0xff, 0, 0xff],
foo: b"!".to_vec(),
pos: FPoint::new(400.0, 200.0),
},
];

// A square is rendered as two triangles (see indices)
// SAFETY: core::mem::offset_of makes sure the offsets are right and alignment is respected.
unsafe {
canvas.render_geometry_raw(
&vertices,
offset_of!(MyVertex, pos),
&vertices,
offset_of!(MyVertex, color),
None::<RenderGeometryTextureParams<()>>,
&[[0, 1, 2], [1, 2, 3]],
)
}
.expect("render_geometry_raw failed (probably unsupported, see error message)");

// Parameters can be reused, here only the positions are swapped out for new ones.
// SAFETY: core::mem::offset_of makes sure the offsets are right and alignment is respected.
// The offset 0 is correct because the element type of positions is `[f32; 2]`.
unsafe {
canvas.render_geometry_raw(
&[
[500.0f32, 100.0],
[600.0, 100.0],
[500.0, 200.0],
[600.0, 200.0],
],
0,
&vertices,
offset_of!(MyVertex, color),
None::<RenderGeometryTextureParams<()>>,
&[[0, 1, 2], [1, 2, 3]],
)
}
.expect("render_geometry_raw failed (probably unsupported, see error message)");

canvas.present();
thread::sleep(Duration::from_millis(16));
}
}
12 changes: 9 additions & 3 deletions src/sdl2/pixels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,14 @@ impl Palette {
// Already validated, so don't check again
let ncolors = colors.len() as ::libc::c_int;

let colors = colors.iter().map(|color| color.raw()).collect::<Vec<_>>();

let result = unsafe { sys::SDL_SetPaletteColors(pal.raw, colors.as_ptr(), 0, ncolors) };
let result = unsafe {
sys::SDL_SetPaletteColors(
pal.raw,
colors.as_ptr().cast::<sys::SDL_Color>(),
0,
ncolors,
)
};

if result < 0 {
Err(get_error())
Expand Down Expand Up @@ -88,6 +93,7 @@ fn create_palette() {
assert!(palette.len() == 255);
}

#[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct Color {
pub r: u8,
Expand Down
Loading
Loading