vg/
lib.rs

1// #![doc(html_logo_url = "https://dudochkin-victor.github.io/assets/ux-primitives/logo.svg")]
2
3#![warn(missing_docs)]
4#![allow(
5    clippy::too_many_arguments,
6    clippy::large_enum_variant,
7    clippy::comparison_chain,
8    clippy::derive_ord_xor_partial_ord,
9    clippy::search_is_some,
10    clippy::unnecessary_to_owned,
11    clippy::needless_range_loop,
12    clippy::manual_map,
13    clippy::map_entry
14)]
15//!
16//! The VG API is (like [NanoVG](https://github.com/memononen/nanovg))
17//! loosely modeled on the
18//! [HTML5 Canvas API](https://bucephalus.org/text/CanvasHandbook/CanvasHandbook.html).
19//!
20//! The coordinate system’s origin is the top-left corner,
21//! with positive X rightwards, positive Y downwards.
22//!
23
24/*
25TODO:
26    - Documentation
27    - Tests
28*/
29
30#[cfg(feature = "serde")]
31#[macro_use]
32extern crate serde;
33
34use std::cell::RefCell;
35use std::ops::Range;
36use std::path::Path as FilePath;
37use std::rc::Rc;
38
39use imgref::ImgVec;
40use rgb::RGBA8;
41
42mod utils;
43
44mod text;
45
46mod error;
47pub use error::ErrorKind;
48
49pub use text::{
50    Align,
51    Baseline,
52    FontId,
53    FontMetrics,
54    TextContext,
55    TextMetrics,
56};
57
58use text::{
59    GlyphAtlas,
60    RenderMode,
61    TextContextImpl,
62};
63
64mod image;
65
66pub use crate::image::{
67    ImageFilter,
68    ImageFlags,
69    ImageId,
70    ImageInfo,
71    ImageSource,
72    ImageStore,
73    PixelFormat,
74};
75
76mod color;
77pub use color::Color;
78
79pub mod renderer;
80pub use renderer::{
81    RenderTarget,
82    Renderer,
83};
84
85pub use renderer::{
86    Command,
87    CommandType,
88    Drawable,
89    Params,
90    ShaderType,
91    Vertex,
92};
93
94pub(crate) mod geometry;
95pub use geometry::Transform2D;
96use geometry::*;
97
98mod paint;
99use paint::PaintFlavor;
100pub use paint::{
101    GlyphTexture,
102    Paint,
103};
104
105mod path;
106use path::Convexity;
107pub use path::{
108    Path,
109    Solidity,
110};
111
112mod gradient_store;
113use gradient_store::GradientStore;
114
115/// The fill rule used when filling paths.
116#[derive(Copy, Clone, Debug, Eq, PartialEq)]
117#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
118pub enum FillRule {
119    /// Represents even odd rule
120    EvenOdd,
121    /// Represents non zero rule
122    NonZero,
123}
124
125impl Default for FillRule {
126    fn default() -> Self {
127        Self::NonZero
128    }
129}
130
131/// Blend factors.
132#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)]
133pub enum BlendFactor {
134    /// Not all
135    Zero,
136    /// All use
137    One,
138    /// Using the source color
139    SrcColor,
140    /// Minus the source color
141    OneMinusSrcColor,
142    /// Using the target color
143    DstColor,
144    /// Minus the target color
145    OneMinusDstColor,
146    /// Using the source alpha
147    SrcAlpha,
148    /// Minus the source alpha
149    OneMinusSrcAlpha,
150    /// Using the target alpha
151    DstAlpha,
152    /// Minus the target alpha
153    OneMinusDstAlpha,
154    /// Scale color by minimum of source alpha and destination alpha
155    SrcAlphaSaturate,
156}
157
158/// Predefined composite oprations.
159#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)]
160pub enum CompositeOperation {
161    /// Displays the source over the destination.
162    SourceOver,
163    /// Displays the source in the destination, i.e. only the part of the source inside the destination is shown and the destination is transparent.
164    SourceIn,
165    /// Only displays the part of the source that is outside the destination, which is made transparent.
166    SourceOut,
167    /// Displays the source on top of the destination. The part of the source outside the destination is not shown.
168    Atop,
169    /// Displays the destination over the source.
170    DestinationOver,
171    /// Only displays the part of the destination that is inside the source, which is made transparent.
172    DestinationIn,
173    /// Only displays the part of the destination that is outside the source, which is made transparent.
174    DestinationOut,
175    /// Displays the destination on top of the source. The part of the destination that is outside the source is not shown.
176    DestinationAtop,
177    /// Displays the source together with the destination, the overlapping area is rendered lighter.
178    Lighter,
179    /// Ignores the destination and just displays the source.
180    Copy,
181    /// Only the areas that exclusively belong either to the destination or the source are displayed. Overlapping parts are ignored.
182    Xor,
183}
184
185/// Determines how a source data is displayed against destination data.
186#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)]
187pub struct CompositeOperationState {
188    /// Represents source rgb blend factor
189    pub src_rgb: BlendFactor,
190    /// Represents source alpha blend factor
191    pub src_alpha: BlendFactor,
192    /// Represents destination rgb blend factor
193    pub dst_rgb: BlendFactor,
194    /// Represents destination alpha blend factor
195    pub dst_alpha: BlendFactor,
196}
197
198impl CompositeOperationState {
199    /// Creates a new CompositeOperationState from the provided CompositeOperation
200    pub fn new(op: CompositeOperation) -> Self {
201        let (sfactor, dfactor) = match op {
202            CompositeOperation::SourceOver => (BlendFactor::One, BlendFactor::OneMinusSrcAlpha),
203            CompositeOperation::SourceIn => (BlendFactor::DstAlpha, BlendFactor::Zero),
204            CompositeOperation::SourceOut => (BlendFactor::OneMinusDstAlpha, BlendFactor::Zero),
205            CompositeOperation::Atop => (BlendFactor::DstAlpha, BlendFactor::OneMinusSrcAlpha),
206            CompositeOperation::DestinationOver => (BlendFactor::OneMinusDstAlpha, BlendFactor::One),
207            CompositeOperation::DestinationIn => (BlendFactor::Zero, BlendFactor::SrcAlpha),
208            CompositeOperation::DestinationOut => (BlendFactor::Zero, BlendFactor::OneMinusSrcAlpha),
209            CompositeOperation::DestinationAtop => (BlendFactor::OneMinusDstAlpha, BlendFactor::SrcAlpha),
210            CompositeOperation::Lighter => (BlendFactor::One, BlendFactor::One),
211            CompositeOperation::Copy => (BlendFactor::One, BlendFactor::Zero),
212            CompositeOperation::Xor => (BlendFactor::OneMinusDstAlpha, BlendFactor::OneMinusSrcAlpha),
213        };
214
215        Self {
216            src_rgb: sfactor,
217            src_alpha: sfactor,
218            dst_rgb: dfactor,
219            dst_alpha: dfactor,
220        }
221    }
222
223    /// Creates a new CompositeOperationState with source and destination blend factors.
224    pub fn with_blend_factors(src_factor: BlendFactor, dst_factor: BlendFactor) -> Self {
225        Self {
226            src_rgb: src_factor,
227            src_alpha: src_factor,
228            dst_rgb: dst_factor,
229            dst_alpha: dst_factor,
230        }
231    }
232}
233
234impl Default for CompositeOperationState {
235    fn default() -> Self {
236        Self::new(CompositeOperation::SourceOver)
237    }
238}
239
240/// Represents scissor
241#[derive(Default, Copy, Clone, Debug)]
242pub struct Scissor {
243    transform: Transform2D,
244    extent: Option<[f32; 2]>,
245}
246
247/// Determines the shape used to draw the end points of lines.
248#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
249#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
250pub enum LineCap {
251    /// The ends of lines are squared off at the endpoints. Default value.
252    Butt,
253    /// The ends of lines are rounded.
254    Round,
255    /// The ends of lines are squared off by adding a box with an equal
256    /// width and half the height of the line's thickness.
257    Square,
258}
259
260impl Default for LineCap {
261    fn default() -> Self {
262        Self::Butt
263    }
264}
265
266/// Determines the shape used to join two line segments where they meet.
267#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
268#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
269pub enum LineJoin {
270    /// Connected segments are joined by extending their outside edges to
271    /// connect at a single point, with the effect of filling an additional
272    /// lozenge-shaped area. This setting is affected by the miterLimit property.
273    /// Default value.
274    Miter,
275    /// Rounds off the corners of a shape by filling an additional sector
276    /// of disc centered at the common endpoint of connected segments.
277    /// The radius for these rounded corners is equal to the line width.
278    Round,
279    /// Fills an additional triangular area between the common endpoint
280    /// of connected segments, and the separate outside rectangular
281    /// corners of each segment.
282    Bevel,
283}
284
285impl Default for LineJoin {
286    fn default() -> Self {
287        Self::Miter
288    }
289}
290
291#[derive(Copy, Clone, Debug)]
292struct State {
293    composite_operation: CompositeOperationState,
294    transform: Transform2D,
295    scissor: Scissor,
296    alpha: f32,
297}
298
299impl Default for State {
300    fn default() -> Self {
301        Self {
302            composite_operation: Default::default(),
303            transform: Transform2D::identity(),
304            scissor: Default::default(),
305            alpha: 1.0,
306        }
307    }
308}
309
310/// Main 2D drawing context.
311pub struct Canvas<T: Renderer> {
312    width: u32,
313    height: u32,
314    renderer: T,
315    text_context: Rc<RefCell<TextContextImpl>>,
316    glyph_atlas: Rc<GlyphAtlas>,
317    // Glyph atlas used for direct rendering of color glyphs, dropped after flush()
318    emphemeral_glyph_atlas: Option<Rc<GlyphAtlas>>,
319    current_render_target: RenderTarget,
320    state_stack: Vec<State>,
321    commands: Vec<Command>,
322    verts: Vec<Vertex>,
323    images: ImageStore<T::Image>,
324    fringe_width: f32,
325    device_px_ratio: f32,
326    tess_tol: f32,
327    dist_tol: f32,
328    gradients: GradientStore,
329}
330
331impl<T> Canvas<T>
332where
333    T: Renderer,
334{
335    /// Creates a new canvas.
336    pub fn new(renderer: T) -> Result<Self, ErrorKind> {
337        let mut canvas = Self {
338            width: 0,
339            height: 0,
340            renderer,
341            text_context: Default::default(),
342            glyph_atlas: Default::default(),
343            emphemeral_glyph_atlas: Default::default(),
344            current_render_target: RenderTarget::Screen,
345            state_stack: Default::default(),
346            commands: Default::default(),
347            verts: Default::default(),
348            images: ImageStore::new(),
349            fringe_width: 1.0,
350            device_px_ratio: 1.0,
351            tess_tol: 0.25,
352            dist_tol: 0.01,
353            gradients: GradientStore::new(),
354        };
355
356        canvas.save();
357
358        Ok(canvas)
359    }
360
361    /// Creates a new canvas with the specified renderer and using the fonts registered with the
362    /// provided [`TextContext`]. Note that the context is explicitly shared, so that any fonts
363    /// registered with a clone of this context will also be visible to this canvas.
364    pub fn new_with_text_context(renderer: T, text_context: TextContext) -> Result<Self, ErrorKind> {
365        let mut canvas = Self {
366            width: 0,
367            height: 0,
368            renderer,
369            text_context: text_context.0,
370            glyph_atlas: Default::default(),
371            emphemeral_glyph_atlas: Default::default(),
372            current_render_target: RenderTarget::Screen,
373            state_stack: Default::default(),
374            commands: Default::default(),
375            verts: Default::default(),
376            images: ImageStore::new(),
377            fringe_width: 1.0,
378            device_px_ratio: 1.0,
379            tess_tol: 0.25,
380            dist_tol: 0.01,
381            gradients: GradientStore::new(),
382        };
383
384        canvas.save();
385
386        Ok(canvas)
387    }
388
389    /// Sets the size of the default framebuffer (screen size)
390    pub fn set_size(&mut self, width: u32, height: u32, dpi: f32) {
391        self.width = width;
392        self.height = height;
393        self.fringe_width = 1.0 / dpi;
394        self.tess_tol = 0.25 / dpi;
395        self.dist_tol = 0.01 / dpi;
396        self.device_px_ratio = dpi;
397
398        self.renderer.set_size(width, height, dpi);
399
400        self.append_cmd(Command::new(CommandType::SetRenderTarget(RenderTarget::Screen)));
401    }
402
403    /// Clears the rectangle area defined by left upper corner (x,y), width and height with the provided color.
404    pub fn clear_rect(&mut self, x: u32, y: u32, width: u32, height: u32, color: Color) {
405        let cmd = Command::new(CommandType::ClearRect {
406            x,
407            y,
408            width,
409            height,
410            color,
411        });
412
413        self.append_cmd(cmd);
414    }
415
416    /// Returns the width of the current render target.
417    pub fn width(&self) -> f32 {
418        match self.current_render_target {
419            RenderTarget::Image(id) => self.image_info(id).map(|info| info.width() as f32).unwrap_or(0.0),
420            RenderTarget::Screen => self.width as f32,
421        }
422    }
423
424    /// Returns the height of the current render target.
425    pub fn height(&self) -> f32 {
426        match self.current_render_target {
427            RenderTarget::Image(id) => self.image_info(id).map(|info| info.height() as f32).unwrap_or(0.0),
428            RenderTarget::Screen => self.height as f32,
429        }
430    }
431
432    /// Tells the renderer to execute all drawing commands and clears the current internal state
433    ///
434    /// Call this at the end of each frame.
435    pub fn flush(&mut self) {
436        self.renderer
437            .render(&mut self.images, &self.verts, std::mem::take(&mut self.commands));
438        self.verts.clear();
439        self.gradients
440            .release_old_gradients(&mut self.images, &mut self.renderer);
441        if let Some(atlas) = self.emphemeral_glyph_atlas.take() {
442            atlas.clear(self);
443        }
444    }
445
446    /// Creates streenshot
447    pub fn screenshot(&mut self) -> Result<ImgVec<RGBA8>, ErrorKind> {
448        self.flush();
449        self.renderer.screenshot()
450    }
451
452    // State Handling
453
454    /// Pushes and saves the current render state into a state stack.
455    ///
456    /// A matching restore() must be used to restore the state.
457    pub fn save(&mut self) {
458        let state = self.state_stack.last().map_or_else(State::default, |state| *state);
459
460        self.state_stack.push(state);
461    }
462
463    /// Restores the previous render state
464    ///
465    /// Restoring the initial/first state will just reset it to the defaults
466    pub fn restore(&mut self) {
467        if self.state_stack.len() > 1 {
468            self.state_stack.pop();
469        } else {
470            self.reset();
471        }
472    }
473
474    /// Resets current state to default values. Does not affect the state stack.
475    pub fn reset(&mut self) {
476        *self.state_mut() = Default::default();
477    }
478
479    /// Saves the current state before calling the callback and restores it afterwards
480    ///
481    /// This is less error prone than remembering to match save() -> restore() calls
482    pub fn save_with(&mut self, mut callback: impl FnMut(&mut Self)) {
483        self.save();
484
485        callback(self);
486
487        self.restore();
488    }
489
490    // Render styles
491
492    /// Sets the transparency applied to all rendered shapes.
493    ///
494    /// Already transparent paths will get proportionally more transparent as well.
495    pub fn set_global_alpha(&mut self, alpha: f32) {
496        self.state_mut().alpha = alpha;
497    }
498
499    /// Sets the composite operation.
500    pub fn global_composite_operation(&mut self, op: CompositeOperation) {
501        self.state_mut().composite_operation = CompositeOperationState::new(op);
502    }
503
504    /// Sets the composite operation with custom pixel arithmetic.
505    pub fn global_composite_blend_func(&mut self, src_factor: BlendFactor, dst_factor: BlendFactor) {
506        self.global_composite_blend_func_separate(src_factor, dst_factor, src_factor, dst_factor);
507    }
508
509    /// Sets the composite operation with custom pixel arithmetic for RGB and alpha components separately.
510    pub fn global_composite_blend_func_separate(
511        &mut self,
512        src_rgb: BlendFactor,
513        dst_rgb: BlendFactor,
514        src_alpha: BlendFactor,
515        dst_alpha: BlendFactor,
516    ) {
517        self.state_mut().composite_operation = CompositeOperationState {
518            src_rgb,
519            src_alpha,
520            dst_rgb,
521            dst_alpha,
522        }
523    }
524
525    /// Sets a new render target. All drawing operations after this call will happen on the provided render target
526    pub fn set_render_target(&mut self, target: RenderTarget) {
527        if self.current_render_target != target {
528            self.append_cmd(Command::new(CommandType::SetRenderTarget(target)));
529            self.current_render_target = target;
530        }
531    }
532
533    fn append_cmd(&mut self, cmd: Command) {
534        self.commands.push(cmd);
535    }
536
537    // Images
538
539    /// Allocates an empty image with the provided domensions and format.
540    pub fn create_image_empty(
541        &mut self,
542        width: usize,
543        height: usize,
544        format: PixelFormat,
545        flags: ImageFlags,
546    ) -> Result<ImageId, ErrorKind> {
547        let info = ImageInfo::new(flags, width, height, format);
548
549        self.images.alloc(&mut self.renderer, info)
550    }
551
552    /// Creates image from specified image data.
553    pub fn create_image<'a, S: Into<ImageSource<'a>>>(
554        &mut self,
555        src: S,
556        flags: ImageFlags,
557    ) -> Result<ImageId, ErrorKind> {
558        let src = src.into();
559        let size = src.dimensions();
560        let id = self.create_image_empty(size.0, size.1, src.format(), flags)?;
561        self.images.update(&mut self.renderer, id, src, 0, 0)?;
562        Ok(id)
563    }
564
565    /// Retrieve image by id
566    pub fn get_image(&self, id: ImageId) -> Option<&T::Image> {
567        self.images.get(id)
568    }
569
570    /// Retrieve mutable image by id
571    pub fn get_image_mut(&mut self, id: ImageId) -> Option<&mut T::Image> {
572        self.images.get_mut(id)
573    }
574
575    /// Resizes an image to the new provided dimensions.
576    pub fn realloc_image(
577        &mut self,
578        id: ImageId,
579        width: usize,
580        height: usize,
581        format: PixelFormat,
582        flags: ImageFlags,
583    ) -> Result<(), ErrorKind> {
584        let info = ImageInfo::new(flags, width, height, format);
585        self.images.realloc(&mut self.renderer, id, info)
586    }
587
588    /// Decode an image from file
589    #[cfg(feature = "image-loading")]
590    pub fn load_image_file<P: AsRef<FilePath>>(
591        &mut self,
592        filename: P,
593        flags: ImageFlags,
594    ) -> Result<ImageId, ErrorKind> {
595        let image = ::image::open(filename)?;
596
597        use std::convert::TryFrom;
598
599        let src = ImageSource::try_from(&image)?;
600
601        self.create_image(src, flags)
602    }
603
604    /// Decode an image from memory
605    #[cfg(feature = "image-loading")]
606    pub fn load_image_mem(&mut self, data: &[u8], flags: ImageFlags) -> Result<ImageId, ErrorKind> {
607        let image = ::image::load_from_memory(data)?;
608
609        use std::convert::TryFrom;
610
611        let src = ImageSource::try_from(&image)?;
612
613        self.create_image(src, flags)
614    }
615
616    /// Updates image data specified by image handle.
617    pub fn update_image<'a, S: Into<ImageSource<'a>>>(
618        &mut self,
619        id: ImageId,
620        src: S,
621        x: usize,
622        y: usize,
623    ) -> Result<(), ErrorKind> {
624        self.images.update(&mut self.renderer, id, src.into(), x, y)
625    }
626
627    /// Deletes created image.
628    pub fn delete_image(&mut self, id: ImageId) {
629        self.images.remove(&mut self.renderer, id);
630    }
631
632    /// Returns image info
633    pub fn image_info(&self, id: ImageId) -> Result<ImageInfo, ErrorKind> {
634        if let Some(info) = self.images.info(id) {
635            Ok(info)
636        } else {
637            Err(ErrorKind::ImageIdNotFound)
638        }
639    }
640
641    /// Returns the size in pixels of the image for the specified id.
642    pub fn image_size(&self, id: ImageId) -> Result<(usize, usize), ErrorKind> {
643        let info = self.image_info(id)?;
644        Ok((info.width(), info.height()))
645    }
646
647    /// Renders the given source_image into target_image while applying a filter effect.
648    ///
649    /// The target image must have the same size as the source image. The filtering is recorded
650    /// as a drawing command and run by the renderer when [`Self::flush()`] is called.
651    ///
652    /// The filtering does not take any transformation set on the Canvas into account nor does it
653    /// change the current rendering target.
654    pub fn filter_image(&mut self, target_image: ImageId, filter: ImageFilter, source_image: ImageId) {
655        let (image_width, image_height) = match self.image_size(source_image) {
656            Ok((w, h)) => (w, h),
657            Err(_) => return,
658        };
659
660        // The renderer will receive a RenderFilteredImage command with two triangles attached that
661        // cover the image and the source image.
662        let mut cmd = Command::new(CommandType::RenderFilteredImage { target_image, filter });
663        cmd.image = Some(source_image);
664
665        let vertex_offset = self.verts.len();
666
667        let image_width = image_width as f32;
668        let image_height = image_height as f32;
669
670        let quad_x0 = 0.0;
671        let quad_y0 = -image_height;
672        let quad_x1 = image_width;
673        let quad_y1 = image_height;
674
675        let texture_x0 = -(image_width / 2.);
676        let texture_y0 = -(image_height / 2.);
677        let texture_x1 = (image_width) / 2.;
678        let texture_y1 = (image_height) / 2.;
679
680        self.verts.push(Vertex::new(quad_x0, quad_y0, texture_x0, texture_y0));
681        self.verts.push(Vertex::new(quad_x1, quad_y1, texture_x1, texture_y1));
682        self.verts.push(Vertex::new(quad_x1, quad_y0, texture_x1, texture_y0));
683        self.verts.push(Vertex::new(quad_x0, quad_y0, texture_x0, texture_y0));
684        self.verts.push(Vertex::new(quad_x0, quad_y1, texture_x0, texture_y1));
685        self.verts.push(Vertex::new(quad_x1, quad_y1, texture_x1, texture_y1));
686
687        cmd.triangles_verts = Some((vertex_offset, 6));
688
689        self.append_cmd(cmd)
690    }
691
692    // Transforms
693
694    /// Resets current transform to a identity matrix.
695    pub fn reset_transform(&mut self) {
696        self.state_mut().transform = Transform2D::identity();
697    }
698
699    /// Premultiplies current coordinate system by specified matrix.
700    ///
701    /// The parameters are interpreted as matrix as follows:
702    ///   [a c e]
703    ///   [b d f]
704    ///   [0 0 1]
705    pub fn set_transform(&mut self, a: f32, b: f32, c: f32, d: f32, e: f32, f: f32) {
706        let transform = Transform2D([a, b, c, d, e, f]);
707        self.state_mut().transform.premultiply(&transform);
708    }
709
710    /// Translates the current coordinate system.
711    pub fn translate(&mut self, x: f32, y: f32) {
712        let mut t = Transform2D::identity();
713        t.translate(x, y);
714        self.state_mut().transform.premultiply(&t);
715    }
716
717    /// Rotates the current coordinate system. Angle is specified in radians.
718    pub fn rotate(&mut self, angle: f32) {
719        let mut t = Transform2D::identity();
720        t.rotate(angle);
721        self.state_mut().transform.premultiply(&t);
722    }
723
724    /// Skews the current coordinate system along X axis. Angle is specified in radians.
725    pub fn skew_x(&mut self, angle: f32) {
726        let mut t = Transform2D::identity();
727        t.skew_x(angle);
728        self.state_mut().transform.premultiply(&t);
729    }
730
731    /// Skews the current coordinate system along Y axis. Angle is specified in radians.
732    pub fn skew_y(&mut self, angle: f32) {
733        let mut t = Transform2D::identity();
734        t.skew_y(angle);
735        self.state_mut().transform.premultiply(&t);
736    }
737
738    /// Scales the current coordinate system.
739    pub fn scale(&mut self, x: f32, y: f32) {
740        let mut t = Transform2D::identity();
741        t.scale(x, y);
742        self.state_mut().transform.premultiply(&t);
743    }
744
745    /// Returns the current transformation matrix
746    ///
747    /// TODO: It's not ok that this method returns Transform2D while set_transform accepts 6 floats - make it consistant
748    pub fn transform(&self) -> Transform2D {
749        self.state().transform
750    }
751
752    // Scissoring
753
754    /// Sets the current scissor rectangle.
755    ///
756    /// The scissor rectangle is transformed by the current transform.
757    pub fn scissor(&mut self, x: f32, y: f32, w: f32, h: f32) {
758        let state = self.state_mut();
759
760        let w = w.max(0.0);
761        let h = h.max(0.0);
762
763        let mut transform = Transform2D::new_translation(x + w * 0.5, y + h * 0.5);
764        transform.multiply(&state.transform);
765        state.scissor.transform = transform;
766
767        state.scissor.extent = Some([w * 0.5, h * 0.5]);
768    }
769
770    /// Intersects current scissor rectangle with the specified rectangle.
771    ///
772    /// The scissor rectangle is transformed by the current transform.
773    /// Note: in case the rotation of previous scissor rect differs from
774    /// the current one, the intersection will be done between the specified
775    /// rectangle and the previous scissor rectangle transformed in the current
776    /// transform space. The resulting shape is always rectangle.
777    pub fn intersect_scissor(&mut self, x: f32, y: f32, w: f32, h: f32) {
778        let state = self.state_mut();
779
780        // If no previous scissor has been set, set the scissor as current scissor.
781        if state.scissor.extent.is_none() {
782            self.scissor(x, y, w, h);
783            return;
784        }
785
786        let extent = state.scissor.extent.unwrap();
787
788        // Transform the current scissor rect into current transform space.
789        // If there is difference in rotation, this will be approximation.
790
791        let mut pxform = state.scissor.transform;
792
793        let mut invxform = state.transform;
794        invxform.inverse();
795
796        pxform.multiply(&invxform);
797
798        let ex = extent[0];
799        let ey = extent[1];
800
801        let tex = ex * pxform[0].abs() + ey * pxform[2].abs();
802        let tey = ex * pxform[1].abs() + ey * pxform[3].abs();
803
804        let rect = Rect::new(pxform[4] - tex, pxform[5] - tey, tex * 2.0, tey * 2.0);
805        let res = rect.intersect(Rect::new(x, y, w, h));
806
807        self.scissor(res.x, res.y, res.w, res.h);
808    }
809
810    /// Reset and disables scissoring.
811    pub fn reset_scissor(&mut self) {
812        self.state_mut().scissor = Scissor::default();
813    }
814
815    // Paths
816
817    /// Returns true if the specified point (x,y) is in the provided path, and false otherwise.
818    pub fn contains_point(&mut self, path: &mut Path, x: f32, y: f32, fill_rule: FillRule) -> bool {
819        let transform = self.state().transform;
820
821        // The path cache saves a flattened and transformed version of the path.
822        let path_cache = path.cache(&transform, self.tess_tol, self.dist_tol);
823
824        // Early out if path is outside the canvas bounds
825        if path_cache.bounds.maxx < 0.0
826            || path_cache.bounds.minx > self.width()
827            || path_cache.bounds.maxy < 0.0
828            || path_cache.bounds.miny > self.height()
829        {
830            return false;
831        }
832
833        path_cache.contains_point(x, y, fill_rule)
834    }
835
836    /// Return the bounding box for a Path
837    pub fn path_bbox(&self, path: &mut Path) -> Bounds {
838        let transform = self.state().transform;
839
840        // The path cache saves a flattened and transformed version of the path.
841        let path_cache = path.cache(&transform, self.tess_tol, self.dist_tol);
842
843        path_cache.bounds
844    }
845
846    /// Fills the provided Path with the specified Paint.
847    pub fn fill_path(&mut self, path: &mut Path, mut paint: Paint) {
848        let transform = self.state().transform;
849
850        // The path cache saves a flattened and transformed version of the path.
851        let path_cache = path.cache(&transform, self.tess_tol, self.dist_tol);
852
853        // Early out if path is outside the canvas bounds
854        if path_cache.bounds.maxx < 0.0
855            || path_cache.bounds.minx > self.width()
856            || path_cache.bounds.maxy < 0.0
857            || path_cache.bounds.miny > self.height()
858        {
859            return;
860        }
861
862        // Transform paint
863        paint.transform = transform;
864
865        // Apply global alpha
866        paint.mul_alpha(self.state().alpha);
867
868        let scissor = self.state().scissor;
869
870        // Calculate fill vertices.
871        // expand_fill will fill path_cache.contours[].{stroke, fill} with vertex data for the GPU
872        // fringe_with is the size of the strip of triangles generated at the path border used for AA
873        let fringe_width = if paint.anti_alias() { self.fringe_width } else { 0.0 };
874        path_cache.expand_fill(fringe_width, LineJoin::Miter, 2.4);
875
876        // GPU uniforms
877        let flavor = if path_cache.contours.len() == 1 && path_cache.contours[0].convexity == Convexity::Convex {
878            let params = Params::new(
879                &self.images,
880                &paint,
881                &scissor,
882                self.fringe_width,
883                self.fringe_width,
884                -1.0,
885            );
886
887            CommandType::ConvexFill { params }
888        } else {
889            let stencil_params = Params {
890                stroke_thr: -1.0,
891                shader_type: ShaderType::Stencil.to_f32(),
892                ..Default::default()
893            };
894
895            let fill_params = Params::new(
896                &self.images,
897                &paint,
898                &scissor,
899                self.fringe_width,
900                self.fringe_width,
901                -1.0,
902            );
903
904            CommandType::ConcaveFill {
905                stencil_params,
906                fill_params,
907            }
908        };
909
910        // GPU command
911        let mut cmd = Command::new(flavor);
912        cmd.fill_rule = paint.fill_rule;
913        cmd.composite_operation = self.state().composite_operation;
914
915        if let PaintFlavor::Image { id, .. } = paint.flavor {
916            cmd.image = Some(id);
917        } else if let Some(paint::GradientColors::MultiStop { stops }) = paint.flavor.gradient_colors() {
918            cmd.image = self
919                .gradients
920                .lookup_or_add(*stops, &mut self.images, &mut self.renderer)
921                .ok();
922        }
923
924        // All verts from all shapes are kept in a single buffer here in the canvas.
925        // Drawable struct is used to describe the range of vertices each draw call will operate on
926        let mut offset = self.verts.len();
927
928        for contour in &path_cache.contours {
929            let mut drawable = Drawable::default();
930
931            // Fill commands can have both fill and stroke vertices. Fill vertices are used to fill
932            // the body of the shape while stroke vertices are used to prodice antialiased edges
933
934            if !contour.fill.is_empty() {
935                drawable.fill_verts = Some((offset, contour.fill.len()));
936                self.verts.extend_from_slice(&contour.fill);
937                offset += contour.fill.len();
938            }
939
940            if !contour.stroke.is_empty() {
941                drawable.stroke_verts = Some((offset, contour.stroke.len()));
942                self.verts.extend_from_slice(&contour.stroke);
943                offset += contour.stroke.len();
944            }
945
946            cmd.drawables.push(drawable);
947        }
948
949        if let CommandType::ConcaveFill { .. } = cmd.cmd_type {
950            // Concave shapes are first filled by writing to a stencil buffer and then drawing a quad
951            // over the shape area with stencil test enabled to produce the final fill. These are
952            // the verts needed for the covering quad
953            self.verts.push(Vertex::new(
954                path_cache.bounds.maxx + fringe_width,
955                path_cache.bounds.maxy + fringe_width,
956                0.5,
957                1.0,
958            ));
959            self.verts.push(Vertex::new(
960                path_cache.bounds.maxx + fringe_width,
961                path_cache.bounds.miny - fringe_width,
962                0.5,
963                1.0,
964            ));
965            self.verts.push(Vertex::new(
966                path_cache.bounds.minx - fringe_width,
967                path_cache.bounds.maxy + fringe_width,
968                0.5,
969                1.0,
970            ));
971            self.verts.push(Vertex::new(
972                path_cache.bounds.minx - fringe_width,
973                path_cache.bounds.miny,
974                0.5,
975                1.0,
976            ));
977
978            cmd.triangles_verts = Some((offset, 4));
979        }
980
981        self.append_cmd(cmd);
982    }
983
984    /// Strokes the provided Path with the specified Paint.
985    pub fn stroke_path(&mut self, path: &mut Path, mut paint: Paint) {
986        let transform = self.state().transform;
987
988        // The path cache saves a flattened and transformed version of the path.
989        let path_cache = path.cache(&transform, self.tess_tol, self.dist_tol);
990
991        // Early out if path is outside the canvas bounds
992        if path_cache.bounds.maxx < 0.0
993            || path_cache.bounds.minx > self.width()
994            || path_cache.bounds.maxy < 0.0
995            || path_cache.bounds.miny > self.height()
996        {
997            return;
998        }
999
1000        let scissor = self.state().scissor;
1001
1002        // Transform paint
1003        paint.transform = transform;
1004
1005        // Scale stroke width by current transform scale.
1006        // Note: I don't know why the original author clamped the max stroke width to 200, but it didn't
1007        // look correct when zooming in. There was probably a good reson for doing so and I may have
1008        // introduced a bug by removing the upper bound.
1009        //paint.set_stroke_width((paint.stroke_width() * transform.average_scale()).max(0.0).min(200.0));
1010        paint.line_width = (paint.line_width * transform.average_scale()).max(0.0);
1011
1012        if paint.line_width < self.fringe_width {
1013            // If the stroke width is less than pixel size, use alpha to emulate coverage.
1014            // Since coverage is area, scale by alpha*alpha.
1015            let alpha = (paint.line_width / self.fringe_width).max(0.0).min(1.0);
1016
1017            paint.mul_alpha(alpha * alpha);
1018            paint.line_width = self.fringe_width;
1019        }
1020
1021        // Apply global alpha
1022        paint.mul_alpha(self.state().alpha);
1023
1024        // Calculate stroke vertices.
1025        // expand_stroke will fill path_cache.contours[].stroke with vertex data for the GPU
1026        let fringe_with = if paint.anti_alias() { self.fringe_width } else { 0.0 };
1027        path_cache.expand_stroke(
1028            paint.line_width * 0.5,
1029            fringe_with,
1030            paint.line_cap_start,
1031            paint.line_cap_end,
1032            paint.line_join,
1033            paint.miter_limit,
1034            self.tess_tol,
1035        );
1036
1037        // GPU uniforms
1038        let params = Params::new(
1039            &self.images,
1040            &paint,
1041            &scissor,
1042            paint.line_width,
1043            self.fringe_width,
1044            -1.0,
1045        );
1046
1047        let flavor = if paint.stencil_strokes() {
1048            let params2 = Params::new(
1049                &self.images,
1050                &paint,
1051                &scissor,
1052                paint.line_width,
1053                self.fringe_width,
1054                1.0 - 0.5 / 255.0,
1055            );
1056
1057            CommandType::StencilStroke {
1058                params1: params,
1059                params2,
1060            }
1061        } else {
1062            CommandType::Stroke { params }
1063        };
1064
1065        // GPU command
1066        let mut cmd = Command::new(flavor);
1067        cmd.composite_operation = self.state().composite_operation;
1068
1069        if let PaintFlavor::Image { id, .. } = paint.flavor {
1070            cmd.image = Some(id);
1071        } else if let Some(paint::GradientColors::MultiStop { stops }) = paint.flavor.gradient_colors() {
1072            cmd.image = self
1073                .gradients
1074                .lookup_or_add(*stops, &mut self.images, &mut self.renderer)
1075                .ok();
1076        }
1077
1078        // All verts from all shapes are kept in a single buffer here in the canvas.
1079        // Drawable struct is used to describe the range of vertices each draw call will operate on
1080        let mut offset = self.verts.len();
1081
1082        for contour in &path_cache.contours {
1083            let mut drawable = Drawable::default();
1084
1085            if !contour.stroke.is_empty() {
1086                drawable.stroke_verts = Some((offset, contour.stroke.len()));
1087                self.verts.extend_from_slice(&contour.stroke);
1088                offset += contour.stroke.len();
1089            }
1090
1091            cmd.drawables.push(drawable);
1092        }
1093
1094        self.append_cmd(cmd);
1095    }
1096
1097    // Text
1098
1099    /// Adds a font file to the canvas
1100    pub fn add_font<P: AsRef<FilePath>>(&mut self, file_path: P) -> Result<FontId, ErrorKind> {
1101        self.text_context.as_ref().borrow_mut().add_font_file(file_path)
1102    }
1103
1104    /// Adds a font to the canvas by reading it from the specified chunk of memory.
1105    pub fn add_font_mem(&mut self, data: &[u8]) -> Result<FontId, ErrorKind> {
1106        self.text_context.as_ref().borrow_mut().add_font_mem(data)
1107    }
1108
1109    /// Adds all .ttf files from a directory
1110    pub fn add_font_dir<P: AsRef<FilePath>>(&mut self, dir_path: P) -> Result<Vec<FontId>, ErrorKind> {
1111        self.text_context.as_ref().borrow_mut().add_font_dir(dir_path)
1112    }
1113
1114    /// Returns information on how the provided text will be drawn with the specified paint.
1115    pub fn measure_text<S: AsRef<str>>(
1116        &mut self,
1117        x: f32,
1118        y: f32,
1119        text: S,
1120        mut paint: Paint,
1121    ) -> Result<TextMetrics, ErrorKind> {
1122        self.transform_text_paint(&mut paint);
1123
1124        let text = text.as_ref();
1125        let scale = self.font_scale() * self.device_px_ratio;
1126        let invscale = 1.0 / scale;
1127
1128        self.text_context
1129            .as_ref()
1130            .borrow_mut()
1131            .measure_text(x * scale, y * scale, text, paint)
1132            .map(|mut metrics| {
1133                metrics.scale(invscale);
1134                metrics
1135            })
1136    }
1137
1138    /// Returns font metrics for a particular Paint.
1139    pub fn measure_font(&mut self, mut paint: Paint) -> Result<FontMetrics, ErrorKind> {
1140        self.transform_text_paint(&mut paint);
1141
1142        self.text_context.as_ref().borrow_mut().measure_font(paint)
1143    }
1144
1145    /// Returns the maximum index-th byte of text that will fit inside max_width.
1146    ///
1147    /// The retuned index will always lie at the start and/or end of a UTF-8 code point sequence or at the start or end of the text
1148    pub fn break_text<S: AsRef<str>>(&mut self, max_width: f32, text: S, mut paint: Paint) -> Result<usize, ErrorKind> {
1149        self.transform_text_paint(&mut paint);
1150
1151        let text = text.as_ref();
1152        let scale = self.font_scale() * self.device_px_ratio;
1153        let max_width = max_width * scale;
1154
1155        self.text_context
1156            .as_ref()
1157            .borrow_mut()
1158            .break_text(max_width, text, paint)
1159    }
1160
1161    /// Returnes a list of ranges representing each line of text that will fit inside max_width
1162    pub fn break_text_vec<S: AsRef<str>>(
1163        &mut self,
1164        max_width: f32,
1165        text: S,
1166        mut paint: Paint,
1167    ) -> Result<Vec<Range<usize>>, ErrorKind> {
1168        self.transform_text_paint(&mut paint);
1169
1170        let text = text.as_ref();
1171        let scale = self.font_scale() * self.device_px_ratio;
1172        let max_width = max_width * scale;
1173
1174        self.text_context
1175            .as_ref()
1176            .borrow_mut()
1177            .break_text_vec(max_width, text, paint)
1178    }
1179
1180    /// Fills the provided string with the specified Paint.
1181    pub fn fill_text<S: AsRef<str>>(
1182        &mut self,
1183        x: f32,
1184        y: f32,
1185        text: S,
1186        paint: Paint,
1187    ) -> Result<TextMetrics, ErrorKind> {
1188        self.draw_text(x, y, text.as_ref(), paint, RenderMode::Fill)
1189    }
1190
1191    /// Strokes the provided string with the specified Paint.
1192    pub fn stroke_text<S: AsRef<str>>(
1193        &mut self,
1194        x: f32,
1195        y: f32,
1196        text: S,
1197        paint: Paint,
1198    ) -> Result<TextMetrics, ErrorKind> {
1199        self.draw_text(x, y, text.as_ref(), paint, RenderMode::Stroke)
1200    }
1201
1202    // Private
1203
1204    fn transform_text_paint(&self, paint: &mut Paint) {
1205        let scale = self.font_scale() * self.device_px_ratio;
1206        paint.font_size *= scale;
1207        paint.letter_spacing *= scale;
1208        paint.line_width *= scale;
1209    }
1210
1211    fn draw_text(
1212        &mut self,
1213        x: f32,
1214        y: f32,
1215        text: &str,
1216        mut paint: Paint,
1217        render_mode: RenderMode,
1218    ) -> Result<TextMetrics, ErrorKind> {
1219        let transform = self.state().transform;
1220        let scale = self.font_scale() * self.device_px_ratio;
1221        let invscale = 1.0 / scale;
1222
1223        self.transform_text_paint(&mut paint);
1224
1225        let mut layout = text::shape(
1226            x * scale,
1227            y * scale,
1228            &mut self.text_context.as_ref().borrow_mut(),
1229            &paint,
1230            text,
1231            None,
1232        )?;
1233        //let layout = self.layout_text(x, y, text, paint)?;
1234
1235        // TODO: Early out if text is outside the canvas bounds, or maybe even check for each character in layout.
1236
1237        let bitmap_glyphs = layout.has_bitmap_glyphs();
1238        let need_direct_rendering = paint.font_size > 92.0;
1239
1240        if need_direct_rendering && !bitmap_glyphs {
1241            text::render_direct(self, &layout, &paint, render_mode, invscale)?;
1242        } else {
1243            let create_vertices = |quads: &Vec<text::Quad>| {
1244                let mut verts = Vec::with_capacity(quads.len() * 6);
1245
1246                for quad in quads {
1247                    let (p0, p1) = transform.transform_point(quad.x0 * invscale, quad.y0 * invscale);
1248                    let (p2, p3) = transform.transform_point(quad.x1 * invscale, quad.y0 * invscale);
1249                    let (p4, p5) = transform.transform_point(quad.x1 * invscale, quad.y1 * invscale);
1250                    let (p6, p7) = transform.transform_point(quad.x0 * invscale, quad.y1 * invscale);
1251
1252                    verts.push(Vertex::new(p0, p1, quad.s0, quad.t0));
1253                    verts.push(Vertex::new(p4, p5, quad.s1, quad.t1));
1254                    verts.push(Vertex::new(p2, p3, quad.s1, quad.t0));
1255                    verts.push(Vertex::new(p0, p1, quad.s0, quad.t0));
1256                    verts.push(Vertex::new(p6, p7, quad.s0, quad.t1));
1257                    verts.push(Vertex::new(p4, p5, quad.s1, quad.t1));
1258                }
1259                verts
1260            };
1261
1262            let atlas = if bitmap_glyphs && need_direct_rendering {
1263                self.emphemeral_glyph_atlas.get_or_insert_with(Default::default).clone()
1264            } else {
1265                self.glyph_atlas.clone()
1266            };
1267
1268            let draw_commands = atlas.render_atlas(self, &layout, &paint, render_mode)?;
1269
1270            for cmd in draw_commands.alpha_glyphs {
1271                let verts = create_vertices(&cmd.quads);
1272
1273                paint.set_glyph_texture(GlyphTexture::AlphaMask(cmd.image_id));
1274
1275                // Apply global alpha
1276                paint.mul_alpha(self.state().alpha);
1277
1278                self.render_triangles(&verts, &paint);
1279            }
1280
1281            for cmd in draw_commands.color_glyphs {
1282                let verts = create_vertices(&cmd.quads);
1283
1284                paint.set_glyph_texture(GlyphTexture::ColorTexture(cmd.image_id));
1285
1286                // Apply global alpha
1287                paint.mul_alpha(self.state().alpha);
1288
1289                self.render_triangles(&verts, &paint);
1290            }
1291        }
1292
1293        layout.scale(invscale);
1294
1295        Ok(layout)
1296    }
1297
1298    fn render_triangles(&mut self, verts: &[Vertex], paint: &Paint) {
1299        let scissor = self.state().scissor;
1300
1301        let params = Params::new(&self.images, paint, &scissor, 1.0, 1.0, -1.0);
1302
1303        let mut cmd = Command::new(CommandType::Triangles { params });
1304        cmd.composite_operation = self.state().composite_operation;
1305        cmd.glyph_texture = paint.glyph_texture();
1306
1307        if let PaintFlavor::Image { id, .. } = paint.flavor {
1308            cmd.image = Some(id);
1309        } else if let Some(paint::GradientColors::MultiStop { stops }) = paint.flavor.gradient_colors() {
1310            cmd.image = self
1311                .gradients
1312                .lookup_or_add(*stops, &mut self.images, &mut self.renderer)
1313                .ok();
1314        }
1315
1316        cmd.triangles_verts = Some((self.verts.len(), verts.len()));
1317        self.append_cmd(cmd);
1318
1319        self.verts.extend_from_slice(verts);
1320    }
1321
1322    fn font_scale(&self) -> f32 {
1323        let avg_scale = self.state().transform.average_scale();
1324
1325        geometry::quantize(avg_scale, 0.1).min(7.0)
1326    }
1327
1328    //
1329
1330    fn state(&self) -> &State {
1331        self.state_stack.last().unwrap()
1332    }
1333
1334    fn state_mut(&mut self) -> &mut State {
1335        self.state_stack.last_mut().unwrap()
1336    }
1337
1338    #[cfg(feature = "debug_inspector")]
1339    pub fn debug_inspector_get_font_textures(&self) -> Vec<ImageId> {
1340        self.glyph_atlas
1341            .glyph_textures
1342            .borrow()
1343            .iter()
1344            .map(|t| t.image_id)
1345            .collect()
1346    }
1347
1348    #[cfg(feature = "debug_inspector")]
1349    pub fn debug_inspector_draw_image(&mut self, id: ImageId) {
1350        if let Ok(size) = self.image_size(id) {
1351            let width = size.0 as f32;
1352            let height = size.1 as f32;
1353            let mut path = Path::new();
1354            path.rect(0f32, 0f32, width, height);
1355            self.fill_path(&mut path, Paint::image(id, 0f32, 0f32, width, height, 0f32, 1f32));
1356        }
1357    }
1358}
1359
1360impl<T: Renderer> Drop for Canvas<T> {
1361    fn drop(&mut self) {
1362        self.images.clear(&mut self.renderer);
1363    }
1364}