orbtk_render/raqote/
mod.rs

1use std::{cmp, collections::HashMap};
2
3use crate::{utils::*, PipelineTrait, RenderConfig, RenderTarget, TextMetrics};
4
5pub use self::font::*;
6pub use self::image::Image;
7
8mod font;
9mod image;
10
11/// The RenderContext2D trait, provides the rendering ctx. It is used for drawing shapes, text, images, and other objects.
12pub struct RenderContext2D {
13    draw_target: raqote::DrawTarget,
14    path: raqote::Path,
15    config: RenderConfig,
16    saved_config: Option<RenderConfig>,
17    fonts: HashMap<String, Font>,
18
19    // hack / work around for faster text clipping
20    clip: bool,
21    last_rect: Rectangle,
22    clip_rect: Option<Rectangle>,
23
24    background: Color,
25}
26
27impl RenderContext2D {
28    /// Creates a new render ctx 2d.
29    pub fn new(width: f64, height: f64) -> Self {
30        RenderContext2D {
31            draw_target: raqote::DrawTarget::new(width as i32, height as i32),
32            path: raqote::Path {
33                ops: Vec::new(),
34                winding: raqote::Winding::NonZero,
35            },
36            config: RenderConfig::default(),
37            saved_config: None,
38            fonts: HashMap::new(),
39            clip: false,
40            last_rect: Rectangle::new((0.0, 0.0), (width, height)),
41            clip_rect: None,
42            background: Color::default(),
43        }
44    }
45
46    /// Set the background of the render context.
47    pub fn set_background(&mut self, background: Color) {
48        self.background = background;
49    }
50
51    pub fn resize(&mut self, width: f64, height: f64) {
52        self.draw_target = raqote::DrawTarget::new(width as i32, height as i32);
53    }
54
55    /// Registers a new font file.
56    pub fn register_font(&mut self, family: &str, font_file: &'static [u8]) {
57        if self.fonts.contains_key(family) {
58            return;
59        }
60
61        if let Ok(font) = Font::from_bytes(font_file) {
62            self.fonts.insert(family.to_string(), font);
63        }
64    }
65
66    // Rectangles
67
68    /// Draws a filled rectangle whose starting point is at the coordinates {x, y} with the specified width and height and whose style is determined by the fillStyle attribute.
69    pub fn fill_rect(&mut self, x: f64, y: f64, width: f64, height: f64) {
70        self.draw_target.fill_rect(
71            x as f32,
72            y as f32,
73            width as f32,
74            height as f32,
75            &brush_to_source(&self.config.fill_style),
76            &raqote::DrawOptions {
77                alpha: self.config.alpha,
78                ..Default::default()
79            },
80        );
81    }
82
83    /// Draws a rectangle that is stroked (outlined) according to the current strokeStyle and other ctx settings.
84    pub fn stroke_rect(&mut self, x: f64, y: f64, width: f64, height: f64) {
85        self.rect(x, y, width, height);
86        self.stroke();
87    }
88
89    // Text
90
91    /// Draws (fills) a given text at the given (x, y) position.
92    pub fn fill_text(&mut self, text: &str, x: f64, y: f64) {
93        if text.is_empty() {
94            return;
95        }
96
97        let color = match self.config.fill_style {
98            Brush::SolidColor(color) => color,
99            _ => Color::from("#000000"),
100        };
101
102        if color.a() == 0 || self.config.alpha == 0.0 {
103            return;
104        }
105
106        if let Some(font) = self.fonts.get(&self.config.font_config.family) {
107            let width = self.draw_target.width() as f64;
108
109            if self.clip {
110                if let Some(rect) = self.clip_rect {
111                    font.render_text_clipped(
112                        text,
113                        self.draw_target.get_data_mut(),
114                        width,
115                        (self.config.font_config.font_size, color, self.config.alpha),
116                        (x, y),
117                        rect,
118                    );
119                } else {
120                    font.render_text(
121                        text,
122                        self.draw_target.get_data_mut(),
123                        width,
124                        (self.config.font_config.font_size, color, self.config.alpha),
125                        (x, y),
126                    );
127                }
128            } else {
129                font.render_text(
130                    text,
131                    self.draw_target.get_data_mut(),
132                    width,
133                    (self.config.font_config.font_size, color, self.config.alpha),
134                    (x, y),
135                );
136            }
137        }
138    }
139
140    /// Returns a TextMetrics object.
141    pub fn measure_text(&mut self, text: &str) -> TextMetrics {
142        let mut text_metrics = TextMetrics::default();
143
144        if text.is_empty() {
145            return text_metrics;
146        }
147
148        if let Some(font) = self.fonts.get(&self.config.font_config.family) {
149            let (width, height) = font.measure_text(text, self.config.font_config.font_size);
150
151            text_metrics.width = width;
152            text_metrics.height = height;
153        }
154
155        text_metrics
156    }
157
158    /// Fills the current or given path with the current file style.
159    pub fn fill(&mut self) {
160        self.draw_target.fill(
161            &self.path,
162            &brush_to_source(&self.config.fill_style),
163            &raqote::DrawOptions {
164                alpha: self.config.alpha,
165                ..Default::default()
166            },
167        );
168    }
169
170    /// Strokes {outlines} the current or given path with the current stroke style.
171    pub fn stroke(&mut self) {
172        self.draw_target.stroke(
173            &self.path,
174            &brush_to_source(&self.config.stroke_style),
175            &raqote::StrokeStyle {
176                width: self.config.line_width as f32,
177                ..Default::default()
178            },
179            &raqote::DrawOptions {
180                alpha: self.config.alpha,
181                ..Default::default()
182            },
183        );
184    }
185
186    /// Starts a new path by emptying the list of sub-paths. Call this when you want to create a new path.
187    pub fn begin_path(&mut self) {
188        self.path = raqote::Path {
189            ops: Vec::new(),
190            winding: raqote::Winding::NonZero,
191        };
192    }
193
194    /// Attempts to add a straight line from the current point to the start of the current sub-path. If the shape has already been closed or has only one point, this function does nothing.
195    pub fn close_path(&mut self) {
196        let mut path_builder = raqote::PathBuilder::from(self.path.clone());
197        path_builder.close();
198        self.path = path_builder.finish();
199    }
200
201    /// Adds a rectangle to the current path.
202    pub fn rect(&mut self, x: f64, y: f64, width: f64, height: f64) {
203        self.last_rect = Rectangle::new((x, y), (width, height));
204        let mut path_builder = raqote::PathBuilder::from(self.path.clone());
205        path_builder.rect(x as f32, y as f32, width as f32, height as f32);
206        self.path = path_builder.finish();
207    }
208
209    /// Creates a circular arc centered at (x, y) with a radius of radius. The path starts at startAngle and ends at endAngle.
210    pub fn arc(&mut self, x: f64, y: f64, radius: f64, start_angle: f64, end_angle: f64) {
211        let mut path_builder = raqote::PathBuilder::from(self.path.clone());
212        path_builder.arc(
213            x as f32,
214            y as f32,
215            radius as f32,
216            start_angle as f32,
217            end_angle as f32,
218        );
219        self.path = path_builder.finish();
220    }
221
222    /// Begins a new sub-path at the point specified by the given {x, y} coordinates.
223
224    pub fn move_to(&mut self, x: f64, y: f64) {
225        let mut path_builder = raqote::PathBuilder::from(self.path.clone());
226        path_builder.move_to(x as f32, y as f32);
227        self.path = path_builder.finish();
228    }
229
230    /// Adds a straight line to the current sub-path by connecting the sub-path's last point to the specified {x, y} coordinates.
231    pub fn line_to(&mut self, x: f64, y: f64) {
232        let mut path_builder = raqote::PathBuilder::from(self.path.clone());
233        path_builder.line_to(x as f32, y as f32);
234        self.path = path_builder.finish();
235    }
236
237    /// Adds a quadratic Bézier curve to the current sub-path.
238    pub fn quadratic_curve_to(&mut self, cpx: f64, cpy: f64, x: f64, y: f64) {
239        let mut path_builder = raqote::PathBuilder::from(self.path.clone());
240        path_builder.quad_to(cpx as f32, cpy as f32, x as f32, y as f32);
241        self.path = path_builder.finish();
242    }
243
244    /// Adds a cubic Bézier curve to the current sub-path.
245    /// It requires three points: the first two are control points and the third one is the end point.
246    /// The starting point is the latest point in the current path, which can be changed using MoveTo{} before creating the Bézier curve.
247    pub fn bezier_curve_to(&mut self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) {
248        let mut path_builder = raqote::PathBuilder::from(self.path.clone());
249        path_builder.cubic_to(
250            cp1x as f32,
251            cp1y as f32,
252            cp2x as f32,
253            cp2y as f32,
254            x as f32,
255            y as f32,
256        );
257    }
258
259    /// Draws a render target.
260    pub fn draw_render_target(&mut self, render_target: &RenderTarget, x: f64, y: f64) {
261        self.draw_target.draw_image_at(
262            x as f32,
263            y as f32,
264            &raqote::Image {
265                data: &render_target.data(),
266                width: render_target.width() as i32,
267                height: render_target.height() as i32,
268            },
269            &raqote::DrawOptions {
270                alpha: self.config.alpha,
271                ..Default::default()
272            },
273        );
274    }
275
276    /// Draws the image.
277    pub fn draw_image(&mut self, image: &Image, x: f64, y: f64) {
278        self.draw_target.draw_image_at(
279            x as f32,
280            y as f32,
281            &raqote::Image {
282                data: &image.data(),
283                width: image.width() as i32,
284                height: image.height() as i32,
285            },
286            &raqote::DrawOptions {
287                alpha: self.config.alpha,
288                ..Default::default()
289            },
290        );
291    }
292
293    /// Draws the given part of the image.
294    pub fn draw_image_with_clip(&mut self, image: &Image, clip: Rectangle, x: f64, y: f64) {
295        let mut y = y as i32;
296        let stride = image.width();
297        let mut offset = clip.y().mul_add(stride, clip.x()) as usize;
298        let last_offset = cmp::min(
299            ((clip.y() + clip.height()).mul_add(stride, clip.x())) as usize,
300            image.data().len(),
301        );
302        while offset < last_offset {
303            let next_offset = offset + stride as usize;
304
305            self.draw_target.draw_image_at(
306                x as f32,
307                y as f32,
308                &raqote::Image {
309                    data: &image.data()[offset..],
310                    width: clip.width() as i32,
311                    height: 1,
312                },
313                &raqote::DrawOptions {
314                    alpha: self.config.alpha,
315                    ..Default::default()
316                },
317            );
318            offset = next_offset;
319            y += 1;
320        }
321    }
322
323    pub fn draw_pipeline(
324        &mut self,
325        x: f64,
326        y: f64,
327        width: f64,
328        height: f64,
329        pipeline: Box<dyn PipelineTrait>,
330    ) {
331        let mut render_target = RenderTarget::new(width as u32, height as u32);
332        pipeline.draw_pipeline(&mut render_target);
333        self.draw_render_target(&render_target, x, y);
334    }
335
336    /// Creates a clipping path from the current sub-paths. Everything drawn after clip() is called appears inside the clipping path only.
337    pub fn clip(&mut self) {
338        self.clip_rect = Some(self.last_rect);
339        self.clip = true;
340        self.draw_target.push_clip(&self.path);
341    }
342
343    // Line styles
344
345    /// Sets the thickness of lines.
346    pub fn set_line_width(&mut self, line_width: f64) {
347        self.config.line_width = line_width;
348    }
349
350    /// Sets the alpha value,
351    pub fn set_alpha(&mut self, alpha: f32) {
352        self.config.alpha = alpha;
353    }
354
355    /// Specifies the font family.
356    pub fn set_font_family(&mut self, family: impl Into<String>) {
357        self.config.font_config.family = family.into();
358    }
359
360    /// Specifies the font size.
361    pub fn set_font_size(&mut self, size: f64) {
362        self.config.font_config.font_size = size + 4.0;
363    }
364
365    // Fill and stroke style
366
367    /// Specifies the fill color to use inside shapes.
368    pub fn set_fill_style(&mut self, fill_style: Brush) {
369        self.config.fill_style = fill_style;
370    }
371
372    /// Specifies the fill stroke to use inside shapes.
373    pub fn set_stroke_style(&mut self, stroke_style: Brush) {
374        self.config.stroke_style = stroke_style;
375    }
376
377    // Transformations
378
379    /// Sets the transformation.
380    pub fn set_transform(
381        &mut self,
382        h_scaling: f64,
383        h_skewing: f64,
384        v_skewing: f64,
385        v_scaling: f64,
386        h_moving: f64,
387        v_moving: f64,
388    ) {
389        self.draw_target
390            .set_transform(&raqote::Transform::row_major(
391                h_scaling as f32,
392                h_skewing as f32,
393                v_skewing as f32,
394                v_scaling as f32,
395                h_moving as f32,
396                v_moving as f32,
397            ));
398    }
399
400    // Canvas states
401
402    /// Saves the entire state of the canvas by pushing the current state onto a stack.
403    pub fn save(&mut self) {
404        self.saved_config = Some(self.config.clone());
405    }
406
407    /// Restores the most recently saved canvas state by popping the top entry in the drawing state stack.
408    /// If there is no saved state, this method does nothing.
409    pub fn restore(&mut self) {
410        self.clip = false;
411        self.clip_rect = None;
412        self.draw_target.pop_clip();
413        if let Some(config) = &self.saved_config {
414            self.config = config.clone();
415        }
416
417        self.saved_config = None;
418    }
419
420    pub fn clear(&mut self, brush: &Brush) {
421        let solid = match *brush {
422            Brush::SolidColor(color) => raqote::SolidSource {
423                r: color.r(),
424                g: color.g(),
425                b: color.b(),
426                a: color.a(),
427            },
428
429            _ => raqote::SolidSource {
430                r: 0x0,
431                g: 0x0,
432                b: 0x80,
433                a: 0x80,
434            },
435        };
436
437        self.draw_target.clear(solid);
438    }
439
440    pub fn data(&self) -> &[u32] {
441        self.draw_target.get_data()
442    }
443
444    pub fn data_mut(&mut self) -> &mut [u32] {
445        self.draw_target.get_data_mut()
446    }
447
448    pub fn data_u8_mut(&mut self) -> &mut [u8] {
449        self.draw_target.get_data_u8_mut()
450    }
451
452    pub fn start(&mut self) {
453        self.clear(&Brush::from(self.background));
454    }
455    pub fn finish(&mut self) {}
456}
457
458fn brush_to_source<'a>(brush: &Brush) -> raqote::Source<'a> {
459    match brush {
460        Brush::SolidColor(color) => raqote::Source::Solid(raqote::SolidSource {
461            r: color.r(),
462            g: color.g(),
463            b: color.b(),
464            a: color.a(),
465        }),
466        Brush::LinearGradient { start, end, stops } => {
467            let g_stops = stops
468                .iter()
469                .map(|stop| raqote::GradientStop {
470                    position: stop.position as f32,
471                    color: raqote::Color::new(
472                        stop.color.a(),
473                        stop.color.r(),
474                        stop.color.g(),
475                        stop.color.b(),
476                    ),
477                })
478                .collect();
479
480            raqote::Source::new_linear_gradient(
481                raqote::Gradient { stops: g_stops },
482                raqote::Point::new(start.x() as f32, start.y() as f32),
483                raqote::Point::new(end.x() as f32, start.y() as f32),
484                raqote::Spread::Pad,
485            )
486        }
487    }
488}