platform/render.rs
1// SPDX-FileCopyrightText: 2024 Jens Pitkänen <jens.pitkanen@helsinki.fi>
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5use bytemuck::{Pod, Zeroable};
6
7/// Vertex describing a 2D point with a texture coordinate and a color.
8#[derive(Debug, Default, Clone, Copy)]
9#[repr(C)]
10pub struct Vertex2D {
11 /// The horizontal coordinate of the position of this vertex.
12 pub x: f32,
13 /// The vertical coordinate of the position of this vertex.
14 pub y: f32,
15
16 /// The horizontal texture coordinate of this vertex, 0 referring to the
17 /// left edge, and 1 referring to the right edge of the texture.
18 pub u: f32,
19 /// The vertical texture coordinate of this vertex, 0 referring to the top
20 /// edge, and 1 referring to the bottom edge of the texture.
21 pub v: f32,
22
23 /// The red channel of the vertex color, which is multiplied with the
24 /// texture's red channel (or used as is, if no texture is used).
25 pub r: u8,
26 /// The green channel of the vertex color, which is multiplied with the
27 /// texture's green channel (or used as is, if no texture is used).
28 pub g: u8,
29 /// The blue channel of the vertex color, which is multiplied with the
30 /// texture's blue channel (or used as is, if no texture is used).
31 pub b: u8,
32 /// The alpha channel of the vertex color, which is multiplied with the
33 /// texture's alpha channel (or used as is, if no texture is used).
34 pub a: u8,
35}
36
37// Safety: Vertex is "inhabited" and all zeroes is a valid value for it, all the
38// fields are Zeroable.
39unsafe impl Zeroable for Vertex2D {}
40
41// Safety: manually checked for f32 typed x/y/u/v at the beginning with u8 typed
42// r/g/b/a at the end.
43// - The type must be inhabited: it is.
44// - The type must allow any bit pattern: it does, f32 and u8 are Pod.
45// - The type must not contain any uninit (or padding) bytes: it does not, it's
46// 4-aligned and the first four fields are 4 bytes, and the last four are
47// 1-byte aligned and there's 4 of them.
48// - The type needs to have all fields also be `Pod`: it does.
49// - The type needs to be `repr(C)` or...: yes, it is repr(C) and does not
50// specify padding or alignment manually.
51// - It is disallowed for types to contain pointer types, `Cell`, `UnsafeCell`,
52// atomics, and any other forms of interior mutability: none of those in this.
53// - More precisely: A shared reference to the type must allow reads, and *only*
54// reads: yes, no hidden inner mutability tricks.
55unsafe impl Pod for Vertex2D {}
56
57impl Vertex2D {
58 /// Creates a [`Vertex2D`] with zeroed texture coordinates and a white
59 /// color, with the given coordinates.
60 pub fn xy(x: f32, y: f32) -> Vertex2D {
61 Vertex2D {
62 x,
63 y,
64 u: 0.0,
65 v: 0.0,
66 r: 0xFF,
67 g: 0xFF,
68 b: 0xFF,
69 a: 0xFF,
70 }
71 }
72
73 /// Creates a [`Vertex2D`] with the given position and texture coordinates,
74 /// and no color modulation (white vertex colors).
75 pub fn new(x: f32, y: f32, u: f32, v: f32) -> Vertex2D {
76 Vertex2D {
77 x,
78 y,
79 u,
80 v,
81 r: 0xFF,
82 g: 0xFF,
83 b: 0xFF,
84 a: 0xFF,
85 }
86 }
87}
88
89/// Various options for controlling how draw commands should be executed.
90#[derive(Debug, Default, Clone, Copy, PartialEq)]
91pub struct DrawSettings2D {
92 /// The sprite used to draw the triangles. If None, rendering should fall
93 /// back to the vertex colors.
94 pub sprite: Option<SpriteRef>,
95 /// The blending mode of the draw, i.e. how the pixels are mixed from this
96 /// draw and any previous draws in the same area.
97 pub blend_mode: BlendMode,
98 /// The filtering mode used to stretch or squeeze the sprite when the
99 /// rendering resolution doesn't exactly match the sprite.
100 pub texture_filter: TextureFilter,
101 /// The draw will only apply to pixels within this rectangle. Layout: `[x,
102 /// y, width, height]`.
103 pub clip_area: Option<[f32; 4]>,
104}
105
106/// Platform-specific sprite reference.
107#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
108pub struct SpriteRef(u64);
109
110impl SpriteRef {
111 /// Creates a new [`SpriteRef`]. Should only be created in the platform
112 /// implementation, which also knows how the inner value is going to be
113 /// used.
114 pub fn new(id: u64) -> SpriteRef {
115 SpriteRef(id)
116 }
117
118 /// Returns the inner value passed into [`SpriteRef::new`]. Generally only
119 /// relevant to the platform implementation.
120 pub fn inner(self) -> u64 {
121 self.0
122 }
123}
124
125/// How drawn pixels are blended with the previously drawn pixels.
126#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
127pub enum BlendMode {
128 /// All channels are replaced with the color being drawn, including alpha.
129 None,
130 /// `dstRGB = (srcRGB * srcA) + (dstRGB * (1 - srcA))`
131 /// `dstA = srcA + (dstA * (1 - srcA))`
132 ///
133 /// Where `dst` is the color of the framebuffer, and `src` is the color
134 /// being drawn on it.
135 #[default]
136 Blend,
137 /// `dstRGB = (srcRGB * srcA) + dstRGB`
138 /// `dstA = dstA`
139 ///
140 /// Where `dst` is the color of the framebuffer, and `src` is the color
141 /// being drawn on it.
142 Add,
143}
144
145/// How the texture is filtered when magnified or minified.
146#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
147pub enum TextureFilter {
148 /// No blending, just picks the pixel from the texture which is nearest to
149 /// the sampled position. When rendering at a higher resolution than the
150 /// texture, this keeps pixel art sharp.
151 NearestNeighbor,
152 /// Linear blending between pixels, which blurs the texture when rendering
153 /// at a higher resolution than the texture itself, but avoids noisy
154 /// aliasing artifacts caused by [`TextureFilter::NearestNeighbor`].
155 #[default]
156 Linear,
157}
158
159/// Descriptions of pixel data layouts, used to interpret the byte arrays passed
160/// into uploading functions.
161#[derive(Debug)]
162#[repr(u8)]
163pub enum PixelFormat {
164 /// 8-bit per channel RGBA colors, arranged in order: `[red, green, blue,
165 /// alpha, red, ...]`.
166 Rgba,
167}
168
169impl PixelFormat {
170 /// Returns the amount of bytes each pixel takes up in a pixel buffer if
171 /// that buffer is using this pixel format.
172 ///
173 /// E.g. for 8-bit RGBA, this returns 4, as each of the four channels takes
174 /// up eight bits.
175 pub const fn bytes_per_pixel(self) -> usize {
176 match self {
177 PixelFormat::Rgba => 4,
178 }
179 }
180}