piet/render_context.rs
1// Copyright 2019 the Piet Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4//! The main render context trait.
5
6use std::borrow::Cow;
7
8use kurbo::{Affine, Point, Rect, Shape};
9
10use crate::{
11 Color, Error, FixedGradient, FixedLinearGradient, FixedRadialGradient, Image, LinearGradient,
12 RadialGradient, StrokeStyle, Text, TextLayout,
13};
14
15/// A requested interpolation mode for drawing images.
16#[derive(Clone, Copy, PartialEq, Eq)]
17pub enum InterpolationMode {
18 /// Don't interpolate, use nearest neighbor.
19 NearestNeighbor,
20 /// Use bilinear interpolation.
21 Bilinear,
22}
23
24/// The pixel format for bitmap images.
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26#[non_exhaustive]
27pub enum ImageFormat {
28 /// 1 byte per pixel.
29 ///
30 /// For example, a white pixel has value 0xff.
31 Grayscale,
32 /// 3 bytes per pixel, in RGB order.
33 ///
34 /// For example, a red pixel consists of three bytes `[0xff, 0, 0]` independent of the system's
35 /// endianness.
36 Rgb,
37 /// 4 bytes per pixel, in RGBA order, with separate alpha.
38 ///
39 /// For example, a full-intensity red pixel with 50% transparency consists of four bytes
40 /// `[0xff, 0, 0, 0x80]` independent of the system's endianness.
41 RgbaSeparate,
42 /// 4 bytes per pixel, in RGBA order, with premultiplied alpha.
43 ///
44 /// For example, a full-intensity red pixel with 50% transparency consists of four bytes
45 /// `[0x80, 0, 0, 0x80]` independent of the system's endianness.
46 RgbaPremul,
47}
48
49impl ImageFormat {
50 /// The number of bytes required to represent a pixel in this format.
51 pub fn bytes_per_pixel(self) -> usize {
52 match self {
53 ImageFormat::Grayscale => 1,
54 ImageFormat::Rgb => 3,
55 ImageFormat::RgbaPremul | ImageFormat::RgbaSeparate => 4,
56 }
57 }
58}
59
60/// The main trait for rendering graphics.
61///
62/// This trait provides an API for drawing 2D graphics. In basic usage, it
63/// wraps a surface of some kind, so that drawing commands paint onto the
64/// surface. It can also be a recording context, creating a display list for
65/// playback later.
66///
67/// The intent of the design is to be general so that any number of back-ends
68/// can implement this trait.
69///
70/// Code that draws graphics will in general take `&mut impl RenderContext`.
71pub trait RenderContext
72where
73 Self::Brush: IntoBrush<Self>,
74{
75 /// The type of a "brush".
76 ///
77 /// Represents solid colors and gradients.
78 type Brush: Clone;
79
80 /// An associated factory for creating text layouts and related resources.
81 type Text: Text<TextLayout = Self::TextLayout>;
82
83 /// The type use to represent text layout objects.
84 type TextLayout: TextLayout;
85
86 /// The associated type of an image.
87 type Image: Image;
88
89 /// Report an internal error.
90 ///
91 /// Drawing operations may cause internal errors, which may also occur
92 /// asynchronously after the drawing command was issued. This method reports
93 /// any such error that has been detected.
94 fn status(&mut self) -> Result<(), Error>;
95
96 /// Create a new brush resource.
97 ///
98 /// TODO: figure out how to document lifetime and rebuilding requirements. Should
99 /// that be the responsibility of the client, or should the back-end take
100 /// responsibility? We could have a cache that is flushed when the Direct2D
101 /// render target is rebuilt. Solid brushes are super lightweight, but
102 /// other potentially retained objects will be heavier.
103 fn solid_brush(&mut self, color: Color) -> Self::Brush;
104
105 /// Create a new gradient brush.
106 fn gradient(&mut self, gradient: impl Into<FixedGradient>) -> Result<Self::Brush, Error>;
107
108 /// Replace a region of the canvas with the provided [`Color`].
109 ///
110 /// The region can be omitted, in which case it will apply to the entire
111 /// canvas.
112 ///
113 /// This operation ignores any existing clipping and transformations.
114 ///
115 /// # Note:
116 ///
117 /// You probably don't want to call this. It is essentially a specialized
118 /// fill method that can be used in GUI contexts for things like clearing
119 /// damage regions. It does not have a good cross-platform implementation,
120 /// and eventually should be deprecated when support is added for blend
121 /// modes, at which point it will be easier to just use [`fill`] for
122 /// everything.
123 ///
124 /// [`fill`]: RenderContext::fill
125 fn clear(&mut self, region: impl Into<Option<Rect>>, color: Color);
126
127 /// Stroke a [`Shape`], using the default [`StrokeStyle`].
128 fn stroke(&mut self, shape: impl Shape, brush: &impl IntoBrush<Self>, width: f64);
129
130 /// Stroke a [`Shape`], providing a custom [`StrokeStyle`].
131 fn stroke_styled(
132 &mut self,
133 shape: impl Shape,
134 brush: &impl IntoBrush<Self>,
135 width: f64,
136 style: &StrokeStyle,
137 );
138
139 /// Fill a [`Shape`], using the [non-zero fill rule].
140 ///
141 /// [non-zero fill rule]: https://en.wikipedia.org/wiki/Nonzero-rule
142 fn fill(&mut self, shape: impl Shape, brush: &impl IntoBrush<Self>);
143
144 /// Fill a shape, using the [even-odd fill rule].
145 ///
146 /// [even-odd fill rule]: https://en.wikipedia.org/wiki/Even–odd_rule
147 fn fill_even_odd(&mut self, shape: impl Shape, brush: &impl IntoBrush<Self>);
148
149 /// Clip to a [`Shape`].
150 ///
151 /// All subsequent drawing operations up to the next [`restore`]
152 /// are clipped by the shape.
153 ///
154 /// [`restore`]: RenderContext::restore
155 fn clip(&mut self, shape: impl Shape);
156
157 /// Returns a reference to a shared [`Text`] object.
158 ///
159 /// This provides access to the text API.
160 fn text(&mut self) -> &mut Self::Text;
161
162 /// Draw a [`TextLayout`].
163 ///
164 /// The `pos` parameter specifies the upper-left corner of the layout object
165 /// (even for right-to-left text). To draw on a baseline, you can use
166 /// [`TextLayout::line_metric`] to get the baseline position of a specific line.
167 fn draw_text(&mut self, layout: &Self::TextLayout, pos: impl Into<Point>);
168
169 /// Save the context state.
170 ///
171 /// Pushes the current context state onto a stack, to be popped by
172 /// [`restore`].
173 ///
174 /// Prefer [`with_save`] if possible, as that statically
175 /// enforces balance of save/restore pairs.
176 ///
177 /// The context state currently consists of a clip region and an affine
178 /// transform, but is expected to grow in the near future.
179 ///
180 /// [`restore`]: RenderContext::restore
181 /// [`with_save`]: RenderContext::with_save
182 fn save(&mut self) -> Result<(), Error>;
183
184 /// Restore the context state.
185 ///
186 /// Pop a context state that was pushed by [`save`]. See
187 /// that method for details.
188 ///
189 /// [`save`]: RenderContext::save
190 fn restore(&mut self) -> Result<(), Error>;
191
192 /// Do graphics operations with the context state saved and then restored.
193 ///
194 /// Equivalent to [`save`], calling `f`, then
195 /// [`restore`]. See those methods for more details.
196 ///
197 /// [`restore`]: RenderContext::restore
198 /// [`save`]: RenderContext::save
199 fn with_save(&mut self, f: impl FnOnce(&mut Self) -> Result<(), Error>) -> Result<(), Error> {
200 self.save()?;
201 // Always try to restore the stack, even if `f` errored.
202 f(self).and(self.restore())
203 }
204
205 /// Finish any pending operations.
206 ///
207 /// This will generally be called by a shell after all user drawing
208 /// operations but before presenting. Not all back-ends will handle this
209 /// the same way.
210 fn finish(&mut self) -> Result<(), Error>;
211
212 /// Apply a transform.
213 ///
214 /// Apply an affine transformation. The transformation remains in effect
215 /// until a [`restore`] operation.
216 ///
217 /// [`restore`]: RenderContext::restore
218 fn transform(&mut self, transform: Affine);
219
220 /// Create a new [`Image`] from a pixel buffer.
221 ///
222 /// This takes raw pixel data and attempts create an object that the
223 /// platform knows how to draw.
224 ///
225 /// The generated image can be cached and reused. This is a good idea for
226 /// images that are used frequently, because creating the image type may
227 /// be expensive.
228 ///
229 /// To draw the generated image, pass it to [`draw_image`]. To draw a portion
230 /// of the image, use [`draw_image_area`].
231 ///
232 /// If you are trying to create an image from the contents of this
233 /// [`RenderContext`], see [`capture_image_area`].
234 ///
235 /// # Arguments
236 ///
237 /// * `width` - The width of the image in pixels.
238 /// * `height` - The height of the image in pixels.
239 /// * `buf` - The pixel data. The length of this buffer must be at least
240 /// `width * height * format.bytes_per_pixel()`.
241 /// * `format` - The format of the pixel data.
242 ///
243 /// [`draw_image`]: RenderContext::draw_image
244 /// [`draw_image_area`]: RenderContext::draw_image_area
245 /// [`capture_image_area`]: RenderContext::capture_image_area
246 fn make_image(
247 &mut self,
248 width: usize,
249 height: usize,
250 buf: &[u8],
251 format: ImageFormat,
252 ) -> Result<Self::Image, Error> {
253 self.make_image_with_stride(width, height, width * format.bytes_per_pixel(), buf, format)
254 }
255
256 /// Create a new [`Image`] from a pixel buffer with a specified row stride.
257 ///
258 /// This has the same semantics as [`make_image`], but allows the caller to
259 /// specify the stride of the image data. It is useful for images that are
260 /// not tightly packed.
261 ///
262 /// # Arguments
263 ///
264 /// * `width` - The width of the image in pixels.
265 /// * `height` - The height of the image in pixels.
266 /// * `stride` - The number of bytes between the start of one row of pixels
267 /// and the start of the next row of pixels.
268 /// * `buf` - The pixel data for the image. The length of this buffer must
269 /// be at least `stride * height`.
270 /// * `format` - The format of the pixel data.
271 ///
272 /// [`make_image`]: RenderContext::make_image
273 fn make_image_with_stride(
274 &mut self,
275 width: usize,
276 height: usize,
277 stride: usize,
278 buf: &[u8],
279 format: ImageFormat,
280 ) -> Result<Self::Image, Error>;
281
282 /// Draw an [`Image`] into the provided [`Rect`].
283 ///
284 /// The image is scaled to fit the provided [`Rect`]; it will be squashed
285 /// if the aspect ratios don't match.
286 fn draw_image(
287 &mut self,
288 image: &Self::Image,
289 dst_rect: impl Into<Rect>,
290 interp: InterpolationMode,
291 );
292
293 /// Draw a specified area of an [`Image`].
294 ///
295 /// The `src_rect` area of `image` is scaled to the provided `dst_rect`.
296 /// It will be squashed if the aspect ratios don't match.
297 fn draw_image_area(
298 &mut self,
299 image: &Self::Image,
300 src_rect: impl Into<Rect>,
301 dst_rect: impl Into<Rect>,
302 interp: InterpolationMode,
303 );
304
305 /// Create an [`Image`] of the specified region of the context.
306 ///
307 /// The `src_rect` area of the current render context will be captured
308 /// as a copy and returned.
309 ///
310 /// This can be used for things like caching expensive drawing operations.
311 fn capture_image_area(&mut self, src_rect: impl Into<Rect>) -> Result<Self::Image, Error>;
312
313 /// Draw a rectangle with Gaussian blur.
314 ///
315 /// The blur radius is sometimes referred to as the "standard deviation" of
316 /// the blur.
317 fn blurred_rect(&mut self, rect: Rect, blur_radius: f64, brush: &impl IntoBrush<Self>);
318
319 /// Returns the transformations currently applied to the context.
320 fn current_transform(&self) -> Affine;
321}
322
323/// A trait for various types that can be used as brushes.
324///
325/// These include backend-independent types such [`Color`] and [`LinearGradient`],
326/// as well as the types used to represent these on a specific backend.
327///
328/// This is an internal trait that you should not have to implement or think about.
329pub trait IntoBrush<P: RenderContext>
330where
331 P: ?Sized,
332{
333 #[doc(hidden)]
334 fn make_brush<'a>(&'a self, piet: &mut P, bbox: impl FnOnce() -> Rect) -> Cow<'a, P::Brush>;
335}
336
337impl<P: RenderContext> IntoBrush<P> for Color {
338 fn make_brush<'a>(&'a self, piet: &mut P, _bbox: impl FnOnce() -> Rect) -> Cow<'a, P::Brush> {
339 Cow::Owned(piet.solid_brush(self.to_owned()))
340 }
341}
342
343/// A color or a gradient.
344///
345/// This type is provided as a convenience, so that library consumers can
346/// easily write methods and types that use or reference *something* that can
347/// be used as a brush, without needing to know what it is.
348///
349/// # Examples
350///
351/// ```no_run
352/// use piet::{Color, PaintBrush, RadialGradient};
353/// use piet::kurbo::Rect;
354///
355/// struct Widget {
356/// frame: Rect,
357/// background: PaintBrush,
358/// }
359///
360/// fn make_widget<T: Into<PaintBrush>>(frame: Rect, bg: T) -> Widget {
361/// Widget {
362/// frame,
363/// background: bg.into(),
364/// }
365/// }
366///
367/// let color_widget = make_widget(Rect::ZERO, Color::BLACK);
368/// let rad_grad = RadialGradient::new(0.8, (Color::WHITE, Color::BLACK));
369/// let gradient_widget = make_widget(Rect::ZERO, rad_grad);
370///
371/// ```
372#[derive(Debug, Clone)]
373pub enum PaintBrush {
374 /// A [`Color`].
375 Color(Color),
376 /// A [`LinearGradient`].
377 Linear(LinearGradient),
378 /// A [`RadialGradient`].
379 Radial(RadialGradient),
380 /// A [`FixedGradient`].
381 Fixed(FixedGradient),
382}
383
384impl<P: RenderContext> IntoBrush<P> for PaintBrush {
385 fn make_brush<'a>(&'a self, piet: &mut P, bbox: impl FnOnce() -> Rect) -> Cow<'a, P::Brush> {
386 match self {
387 PaintBrush::Color(color) => color.make_brush(piet, bbox),
388 PaintBrush::Linear(linear) => linear.make_brush(piet, bbox),
389 PaintBrush::Radial(radial) => radial.make_brush(piet, bbox),
390 PaintBrush::Fixed(fixed) => fixed.make_brush(piet, bbox),
391 }
392 }
393}
394
395impl From<Color> for PaintBrush {
396 fn from(src: Color) -> PaintBrush {
397 PaintBrush::Color(src)
398 }
399}
400
401impl From<LinearGradient> for PaintBrush {
402 fn from(src: LinearGradient) -> PaintBrush {
403 PaintBrush::Linear(src)
404 }
405}
406
407impl From<RadialGradient> for PaintBrush {
408 fn from(src: RadialGradient) -> PaintBrush {
409 PaintBrush::Radial(src)
410 }
411}
412
413impl From<FixedGradient> for PaintBrush {
414 fn from(src: FixedGradient) -> PaintBrush {
415 PaintBrush::Fixed(src)
416 }
417}
418
419impl From<FixedLinearGradient> for PaintBrush {
420 fn from(src: FixedLinearGradient) -> PaintBrush {
421 PaintBrush::Fixed(src.into())
422 }
423}
424
425impl From<FixedRadialGradient> for PaintBrush {
426 fn from(src: FixedRadialGradient) -> PaintBrush {
427 PaintBrush::Fixed(src.into())
428 }
429}