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}