Skip to main content

carnelian/render/
dynamic.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::color::Color;
6use crate::render::generic::forma::*;
7use crate::render::generic::{
8    self, Composition as _, Context as _, PathBuilder as _, Raster as _, RasterBuilder as _,
9};
10pub use crate::render::generic::{BlendMode, Fill, FillRule, Gradient, GradientType, Order, Style};
11use crate::{Point, ViewAssistantContext};
12use anyhow::Error;
13use display_utils::PixelFormat;
14use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D};
15use euclid::{point2, size2};
16use std::io::Read;
17use std::ops::Add;
18use std::{mem, u32};
19/// Rendering context and API start point.
20#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
21pub struct Image {
22    inner: ImageInner,
23}
24#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
25enum ImageInner {
26    Forma(FormaImage),
27}
28/// Rectangular copy region.
29#[derive(Clone, Copy, Debug, PartialEq)]
30pub struct CopyRegion {
31    /// Top-left origin of source rectangle.
32    pub src_offset: Point2D<u32>,
33    /// Top-left origin of destination rectangle.
34    pub dst_offset: Point2D<u32>,
35    /// Size of both source and destination rectangles.
36    pub extent: Size2D<u32>,
37}
38/// Rendering extensions.
39#[derive(Clone, Debug, Default, PartialEq)]
40pub struct RenderExt {
41    /// Clears render image before rendering.
42    pub pre_clear: Option<PreClear>,
43    /// Copies from source image to render image before rendering.
44    pub pre_copy: Option<PreCopy>,
45    /// Copies from render image to destination image after rendering.
46    pub post_copy: Option<PostCopy>,
47}
48/// Pre-render image clear.
49#[derive(Clone, Debug, PartialEq)]
50pub struct PreClear {
51    /// Clear color.
52    pub color: Color,
53}
54/// Pre-render image copy.
55#[derive(Clone, Debug, PartialEq)]
56pub struct PreCopy {
57    /// Source image to copy from.
58    pub image: Image,
59    /// Copy region properties.
60    pub copy_region: CopyRegion,
61}
62/// Post-render image copy.
63#[derive(Clone, Debug, PartialEq)]
64pub struct PostCopy {
65    /// Destination image to copy to. Must be different from render image.
66    pub image: Image,
67    /// Copy region properties.
68    pub copy_region: CopyRegion,
69}
70/// Rendering context and API start point.
71#[derive(Debug)]
72pub struct Context {
73    pub inner: ContextInner,
74}
75#[derive(Debug)]
76pub enum ContextInner {
77    Forma(FormaContext),
78}
79impl Context {
80    /// Returns the context's pixel format.
81    pub fn pixel_format(&self) -> PixelFormat {
82        match &self.inner {
83            ContextInner::Forma(context) => context.pixel_format(),
84        }
85    }
86    /// Optionally returns a `PathBuilder`. May return `None` of old builder is still alive.
87    pub fn path_builder(&self) -> Option<PathBuilder> {
88        match &self.inner {
89            ContextInner::Forma(context) => context
90                .path_builder()
91                .map(|path_builder| PathBuilder { inner: PathBuilderInner::Forma(path_builder) }),
92        }
93    }
94    /// Optionally returns a `RasterBuilder`. May return `None` of old builder is still alive.
95    pub fn raster_builder(&self) -> Option<RasterBuilder> {
96        match &self.inner {
97            ContextInner::Forma(context) => context.raster_builder().map(|raster_builder| {
98                RasterBuilder { inner: RasterBuilderInner::Forma(raster_builder) }
99            }),
100        }
101    }
102    /// Creates a new image with `size`.
103    pub fn new_image(&mut self, size: Size2D<u32>) -> Image {
104        match &mut self.inner {
105            ContextInner::Forma(context) => {
106                Image { inner: ImageInner::Forma(context.new_image(size)) }
107            }
108        }
109    }
110    /// Creates a new image from PNG `reader`.
111    pub fn new_image_from_png<R: Read>(
112        &mut self,
113        reader: &mut png::Reader<R>,
114    ) -> Result<Image, Error> {
115        Ok(Image {
116            inner: match &mut self.inner {
117                ContextInner::Forma(context) => {
118                    ImageInner::Forma(context.new_image_from_png(reader)?)
119                }
120            },
121        })
122    }
123    /// Returns the image at `image_index`.
124    pub fn get_image(&mut self, image_index: u32) -> Image {
125        match &mut self.inner {
126            ContextInner::Forma(render_context) => {
127                Image { inner: ImageInner::Forma(render_context.get_image(image_index)) }
128            }
129        }
130    }
131    /// Returns the `context`'s current image.
132    pub fn get_current_image(&mut self, context: &ViewAssistantContext) -> Image {
133        match &mut self.inner {
134            ContextInner::Forma(render_context) => {
135                Image { inner: ImageInner::Forma(render_context.get_current_image(context)) }
136            }
137        }
138    }
139
140    /// Renders the composition with an optional clip to the image.
141    pub fn render(
142        &mut self,
143        composition: &mut Composition,
144        clip: Option<Rect<u32>>,
145        image: Image,
146        ext: &RenderExt,
147    ) {
148        self.render_with_clip(
149            composition,
150            clip.unwrap_or_else(|| {
151                Rect::new(point2(u32::MIN, u32::MIN), size2(u32::MAX, u32::MAX))
152            }),
153            image,
154            ext,
155        );
156    }
157    /// Renders the composition with a clip to the image.
158    pub fn render_with_clip(
159        &mut self,
160        composition: &mut Composition,
161        clip: Rect<u32>,
162        image: Image,
163        ext: &RenderExt,
164    ) {
165        let background_color = composition.background_color;
166
167        match &mut self.inner {
168            ContextInner::Forma(context) => {
169                let ImageInner::Forma(image) = image.inner;
170                let ext = generic::RenderExt {
171                    pre_clear: ext
172                        .pre_clear
173                        .clone()
174                        .map(|pre_clear| generic::PreClear { color: pre_clear.color }),
175                    pre_copy: ext.pre_copy.clone().map(|pre_copy| {
176                        let ImageInner::Forma(src_image) = pre_copy.image.inner;
177                        generic::PreCopy {
178                            image: src_image,
179                            copy_region: generic::CopyRegion {
180                                src_offset: pre_copy.copy_region.src_offset,
181                                dst_offset: pre_copy.copy_region.dst_offset,
182                                extent: pre_copy.copy_region.extent,
183                            },
184                        }
185                    }),
186                    post_copy: ext.post_copy.clone().map(|post_copy| {
187                        let ImageInner::Forma(dst_image) = post_copy.image.inner;
188                        generic::PostCopy {
189                            image: dst_image,
190                            copy_region: generic::CopyRegion {
191                                src_offset: post_copy.copy_region.src_offset,
192                                dst_offset: post_copy.copy_region.dst_offset,
193                                extent: post_copy.copy_region.extent,
194                            },
195                        }
196                    }),
197                };
198                composition.with_inner_composition(|inner| {
199                    let mut composition = match inner {
200                        CompositionInner::Forma(composition) => composition,
201                        CompositionInner::Empty => FormaComposition::new(background_color),
202                    };
203                    context.render_with_clip(&mut composition, clip, image, &ext);
204                    CompositionInner::Forma(composition)
205                });
206            }
207        }
208    }
209}
210/// Closed path built by a `PathBuilder`.
211#[derive(Clone, Debug, Eq, PartialEq)]
212pub struct Path {
213    inner: PathInner,
214}
215#[derive(Clone, Debug, Eq, PartialEq)]
216enum PathInner {
217    Forma(FormaPath),
218}
219/// Builds one closed path.
220#[derive(Debug)]
221pub struct PathBuilder {
222    inner: PathBuilderInner,
223}
224#[derive(Debug)]
225enum PathBuilderInner {
226    Forma(FormaPathBuilder),
227}
228impl PathBuilder {
229    /// Move end-point to.
230    pub fn move_to(&mut self, point: Point) -> &mut Self {
231        match &mut self.inner {
232            PathBuilderInner::Forma(path_builder) => {
233                path_builder.move_to(point);
234            }
235        }
236        self
237    }
238    /// Create line from end-point to point and update end-point.
239    pub fn line_to(&mut self, point: Point) -> &mut Self {
240        match &mut self.inner {
241            PathBuilderInner::Forma(path_builder) => {
242                path_builder.line_to(point);
243            }
244        }
245        self
246    }
247    /// Create quadratic Bézier from end-point to `p2` with `p1` as control point.
248    pub fn quad_to(&mut self, p1: Point, p2: Point) -> &mut Self {
249        match &mut self.inner {
250            PathBuilderInner::Forma(path_builder) => {
251                path_builder.quad_to(p1, p2);
252            }
253        }
254        self
255    }
256    /// Create cubic Bézier from end-point to `p3` with `p1` and `p2` as control points.
257    pub fn cubic_to(&mut self, p1: Point, p2: Point, p3: Point) -> &mut Self {
258        match &mut self.inner {
259            PathBuilderInner::Forma(path_builder) => {
260                path_builder.cubic_to(p1, p2, p3);
261            }
262        }
263        self
264    }
265    /// Create rational quadratic Bézier from end-point to `p2` with `p1` as control point
266    /// and `w` as its weight.
267    pub fn rat_quad_to(&mut self, p1: Point, p2: Point, w: f32) -> &mut Self {
268        match &mut self.inner {
269            PathBuilderInner::Forma(path_builder) => {
270                path_builder.rat_quad_to(p1, p2, w);
271            }
272        }
273        self
274    }
275    /// Create rational cubic Bézier from end-point to `p3` with `p1` and `p2` as control
276    /// points, and `w1` and `w2` their weights.
277    pub fn rat_cubic_to(&mut self, p1: Point, p2: Point, p3: Point, w1: f32, w2: f32) -> &mut Self {
278        match &mut self.inner {
279            PathBuilderInner::Forma(path_builder) => {
280                path_builder.rat_cubic_to(p1, p2, p3, w1, w2);
281            }
282        }
283        self
284    }
285    /// Closes the path with a line if not yet closed and builds the path.
286    ///
287    /// Consumes the builder; another one can be requested from the `Context`.
288    pub fn build(self) -> Path {
289        match self.inner {
290            PathBuilderInner::Forma(path_builder) => {
291                Path { inner: PathInner::Forma(path_builder.build()) }
292            }
293        }
294    }
295}
296/// Raster built by a `RasterBuilder`.
297#[derive(Clone, Debug, Eq, PartialEq)]
298pub struct Raster {
299    inner: RasterInner,
300}
301#[derive(Clone, Debug, Eq, PartialEq)]
302enum RasterInner {
303    Forma(FormaRaster),
304}
305impl Raster {
306    /// Translate raster.
307    pub fn translate(self, translation: Vector2D<i32>) -> Self {
308        match self.inner {
309            RasterInner::Forma(raster) => {
310                Raster { inner: RasterInner::Forma(raster.translate(translation)) }
311            }
312        }
313    }
314}
315impl Add for Raster {
316    type Output = Self;
317    fn add(self, other: Self) -> Self::Output {
318        let RasterInner::Forma(other_raster) = other.inner;
319        match self.inner {
320            RasterInner::Forma(raster) => {
321                Raster { inner: RasterInner::Forma(raster + other_raster) }
322            }
323        }
324    }
325}
326/// Builds one Raster.
327#[derive(Debug)]
328pub struct RasterBuilder {
329    inner: RasterBuilderInner,
330}
331#[derive(Debug)]
332enum RasterBuilderInner {
333    Forma(FormaRasterBuilder),
334}
335impl RasterBuilder {
336    /// Add a path to the raster with optional transform.
337    pub fn add(&mut self, path: &Path, transform: Option<&Transform2D<f32>>) -> &mut Self {
338        let identity = Transform2D::identity();
339        self.add_with_transform(path, transform.unwrap_or(&identity))
340    }
341    /// Add a path to the raster with transform.
342    pub fn add_with_transform(&mut self, path: &Path, transform: &Transform2D<f32>) -> &mut Self {
343        match &mut self.inner {
344            RasterBuilderInner::Forma(raster_builder) => {
345                let PathInner::Forma(path) = &path.inner;
346                raster_builder.add_with_transform(path, transform);
347            }
348        }
349        self
350    }
351    /// Builds the raster.
352    ///
353    /// Consumes the builder; another one can be requested from the `Context`.
354    pub fn build(self) -> Raster {
355        match self.inner {
356            RasterBuilderInner::Forma(raster_builder) => {
357                Raster { inner: RasterInner::Forma(raster_builder.build()) }
358            }
359        }
360    }
361}
362
363#[derive(Clone, Debug)]
364pub struct Layer {
365    /// Layer raster.
366    pub raster: Raster,
367    /// Layer clip.
368    pub clip: Option<Raster>,
369    /// Layer style.
370    pub style: Style, // Will also contain txty when available.
371}
372#[derive(Debug)]
373/// A group of ordered layers.
374pub struct Composition {
375    inner: CompositionInner,
376    background_color: Color,
377}
378#[derive(Debug)]
379enum CompositionInner {
380    Forma(FormaComposition),
381    Empty,
382}
383impl Composition {
384    /// Creates a composition of ordered layers where the layers with lower index appear on top.
385    pub fn new(background_color: Color) -> Self {
386        Self { inner: CompositionInner::Empty, background_color }
387    }
388    fn with_inner_composition(&mut self, mut f: impl FnMut(CompositionInner) -> CompositionInner) {
389        let inner = f(mem::replace(&mut self.inner, CompositionInner::Empty));
390        self.inner = inner;
391    }
392    /// Resets composition by removing all layers.
393    pub fn clear(&mut self) {
394        match &mut self.inner {
395            CompositionInner::Forma(composition) => composition.clear(),
396            CompositionInner::Empty => (),
397        }
398    }
399    /// Insert layer into composition.
400    pub fn insert(&mut self, order: Order, layer: Layer) {
401        if let CompositionInner::Empty = self.inner {
402            match layer {
403                Layer { raster: Raster { inner: RasterInner::Forma(_) }, .. } => {
404                    self.inner =
405                        CompositionInner::Forma(FormaComposition::new(self.background_color));
406                }
407            }
408        }
409        match &mut self.inner {
410            CompositionInner::Forma(composition) => composition.insert(
411                order,
412                generic::Layer {
413                    raster: {
414                        let RasterInner::Forma(raster) = layer.raster.inner;
415                        raster
416                    },
417                    clip: layer.clip.map(|clip| {
418                        let RasterInner::Forma(clip) = clip.inner;
419                        clip
420                    }),
421                    style: layer.style,
422                },
423            ),
424            _ => unreachable!(),
425        }
426    }
427    /// Remove layer from composition.
428    pub fn remove(&mut self, order: Order) {
429        match &mut self.inner {
430            CompositionInner::Forma(composition) => composition.remove(order),
431            _ => (),
432        }
433    }
434}