forma_render/
lib.rs

1// Copyright 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#![doc(test(attr(deny(warnings))))]
16
17//! forma is a high-performance vector-graphics renderer with a CPU & GPU back-end.
18//!
19//! ## Example
20//!
21//! The following example renders two overlapping rectangles and highlights the most common API
22//! usage:
23//!
24//! ```
25//! # use forma_render as forma;
26//! use forma::{cpu::{buffer::{BufferBuilder, layout::LinearLayout}, Renderer, RGBA}, prelude::*};
27//!
28//! fn rect(x: f32, y: f32, width: f32, height: f32) -> Path {
29//!     PathBuilder::new()
30//!         .move_to(Point::new(x, y))
31//!         .line_to(Point::new(x + width, y))
32//!         .line_to(Point::new(x + width, y + height))
33//!         .line_to(Point::new(x, y + height))
34//!         .build()
35//! }
36//!
37//! fn solid(r: f32, g: f32, b: f32) -> Props {
38//!     Props {
39//!         func: Func::Draw(Style {
40//!             fill: Fill::Solid(Color { r, g, b, a: 1.0 }),
41//!             ..Default::default()
42//!         }),
43//!         ..Default::default()
44//!     }
45//! }
46//!
47//! // The composition is akin to a `HashMap<Order, Layer>`. Layers can be inserted and
48//! // removed from the composition by their orders.
49//! let mut composition = Composition::new();
50//! let mut renderer = Renderer::new();
51//!
52//! // The layer cache enables updating only tiles that changed from last frame.
53//! let layer_cache = renderer.create_buffer_layer_cache().unwrap();
54//!
55//! composition
56//!     .get_mut_or_insert_default(Order::new(0).unwrap())
57//!     .insert(&rect(50.0, 50.0, 100.0, 50.0))
58//!     .set_props(solid(1.0, 0.0, 0.0));
59//!
60//! composition
61//!     .get_mut_or_insert_default(Order::new(1).unwrap())
62//!     .insert(&rect(100.0, 50.0, 100.0, 50.0))
63//!     .set_props(solid(0.0, 0.0, 1.0));
64//!
65//! let width = 250;
66//! let height = 150;
67//! let mut buffer = vec![0; width * height * 4]; // 4 bytes per pixel.
68//!
69//! renderer.render(
70//!     &mut composition,
71//!     // Stride is width * 4 bytes per pixel.
72//!     &mut BufferBuilder::new(&mut buffer, &mut LinearLayout::new(width, width * 4, height))
73//!         .layer_cache(layer_cache.clone())
74//!         .build(),
75//!     RGBA,
76//!     Color { r: 1.0, g: 1.0, b: 1.0, a: 1.0 },
77//!     None,
78//! );
79//!
80//! // Background is white.
81//! assert_eq!(buffer.chunks(4).next().unwrap(), [255, 255, 255, 255]);
82//!
83//! // First rectangle is red.
84//! let index = 75 + 75 * width;
85//! assert_eq!(buffer.chunks(4).nth(index).unwrap(), [255, 0, 0, 255]);
86//!
87//! // Overlap is blue.
88//! let index = 125 + 75 * width;
89//! assert_eq!(buffer.chunks(4).nth(index).unwrap(), [0, 0, 255, 255]);
90//!
91//! // Second rectangle is blue.
92//! let index = 175 + 75 * width;
93//! assert_eq!(buffer.chunks(4).nth(index).unwrap(), [0, 0, 255, 255]);
94//! ```
95//!
96//! ## Reusing the composition
97//!
98//! For best possible performance, reusing the composition is essential. This may mean that in some
99//! cases one might have to remove, then re-insert some layers around in order to achieve the
100//! desired ordering of layers.
101//!
102//! For simple cases, [Layer::set_is_enabled] can provide an alternative to removing and
103//! re-inserting layers into the composition.
104
105#[cfg(target_os = "fuchsia")]
106macro_rules! duration {
107    ($category:expr, $name:expr $(, $key:expr => $val:expr)*) => {
108        fuchsia_trace::duration!($category, $name $(, $key => $val)*)
109    }
110}
111
112#[cfg(not(target_os = "fuchsia"))]
113macro_rules! duration {
114    ($category:expr, $name:expr $(, $key:expr => $val:expr)*) => {};
115}
116
117mod composition;
118pub mod consts;
119pub mod cpu;
120#[cfg(feature = "gpu")]
121pub mod gpu;
122pub mod math;
123mod path;
124mod segment;
125pub mod styling;
126mod utils;
127
128pub(crate) use self::segment::{SegmentBuffer, SegmentBufferView};
129
130pub use self::{
131    composition::{Composition, Layer},
132    path::{Path, PathBuilder},
133    segment::GeomId,
134    utils::{Order, OrderError},
135};
136
137pub mod prelude {
138    pub use crate::cpu::{
139        buffer::{
140            layout::{Layout, LinearLayout},
141            BufferBuilder, BufferLayerCache,
142        },
143        Channel, BGR0, BGR1, BGRA, RGB0, RGB1, RGBA,
144    };
145    pub use crate::gpu::Timings;
146    pub use crate::{
147        math::{AffineTransform, GeomPresTransform, Point},
148        styling::{
149            BlendMode, Color, Fill, FillRule, Func, Gradient, GradientBuilder, GradientType, Image,
150            Props, Style, Texture,
151        },
152        Composition, Layer, Order, Path, PathBuilder,
153    };
154}