kas_core/draw/draw_shared.rs
1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4// https://www.apache.org/licenses/LICENSE-2.0
5
6//! Drawing APIs — shared draw state
7
8use super::color::Rgba;
9use super::{DrawImpl, PassId};
10use crate::cast::Cast;
11use crate::config::RasterConfig;
12use crate::geom::{Quad, Rect, Size};
13use crate::text::{Effect, TextDisplay};
14use std::any::Any;
15use std::num::NonZeroU32;
16use std::rc::Rc;
17use thiserror::Error;
18
19/// Identifier for an image allocation
20#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
21pub struct ImageId(NonZeroU32);
22
23/// Handle for an image
24///
25/// Serves both to identify an allocated image and to track the number of users
26/// via reference counting.
27#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
28pub struct ImageHandle(ImageId, Rc<()>);
29
30impl ImageHandle {
31 /// Convert to an [`ImageId`]
32 #[inline]
33 pub fn id(&self) -> ImageId {
34 self.0
35 }
36}
37
38impl ImageId {
39 /// Construct a new identifier from `u32` value not equal to 0
40 #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
41 #[cfg_attr(docsrs, doc(cfg(internal_doc)))]
42 #[inline]
43 pub const fn try_new(n: u32) -> Option<Self> {
44 // We can't use ? or .map in a const fn so do it the tedious way:
45 if let Some(nz) = NonZeroU32::new(n) {
46 Some(ImageId(nz))
47 } else {
48 None
49 }
50 }
51}
52
53/// Image formats available for upload
54#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
55pub enum ImageFormat {
56 /// 8-bit unsigned RGBA values (4 bytes per pixel)
57 Rgba8,
58}
59
60/// Allocation failed: too large or zero sized
61#[derive(Error, Debug)]
62#[error("failed to allocate: size too large or zero-sized")]
63pub struct AllocError;
64
65/// Shared draw state
66///
67/// A single [`SharedState`] instance is shared by all windows and draw contexts.
68/// This struct is built over a [`DrawSharedImpl`] object provided by the graphics backend,
69/// which may be accessed directly for a lower-level API (though most methods
70/// are available through [`SharedState`] directly).
71///
72/// Note: all functionality is implemented through the [`DrawShared`] trait to
73/// allow usage where the `DS` type parameter is unknown. Some functionality is
74/// also implemented directly to avoid the need for downcasting.
75pub struct SharedState<DS: DrawSharedImpl> {
76 /// The graphics backend's [`DrawSharedImpl`] object
77 pub draw: DS,
78}
79
80#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
81#[cfg_attr(docsrs, doc(cfg(internal_doc)))]
82impl<DS: DrawSharedImpl> SharedState<DS> {
83 /// Construct (this is only called by the graphics backend)
84 pub fn new(draw: DS) -> Self {
85 SharedState { draw }
86 }
87}
88
89/// Interface over [`SharedState`]
90///
91/// All methods concern management of resources for drawing.
92pub trait DrawShared {
93 /// Allocate an image
94 ///
95 /// Use [`SharedState::image_upload`] to set contents of the new image.
96 fn image_alloc(&mut self, size: (u32, u32)) -> Result<ImageHandle, AllocError>;
97
98 /// Upload an image to the GPU
99 ///
100 /// This should be called at least once on each image before display. May be
101 /// called again to update the image contents.
102 ///
103 /// `handle` must refer to an allocation of some size `(w, h)`, such that
104 /// `data.len() == b * w * h` where `b` is the number of bytes per pixel,
105 /// according to `format`. Data must be in row-major order.
106 fn image_upload(&mut self, handle: &ImageHandle, data: &[u8], format: ImageFormat);
107
108 /// Potentially free an image
109 ///
110 /// The input `handle` is consumed. If this reduces its reference count to
111 /// zero, then the image is freed.
112 fn image_free(&mut self, handle: ImageHandle);
113
114 /// Get the size of an image
115 fn image_size(&self, handle: &ImageHandle) -> Option<Size>;
116}
117
118impl<DS: DrawSharedImpl> DrawShared for SharedState<DS> {
119 #[inline]
120 fn image_alloc(&mut self, size: (u32, u32)) -> Result<ImageHandle, AllocError> {
121 self.draw
122 .image_alloc(size)
123 .map(|id| ImageHandle(id, Rc::new(())))
124 }
125
126 #[inline]
127 fn image_upload(&mut self, handle: &ImageHandle, data: &[u8], format: ImageFormat) {
128 self.draw.image_upload(handle.0, data, format);
129 }
130
131 #[inline]
132 fn image_free(&mut self, handle: ImageHandle) {
133 if let Ok(()) = Rc::try_unwrap(handle.1) {
134 self.draw.image_free(handle.0);
135 }
136 }
137
138 #[inline]
139 fn image_size(&self, handle: &ImageHandle) -> Option<Size> {
140 self.draw.image_size(handle.0).map(|size| size.cast())
141 }
142}
143
144/// Implementation target for [`DrawShared`]
145///
146/// This is typically used via [`SharedState`].
147pub trait DrawSharedImpl: Any {
148 type Draw: DrawImpl;
149
150 /// Get the maximum 2D texture size
151 fn max_texture_dimension_2d(&self) -> u32;
152
153 /// Set font raster config
154 fn set_raster_config(&mut self, config: &RasterConfig);
155
156 /// Allocate an image
157 ///
158 /// Use [`DrawSharedImpl::image_upload`] to set contents of the new image.
159 fn image_alloc(&mut self, size: (u32, u32)) -> Result<ImageId, AllocError>;
160
161 /// Upload an image to the GPU
162 ///
163 /// This should be called at least once on each image before display. May be
164 /// called again to update the image contents.
165 fn image_upload(&mut self, id: ImageId, data: &[u8], format: ImageFormat);
166
167 /// Free an image allocation
168 fn image_free(&mut self, id: ImageId);
169
170 /// Query an image's size
171 fn image_size(&self, id: ImageId) -> Option<(u32, u32)>;
172
173 /// Draw the image in the given `rect`
174 fn draw_image(&self, draw: &mut Self::Draw, pass: PassId, id: ImageId, rect: Quad);
175
176 /// Draw text with a colour
177 fn draw_text(
178 &mut self,
179 draw: &mut Self::Draw,
180 pass: PassId,
181 rect: Rect,
182 text: &TextDisplay,
183 col: Rgba,
184 );
185
186 /// Draw text with a colour and effects
187 ///
188 /// The effects list does not contain colour information, but may contain
189 /// underlining/strikethrough information. It may be empty.
190 fn draw_text_effects(
191 &mut self,
192 draw: &mut Self::Draw,
193 pass: PassId,
194 rect: Rect,
195 text: &TextDisplay,
196 col: Rgba,
197 effects: &[Effect<()>],
198 );
199
200 /// Draw text with effects
201 ///
202 /// The `effects` list provides both underlining and colour information.
203 /// If the `effects` list is empty or the first entry has `start > 0`, a
204 /// default entity will be assumed.
205 fn draw_text_effects_rgba(
206 &mut self,
207 draw: &mut Self::Draw,
208 pass: PassId,
209 rect: Rect,
210 text: &TextDisplay,
211 effects: &[Effect<Rgba>],
212 );
213}