Canvas
Initialization
To initialize a Canvas struct, use a Window
:
let mut canvas = window.into_canvas().build().unwrap();
Set Draw Color
To set the draw color for the Canvas, use a Color:
pub fn set_draw_color<C: Into<Color>>(&mut self, color: C)
Clear Canvas
To clear the Canvas with the draw color:
pub fn clear(&mut self)
Present Canvas
To present the actual canvas onto the screen, updating any buffered rendering changes:
pub fn present(&mut self)
Draw Line
To draw a line on the canvas, use a start and end Point:
pub fn draw_line<P1: Into<Point>, P2: Into<Point>>(
&mut self,
start: P1,
end: P2,
) -> Result<(), String>
Draw Rectangle
To draw a rectangle on the canvas, pass in a Rect to draw_rect
:
pub fn draw_rect(&mut self, rect: Rect) -> Result<(), String>
NOTE: This only draws an outline of the rectangle. To fill the rectangle, use fill_rect
instead:
Fill Rectangle
To fill a rectangle on the canvas, pass in a Rect to fill_rect
:
pub fn fill_rect<R: Into<Option<Rect>>>(
&mut self,
rect: R,
) -> Result<(), String>
Draw Triangle
SDL2-rs does not implement the RenderGeometry functions, so we are left to create them for ourselves. Drawing the outline of a triangle is easy enough:
/// Given three points, render the outline of a triangle abc
pub fn draw_triangle(canvas: &mut Canvas<Window>, a: Point, b: Point, c: Point) {
canvas.draw_line(a, b).unwrap();
canvas.draw_line(b, c).unwrap();
canvas.draw_line(c, a).unwrap();
}
Fill Triangle
However, filling a triangle requires more work. We can utilize a scanline algorithm, which as a high-level overview simply iterates through y-levels of the triangle, figuring out the bounding edges, and then drawing a line to fill the triangle.
The following is a basic implementation of this, which uses a Vector2
(see
here):
/// Given two points that form a line, and a y-coordinate, interpolate the x-coordinate
fn interpolate(a: Vector2, b: Vector2, y: i32) -> i32 {
let m = (b.y - a.y) as f32 / (b.x - a.x) as f32;
((y - a.y) as f32 / m) as i32 + a.x
}
/// Given three points, fill the triangle abc using a scanline algorithm
pub fn fill_triangle(canvas: &mut Canvas<Window>, a: Vector2, b: Vector2, c: Vector2) {
let mut points: Vec<Vector2> = vec![a, b, c];
points.sort_by(|first, second| first.y.cmp(&second.y));
// iterate through each y-level
for y in points[0].y..=points[2].y {
// compute active edges
let left_edge: (Vector2, Vector2);
let right_edge: (Vector2, Vector2);
// configuration where the intermediate point is to the right of the lowest point
if points[0].x <= points[1].x && y <= points[1].y {
left_edge = (points[0], points[2]);
right_edge = (points[0], points[1]);
} else if points[0].x <= points[1].x {
left_edge = (points[0], points[2]);
right_edge = (points[1], points[2]);
}
// configuration where the intermediate point is to the left of the lowest point
else if y <= points[1].y {
left_edge = (points[0], points[1]);
right_edge = (points[0], points[2]);
} else {
left_edge = (points[1], points[2]);
right_edge = (points[0], points[2]);
}
// get start and end points of line to draw
let start: Vector2 = Vector2::new(interpolate(left_edge.0, left_edge.1, y), y);
let end: Vector2 = Vector2::new(interpolate(right_edge.0, right_edge.1, y), y);
// draw the line
canvas.draw_line(start, end).unwrap();
}
}
NOTE: this may not be the most efficient implementation, as it is possible to reduce the number of times interpolation occurs as well as the number of times the active edges are calculated.
Get Output Size
To get the output or window size, use the output_size()
method:
pub fn output_size(&self) -> Result<(u32, u32), String>
Create a TextureCreator
Returns a TextureCreator
that can create Textures to be drawn on this Canvas
This TextureCreator
will share a reference to the renderer and target context.
The target (i.e., Window
) will not be destroyed and the SDL_Renderer will not be destroyed if
the TextureCreator
is still in scope.
pub fn texture_creator(&self) -> TextureCreator<SurfaceContext<'s>>