1use crate::color::Color;
6use crate::render::{
7 BlendMode, Context as RenderContext, Fill, FillRule, Gradient, GradientType, Layer, Path,
8 PathBuilder, Raster, Style,
9};
10use crate::Point;
11use anyhow::{Context, Error};
12use euclid::{vec2, Transform2D};
13use fuchsia_trace::duration;
14use rive_rs::math::{self, Mat};
15use rive_rs::shapes::{Command, CommandPath};
16use rive_rs::{self as rive, ImportError, PaintColor, RenderPaint};
17use std::collections::HashMap;
18use std::fs;
19use std::num::NonZeroU64;
20
21pub fn load_rive<P: AsRef<std::path::Path> + std::fmt::Debug>(
22 path: P,
23) -> Result<rive::File, Error> {
24 let buffer = fs::read(&path).with_context(|| format!("Failed to read rive from {:?}", path))?;
25 let mut reader = rive::BinaryReader::new(&buffer);
26 let file = rive::File::import(&mut reader).map_err(|error| {
27 let context = match error {
28 ImportError::UnsupportedVersion => format!("Unsupported version: {:?}", path),
29 ImportError::Malformed => format!("Malformed: {:?}", path),
30 };
31 anyhow::anyhow!(context)
32 })?;
33
34 Ok(file)
35}
36
37#[derive(Clone, Debug)]
38struct CachedRaster {
39 raster: Raster,
40 inverted_transform: Mat,
41 was_used: bool,
42}
43
44#[derive(Debug)]
45pub struct RenderCache {
46 cached_rasters: HashMap<NonZeroU64, CachedRaster>,
47 tag: NonZeroU64,
48 pub layers: Vec<Layer>,
49}
50
51impl RenderCache {
52 pub fn new() -> Self {
53 Self {
54 cached_rasters: HashMap::new(),
55 tag: NonZeroU64::new(1).unwrap(),
56 layers: Vec::new(),
57 }
58 }
59
60 fn reset(&mut self) {
61 duration!(c"gfx", c"render::Rive::RenderCache::reset");
62
63 self.cached_rasters
65 .retain(|_, cached_raster| std::mem::replace(&mut cached_raster.was_used, false));
66 }
67
68 pub fn with_renderer(&mut self, context: &RenderContext, f: impl FnOnce(&mut Renderer<'_>)) {
69 duration!(c"gfx", c"render::Rive::RenderCache::with_renderer");
70
71 self.reset();
72 f(&mut Renderer { context, cache: self, clip: None });
73 }
74}
75
76#[derive(Debug)]
77pub struct Renderer<'c> {
78 context: &'c RenderContext,
79 cache: &'c mut RenderCache,
80 clip: Option<Raster>,
81}
82
83impl<'c> Renderer<'c> {
84 pub fn tag(&mut self) -> NonZeroU64 {
85 let tag = self.cache.tag;
86 self.cache.tag = NonZeroU64::new(tag.get().checked_add(1).unwrap_or(1)).unwrap();
87 tag
88 }
89}
90
91fn to_point(p: math::Vec) -> Point {
92 Point::new(p.x, p.y)
93}
94
95fn to_path(mut path_builder: PathBuilder, commands: &[Command]) -> Path {
96 let mut start_point = None;
97 let mut end_point = None;
98
99 for command in commands {
100 match *command {
101 Command::MoveTo(p) => {
102 if start_point != end_point {
103 path_builder.line_to(to_point(start_point.unwrap()));
104 }
105 path_builder.move_to(to_point(p));
106 start_point = Some(p);
107 end_point = Some(p);
108 }
109 Command::LineTo(p) => {
110 path_builder.line_to(to_point(p));
111 end_point = Some(p);
112 }
113 Command::CubicTo(c0, c1, p) => {
114 path_builder.cubic_to(to_point(c0), to_point(c1), to_point(p));
115 end_point = Some(p);
116 }
117 Command::Close => {
118 if start_point != end_point {
119 path_builder.line_to(to_point(start_point.unwrap()));
120 }
121 end_point = start_point;
122 }
123 }
124 }
125
126 if start_point != end_point {
127 path_builder.line_to(to_point(start_point.unwrap()));
128 }
129
130 path_builder.build()
131}
132
133impl rive::Renderer for Renderer<'_> {
134 fn draw(&mut self, path: &CommandPath, transform: Mat, paint: &RenderPaint) {
135 fn transform_translates_by_integers(transform: &Mat) -> bool {
136 fn approx_eq(a: f32, b: f32) -> bool {
137 (a - b).abs() < 0.001
138 }
139
140 approx_eq(transform.scale_x, 1.0)
141 && approx_eq(transform.shear_x, 0.0)
142 && approx_eq(transform.shear_y, 0.0)
143 && approx_eq(transform.scale_y, 1.0)
144 && transform.translate_x.fract().abs() < 0.001
145 && transform.translate_y.fract().abs() < 0.001
146 }
147
148 let raster = match path
149 .user_tag
150 .get()
151 .and_then(|tag| self.cache.cached_rasters.get_mut(&tag))
152 .and_then(|cached_raster| {
153 if cached_raster.was_used {
154 return None;
155 }
156
157 cached_raster.was_used = true;
158 Some((&mut cached_raster.raster, transform * cached_raster.inverted_transform))
159 }) {
160 Some((raster, transform)) if transform_translates_by_integers(&transform) => {
161 *raster = raster.clone().translate(vec2(
162 transform.translate_x.round() as i32,
163 transform.translate_y.round() as i32,
164 ));
165 raster.clone()
166 }
167 _ => {
168 let render_path = to_path(self.context.path_builder().unwrap(), &path.commands);
169
170 let mut raster_builder = self.context.raster_builder().unwrap();
171
172 raster_builder.add_with_transform(
173 &render_path,
174 &Transform2D::new(
175 transform.scale_x,
176 transform.shear_y,
177 transform.shear_x,
178 transform.scale_y,
179 transform.translate_x,
180 transform.translate_y,
181 ),
182 );
183
184 let raster = raster_builder.build();
185
186 if let Some(inverted_transform) = transform.invert() {
187 let tag = self.tag();
188
189 path.user_tag.set(Some(tag));
190 self.cache.cached_rasters.insert(
191 tag,
192 CachedRaster { raster: raster.clone(), inverted_transform, was_used: true },
193 );
194 }
195
196 raster
197 }
198 };
199
200 let blend_mode = match paint.blend_mode {
201 rive::shapes::paint::BlendMode::SrcOver => BlendMode::Over,
202 rive::shapes::paint::BlendMode::Screen => BlendMode::Screen,
203 rive::shapes::paint::BlendMode::Overlay => BlendMode::Overlay,
204 rive::shapes::paint::BlendMode::Darken => BlendMode::Darken,
205 rive::shapes::paint::BlendMode::Lighten => BlendMode::Lighten,
206 rive::shapes::paint::BlendMode::ColorDodge => BlendMode::ColorDodge,
207 rive::shapes::paint::BlendMode::ColorBurn => BlendMode::ColorBurn,
208 rive::shapes::paint::BlendMode::HardLight => BlendMode::HardLight,
209 rive::shapes::paint::BlendMode::SoftLight => BlendMode::SoftLight,
210 rive::shapes::paint::BlendMode::Difference => BlendMode::Difference,
211 rive::shapes::paint::BlendMode::Exclusion => BlendMode::Exclusion,
212 rive::shapes::paint::BlendMode::Multiply => BlendMode::Multiply,
213 rive::shapes::paint::BlendMode::Hue => BlendMode::Hue,
214 rive::shapes::paint::BlendMode::Saturation => BlendMode::Saturation,
215 rive::shapes::paint::BlendMode::Color => BlendMode::Color,
216 rive::shapes::paint::BlendMode::Luminosity => BlendMode::Luminosity,
217 };
218
219 let fill_rule = match paint.fill_rule {
220 rive::shapes::FillRule::NonZero => FillRule::NonZero,
221 rive::shapes::FillRule::EvenOdd => FillRule::EvenOdd,
222 };
223
224 fn to_color(color: &rive::shapes::paint::Color32) -> Color {
225 Color { r: color.red(), g: color.green(), b: color.blue(), a: color.alpha() }
226 }
227
228 let style = match &paint.color {
229 PaintColor::Solid(color) => {
230 Style { fill_rule, fill: Fill::Solid(to_color(color)), blend_mode }
231 }
232 PaintColor::Gradient(gradient) => {
233 let start = transform * gradient.start;
234 let end = transform * gradient.end;
235
236 Style {
237 fill_rule,
238 fill: Fill::Gradient(Gradient {
239 r#type: match gradient.r#type {
240 rive::GradientType::Linear => GradientType::Linear,
241 rive::GradientType::Radial => GradientType::Radial,
242 },
243 start: Point::new(start.x, start.y),
244 end: Point::new(end.x, end.y),
245 stops: gradient
246 .stops
247 .iter()
248 .map(|(color, stop)| (to_color(color), *stop))
249 .collect::<Vec<_>>(),
250 }),
251 blend_mode,
252 }
253 }
254 };
255
256 self.cache.layers.push(Layer {
257 raster,
258 clip: paint.is_clipped.then(|| self.clip.clone()).flatten(),
259 style,
260 });
261 }
262
263 fn clip(&mut self, path: &CommandPath, transform: Mat, _: usize) {
264 fn transform_translates_by_integers(transform: &Mat) -> bool {
265 fn approx_eq(a: f32, b: f32) -> bool {
266 (a - b).abs() < 0.001
267 }
268
269 approx_eq(transform.scale_x, 1.0)
270 && approx_eq(transform.shear_x, 0.0)
271 && approx_eq(transform.shear_y, 0.0)
272 && approx_eq(transform.scale_y, 1.0)
273 && transform.translate_x.fract().abs() < 0.001
274 && transform.translate_y.fract().abs() < 0.001
275 }
276
277 let raster = match path
278 .user_tag
279 .get()
280 .and_then(|tag| self.cache.cached_rasters.get_mut(&tag))
281 .and_then(|cached_raster| {
282 if cached_raster.was_used {
283 return None;
284 }
285
286 cached_raster.was_used = true;
287 Some((&mut cached_raster.raster, transform * cached_raster.inverted_transform))
288 }) {
289 Some((raster, transform)) if transform_translates_by_integers(&transform) => {
290 *raster = raster.clone().translate(vec2(
291 transform.translate_x.round() as i32,
292 transform.translate_y.round() as i32,
293 ));
294 raster.clone()
295 }
296 _ => {
297 let render_path = to_path(self.context.path_builder().unwrap(), &path.commands);
298
299 let mut raster_builder = self.context.raster_builder().unwrap();
300
301 raster_builder.add_with_transform(
302 &render_path,
303 &Transform2D::new(
304 transform.scale_x,
305 transform.shear_y,
306 transform.shear_x,
307 transform.scale_y,
308 transform.translate_x,
309 transform.translate_y,
310 ),
311 );
312
313 let raster = raster_builder.build();
314
315 if let Some(inverted_transform) = transform.invert() {
316 let tag = self.tag();
317
318 path.user_tag.set(Some(tag));
319 self.cache.cached_rasters.insert(
320 tag,
321 CachedRaster { raster: raster.clone(), inverted_transform, was_used: true },
322 );
323 }
324
325 raster
326 }
327 };
328
329 self.clip = Some(raster);
330 }
331}