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}