skia_safe/core/canvas.rs
1use std::{cell::UnsafeCell, ffi::CString, fmt, marker::PhantomData, mem, ops::Deref, ptr, slice};
2
3use sb::SkCanvas_FilterSpan;
4use skia_bindings::{
5 self as sb, SkAutoCanvasRestore, SkCanvas, SkCanvas_SaveLayerRec, SkColorSpace, SkImageFilter,
6 SkPaint, SkRect, U8CPU,
7};
8
9#[cfg(feature = "gpu")]
10use crate::gpu;
11use crate::{
12 prelude::*, scalar, Bitmap, BlendMode, ClipOp, Color, Color4f, Data, Drawable, FilterMode,
13 Font, GlyphId, IPoint, IRect, ISize, Image, ImageFilter, ImageInfo, Matrix, Paint, Path,
14 Picture, Pixmap, Point, QuickReject, RRect, RSXform, Rect, Region, SamplingOptions, Shader,
15 Surface, SurfaceProps, TextBlob, TextEncoding, TileMode, Vector, Vertices, M44,
16};
17use crate::{Arc, ColorSpace};
18
19pub use lattice::Lattice;
20
21bitflags! {
22 /// [`SaveLayerFlags`] provides options that may be used in any combination in [`SaveLayerRec`],
23 /// defining how layer allocated by [`Canvas::save_layer()`] operates. It may be set to zero,
24 /// [`PRESERVE_LCD_TEXT`], [`INIT_WITH_PREVIOUS`], or both flags.
25 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
26 pub struct SaveLayerFlags: u32 {
27 const PRESERVE_LCD_TEXT = sb::SkCanvas_SaveLayerFlagsSet_kPreserveLCDText_SaveLayerFlag as _;
28 /// initializes with previous contents
29 const INIT_WITH_PREVIOUS = sb::SkCanvas_SaveLayerFlagsSet_kInitWithPrevious_SaveLayerFlag as _;
30 const F16_COLOR_TYPE = sb::SkCanvas_SaveLayerFlagsSet_kF16ColorType as _;
31 }
32}
33
34/// [`SaveLayerRec`] contains the state used to create the layer.
35#[repr(C)]
36pub struct SaveLayerRec<'a> {
37 // We _must_ store _references_ to the native types here, because not all of them are native
38 // transmutable, like ImageFilter or Image, which are represented as ref counted pointers and so
39 // we would store a reference to a pointer only.
40 bounds: Option<&'a SkRect>,
41 paint: Option<&'a SkPaint>,
42 filters: SkCanvas_FilterSpan,
43 backdrop: Option<&'a SkImageFilter>,
44 backdrop_tile_mode: sb::SkTileMode,
45 color_space: Option<&'a SkColorSpace>,
46 flags: SaveLayerFlags,
47 experimental_backdrop_scale: scalar,
48}
49
50native_transmutable!(
51 SkCanvas_SaveLayerRec,
52 SaveLayerRec<'_>,
53 save_layer_rec_layout
54);
55
56impl Default for SaveLayerRec<'_> {
57 /// Sets [`Self::bounds`], [`Self::paint`], and [`Self::backdrop`] to `None`. Clears
58 /// [`Self::flags`].
59 ///
60 /// Returns empty [`SaveLayerRec`]
61 fn default() -> Self {
62 SaveLayerRec::construct(|slr| unsafe { sb::C_SkCanvas_SaveLayerRec_Construct(slr) })
63 }
64}
65
66impl Drop for SaveLayerRec<'_> {
67 fn drop(&mut self) {
68 unsafe { sb::C_SkCanvas_SaveLayerRec_destruct(self.native_mut()) }
69 }
70}
71
72impl fmt::Debug for SaveLayerRec<'_> {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 f.debug_struct("SaveLayerRec")
75 .field("bounds", &self.bounds.map(Rect::from_native_ref))
76 .field("paint", &self.paint.map(Paint::from_native_ref))
77 .field(
78 "backdrop",
79 &ImageFilter::from_unshared_ptr_ref(&(self.backdrop.as_ptr_or_null() as *mut _)),
80 )
81 .field("backdrop_tile_mode", &self.backdrop_tile_mode)
82 .field(
83 "color_space",
84 &ColorSpace::from_unshared_ptr_ref(&(self.color_space.as_ptr_or_null() as *mut _)),
85 )
86 .field("flags", &self.flags)
87 .field(
88 "experimental_backdrop_scale",
89 &self.experimental_backdrop_scale,
90 )
91 .finish()
92 }
93}
94
95impl<'a> SaveLayerRec<'a> {
96 /// Hints at layer size limit
97 #[must_use]
98 pub fn bounds(mut self, bounds: &'a Rect) -> Self {
99 self.bounds = Some(bounds.native());
100 self
101 }
102
103 /// Modifies overlay
104 #[must_use]
105 pub fn paint(mut self, paint: &'a Paint) -> Self {
106 self.paint = Some(paint.native());
107 self
108 }
109
110 /// If not `None`, this triggers the same initialization behavior as setting
111 /// [`SaveLayerFlags::INIT_WITH_PREVIOUS`] on [`Self::flags`]: the current layer is copied into
112 /// the new layer, rather than initializing the new layer with transparent-black. This is then
113 /// filtered by [`Self::backdrop`] (respecting the current clip).
114 #[must_use]
115 pub fn backdrop(mut self, backdrop: &'a ImageFilter) -> Self {
116 self.backdrop = Some(backdrop.native());
117 self
118 }
119
120 /// If the layer is initialized with prior content (and/or with a backdrop filter) and this
121 /// would require sampling outside of the available backdrop, this is the tilemode applied
122 /// to the boundary of the prior layer's image.
123 #[must_use]
124 pub fn backdrop_tile_mode(mut self, backdrop_tile_mode: TileMode) -> Self {
125 self.backdrop_tile_mode = backdrop_tile_mode;
126 self
127 }
128
129 /// If not `None`, this triggers a color space conversion when the layer is restored. It
130 /// will be as if the layer's contents are drawn in this color space. Filters from
131 /// `backdrop` and `paint` will be applied in this color space.
132 pub fn color_space(mut self, color_space: &'a ColorSpace) -> Self {
133 self.color_space = Some(color_space.native());
134 self
135 }
136
137 /// Preserves LCD text, creates with prior layer contents
138 #[must_use]
139 pub fn flags(mut self, flags: SaveLayerFlags) -> Self {
140 self.flags = flags;
141 self
142 }
143}
144
145/// Selects if an array of points are drawn as discrete points, as lines, or as an open polygon.
146pub use sb::SkCanvas_PointMode as PointMode;
147variant_name!(PointMode::Polygon);
148
149/// [`SrcRectConstraint`] controls the behavior at the edge of source [`Rect`], provided to
150/// [`Canvas::draw_image_rect()`] when there is any filtering. If kStrict is set, then extra code is
151/// used to ensure it nevers samples outside of the src-rect.
152///
153/// [`SrcRectConstraint::Strict`] disables the use of mipmaps and anisotropic filtering.
154pub use sb::SkCanvas_SrcRectConstraint as SrcRectConstraint;
155variant_name!(SrcRectConstraint::Fast);
156
157/// Provides access to Canvas's pixels.
158///
159/// Returned by [`Canvas::access_top_layer_pixels()`]
160#[derive(Debug)]
161pub struct TopLayerPixels<'a> {
162 /// Address of pixels
163 pub pixels: &'a mut [u8],
164 /// Writable pixels' [`ImageInfo`]
165 pub info: ImageInfo,
166 /// Writable pixels' row bytes
167 pub row_bytes: usize,
168 /// [`Canvas`] top layer origin, its top-left corner
169 pub origin: IPoint,
170}
171
172/// Used to pass either a slice of [`Point`] or [`RSXform`] to [`Canvas::draw_glyphs_at`].
173#[derive(Clone, Debug)]
174pub enum GlyphPositions<'a> {
175 Points(&'a [Point]),
176 RSXforms(&'a [RSXform]),
177}
178
179impl<'a> From<&'a [Point]> for GlyphPositions<'a> {
180 fn from(points: &'a [Point]) -> Self {
181 Self::Points(points)
182 }
183}
184
185impl<'a> From<&'a [RSXform]> for GlyphPositions<'a> {
186 fn from(rs_xforms: &'a [RSXform]) -> Self {
187 Self::RSXforms(rs_xforms)
188 }
189}
190
191/// [`Canvas`] provides an interface for drawing, and how the drawing is clipped and transformed.
192/// [`Canvas`] contains a stack of [`Matrix`] and clip values.
193///
194/// [`Canvas`] and [`Paint`] together provide the state to draw into [`Surface`] or `Device`.
195/// Each [`Canvas`] draw call transforms the geometry of the object by the concatenation of all
196/// [`Matrix`] values in the stack. The transformed geometry is clipped by the intersection
197/// of all of clip values in the stack. The [`Canvas`] draw calls use [`Paint`] to supply drawing
198/// state such as color, [`crate::Typeface`], text size, stroke width, [`Shader`] and so on.
199///
200/// To draw to a pixel-based destination, create raster surface or GPU surface.
201/// Request [`Canvas`] from [`Surface`] to obtain the interface to draw.
202/// [`Canvas`] generated by raster surface draws to memory visible to the CPU.
203/// [`Canvas`] generated by GPU surface uses Vulkan or OpenGL to draw to the GPU.
204///
205/// To draw to a document, obtain [`Canvas`] from SVG canvas, document PDF, or
206/// [`crate::PictureRecorder`]. [`crate::Document`] based [`Canvas`] and other [`Canvas`]
207/// subclasses reference Device describing the destination.
208///
209/// [`Canvas`] can be constructed to draw to [`Bitmap`] without first creating raster surface.
210/// This approach may be deprecated in the future.
211#[repr(transparent)]
212pub struct Canvas(UnsafeCell<SkCanvas>);
213
214impl Canvas {
215 pub(self) fn native(&self) -> &SkCanvas {
216 unsafe { &*self.0.get() }
217 }
218
219 #[allow(clippy::mut_from_ref)]
220 pub(crate) fn native_mut(&self) -> &mut SkCanvas {
221 unsafe { &mut (*self.0.get()) }
222 }
223}
224
225impl fmt::Debug for Canvas {
226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227 f.debug_struct("Canvas")
228 .field("image_info", &self.image_info())
229 .field("props", &self.props())
230 .field("base_layer_size", &self.base_layer_size())
231 .field("save_count", &self.save_count())
232 .field("local_clip_bounds", &self.local_clip_bounds())
233 .field("device_clip_bounds", &self.device_clip_bounds())
234 .field("local_to_device", &self.local_to_device())
235 .finish()
236 }
237}
238
239/// Represents a [`Canvas`] that is owned and dropped when it goes out of scope _and_ is bound to
240/// the lifetime of some other value (an array of pixels for example).
241///
242/// Access to the [`Canvas`] functions are resolved with the [`Deref`] trait.
243#[repr(transparent)]
244pub struct OwnedCanvas<'lt>(ptr::NonNull<Canvas>, PhantomData<&'lt ()>);
245
246impl Deref for OwnedCanvas<'_> {
247 type Target = Canvas;
248
249 fn deref(&self) -> &Self::Target {
250 unsafe { self.0.as_ref() }
251 }
252}
253
254impl Drop for OwnedCanvas<'_> {
255 /// Draws saved layers, if any.
256 /// Frees up resources used by [`Canvas`].
257 ///
258 /// example: <https://fiddle.skia.org/c/@Canvas_destructor>
259 fn drop(&mut self) {
260 unsafe { sb::C_SkCanvas_delete(self.native()) }
261 }
262}
263
264impl Default for OwnedCanvas<'_> {
265 /// Creates an empty [`Canvas`] with no backing device or pixels, with
266 /// a width and height of zero.
267 ///
268 /// Returns empty [`Canvas`]
269 ///
270 /// example: <https://fiddle.skia.org/c/@Canvas_empty_constructor>
271 fn default() -> Self {
272 let ptr = unsafe { sb::C_SkCanvas_newEmpty() };
273 Canvas::own_from_native_ptr(ptr).unwrap()
274 }
275}
276
277impl fmt::Debug for OwnedCanvas<'_> {
278 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279 f.debug_tuple("OwnedCanvas").field(self as &Canvas).finish()
280 }
281}
282
283impl Canvas {
284 /// Allocates raster [`Canvas`] that will draw directly into pixels.
285 ///
286 /// [`Canvas`] is returned if all parameters are valid.
287 /// Valid parameters include:
288 /// - `info` dimensions are zero or positive
289 /// - `info` contains [`crate::ColorType`] and [`crate::AlphaType`] supported by raster surface
290 /// - `row_bytes` is `None` or large enough to contain info width pixels of [`crate::ColorType`]
291 ///
292 /// Pass `None` for `row_bytes` to compute `row_bytes` from info width and size of pixel.
293 /// If `row_bytes` is not `None`, it must be equal to or greater than `info` width times
294 /// bytes required for [`crate::ColorType`].
295 ///
296 /// Pixel buffer size should be info height times computed `row_bytes`.
297 /// Pixels are not initialized.
298 /// To access pixels after drawing, call `flush()` or [`Self::peek_pixels()`].
299 ///
300 /// - `info` width, height, [`crate::ColorType`], [`crate::AlphaType`], [`crate::ColorSpace`],
301 /// of raster surface; width, or height, or both, may be zero
302 /// - `pixels` pointer to destination pixels buffer
303 /// - `row_bytes` interval from one [`Surface`] row to the next, or zero
304 /// - `props` LCD striping orientation and setting for device independent fonts;
305 /// may be `None`
306 ///
307 /// Returns [`OwnedCanvas`] if all parameters are valid; otherwise, `None`.
308 pub fn from_raster_direct<'pixels>(
309 info: &ImageInfo,
310 pixels: &'pixels mut [u8],
311 row_bytes: impl Into<Option<usize>>,
312 props: Option<&SurfaceProps>,
313 ) -> Option<OwnedCanvas<'pixels>> {
314 let row_bytes = row_bytes.into().unwrap_or_else(|| info.min_row_bytes());
315 if info.valid_pixels(row_bytes, pixels) {
316 let ptr = unsafe {
317 sb::C_SkCanvas_MakeRasterDirect(
318 info.native(),
319 pixels.as_mut_ptr() as _,
320 row_bytes,
321 props.native_ptr_or_null(),
322 )
323 };
324 Self::own_from_native_ptr(ptr)
325 } else {
326 None
327 }
328 }
329
330 /// Allocates raster [`Canvas`] specified by inline image specification. Subsequent [`Canvas`]
331 /// calls draw into pixels.
332 /// [`crate::ColorType`] is set to [`crate::ColorType::n32()`].
333 /// [`crate::AlphaType`] is set to [`crate::AlphaType::Premul`].
334 /// To access pixels after drawing, call `flush()` or [`Self::peek_pixels()`].
335 ///
336 /// [`OwnedCanvas`] is returned if all parameters are valid.
337 /// Valid parameters include:
338 /// - width and height are zero or positive
339 /// - `row_bytes` is zero or large enough to contain width pixels of [`crate::ColorType::n32()`]
340 ///
341 /// Pass `None` for `row_bytes` to compute `row_bytes` from width and size of pixel.
342 /// If `row_bytes` is greater than zero, it must be equal to or greater than width times bytes
343 /// required for [`crate::ColorType`].
344 ///
345 /// Pixel buffer size should be height times `row_bytes`.
346 ///
347 /// - `size` pixel column and row count on raster surface created; must both be zero or greater
348 /// - `pixels` pointer to destination pixels buffer; buffer size should be height times
349 /// `row_bytes`
350 /// - `row_bytes` interval from one [`Surface`] row to the next, or zero
351 ///
352 /// Returns [`OwnedCanvas`] if all parameters are valid; otherwise, `None`
353 pub fn from_raster_direct_n32<'pixels>(
354 size: impl Into<ISize>,
355 pixels: &'pixels mut [u32],
356 row_bytes: impl Into<Option<usize>>,
357 ) -> Option<OwnedCanvas<'pixels>> {
358 let info = ImageInfo::new_n32_premul(size, None);
359 let pixels_ptr: *mut u8 = pixels.as_mut_ptr() as _;
360 let pixels_u8: &'pixels mut [u8] =
361 unsafe { slice::from_raw_parts_mut(pixels_ptr, mem::size_of_val(pixels)) };
362 Self::from_raster_direct(&info, pixels_u8, row_bytes, None)
363 }
364
365 /// Creates [`Canvas`] of the specified dimensions without a [`Surface`].
366 /// Used by subclasses with custom implementations for draw member functions.
367 ///
368 /// If props equals `None`, [`SurfaceProps`] are created with `SurfaceProps::InitType` settings,
369 /// which choose the pixel striping direction and order. Since a platform may dynamically change
370 /// its direction when the device is rotated, and since a platform may have multiple monitors
371 /// with different characteristics, it is best not to rely on this legacy behavior.
372 ///
373 /// - `size` with and height zero or greater
374 /// - `props` LCD striping orientation and setting for device independent fonts;
375 /// may be `None`
376 ///
377 /// Returns [`Canvas`] placeholder with dimensions
378 ///
379 /// example: <https://fiddle.skia.org/c/@Canvas_int_int_const_SkSurfaceProps_star>
380 #[allow(clippy::new_ret_no_self)]
381 pub fn new<'lt>(
382 size: impl Into<ISize>,
383 props: Option<&SurfaceProps>,
384 ) -> Option<OwnedCanvas<'lt>> {
385 let size = size.into();
386 if size.width >= 0 && size.height >= 0 {
387 let ptr = unsafe {
388 sb::C_SkCanvas_newWidthHeightAndProps(
389 size.width,
390 size.height,
391 props.native_ptr_or_null(),
392 )
393 };
394 Canvas::own_from_native_ptr(ptr)
395 } else {
396 None
397 }
398 }
399
400 /// Constructs a canvas that draws into bitmap.
401 /// Use props to match the device characteristics, like LCD striping.
402 ///
403 /// bitmap is copied so that subsequently editing bitmap will not affect constructed [`Canvas`].
404 ///
405 /// - `bitmap` width, height, [`crate::ColorType`], [`crate::AlphaType`], and pixel storage of
406 /// raster surface
407 /// - `props` order and orientation of RGB striping; and whether to use device independent fonts
408 ///
409 /// Returns [`Canvas`] that can be used to draw into bitmap
410 ///
411 /// example: <https://fiddle.skia.org/c/@Canvas_const_SkBitmap_const_SkSurfaceProps>
412 pub fn from_bitmap<'lt>(
413 bitmap: &Bitmap,
414 props: Option<&SurfaceProps>,
415 ) -> Option<OwnedCanvas<'lt>> {
416 // <https://github.com/rust-skia/rust-skia/issues/669>
417 if !bitmap.is_ready_to_draw() {
418 return None;
419 }
420 let props_ptr = props.native_ptr_or_null();
421 let ptr = if props_ptr.is_null() {
422 unsafe { sb::C_SkCanvas_newFromBitmap(bitmap.native()) }
423 } else {
424 unsafe { sb::C_SkCanvas_newFromBitmapAndProps(bitmap.native(), props_ptr) }
425 };
426 Canvas::own_from_native_ptr(ptr)
427 }
428
429 /// Returns [`ImageInfo`] for [`Canvas`]. If [`Canvas`] is not associated with raster surface or
430 /// GPU surface, returned [`crate::ColorType`] is set to [`crate::ColorType::Unknown`]
431 ///
432 /// Returns dimensions and [`crate::ColorType`] of [`Canvas`]
433 ///
434 /// example: <https://fiddle.skia.org/c/@Canvas_imageInfo>
435 pub fn image_info(&self) -> ImageInfo {
436 let mut ii = ImageInfo::default();
437 unsafe { sb::C_SkCanvas_imageInfo(self.native(), ii.native_mut()) };
438 ii
439 }
440
441 /// Copies [`SurfaceProps`], if [`Canvas`] is associated with raster surface or GPU surface, and
442 /// returns `true`. Otherwise, returns `false` and leave props unchanged.
443 ///
444 /// - `props` storage for writable [`SurfaceProps`]
445 ///
446 /// Returns `true` if [`SurfaceProps`] was copied
447 ///
448 /// example: <https://fiddle.skia.org/c/@Canvas_getProps>
449 pub fn props(&self) -> Option<SurfaceProps> {
450 let mut sp = SurfaceProps::default();
451 unsafe { self.native().getProps(sp.native_mut()) }.if_true_some(sp)
452 }
453
454 /// Returns the [`SurfaceProps`] associated with the canvas (i.e., at the base of the layer
455 /// stack).
456 pub fn base_props(&self) -> SurfaceProps {
457 SurfaceProps::from_native_c(unsafe { self.native().getBaseProps() })
458 }
459
460 /// Returns the [`SurfaceProps`] associated with the canvas that are currently active (i.e., at
461 /// the top of the layer stack). This can differ from [`Self::base_props`] depending on the flags
462 /// passed to saveLayer (see [`SaveLayerFlags`]).
463 pub fn top_props(&self) -> SurfaceProps {
464 SurfaceProps::from_native_c(unsafe { self.native().getTopProps() })
465 }
466
467 /// Gets the size of the base or root layer in global canvas coordinates. The
468 /// origin of the base layer is always (0,0). The area available for drawing may be
469 /// smaller (due to clipping or saveLayer).
470 ///
471 /// Returns integral size of base layer
472 ///
473 /// example: <https://fiddle.skia.org/c/@Canvas_getBaseLayerSize>
474 pub fn base_layer_size(&self) -> ISize {
475 let mut size = ISize::default();
476 unsafe { sb::C_SkCanvas_getBaseLayerSize(self.native(), size.native_mut()) }
477 size
478 }
479
480 /// Creates [`Surface`] matching info and props, and associates it with [`Canvas`].
481 /// Returns `None` if no match found.
482 ///
483 /// If props is `None`, matches [`SurfaceProps`] in [`Canvas`]. If props is `None` and
484 /// [`Canvas`] does not have [`SurfaceProps`], creates [`Surface`] with default
485 /// [`SurfaceProps`].
486 ///
487 /// - `info` width, height, [`crate::ColorType`], [`crate::AlphaType`], and
488 /// [`crate::ColorSpace`]
489 /// - `props` [`SurfaceProps`] to match; may be `None` to match [`Canvas`]
490 ///
491 /// Returns [`Surface`] matching info and props, or `None` if no match is available
492 ///
493 /// example: <https://fiddle.skia.org/c/@Canvas_makeSurface>
494 pub fn new_surface(&self, info: &ImageInfo, props: Option<&SurfaceProps>) -> Option<Surface> {
495 Surface::from_ptr(unsafe {
496 sb::C_SkCanvas_makeSurface(self.native_mut(), info.native(), props.native_ptr_or_null())
497 })
498 }
499
500 /// Returns Ganesh context of the GPU surface associated with [`Canvas`].
501 ///
502 /// Returns GPU context, if available; `None` otherwise
503 ///
504 /// example: <https://fiddle.skia.org/c/@Canvas_recordingContext>
505 #[cfg(feature = "gpu")]
506 pub fn recording_context(&self) -> Option<gpu::RecordingContext> {
507 gpu::RecordingContext::from_unshared_ptr(unsafe {
508 sb::C_SkCanvas_recordingContext(self.native())
509 })
510 }
511
512 /// Returns the [`gpu::DirectContext`].
513 /// This is a rust-skia helper for that makes it simpler to call [`Image::encode`].
514 #[cfg(feature = "gpu")]
515 pub fn direct_context(&self) -> Option<gpu::DirectContext> {
516 self.recording_context()
517 .and_then(|mut c| c.as_direct_context())
518 }
519
520 /// Sometimes a canvas is owned by a surface. If it is, [`Self::surface()`] will return a bare
521 /// pointer to that surface, else this will return `None`.
522 ///
523 /// # Safety
524 /// This function is unsafe because it is not clear how exactly the lifetime of the canvas
525 /// relates to surface returned.
526 /// See also [`OwnedCanvas`], [`RCHandle<SkSurface>::canvas()`].
527 pub unsafe fn surface(&self) -> Option<Surface> {
528 // TODO: It might be possible to make this safe by returning a _kind of_ reference to the
529 // Surface that can not be cloned and stays bound to the lifetime of canvas.
530 // But even then, the Surface might exist twice then, which is confusing, but
531 // probably safe, because the first instance is borrowed by the canvas.
532 Surface::from_unshared_ptr(self.native().getSurface())
533 }
534
535 /// Returns the pixel base address, [`ImageInfo`], `row_bytes`, and origin if the pixels
536 /// can be read directly.
537 ///
538 /// - `info` storage for writable pixels' [`ImageInfo`]
539 /// - `row_bytes` storage for writable pixels' row bytes
540 /// - `origin` storage for [`Canvas`] top layer origin, its top-left corner
541 ///
542 /// Returns address of pixels, or `None` if inaccessible
543 ///
544 /// example: <https://fiddle.skia.org/c/@Canvas_accessTopLayerPixels_a>
545 /// example: <https://fiddle.skia.org/c/@Canvas_accessTopLayerPixels_b>
546 pub fn access_top_layer_pixels(&self) -> Option<TopLayerPixels> {
547 let mut info = ImageInfo::default();
548 let mut row_bytes = 0;
549 let mut origin = IPoint::default();
550 let ptr = unsafe {
551 self.native_mut().accessTopLayerPixels(
552 info.native_mut(),
553 &mut row_bytes,
554 origin.native_mut(),
555 )
556 };
557 if !ptr.is_null() {
558 let size = info.compute_byte_size(row_bytes);
559 let pixels = unsafe { slice::from_raw_parts_mut(ptr as _, size) };
560 Some(TopLayerPixels {
561 pixels,
562 info,
563 row_bytes,
564 origin,
565 })
566 } else {
567 None
568 }
569 }
570
571 // TODO: accessTopRasterHandle()
572
573 /// Returns `true` if [`Canvas`] has direct access to its pixels.
574 ///
575 /// Pixels are readable when `Device` is raster. Pixels are not readable when [`Canvas`] is
576 /// returned from GPU surface, returned by [`crate::Document::begin_page()`], returned by
577 /// [`Handle<SkPictureRecorder>::begin_recording()`], or [`Canvas`] is the base of a utility
578 /// class like `DebugCanvas`.
579 ///
580 /// pixmap is valid only while [`Canvas`] is in scope and unchanged. Any [`Canvas`] or
581 /// [`Surface`] call may invalidate the pixmap values.
582 ///
583 /// Returns [`Pixmap`] if [`Canvas`] has direct access to pixels
584 ///
585 /// example: <https://fiddle.skia.org/c/@Canvas_peekPixels>
586 pub fn peek_pixels(&self) -> Option<Pixmap> {
587 let mut pixmap = Pixmap::default();
588 unsafe { self.native_mut().peekPixels(pixmap.native_mut()) }.if_true_some(pixmap)
589 }
590
591 /// Copies [`Rect`] of pixels from [`Canvas`] into `dst_pixels`. [`Matrix`] and clip are
592 /// ignored.
593 ///
594 /// Source [`Rect`] corners are `src_point` and `(image_info().width(), image_info().height())`.
595 /// Destination [`Rect`] corners are `(0, 0)` and `(dst_Info.width(), dst_info.height())`.
596 /// Copies each readable pixel intersecting both rectangles, without scaling,
597 /// converting to `dst_info.color_type()` and `dst_info.alpha_type()` if required.
598 ///
599 /// Pixels are readable when `Device` is raster, or backed by a GPU.
600 /// Pixels are not readable when [`Canvas`] is returned by [`crate::Document::begin_page()`],
601 /// returned by [`Handle<SkPictureRecorder>::begin_recording()`], or [`Canvas`] is the base of a
602 /// utility class like `DebugCanvas`.
603 ///
604 /// The destination pixel storage must be allocated by the caller.
605 ///
606 /// Pixel values are converted only if [`crate::ColorType`] and [`crate::AlphaType`]
607 /// do not match. Only pixels within both source and destination rectangles
608 /// are copied. `dst_pixels` contents outside [`Rect`] intersection are unchanged.
609 ///
610 /// Pass negative values for `src_point.x` or `src_point.y` to offset pixels across or down
611 /// destination.
612 ///
613 /// Does not copy, and returns `false` if:
614 /// - Source and destination rectangles do not intersect.
615 /// - [`Canvas`] pixels could not be converted to `dst_info.color_type()` or
616 /// `dst_info.alpha_type()`.
617 /// - [`Canvas`] pixels are not readable; for instance, [`Canvas`] is document-based.
618 /// - `dst_row_bytes` is too small to contain one row of pixels.
619 ///
620 /// - `dst_info` width, height, [`crate::ColorType`], and [`crate::AlphaType`] of dstPixels
621 /// - `dst_pixels` storage for pixels; `dst_info.height()` times `dst_row_bytes`, or larger
622 /// - `dst_row_bytes` size of one destination row; `dst_info.width()` times pixel size, or
623 /// larger
624 /// - `src_point` offset into readable pixels; may be negative
625 ///
626 /// Returns `true` if pixels were copied
627 #[must_use]
628 pub fn read_pixels(
629 &self,
630 dst_info: &ImageInfo,
631 dst_pixels: &mut [u8],
632 dst_row_bytes: usize,
633 src_point: impl Into<IPoint>,
634 ) -> bool {
635 let src_point = src_point.into();
636 let required_size = dst_info.compute_byte_size(dst_row_bytes);
637 (dst_pixels.len() >= required_size)
638 && unsafe {
639 self.native_mut().readPixels(
640 dst_info.native(),
641 dst_pixels.as_mut_ptr() as _,
642 dst_row_bytes,
643 src_point.x,
644 src_point.y,
645 )
646 }
647 }
648
649 /// Copies [`Rect`] of pixels from [`Canvas`] into pixmap. [`Matrix`] and clip are
650 /// ignored.
651 ///
652 /// Source [`Rect`] corners are `(src.x, src.y)` and `(image_info().width(),
653 /// image_info().height())`.
654 /// Destination [`Rect`] corners are `(0, 0)` and `(pixmap.width(), pixmap.height())`.
655 /// Copies each readable pixel intersecting both rectangles, without scaling,
656 /// converting to `pixmap.color_type()` and `pixmap.alpha_type()` if required.
657 ///
658 /// Pixels are readable when `Device` is raster, or backed by a GPU. Pixels are not readable
659 /// when [`Canvas`] is returned by [`crate::Document::begin_page()`], returned by
660 /// [`Handle<SkPictureRecorder>::begin_recording()`], or [`Canvas`] is the base of a utility
661 /// class like `DebugCanvas`.
662 ///
663 /// Caller must allocate pixel storage in pixmap if needed.
664 ///
665 /// Pixel values are converted only if [`crate::ColorType`] and [`crate::AlphaType`] do not
666 /// match. Only pixels within both source and destination [`Rect`] are copied. pixmap pixels
667 /// contents outside [`Rect`] intersection are unchanged.
668 ///
669 /// Pass negative values for `src.x` or `src.y` to offset pixels across or down pixmap.
670 ///
671 /// Does not copy, and returns `false` if:
672 /// - Source and destination rectangles do not intersect.
673 /// - [`Canvas`] pixels could not be converted to `pixmap.color_type()` or
674 /// `pixmap.alpha_type()`.
675 /// - [`Canvas`] pixels are not readable; for instance, [`Canvas`] is document-based.
676 /// - [`Pixmap`] pixels could not be allocated.
677 /// - `pixmap.row_bytes()` is too small to contain one row of pixels.
678 ///
679 /// - `pixmap` storage for pixels copied from [`Canvas`]
680 /// - `src` offset into readable pixels ; may be negative
681 ///
682 /// Returns `true` if pixels were copied
683 ///
684 /// example: <https://fiddle.skia.org/c/@Canvas_readPixels_2>
685 #[must_use]
686 pub fn read_pixels_to_pixmap(&self, pixmap: &mut Pixmap, src: impl Into<IPoint>) -> bool {
687 let src = src.into();
688 unsafe { self.native_mut().readPixels1(pixmap.native(), src.x, src.y) }
689 }
690
691 /// Copies [`Rect`] of pixels from [`Canvas`] into bitmap. [`Matrix`] and clip are
692 /// ignored.
693 ///
694 /// Source [`Rect`] corners are `(src.x, src.y)` and `(image_info().width(),
695 /// image_info().height())`.
696 /// Destination [`Rect`] corners are `(0, 0)` and `(bitmap.width(), bitmap.height())`.
697 /// Copies each readable pixel intersecting both rectangles, without scaling,
698 /// converting to `bitmap.color_type()` and `bitmap.alpha_type()` if required.
699 ///
700 /// Pixels are readable when `Device` is raster, or backed by a GPU. Pixels are not readable
701 /// when [`Canvas`] is returned by [`crate::Document::begin_page()`], returned by
702 /// [`Handle<SkPictureRecorder>::begin_recording()`], or [`Canvas`] is the base of a utility
703 /// class like DebugCanvas.
704 ///
705 /// Caller must allocate pixel storage in bitmap if needed.
706 ///
707 /// [`Bitmap`] values are converted only if [`crate::ColorType`] and [`crate::AlphaType`]
708 /// do not match. Only pixels within both source and destination rectangles
709 /// are copied. [`Bitmap`] pixels outside [`Rect`] intersection are unchanged.
710 ///
711 /// Pass negative values for srcX or srcY to offset pixels across or down bitmap.
712 ///
713 /// Does not copy, and returns `false` if:
714 /// - Source and destination rectangles do not intersect.
715 /// - [`Canvas`] pixels could not be converted to `bitmap.color_type()` or
716 /// `bitmap.alpha_type()`.
717 /// - [`Canvas`] pixels are not readable; for instance, [`Canvas`] is document-based.
718 /// - bitmap pixels could not be allocated.
719 /// - `bitmap.row_bytes()` is too small to contain one row of pixels.
720 ///
721 /// - `bitmap` storage for pixels copied from [`Canvas`]
722 /// - `src` offset into readable pixels; may be negative
723 ///
724 /// Returns `true` if pixels were copied
725 ///
726 /// example: <https://fiddle.skia.org/c/@Canvas_readPixels_3>
727 #[must_use]
728 pub fn read_pixels_to_bitmap(&self, bitmap: &mut Bitmap, src: impl Into<IPoint>) -> bool {
729 let src = src.into();
730 unsafe {
731 self.native_mut()
732 .readPixels2(bitmap.native_mut(), src.x, src.y)
733 }
734 }
735
736 /// Copies [`Rect`] from pixels to [`Canvas`]. [`Matrix`] and clip are ignored.
737 /// Source [`Rect`] corners are `(0, 0)` and `(info.width(), info.height())`.
738 /// Destination [`Rect`] corners are `(offset.x, offset.y)` and
739 /// `(image_info().width(), image_info().height())`.
740 ///
741 /// Copies each readable pixel intersecting both rectangles, without scaling,
742 /// converting to `image_info().color_type()` and `image_info().alpha_type()` if required.
743 ///
744 /// Pixels are writable when `Device` is raster, or backed by a GPU.
745 /// Pixels are not writable when [`Canvas`] is returned by [`crate::Document::begin_page()`],
746 /// returned by [`Handle<SkPictureRecorder>::begin_recording()`], or [`Canvas`] is the base of a
747 /// utility class like `DebugCanvas`.
748 ///
749 /// Pixel values are converted only if [`crate::ColorType`] and [`crate::AlphaType`]
750 /// do not match. Only pixels within both source and destination rectangles
751 /// are copied. [`Canvas`] pixels outside [`Rect`] intersection are unchanged.
752 ///
753 /// Pass negative values for `offset.x` or `offset.y` to offset pixels to the left or
754 /// above [`Canvas`] pixels.
755 ///
756 /// Does not copy, and returns `false` if:
757 /// - Source and destination rectangles do not intersect.
758 /// - pixels could not be converted to [`Canvas`] `image_info().color_type()` or
759 /// `image_info().alpha_type()`.
760 /// - [`Canvas`] pixels are not writable; for instance, [`Canvas`] is document-based.
761 /// - `row_bytes` is too small to contain one row of pixels.
762 ///
763 /// - `info` width, height, [`crate::ColorType`], and [`crate::AlphaType`] of pixels
764 /// - `pixels` pixels to copy, of size `info.height()` times `row_bytes`, or larger
765 /// - `row_bytes` size of one row of pixels; info.width() times pixel size, or larger
766 /// - `offset` offset into [`Canvas`] writable pixels; may be negative
767 ///
768 /// Returns `true` if pixels were written to [`Canvas`]
769 ///
770 /// example: <https://fiddle.skia.org/c/@Canvas_writePixels>
771 #[must_use]
772 pub fn write_pixels(
773 &self,
774 info: &ImageInfo,
775 pixels: &[u8],
776 row_bytes: usize,
777 offset: impl Into<IPoint>,
778 ) -> bool {
779 let offset = offset.into();
780 let required_size = info.compute_byte_size(row_bytes);
781 (pixels.len() >= required_size)
782 && unsafe {
783 self.native_mut().writePixels(
784 info.native(),
785 pixels.as_ptr() as _,
786 row_bytes,
787 offset.x,
788 offset.y,
789 )
790 }
791 }
792
793 /// Copies [`Rect`] from pixels to [`Canvas`]. [`Matrix`] and clip are ignored.
794 /// Source [`Rect`] corners are `(0, 0)` and `(bitmap.width(), bitmap.height())`.
795 ///
796 /// Destination [`Rect`] corners are `(offset.x, offset.y)` and
797 /// `(image_info().width(), image_info().height())`.
798 ///
799 /// Copies each readable pixel intersecting both rectangles, without scaling,
800 /// converting to `image_info().color_type()` and `image_info().alpha_type()` if required.
801 ///
802 /// Pixels are writable when `Device` is raster, or backed by a GPU. Pixels are not writable
803 /// when [`Canvas`] is returned by [`crate::Document::begin_page()`], returned by
804 /// [`Handle<SkPictureRecorder>::begin_recording()`], or [`Canvas`] is the base of a utility
805 /// class like `DebugCanvas`.
806 ///
807 /// Pixel values are converted only if [`crate::ColorType`] and [`crate::AlphaType`]
808 /// do not match. Only pixels within both source and destination rectangles
809 /// are copied. [`Canvas`] pixels outside [`Rect`] intersection are unchanged.
810 ///
811 /// Pass negative values for `offset` to offset pixels to the left or
812 /// above [`Canvas`] pixels.
813 ///
814 /// Does not copy, and returns `false` if:
815 /// - Source and destination rectangles do not intersect.
816 /// - bitmap does not have allocated pixels.
817 /// - bitmap pixels could not be converted to [`Canvas`] `image_info().color_type()` or
818 /// `image_info().alpha_type()`.
819 /// - [`Canvas`] pixels are not writable; for instance, [`Canvas`] is document based.
820 /// - bitmap pixels are inaccessible; for instance, bitmap wraps a texture.
821 ///
822 /// - `bitmap` contains pixels copied to [`Canvas`]
823 /// - `offset` offset into [`Canvas`] writable pixels; may be negative
824 ///
825 /// Returns `true` if pixels were written to [`Canvas`]
826 ///
827 /// example: <https://fiddle.skia.org/c/@Canvas_writePixels_2>
828 /// example: <https://fiddle.skia.org/c/@State_Stack_a>
829 /// example: <https://fiddle.skia.org/c/@State_Stack_b>
830 #[must_use]
831 pub fn write_pixels_from_bitmap(&self, bitmap: &Bitmap, offset: impl Into<IPoint>) -> bool {
832 let offset = offset.into();
833 unsafe {
834 self.native_mut()
835 .writePixels1(bitmap.native(), offset.x, offset.y)
836 }
837 }
838
839 /// Saves [`Matrix`] and clip.
840 /// Calling [`Self::restore()`] discards changes to [`Matrix`] and clip,
841 /// restoring the [`Matrix`] and clip to their state when [`Self::save()`] was called.
842 ///
843 /// [`Matrix`] may be changed by [`Self::translate()`], [`Self::scale()`], [`Self::rotate()`],
844 /// [`Self::skew()`], [`Self::concat()`], [`Self::set_matrix()`], and [`Self::reset_matrix()`].
845 /// Clip may be changed by [`Self::clip_rect()`], [`Self::clip_rrect()`], [`Self::clip_path()`],
846 /// [`Self::clip_region()`].
847 ///
848 /// Saved [`Canvas`] state is put on a stack; multiple calls to [`Self::save()`] should be
849 /// balance by an equal number of calls to [`Self::restore()`].
850 ///
851 /// Call [`Self::restore_to_count()`] with result to restore this and subsequent saves.
852 ///
853 /// Returns depth of saved stack
854 ///
855 /// example: <https://fiddle.skia.org/c/@Canvas_save>
856 pub fn save(&self) -> usize {
857 unsafe { self.native_mut().save().try_into().unwrap() }
858 }
859
860 // The save_layer(bounds, paint) variants have been replaced by SaveLayerRec.
861
862 /// Saves [`Matrix`] and clip, and allocates [`Surface`] for subsequent drawing.
863 ///
864 /// Calling [`Self::restore()`] discards changes to [`Matrix`] and clip, and blends layer with
865 /// alpha opacity onto prior layer.
866 ///
867 /// [`Matrix`] may be changed by [`Self::translate()`], [`Self::scale()`], [`Self::rotate()`],
868 /// [`Self::skew()`], [`Self::concat()`], [`Self::set_matrix()`], and [`Self::reset_matrix()`].
869 /// Clip may be changed by [`Self::clip_rect()`], [`Self::clip_rrect()`], [`Self::clip_path()`],
870 /// [`Self::clip_region()`].
871 ///
872 /// [`Rect`] bounds suggests but does not define layer size. To clip drawing to a specific
873 /// rectangle, use [`Self::clip_rect()`].
874 ///
875 /// alpha of zero is fully transparent, 1.0 is fully opaque.
876 ///
877 /// Call [`Self::restore_to_count()`] with result to restore this and subsequent saves.
878 ///
879 /// - `bounds` hint to limit the size of layer; may be `None`
880 /// - `alpha` opacity of layer
881 ///
882 /// Returns depth of saved stack
883 ///
884 /// example: <https://fiddle.skia.org/c/@Canvas_saveLayerAlpha>
885 pub fn save_layer_alpha_f(&self, bounds: impl Into<Option<Rect>>, alpha: f32) -> usize {
886 unsafe {
887 self.native_mut()
888 .saveLayerAlphaf(bounds.into().native().as_ptr_or_null(), alpha)
889 }
890 .try_into()
891 .unwrap()
892 }
893
894 /// Helper that accepts an int between 0 and 255, and divides it by 255.0
895 pub fn save_layer_alpha(&self, bounds: impl Into<Option<Rect>>, alpha: U8CPU) -> usize {
896 self.save_layer_alpha_f(bounds, alpha as f32 * (1.0 / 255.0))
897 }
898
899 /// Saves [`Matrix`] and clip, and allocates [`Surface`] for subsequent drawing.
900 ///
901 /// Calling [`Self::restore()`] discards changes to [`Matrix`] and clip,
902 /// and blends [`Surface`] with alpha opacity onto the prior layer.
903 ///
904 /// [`Matrix`] may be changed by [`Self::translate()`], [`Self::scale()`], [`Self::rotate()`],
905 /// [`Self::skew()`], [`Self::concat()`], [`Self::set_matrix()`], and [`Self::reset_matrix()`].
906 /// Clip may be changed by [`Self::clip_rect()`], [`Self::clip_rrect()`], [`Self::clip_path()`],
907 /// [`Self::clip_region()`].
908 ///
909 /// [`SaveLayerRec`] contains the state used to create the layer.
910 ///
911 /// Call [`Self::restore_to_count()`] with result to restore this and subsequent saves.
912 ///
913 /// - `layer_rec` layer state
914 ///
915 /// Returns depth of save state stack before this call was made.
916 ///
917 /// example: <https://fiddle.skia.org/c/@Canvas_saveLayer_3>
918 pub fn save_layer(&self, layer_rec: &SaveLayerRec) -> usize {
919 unsafe { self.native_mut().saveLayer1(layer_rec.native()) }
920 .try_into()
921 .unwrap()
922 }
923
924 /// Removes changes to [`Matrix`] and clip since [`Canvas`] state was
925 /// last saved. The state is removed from the stack.
926 ///
927 /// Does nothing if the stack is empty.
928 ///
929 /// example: <https://fiddle.skia.org/c/@AutoCanvasRestore_restore>
930 ///
931 /// example: <https://fiddle.skia.org/c/@Canvas_restore>
932 pub fn restore(&self) -> &Self {
933 unsafe { self.native_mut().restore() };
934 self
935 }
936
937 /// Returns the number of saved states, each containing: [`Matrix`] and clip.
938 /// Equals the number of [`Self::save()`] calls less the number of [`Self::restore()`] calls
939 /// plus one.
940 /// The save count of a new canvas is one.
941 ///
942 /// Returns depth of save state stack
943 ///
944 /// example: <https://fiddle.skia.org/c/@Canvas_getSaveCount>
945 pub fn save_count(&self) -> usize {
946 unsafe { self.native().getSaveCount() }.try_into().unwrap()
947 }
948
949 /// Restores state to [`Matrix`] and clip values when [`Self::save()`], [`Self::save_layer()`],
950 /// or [`Self::save_layer_alpha()`] returned `save_count`.
951 ///
952 /// Does nothing if `save_count` is greater than state stack count.
953 /// Restores state to initial values if `save_count` is less than or equal to one.
954 ///
955 /// - `saveCount` depth of state stack to restore
956 ///
957 /// example: <https://fiddle.skia.org/c/@Canvas_restoreToCount>
958 pub fn restore_to_count(&self, save_count: usize) -> &Self {
959 unsafe {
960 self.native_mut()
961 .restoreToCount(save_count.try_into().unwrap())
962 }
963 self
964 }
965
966 /// Translates [`Matrix`] by `d`.
967 ///
968 /// Mathematically, replaces [`Matrix`] with a translation matrix premultiplied with [`Matrix`].
969 ///
970 /// This has the effect of moving the drawing by `(d.x, d.y)` before transforming the result
971 /// with [`Matrix`].
972 ///
973 /// - `d` distance to translate
974 ///
975 /// example: <https://fiddle.skia.org/c/@Canvas_translate>
976 pub fn translate(&self, d: impl Into<Vector>) -> &Self {
977 let d = d.into();
978 unsafe { self.native_mut().translate(d.x, d.y) }
979 self
980 }
981
982 /// Scales [`Matrix`] by `sx` on the x-axis and `sy` on the y-axis.
983 ///
984 /// Mathematically, replaces [`Matrix`] with a scale matrix premultiplied with [`Matrix`].
985 ///
986 /// This has the effect of scaling the drawing by `(sx, sy)` before transforming the result with
987 /// [`Matrix`].
988 ///
989 /// - `sx` amount to scale on x-axis
990 /// - `sy` amount to scale on y-axis
991 ///
992 /// example: <https://fiddle.skia.org/c/@Canvas_scale>
993 pub fn scale(&self, (sx, sy): (scalar, scalar)) -> &Self {
994 unsafe { self.native_mut().scale(sx, sy) }
995 self
996 }
997
998 /// Rotates [`Matrix`] by degrees about a point at `(p.x, p.y)`. Positive degrees rotates
999 /// clockwise.
1000 ///
1001 /// Mathematically, constructs a rotation matrix; premultiplies the rotation matrix by a
1002 /// translation matrix; then replaces [`Matrix`] with the resulting matrix premultiplied with
1003 /// [`Matrix`].
1004 ///
1005 /// This has the effect of rotating the drawing about a given point before transforming the
1006 /// result with [`Matrix`].
1007 ///
1008 /// - `degrees` amount to rotate, in degrees
1009 /// - `p` the point to rotate about
1010 ///
1011 /// example: <https://fiddle.skia.org/c/@Canvas_rotate_2>
1012 pub fn rotate(&self, degrees: scalar, p: Option<Point>) -> &Self {
1013 unsafe {
1014 match p {
1015 Some(point) => self.native_mut().rotate1(degrees, point.x, point.y),
1016 None => self.native_mut().rotate(degrees),
1017 }
1018 }
1019 self
1020 }
1021
1022 /// Skews [`Matrix`] by `sx` on the x-axis and `sy` on the y-axis. A positive value of `sx`
1023 /// skews the drawing right as y-axis values increase; a positive value of `sy` skews the
1024 /// drawing down as x-axis values increase.
1025 ///
1026 /// Mathematically, replaces [`Matrix`] with a skew matrix premultiplied with [`Matrix`].
1027 ///
1028 /// This has the effect of skewing the drawing by `(sx, sy)` before transforming the result with
1029 /// [`Matrix`].
1030 ///
1031 /// - `sx` amount to skew on x-axis
1032 /// - `sy` amount to skew on y-axis
1033 ///
1034 /// example: <https://fiddle.skia.org/c/@Canvas_skew>
1035 pub fn skew(&self, (sx, sy): (scalar, scalar)) -> &Self {
1036 unsafe { self.native_mut().skew(sx, sy) }
1037 self
1038 }
1039
1040 /// Replaces [`Matrix`] with matrix premultiplied with existing [`Matrix`].
1041 ///
1042 /// This has the effect of transforming the drawn geometry by matrix, before transforming the
1043 /// result with existing [`Matrix`].
1044 ///
1045 /// - `matrix` matrix to premultiply with existing [`Matrix`]
1046 ///
1047 /// example: <https://fiddle.skia.org/c/@Canvas_concat>
1048 pub fn concat(&self, matrix: &Matrix) -> &Self {
1049 unsafe { self.native_mut().concat(matrix.native()) }
1050 self
1051 }
1052
1053 pub fn concat_44(&self, m: &M44) -> &Self {
1054 unsafe { self.native_mut().concat1(m.native()) }
1055 self
1056 }
1057
1058 /// Replaces [`Matrix`] with `matrix`.
1059 /// Unlike [`Self::concat()`], any prior matrix state is overwritten.
1060 ///
1061 /// - `matrix` matrix to copy, replacing existing [`Matrix`]
1062 ///
1063 /// example: <https://fiddle.skia.org/c/@Canvas_setMatrix>
1064 pub fn set_matrix(&self, matrix: &M44) -> &Self {
1065 unsafe { self.native_mut().setMatrix(matrix.native()) }
1066 self
1067 }
1068
1069 /// Sets [`Matrix`] to the identity matrix.
1070 /// Any prior matrix state is overwritten.
1071 ///
1072 /// example: <https://fiddle.skia.org/c/@Canvas_resetMatrix>
1073 pub fn reset_matrix(&self) -> &Self {
1074 unsafe { self.native_mut().resetMatrix() }
1075 self
1076 }
1077
1078 /// Replaces clip with the intersection or difference of clip and `rect`,
1079 /// with an aliased or anti-aliased clip edge. `rect` is transformed by [`Matrix`]
1080 /// before it is combined with clip.
1081 ///
1082 /// - `rect` [`Rect`] to combine with clip
1083 /// - `op` [`ClipOp`] to apply to clip
1084 /// - `do_anti_alias` `true` if clip is to be anti-aliased
1085 ///
1086 /// example: <https://fiddle.skia.org/c/@Canvas_clipRect>
1087 pub fn clip_rect(
1088 &self,
1089 rect: impl AsRef<Rect>,
1090 op: impl Into<Option<ClipOp>>,
1091 do_anti_alias: impl Into<Option<bool>>,
1092 ) -> &Self {
1093 unsafe {
1094 self.native_mut().clipRect(
1095 rect.as_ref().native(),
1096 op.into().unwrap_or_default(),
1097 do_anti_alias.into().unwrap_or_default(),
1098 )
1099 }
1100 self
1101 }
1102
1103 pub fn clip_irect(&self, irect: impl AsRef<IRect>, op: impl Into<Option<ClipOp>>) -> &Self {
1104 let r = Rect::from(*irect.as_ref());
1105 self.clip_rect(r, op, false)
1106 }
1107
1108 /// Replaces clip with the intersection or difference of clip and `rrect`,
1109 /// with an aliased or anti-aliased clip edge.
1110 /// `rrect` is transformed by [`Matrix`]
1111 /// before it is combined with clip.
1112 ///
1113 /// - `rrect` [`RRect`] to combine with clip
1114 /// - `op` [`ClipOp`] to apply to clip
1115 /// - `do_anti_alias` `true` if clip is to be anti-aliased
1116 ///
1117 /// example: <https://fiddle.skia.org/c/@Canvas_clipRRect>
1118 pub fn clip_rrect(
1119 &self,
1120 rrect: impl AsRef<RRect>,
1121 op: impl Into<Option<ClipOp>>,
1122 do_anti_alias: impl Into<Option<bool>>,
1123 ) -> &Self {
1124 unsafe {
1125 self.native_mut().clipRRect(
1126 rrect.as_ref().native(),
1127 op.into().unwrap_or_default(),
1128 do_anti_alias.into().unwrap_or_default(),
1129 )
1130 }
1131 self
1132 }
1133
1134 /// Replaces clip with the intersection or difference of clip and `path`,
1135 /// with an aliased or anti-aliased clip edge. [`crate::path::FillType`] determines if `path`
1136 /// describes the area inside or outside its contours; and if path contour overlaps
1137 /// itself or another path contour, whether the overlaps form part of the area.
1138 /// `path` is transformed by [`Matrix`] before it is combined with clip.
1139 ///
1140 /// - `path` [`Path`] to combine with clip
1141 /// - `op` [`ClipOp`] to apply to clip
1142 /// - `do_anti_alias` `true` if clip is to be anti-aliased
1143 ///
1144 /// example: <https://fiddle.skia.org/c/@Canvas_clipPath>
1145 pub fn clip_path(
1146 &self,
1147 path: &Path,
1148 op: impl Into<Option<ClipOp>>,
1149 do_anti_alias: impl Into<Option<bool>>,
1150 ) -> &Self {
1151 unsafe {
1152 self.native_mut().clipPath(
1153 path.native(),
1154 op.into().unwrap_or_default(),
1155 do_anti_alias.into().unwrap_or_default(),
1156 )
1157 }
1158 self
1159 }
1160
1161 pub fn clip_shader(&self, shader: impl Into<Shader>, op: impl Into<Option<ClipOp>>) -> &Self {
1162 unsafe {
1163 sb::C_SkCanvas_clipShader(
1164 self.native_mut(),
1165 shader.into().into_ptr(),
1166 op.into().unwrap_or(ClipOp::Intersect),
1167 )
1168 }
1169 self
1170 }
1171
1172 /// Replaces clip with the intersection or difference of clip and [`Region`] `device_rgn`.
1173 /// Resulting clip is aliased; pixels are fully contained by the clip.
1174 /// `device_rgn` is unaffected by [`Matrix`].
1175 ///
1176 /// - `device_rgn` [`Region`] to combine with clip
1177 /// - `op` [`ClipOp`] to apply to clip
1178 ///
1179 /// example: <https://fiddle.skia.org/c/@Canvas_clipRegion>
1180 pub fn clip_region(&self, device_rgn: &Region, op: impl Into<Option<ClipOp>>) -> &Self {
1181 unsafe {
1182 self.native_mut()
1183 .clipRegion(device_rgn.native(), op.into().unwrap_or_default())
1184 }
1185 self
1186 }
1187
1188 // quickReject() functions are implemented as a trait.
1189
1190 /// Returns bounds of clip, transformed by inverse of [`Matrix`]. If clip is empty,
1191 /// return [`Rect::new_empty()`], where all [`Rect`] sides equal zero.
1192 ///
1193 /// [`Rect`] returned is outset by one to account for partial pixel coverage if clip
1194 /// is anti-aliased.
1195 ///
1196 /// Returns bounds of clip in local coordinates
1197 ///
1198 /// example: <https://fiddle.skia.org/c/@Canvas_getLocalClipBounds>
1199 pub fn local_clip_bounds(&self) -> Option<Rect> {
1200 let r = Rect::construct(|r| unsafe { sb::C_SkCanvas_getLocalClipBounds(self.native(), r) });
1201 r.is_empty().if_false_some(r)
1202 }
1203
1204 /// Returns [`IRect`] bounds of clip, unaffected by [`Matrix`]. If clip is empty,
1205 /// return [`Rect::new_empty()`], where all [`Rect`] sides equal zero.
1206 ///
1207 /// Unlike [`Self::local_clip_bounds()`], returned [`IRect`] is not outset.
1208 ///
1209 /// Returns bounds of clip in `Device` coordinates
1210 ///
1211 /// example: <https://fiddle.skia.org/c/@Canvas_getDeviceClipBounds>
1212 pub fn device_clip_bounds(&self) -> Option<IRect> {
1213 let r =
1214 IRect::construct(|r| unsafe { sb::C_SkCanvas_getDeviceClipBounds(self.native(), r) });
1215 r.is_empty().if_false_some(r)
1216 }
1217
1218 /// Fills clip with color `color`.
1219 /// `mode` determines how ARGB is combined with destination.
1220 ///
1221 /// - `color` [`Color4f`] representing unpremultiplied color.
1222 /// - `mode` [`BlendMode`] used to combine source color and destination
1223 pub fn draw_color(
1224 &self,
1225 color: impl Into<Color4f>,
1226 mode: impl Into<Option<BlendMode>>,
1227 ) -> &Self {
1228 unsafe {
1229 self.native_mut()
1230 .drawColor(&color.into().into_native(), mode.into().unwrap_or_default())
1231 }
1232 self
1233 }
1234
1235 /// Fills clip with color `color` using [`BlendMode::Src`].
1236 /// This has the effect of replacing all pixels contained by clip with `color`.
1237 ///
1238 /// - `color` [`Color4f`] representing unpremultiplied color.
1239 pub fn clear(&self, color: impl Into<Color4f>) -> &Self {
1240 self.draw_color(color, BlendMode::Src)
1241 }
1242
1243 /// Makes [`Canvas`] contents undefined. Subsequent calls that read [`Canvas`] pixels,
1244 /// such as drawing with [`BlendMode`], return undefined results. `discard()` does
1245 /// not change clip or [`Matrix`].
1246 ///
1247 /// `discard()` may do nothing, depending on the implementation of [`Surface`] or `Device`
1248 /// that created [`Canvas`].
1249 ///
1250 /// `discard()` allows optimized performance on subsequent draws by removing
1251 /// cached data associated with [`Surface`] or `Device`.
1252 /// It is not necessary to call `discard()` once done with [`Canvas`];
1253 /// any cached data is deleted when owning [`Surface`] or `Device` is deleted.
1254 pub fn discard(&self) -> &Self {
1255 unsafe { sb::C_SkCanvas_discard(self.native_mut()) }
1256 self
1257 }
1258
1259 /// Fills clip with [`Paint`] `paint`. [`Paint`] components, [`Shader`],
1260 /// [`crate::ColorFilter`], [`ImageFilter`], and [`BlendMode`] affect drawing;
1261 /// [`crate::MaskFilter`] and [`crate::PathEffect`] in `paint` are ignored.
1262 ///
1263 /// - `paint` graphics state used to fill [`Canvas`]
1264 ///
1265 /// example: <https://fiddle.skia.org/c/@Canvas_drawPaint>
1266 pub fn draw_paint(&self, paint: &Paint) -> &Self {
1267 unsafe { self.native_mut().drawPaint(paint.native()) }
1268 self
1269 }
1270
1271 /// Draws `pts` using clip, [`Matrix`] and [`Paint`] `pain`.
1272 /// if the number of points is less than one, has no effect.
1273 /// `mode` may be one of: [`PointMode::Points`], [`PointMode::Lines`], or [`PointMode::Polygon`]
1274 ///
1275 /// If `mode` is [`PointMode::Points`], the shape of point drawn depends on `paint`
1276 /// [`crate::paint::Cap`]. If `paint` is set to [`crate::paint::Cap::Round`], each point draws a
1277 /// circle of diameter [`Paint`] stroke width. If `paint` is set to [`crate::paint::Cap::Square`]
1278 /// or [`crate::paint::Cap::Butt`], each point draws a square of width and height
1279 /// [`Paint`] stroke width.
1280 ///
1281 /// If `mode` is [`PointMode::Lines`], each pair of points draws a line segment.
1282 /// One line is drawn for every two points; each point is used once. If count is odd,
1283 /// the final point is ignored.
1284 ///
1285 /// If mode is [`PointMode::Polygon`], each adjacent pair of points draws a line segment.
1286 /// count minus one lines are drawn; the first and last point are used once.
1287 ///
1288 /// Each line segment respects `paint` [`crate::paint::Cap`] and [`Paint`] stroke width.
1289 /// [`crate::paint::Style`] is ignored, as if were set to [`crate::paint::Style::Stroke`].
1290 ///
1291 /// Always draws each element one at a time; is not affected by
1292 /// [`crate::paint::Join`], and unlike [`Self::draw_path()`], does not create a mask from all points
1293 /// and lines before drawing.
1294 ///
1295 /// - `mode` whether pts draws points or lines
1296 /// - `pts` array of points to draw
1297 /// - `paint` stroke, blend, color, and so on, used to draw
1298 ///
1299 /// example: <https://fiddle.skia.org/c/@Canvas_drawPoints>
1300 pub fn draw_points(&self, mode: PointMode, pts: &[Point], paint: &Paint) -> &Self {
1301 unsafe {
1302 self.native_mut()
1303 .drawPoints(mode, pts.len(), pts.native().as_ptr(), paint.native())
1304 }
1305 self
1306 }
1307
1308 /// Draws point `p` using clip, [`Matrix`] and [`Paint`] paint.
1309 ///
1310 /// The shape of point drawn depends on `paint` [`crate::paint::Cap`].
1311 /// If `paint` is set to [`crate::paint::Cap::Round`], draw a circle of diameter [`Paint`]
1312 /// stroke width. If `paint` is set to [`crate::paint::Cap::Square`] or
1313 /// [`crate::paint::Cap::Butt`], draw a square of width and height [`Paint`] stroke width.
1314 /// [`crate::paint::Style`] is ignored, as if were set to [`crate::paint::Style::Stroke`].
1315 ///
1316 /// - `p` top-left edge of circle or square
1317 /// - `paint` stroke, blend, color, and so on, used to draw
1318 pub fn draw_point(&self, p: impl Into<Point>, paint: &Paint) -> &Self {
1319 let p = p.into();
1320 unsafe { self.native_mut().drawPoint(p.x, p.y, paint.native()) }
1321 self
1322 }
1323
1324 /// Draws line segment from `p1` to `p2` using clip, [`Matrix`], and [`Paint`] paint.
1325 /// In paint: [`Paint`] stroke width describes the line thickness;
1326 /// [`crate::paint::Cap`] draws the end rounded or square;
1327 /// [`crate::paint::Style`] is ignored, as if were set to [`crate::paint::Style::Stroke`].
1328 ///
1329 /// - `p1` start of line segment
1330 /// - `p2` end of line segment
1331 /// - `paint` stroke, blend, color, and so on, used to draw
1332 pub fn draw_line(&self, p1: impl Into<Point>, p2: impl Into<Point>, paint: &Paint) -> &Self {
1333 let (p1, p2) = (p1.into(), p2.into());
1334 unsafe {
1335 self.native_mut()
1336 .drawLine(p1.x, p1.y, p2.x, p2.y, paint.native())
1337 }
1338 self
1339 }
1340
1341 /// Draws [`Rect`] rect using clip, [`Matrix`], and [`Paint`] `paint`.
1342 /// In paint: [`crate::paint::Style`] determines if rectangle is stroked or filled;
1343 /// if stroked, [`Paint`] stroke width describes the line thickness, and
1344 /// [`crate::paint::Join`] draws the corners rounded or square.
1345 ///
1346 /// - `rect` rectangle to draw
1347 /// - `paint` stroke or fill, blend, color, and so on, used to draw
1348 ///
1349 /// example: <https://fiddle.skia.org/c/@Canvas_drawRect>
1350 pub fn draw_rect(&self, rect: impl AsRef<Rect>, paint: &Paint) -> &Self {
1351 unsafe {
1352 self.native_mut()
1353 .drawRect(rect.as_ref().native(), paint.native())
1354 }
1355 self
1356 }
1357
1358 /// Draws [`IRect`] rect using clip, [`Matrix`], and [`Paint`] `paint`.
1359 /// In `paint`: [`crate::paint::Style`] determines if rectangle is stroked or filled;
1360 /// if stroked, [`Paint`] stroke width describes the line thickness, and
1361 /// [`crate::paint::Join`] draws the corners rounded or square.
1362 ///
1363 /// - `rect` rectangle to draw
1364 /// - `paint` stroke or fill, blend, color, and so on, used to draw
1365 pub fn draw_irect(&self, rect: impl AsRef<IRect>, paint: &Paint) -> &Self {
1366 self.draw_rect(Rect::from(*rect.as_ref()), paint)
1367 }
1368
1369 /// Draws [`Region`] region using clip, [`Matrix`], and [`Paint`] `paint`.
1370 /// In `paint`: [`crate::paint::Style`] determines if rectangle is stroked or filled;
1371 /// if stroked, [`Paint`] stroke width describes the line thickness, and
1372 /// [`crate::paint::Join`] draws the corners rounded or square.
1373 ///
1374 /// - `region` region to draw
1375 /// - `paint` [`Paint`] stroke or fill, blend, color, and so on, used to draw
1376 ///
1377 /// example: <https://fiddle.skia.org/c/@Canvas_drawRegion>
1378 pub fn draw_region(&self, region: &Region, paint: &Paint) -> &Self {
1379 unsafe {
1380 self.native_mut()
1381 .drawRegion(region.native(), paint.native())
1382 }
1383 self
1384 }
1385
1386 /// Draws oval oval using clip, [`Matrix`], and [`Paint`].
1387 /// In `paint`: [`crate::paint::Style`] determines if oval is stroked or filled;
1388 /// if stroked, [`Paint`] stroke width describes the line thickness.
1389 ///
1390 /// - `oval` [`Rect`] bounds of oval
1391 /// - `paint` [`Paint`] stroke or fill, blend, color, and so on, used to draw
1392 ///
1393 /// example: <https://fiddle.skia.org/c/@Canvas_drawOval>
1394 pub fn draw_oval(&self, oval: impl AsRef<Rect>, paint: &Paint) -> &Self {
1395 unsafe {
1396 self.native_mut()
1397 .drawOval(oval.as_ref().native(), paint.native())
1398 }
1399 self
1400 }
1401
1402 /// Draws [`RRect`] rrect using clip, [`Matrix`], and [`Paint`] `paint`.
1403 /// In `paint`: [`crate::paint::Style`] determines if rrect is stroked or filled;
1404 /// if stroked, [`Paint`] stroke width describes the line thickness.
1405 ///
1406 /// `rrect` may represent a rectangle, circle, oval, uniformly rounded rectangle, or
1407 /// may have any combination of positive non-square radii for the four corners.
1408 ///
1409 /// - `rrect` [`RRect`] with up to eight corner radii to draw
1410 /// - `paint` [`Paint`] stroke or fill, blend, color, and so on, used to draw
1411 ///
1412 /// example: <https://fiddle.skia.org/c/@Canvas_drawRRect>
1413 pub fn draw_rrect(&self, rrect: impl AsRef<RRect>, paint: &Paint) -> &Self {
1414 unsafe {
1415 self.native_mut()
1416 .drawRRect(rrect.as_ref().native(), paint.native())
1417 }
1418 self
1419 }
1420
1421 /// Draws [`RRect`] outer and inner
1422 /// using clip, [`Matrix`], and [`Paint`] `paint`.
1423 /// outer must contain inner or the drawing is undefined.
1424 /// In paint: [`crate::paint::Style`] determines if [`RRect`] is stroked or filled;
1425 /// if stroked, [`Paint`] stroke width describes the line thickness.
1426 /// If stroked and [`RRect`] corner has zero length radii, [`crate::paint::Join`] can
1427 /// draw corners rounded or square.
1428 ///
1429 /// GPU-backed platforms optimize drawing when both outer and inner are
1430 /// concave and outer contains inner. These platforms may not be able to draw
1431 /// [`Path`] built with identical data as fast.
1432 ///
1433 /// - `outer` [`RRect`] outer bounds to draw
1434 /// - `inner` [`RRect`] inner bounds to draw
1435 /// - `paint` [`Paint`] stroke or fill, blend, color, and so on, used to draw
1436 ///
1437 /// example: <https://fiddle.skia.org/c/@Canvas_drawDRRect_a>
1438 /// example: <https://fiddle.skia.org/c/@Canvas_drawDRRect_b>
1439 pub fn draw_drrect(
1440 &self,
1441 outer: impl AsRef<RRect>,
1442 inner: impl AsRef<RRect>,
1443 paint: &Paint,
1444 ) -> &Self {
1445 unsafe {
1446 self.native_mut().drawDRRect(
1447 outer.as_ref().native(),
1448 inner.as_ref().native(),
1449 paint.native(),
1450 )
1451 }
1452 self
1453 }
1454
1455 /// Draws circle at center with radius using clip, [`Matrix`], and [`Paint`] `paint`.
1456 /// If radius is zero or less, nothing is drawn.
1457 /// In `paint`: [`crate::paint::Style`] determines if circle is stroked or filled;
1458 /// if stroked, [`Paint`] stroke width describes the line thickness.
1459 ///
1460 /// - `center` circle center
1461 /// - `radius` half the diameter of circle
1462 /// - `paint` [`Paint`] stroke or fill, blend, color, and so on, used to draw
1463 pub fn draw_circle(&self, center: impl Into<Point>, radius: scalar, paint: &Paint) -> &Self {
1464 let center = center.into();
1465 unsafe {
1466 self.native_mut()
1467 .drawCircle(center.x, center.y, radius, paint.native())
1468 }
1469 self
1470 }
1471
1472 /// Draws arc using clip, [`Matrix`], and [`Paint`] paint.
1473 ///
1474 /// Arc is part of oval bounded by oval, sweeping from `start_angle` to `start_angle` plus
1475 /// `sweep_angle`. `start_angle` and `sweep_angle` are in degrees.
1476 ///
1477 /// `start_angle` of zero places start point at the right middle edge of oval.
1478 /// A positive `sweep_angle` places arc end point clockwise from start point;
1479 /// a negative `sweep_angle` places arc end point counterclockwise from start point.
1480 /// `sweep_angle` may exceed 360 degrees, a full circle.
1481 /// If `use_center` is `true`, draw a wedge that includes lines from oval
1482 /// center to arc end points. If `use_center` is `false`, draw arc between end points.
1483 ///
1484 /// If [`Rect`] oval is empty or `sweep_angle` is zero, nothing is drawn.
1485 ///
1486 /// - `oval` [`Rect`] bounds of oval containing arc to draw
1487 /// - `start_angle` angle in degrees where arc begins
1488 /// - `sweep_angle` sweep angle in degrees; positive is clockwise
1489 /// - `use_center` if `true`, include the center of the oval
1490 /// - `paint` [`Paint`] stroke or fill, blend, color, and so on, used to draw
1491 pub fn draw_arc(
1492 &self,
1493 oval: impl AsRef<Rect>,
1494 start_angle: scalar,
1495 sweep_angle: scalar,
1496 use_center: bool,
1497 paint: &Paint,
1498 ) -> &Self {
1499 unsafe {
1500 self.native_mut().drawArc(
1501 oval.as_ref().native(),
1502 start_angle,
1503 sweep_angle,
1504 use_center,
1505 paint.native(),
1506 )
1507 }
1508 self
1509 }
1510
1511 /// Draws arc using clip, [`Matrix`], and [`Paint`] paint.
1512 ///
1513 /// Arc is part of oval bounded by oval, sweeping from `start_angle` to `start_angle` plus
1514 /// `sweep_angle`. `start_angle` and `sweep_angle` are in degrees.
1515 ///
1516 /// `start_angle` of zero places start point at the right middle edge of oval.
1517 /// A positive `sweep_angle` places arc end point clockwise from start point;
1518 /// a negative `sweep_angle` places arc end point counterclockwise from start point.
1519 /// `sweep_angle` may exceed 360 degrees, a full circle.
1520 /// If `use_center` is `true`, draw a wedge that includes lines from oval
1521 /// center to arc end points. If `use_center` is `false`, draw arc between end points.
1522 ///
1523 /// If [`Rect`] oval is empty or `sweep_angle` is zero, nothing is drawn.
1524 ///
1525 /// - `arc` [`Arc`] SkArc specifying oval, startAngle, sweepAngle, and arc-vs-wedge
1526 /// - `paint` [`Paint`] stroke or fill, blend, color, and so on, used to draw
1527 pub fn draw_arc_2(&self, arc: &Arc, paint: &Paint) -> &Self {
1528 self.draw_arc(
1529 arc.oval,
1530 arc.start_angle,
1531 arc.sweep_angle,
1532 arc.is_wedge(),
1533 paint,
1534 );
1535 self
1536 }
1537
1538 /// Draws [`RRect`] bounded by [`Rect`] rect, with corner radii `(rx, ry)` using clip,
1539 /// [`Matrix`], and [`Paint`] `paint`.
1540 ///
1541 /// In `paint`: [`crate::paint::Style`] determines if [`RRect`] is stroked or filled;
1542 /// if stroked, [`Paint`] stroke width describes the line thickness.
1543 /// If `rx` or `ry` are less than zero, they are treated as if they are zero.
1544 /// If `rx` plus `ry` exceeds rect width or rect height, radii are scaled down to fit.
1545 /// If `rx` and `ry` are zero, [`RRect`] is drawn as [`Rect`] and if stroked is affected by
1546 /// [`crate::paint::Join`].
1547 ///
1548 /// - `rect` [`Rect`] bounds of [`RRect`] to draw
1549 /// - `rx` axis length on x-axis of oval describing rounded corners
1550 /// - `ry` axis length on y-axis of oval describing rounded corners
1551 /// - `paint` stroke, blend, color, and so on, used to draw
1552 ///
1553 /// example: <https://fiddle.skia.org/c/@Canvas_drawRoundRect>
1554 pub fn draw_round_rect(
1555 &self,
1556 rect: impl AsRef<Rect>,
1557 rx: scalar,
1558 ry: scalar,
1559 paint: &Paint,
1560 ) -> &Self {
1561 unsafe {
1562 self.native_mut()
1563 .drawRoundRect(rect.as_ref().native(), rx, ry, paint.native())
1564 }
1565 self
1566 }
1567
1568 /// Draws [`Path`] path using clip, [`Matrix`], and [`Paint`] `paint`.
1569 /// [`Path`] contains an array of path contour, each of which may be open or closed.
1570 ///
1571 /// In `paint`: [`crate::paint::Style`] determines if [`RRect`] is stroked or filled:
1572 /// if filled, [`crate::path::FillType`] determines whether path contour describes inside or
1573 /// outside of fill; if stroked, [`Paint`] stroke width describes the line thickness,
1574 /// [`crate::paint::Cap`] describes line ends, and [`crate::paint::Join`] describes how
1575 /// corners are drawn.
1576 ///
1577 /// - `path` [`Path`] to draw
1578 /// - `paint` stroke, blend, color, and so on, used to draw
1579 ///
1580 /// example: <https://fiddle.skia.org/c/@Canvas_drawPath>
1581 pub fn draw_path(&self, path: &Path, paint: &Paint) -> &Self {
1582 unsafe { self.native_mut().drawPath(path.native(), paint.native()) }
1583 self
1584 }
1585
1586 pub fn draw_image(
1587 &self,
1588 image: impl AsRef<Image>,
1589 left_top: impl Into<Point>,
1590 paint: Option<&Paint>,
1591 ) -> &Self {
1592 let left_top = left_top.into();
1593 self.draw_image_with_sampling_options(image, left_top, SamplingOptions::default(), paint)
1594 }
1595
1596 pub fn draw_image_rect(
1597 &self,
1598 image: impl AsRef<Image>,
1599 src: Option<(&Rect, SrcRectConstraint)>,
1600 dst: impl AsRef<Rect>,
1601 paint: &Paint,
1602 ) -> &Self {
1603 self.draw_image_rect_with_sampling_options(
1604 image,
1605 src,
1606 dst,
1607 SamplingOptions::default(),
1608 paint,
1609 )
1610 }
1611
1612 pub fn draw_image_with_sampling_options(
1613 &self,
1614 image: impl AsRef<Image>,
1615 left_top: impl Into<Point>,
1616 sampling: impl Into<SamplingOptions>,
1617 paint: Option<&Paint>,
1618 ) -> &Self {
1619 let left_top = left_top.into();
1620 unsafe {
1621 self.native_mut().drawImage(
1622 image.as_ref().native(),
1623 left_top.x,
1624 left_top.y,
1625 sampling.into().native(),
1626 paint.native_ptr_or_null(),
1627 )
1628 }
1629 self
1630 }
1631
1632 pub fn draw_image_rect_with_sampling_options(
1633 &self,
1634 image: impl AsRef<Image>,
1635 src: Option<(&Rect, SrcRectConstraint)>,
1636 dst: impl AsRef<Rect>,
1637 sampling: impl Into<SamplingOptions>,
1638 paint: &Paint,
1639 ) -> &Self {
1640 let sampling = sampling.into();
1641 match src {
1642 Some((src, constraint)) => unsafe {
1643 self.native_mut().drawImageRect(
1644 image.as_ref().native(),
1645 src.native(),
1646 dst.as_ref().native(),
1647 sampling.native(),
1648 paint.native(),
1649 constraint,
1650 )
1651 },
1652 None => unsafe {
1653 self.native_mut().drawImageRect1(
1654 image.as_ref().native(),
1655 dst.as_ref().native(),
1656 sampling.native(),
1657 paint.native(),
1658 )
1659 },
1660 }
1661 self
1662 }
1663
1664 /// Draws [`Image`] `image` stretched proportionally to fit into [`Rect`] `dst`.
1665 /// [`IRect`] `center` divides the image into nine sections: four sides, four corners, and
1666 /// the center. Corners are unmodified or scaled down proportionately if their sides
1667 /// are larger than `dst`; center and four sides are scaled to fit remaining space, if any.
1668 ///
1669 /// Additionally transform draw using clip, [`Matrix`], and optional [`Paint`] `paint`.
1670 ///
1671 /// If [`Paint`] `paint` is supplied, apply [`crate::ColorFilter`], alpha, [`ImageFilter`], and
1672 /// [`BlendMode`]. If `image` is [`crate::ColorType::Alpha8`], apply [`Shader`].
1673 /// If `paint` contains [`crate::MaskFilter`], generate mask from `image` bounds.
1674 /// Any [`crate::MaskFilter`] on `paint` is ignored as is paint anti-aliasing state.
1675 ///
1676 /// If generated mask extends beyond image bounds, replicate image edge colors, just
1677 /// as [`Shader`] made from [`RCHandle<Image>::to_shader()`] with [`crate::TileMode::Clamp`] set
1678 /// replicates the image edge color when it samples outside of its bounds.
1679 ///
1680 /// - `image` [`Image`] containing pixels, dimensions, and format
1681 /// - `center` [`IRect`] edge of image corners and sides
1682 /// - `dst` destination [`Rect`] of image to draw to
1683 /// - `filter` what technique to use when sampling the image
1684 /// - `paint` [`Paint`] containing [`BlendMode`], [`crate::ColorFilter`], [`ImageFilter`],
1685 /// and so on; or `None`
1686 pub fn draw_image_nine(
1687 &self,
1688 image: impl AsRef<Image>,
1689 center: impl AsRef<IRect>,
1690 dst: impl AsRef<Rect>,
1691 filter_mode: FilterMode,
1692 paint: Option<&Paint>,
1693 ) -> &Self {
1694 unsafe {
1695 self.native_mut().drawImageNine(
1696 image.as_ref().native(),
1697 center.as_ref().native(),
1698 dst.as_ref().native(),
1699 filter_mode,
1700 paint.native_ptr_or_null(),
1701 )
1702 }
1703 self
1704 }
1705
1706 /// Draws [`Image`] `image` stretched proportionally to fit into [`Rect`] `dst`.
1707 ///
1708 /// [`lattice::Lattice`] lattice divides image into a rectangular grid.
1709 /// Each intersection of an even-numbered row and column is fixed;
1710 /// fixed lattice elements never scale larger than their initial
1711 /// size and shrink proportionately when all fixed elements exceed the bitmap
1712 /// dimension. All other grid elements scale to fill the available space, if any.
1713 ///
1714 /// Additionally transform draw using clip, [`Matrix`], and optional [`Paint`] `paint`.
1715 ///
1716 /// If [`Paint`] `paint` is supplied, apply [`crate::ColorFilter`], alpha, [`ImageFilter`], and
1717 /// [`BlendMode`]. If image is [`crate::ColorType::Alpha8`], apply [`Shader`].
1718 /// If `paint` contains [`crate::MaskFilter`], generate mask from image bounds.
1719 /// Any [`crate::MaskFilter`] on `paint` is ignored as is `paint` anti-aliasing state.
1720 ///
1721 /// If generated mask extends beyond bitmap bounds, replicate bitmap edge colors,
1722 /// just as [`Shader`] made from `SkShader::MakeBitmapShader` with
1723 /// [`crate::TileMode::Clamp`] set replicates the bitmap edge color when it samples
1724 /// outside of its bounds.
1725 ///
1726 /// - `image` [`Image`] containing pixels, dimensions, and format
1727 /// - `lattice` division of bitmap into fixed and variable rectangles
1728 /// - `dst` destination [`Rect`] of image to draw to
1729 /// - `filter` what technique to use when sampling the image
1730 /// - `paint` [`Paint`] containing [`BlendMode`], [`crate::ColorFilter`], [`ImageFilter`],
1731 /// and so on; or `None`
1732 pub fn draw_image_lattice(
1733 &self,
1734 image: impl AsRef<Image>,
1735 lattice: &Lattice,
1736 dst: impl AsRef<Rect>,
1737 filter: FilterMode,
1738 paint: Option<&Paint>,
1739 ) -> &Self {
1740 unsafe {
1741 self.native_mut().drawImageLattice(
1742 image.as_ref().native(),
1743 &lattice.native().native,
1744 dst.as_ref().native(),
1745 filter,
1746 paint.native_ptr_or_null(),
1747 )
1748 }
1749 self
1750 }
1751
1752 // TODO: drawSimpleText?
1753
1754 /// Draws [`String`], with origin at `(origin.x, origin.y)`, using clip, [`Matrix`], [`Font`]
1755 /// `font`, and [`Paint`] `paint`.
1756 ///
1757 /// This function uses the default character-to-glyph mapping from the [`crate::Typeface`] in
1758 /// font. It does not perform typeface fallback for characters not found in the
1759 /// [`crate::Typeface`]. It does not perform kerning; glyphs are positioned based on their
1760 /// default advances.
1761 ///
1762 /// Text size is affected by [`Matrix`] and [`Font`] text size. Default text size is 12 point.
1763 ///
1764 /// All elements of `paint`: [`crate::PathEffect`], [`crate::MaskFilter`], [`Shader`],
1765 /// [`crate::ColorFilter`], and [`ImageFilter`]; apply to text. By default, draws filled black
1766 /// glyphs.
1767 ///
1768 /// - `str` character code points drawn,
1769 /// ending with a char value of zero
1770 /// - `origin` start of string on x,y-axis
1771 /// - `font` typeface, text size and so, used to describe the text
1772 /// - `paint` blend, color, and so on, used to draw
1773 pub fn draw_str(
1774 &self,
1775 str: impl AsRef<str>,
1776 origin: impl Into<Point>,
1777 font: &Font,
1778 paint: &Paint,
1779 ) -> &Self {
1780 // rust specific, based on drawSimpleText with fixed UTF8 encoding,
1781 // implementation is similar to Font's *_str methods.
1782 let origin = origin.into();
1783 let bytes = str.as_ref().as_bytes();
1784 unsafe {
1785 self.native_mut().drawSimpleText(
1786 bytes.as_ptr() as _,
1787 bytes.len(),
1788 TextEncoding::UTF8.into_native(),
1789 origin.x,
1790 origin.y,
1791 font.native(),
1792 paint.native(),
1793 )
1794 }
1795 self
1796 }
1797
1798 /// Draws glyphs at positions relative to `origin` styled with `font` and `paint` with
1799 /// supporting utf8 and cluster information.
1800 ///
1801 /// This function draw glyphs at the given positions relative to the given origin. It does not
1802 /// perform typeface fallback for glyphs not found in the [`crate::Typeface`] in font.
1803 ///
1804 /// The drawing obeys the current transform matrix and clipping.
1805 ///
1806 /// All elements of paint: [`crate::PathEffect`], [`crate::MaskFilter`], [`Shader`],
1807 /// [`crate::ColorFilter`], and [`ImageFilter`]; apply to text. By default, draws filled black
1808 /// glyphs.
1809 ///
1810 /// - `count` number of glyphs to draw
1811 /// - `glyphs` the array of glyphIDs to draw
1812 /// - `positions` where to draw each glyph relative to origin
1813 /// - `clusters` array of size count of cluster information
1814 /// - `utf8_text` utf8text supporting information for the glyphs
1815 /// - `origin` the origin of all the positions
1816 /// - `font` typeface, text size and so, used to describe the text
1817 /// - `paint` blend, color, and so on, used to draw
1818 #[allow(clippy::too_many_arguments)]
1819 pub fn draw_glyphs_utf8(
1820 &self,
1821 glyphs: &[GlyphId],
1822 positions: &[Point],
1823 clusters: &[u32],
1824 utf8_text: impl AsRef<str>,
1825 origin: impl Into<Point>,
1826 font: &Font,
1827 paint: &Paint,
1828 ) {
1829 let count = glyphs.len();
1830 if count == 0 {
1831 return;
1832 }
1833 assert_eq!(positions.len(), count);
1834 assert_eq!(clusters.len(), count);
1835 let utf8_text = utf8_text.as_ref().as_bytes();
1836 let origin = origin.into();
1837 unsafe {
1838 self.native_mut().drawGlyphs(
1839 count.try_into().unwrap(),
1840 glyphs.as_ptr(),
1841 positions.native().as_ptr(),
1842 clusters.as_ptr(),
1843 utf8_text.len().try_into().unwrap(),
1844 utf8_text.as_ptr() as _,
1845 origin.into_native(),
1846 font.native(),
1847 paint.native(),
1848 )
1849 }
1850 }
1851
1852 /// Draws `count` glyphs, at positions relative to `origin` styled with `font` and `paint`.
1853 ///
1854 /// This function draw glyphs at the given positions relative to the given origin.
1855 /// It does not perform typeface fallback for glyphs not found in the [`crate::Typeface`]] in
1856 /// font.
1857 ///
1858 /// The drawing obeys the current transform matrix and clipping.
1859 ///
1860 /// All elements of paint: [`crate::PathEffect`], [`crate::MaskFilter`], [`Shader`],
1861 /// [`crate::ColorFilter`], and [`ImageFilter`]; apply to text. By default, draws filled black
1862 /// glyphs.
1863 ///
1864 /// - `count` number of glyphs to draw
1865 /// - `glyphs` the array of glyphIDs to draw
1866 /// - `positions` where to draw each glyph relative to origin, either a `&[Point]` or
1867 /// `&[RSXform]` slice
1868 /// - `origin` the origin of all the positions
1869 /// - `font` typeface, text size and so, used to describe the text
1870 /// - `paint` blend, color, and so on, used to draw
1871 pub fn draw_glyphs_at<'a>(
1872 &self,
1873 glyphs: &[GlyphId],
1874 positions: impl Into<GlyphPositions<'a>>,
1875 origin: impl Into<Point>,
1876 font: &Font,
1877 paint: &Paint,
1878 ) {
1879 let count = glyphs.len();
1880 if count == 0 {
1881 return;
1882 }
1883 let positions: GlyphPositions = positions.into();
1884 let origin = origin.into();
1885
1886 let glyphs = glyphs.as_ptr();
1887 let origin = origin.into_native();
1888 let font = font.native();
1889 let paint = paint.native();
1890
1891 match positions {
1892 GlyphPositions::Points(points) => {
1893 assert_eq!(points.len(), count);
1894 unsafe {
1895 self.native_mut().drawGlyphs1(
1896 count.try_into().unwrap(),
1897 glyphs,
1898 points.native().as_ptr(),
1899 origin,
1900 font,
1901 paint,
1902 )
1903 }
1904 }
1905 GlyphPositions::RSXforms(xforms) => {
1906 assert_eq!(xforms.len(), count);
1907 unsafe {
1908 self.native_mut().drawGlyphs2(
1909 count.try_into().unwrap(),
1910 glyphs,
1911 xforms.native().as_ptr(),
1912 origin,
1913 font,
1914 paint,
1915 )
1916 }
1917 }
1918 }
1919 }
1920
1921 /// Draws [`TextBlob`] blob at `(origin.x, origin.y)`, using clip, [`Matrix`], and [`Paint`]
1922 /// paint.
1923 ///
1924 /// `blob` contains glyphs, their positions, and paint attributes specific to text:
1925 /// [`crate::Typeface`], [`Paint`] text size, [`Paint`] text scale x, [`Paint`] text skew x,
1926 /// [`Paint`] align, [`Paint`] hinting, anti-alias, [`Paint`] fake bold, [`Paint`] font embedded
1927 /// bitmaps, [`Paint`] full hinting spacing, LCD text, [`Paint`] linear text, and [`Paint`]
1928 /// subpixel text.
1929 ///
1930 /// [`TextEncoding`] must be set to [`TextEncoding::GlyphId`].
1931 ///
1932 /// Elements of `paint`: [`crate::PathEffect`], [`crate::MaskFilter`], [`Shader`],
1933 /// [`crate::ColorFilter`], and [`ImageFilter`]; apply to blob.
1934 ///
1935 /// - `blob` glyphs, positions, and their paints' text size, typeface, and so on
1936 /// - `origin` horizontal and vertical offset applied to blob
1937 /// - `paint` blend, color, stroking, and so on, used to draw
1938 pub fn draw_text_blob(
1939 &self,
1940 blob: impl AsRef<TextBlob>,
1941 origin: impl Into<Point>,
1942 paint: &Paint,
1943 ) -> &Self {
1944 let origin = origin.into();
1945 #[cfg(all(feature = "textlayout", feature = "embed-icudtl"))]
1946 crate::icu::init();
1947 unsafe {
1948 self.native_mut().drawTextBlob(
1949 blob.as_ref().native(),
1950 origin.x,
1951 origin.y,
1952 paint.native(),
1953 )
1954 }
1955 self
1956 }
1957
1958 /// Draws [`Picture`] picture, using clip and [`Matrix`]; transforming picture with
1959 /// [`Matrix`] matrix, if provided; and use [`Paint`] `paint` alpha, [`crate::ColorFilter`],
1960 /// [`ImageFilter`], and [`BlendMode`], if provided.
1961 ///
1962 /// If paint is not `None`, then the picture is always drawn into a temporary layer before
1963 /// actually landing on the canvas. Note that drawing into a layer can also change its
1964 /// appearance if there are any non-associative blend modes inside any of the pictures elements.
1965 ///
1966 /// - `picture` recorded drawing commands to play
1967 /// - `matrix` [`Matrix`] to rotate, scale, translate, and so on; may be `None`
1968 /// - `paint` [`Paint`] to apply transparency, filtering, and so on; may be `None`
1969 pub fn draw_picture(
1970 &self,
1971 picture: impl AsRef<Picture>,
1972 matrix: Option<&Matrix>,
1973 paint: Option<&Paint>,
1974 ) -> &Self {
1975 unsafe {
1976 self.native_mut().drawPicture(
1977 picture.as_ref().native(),
1978 matrix.native_ptr_or_null(),
1979 paint.native_ptr_or_null(),
1980 )
1981 }
1982 self
1983 }
1984
1985 /// Draws [`Vertices`] vertices, a triangle mesh, using clip and [`Matrix`].
1986 /// If `paint` contains an [`Shader`] and vertices does not contain tex coords, the shader is
1987 /// mapped using the vertices' positions.
1988 ///
1989 /// [`BlendMode`] is ignored if [`Vertices`] does not have colors. Otherwise, it combines
1990 /// - the [`Shader`] if [`Paint`] contains [`Shader`
1991 /// - or the opaque [`Paint`] color if [`Paint`] does not contain [`Shader`]
1992 ///
1993 /// as the src of the blend and the interpolated vertex colors as the dst.
1994 ///
1995 /// [`crate::MaskFilter`], [`crate::PathEffect`], and antialiasing on [`Paint`] are ignored.
1996 //
1997 /// - `vertices` triangle mesh to draw
1998 /// - `mode` combines vertices' colors with [`Shader`] if present or [`Paint`] opaque color if
1999 /// not. Ignored if the vertices do not contain color.
2000 /// - `paint` specifies the [`Shader`], used as [`Vertices`] texture, and
2001 /// [`crate::ColorFilter`].
2002 ///
2003 /// example: <https://fiddle.skia.org/c/@Canvas_drawVertices>
2004 /// example: <https://fiddle.skia.org/c/@Canvas_drawVertices_2>
2005 pub fn draw_vertices(&self, vertices: &Vertices, mode: BlendMode, paint: &Paint) -> &Self {
2006 unsafe {
2007 self.native_mut()
2008 .drawVertices(vertices.native(), mode, paint.native())
2009 }
2010 self
2011 }
2012
2013 /// Draws a Coons patch: the interpolation of four cubics with shared corners,
2014 /// associating a color, and optionally a texture [`Point`], with each corner.
2015 ///
2016 /// [`Point`] array cubics specifies four [`Path`] cubic starting at the top-left corner,
2017 /// in clockwise order, sharing every fourth point. The last [`Path`] cubic ends at the
2018 /// first point.
2019 ///
2020 /// Color array color associates colors with corners in top-left, top-right,
2021 /// bottom-right, bottom-left order.
2022 ///
2023 /// If paint contains [`Shader`], [`Point`] array `tex_coords` maps [`Shader`] as texture to
2024 /// corners in top-left, top-right, bottom-right, bottom-left order. If `tex_coords` is
2025 /// `None`, [`Shader`] is mapped using positions (derived from cubics).
2026 ///
2027 /// [`BlendMode`] is ignored if colors is `None`. Otherwise, it combines
2028 /// - the [`Shader`] if [`Paint`] contains [`Shader`]
2029 /// - or the opaque [`Paint`] color if [`Paint`] does not contain [`Shader`]
2030 ///
2031 /// as the src of the blend and the interpolated patch colors as the dst.
2032 ///
2033 /// [`crate::MaskFilter`], [`crate::PathEffect`], and antialiasing on [`Paint`] are ignored.
2034 ///
2035 /// - `cubics` [`Path`] cubic array, sharing common points
2036 /// - `colors` color array, one for each corner
2037 /// - `tex_coords` [`Point`] array of texture coordinates, mapping [`Shader`] to corners;
2038 /// may be `None`
2039 /// - `mode` combines patch's colors with [`Shader`] if present or [`Paint`] opaque color if
2040 /// not. Ignored if colors is `None`.
2041 /// - `paint` [`Shader`], [`crate::ColorFilter`], [`BlendMode`], used to draw
2042 pub fn draw_patch<'a>(
2043 &self,
2044 cubics: &[Point; 12],
2045 colors: impl Into<Option<&'a [Color; 4]>>,
2046 tex_coords: Option<&[Point; 4]>,
2047 mode: BlendMode,
2048 paint: &Paint,
2049 ) -> &Self {
2050 let colors = colors
2051 .into()
2052 .map(|c| c.native().as_ptr())
2053 .unwrap_or(ptr::null());
2054 unsafe {
2055 self.native_mut().drawPatch(
2056 cubics.native().as_ptr(),
2057 colors,
2058 tex_coords
2059 .map(|tc| tc.native().as_ptr())
2060 .unwrap_or(ptr::null()),
2061 mode,
2062 paint.native(),
2063 )
2064 }
2065 self
2066 }
2067
2068 /// Draws a set of sprites from atlas, using clip, [`Matrix`], and optional [`Paint`] paint.
2069 /// paint uses anti-alias, alpha, [`crate::ColorFilter`], [`ImageFilter`], and [`BlendMode`]
2070 /// to draw, if present. For each entry in the array, [`Rect`] tex locates sprite in
2071 /// atlas, and [`RSXform`] xform transforms it into destination space.
2072 ///
2073 /// [`crate::MaskFilter`] and [`crate::PathEffect`] on paint are ignored.
2074 ///
2075 /// xform, tex, and colors if present, must contain the same number of entries.
2076 /// Optional colors are applied for each sprite using [`BlendMode`] mode, treating
2077 /// sprite as source and colors as destination.
2078 /// Optional `cull_rect` is a conservative bounds of all transformed sprites.
2079 /// If `cull_rect` is outside of clip, canvas can skip drawing.
2080 ///
2081 /// * `atlas` - [`Image`] containing sprites
2082 /// * `xform` - [`RSXform`] mappings for sprites in atlas
2083 /// * `tex` - [`Rect`] locations of sprites in atlas
2084 /// * `colors` - one per sprite, blended with sprite using [`BlendMode`]; may be `None`
2085 /// * `count` - number of sprites to draw
2086 /// * `mode` - [`BlendMode`] combining colors and sprites
2087 /// * `sampling` - [`SamplingOptions`] used when sampling from the atlas image
2088 /// * `cull_rect` - bounds of transformed sprites for efficient clipping; may be `None`
2089 /// * `paint` - [`crate::ColorFilter`], [`ImageFilter`], [`BlendMode`], and so on; may be `None`
2090 #[allow(clippy::too_many_arguments)]
2091 pub fn draw_atlas<'a>(
2092 &self,
2093 atlas: &Image,
2094 xform: &[RSXform],
2095 tex: &[Rect],
2096 colors: impl Into<Option<&'a [Color]>>,
2097 mode: BlendMode,
2098 sampling: impl Into<SamplingOptions>,
2099 cull_rect: impl Into<Option<Rect>>,
2100 paint: impl Into<Option<&'a Paint>>,
2101 ) {
2102 let count = xform.len();
2103 assert_eq!(tex.len(), count);
2104 let colors = colors.into();
2105 if let Some(color_slice) = colors {
2106 assert_eq!(color_slice.len(), count);
2107 }
2108 unsafe {
2109 self.native_mut().drawAtlas(
2110 atlas.native(),
2111 xform.native().as_ptr(),
2112 tex.native().as_ptr(),
2113 colors.native().as_ptr_or_null(),
2114 count.try_into().unwrap(),
2115 mode,
2116 sampling.into().native(),
2117 cull_rect.into().native().as_ptr_or_null(),
2118 paint.into().native_ptr_or_null(),
2119 )
2120 }
2121 }
2122
2123 /// Draws [`Drawable`] drawable using clip and [`Matrix`], concatenated with
2124 /// optional matrix.
2125 ///
2126 /// If [`Canvas`] has an asynchronous implementation, as is the case when it is recording into
2127 /// [`Picture`], then drawable will be referenced, so that [`RCHandle<Drawable>::draw()`] can be
2128 /// called when the operation is finalized. To force immediate drawing, call
2129 /// [`RCHandle<Drawable>::draw()`] instead.
2130 ///
2131 /// - `drawable` custom struct encapsulating drawing commands
2132 /// - `matrix` transformation applied to drawing; may be `None`
2133 ///
2134 /// example: <https://fiddle.skia.org/c/@Canvas_drawDrawable>
2135 pub fn draw_drawable(&self, drawable: &mut Drawable, matrix: Option<&Matrix>) {
2136 unsafe {
2137 self.native_mut()
2138 .drawDrawable(drawable.native_mut(), matrix.native_ptr_or_null())
2139 }
2140 }
2141
2142 /// Draws [`Drawable`] drawable using clip and [`Matrix`], offset by `(offset.x, offset.y)`.
2143 ///
2144 /// If [`Canvas`] has an asynchronous implementation, as is the case when it is recording into
2145 /// [`Picture`], then drawable will be referenced, so that [`RCHandle<Drawable>::draw()`] can be
2146 /// called when the operation is finalized. To force immediate drawing, call
2147 /// [`RCHandle<Drawable>::draw()`] instead.
2148 ///
2149 /// - `drawable` custom struct encapsulating drawing commands
2150 /// - `offset` offset into [`Canvas`] writable pixels on x,y-axis
2151 ///
2152 /// example: <https://fiddle.skia.org/c/@Canvas_drawDrawable_2>
2153 pub fn draw_drawable_at(&self, drawable: &mut Drawable, offset: impl Into<Point>) {
2154 let offset = offset.into();
2155 unsafe {
2156 self.native_mut()
2157 .drawDrawable1(drawable.native_mut(), offset.x, offset.y)
2158 }
2159 }
2160
2161 /// Associates [`Rect`] on [`Canvas`] when an annotation; a key-value pair, where the key is
2162 /// a UTF-8 string, and optional value is stored as [`Data`].
2163 ///
2164 /// Only some canvas implementations, such as recording to [`Picture`], or drawing to
2165 /// document PDF, use annotations.
2166 ///
2167 /// - `rect` [`Rect`] extent of canvas to annotate
2168 /// - `key` string used for lookup
2169 /// - `value` data holding value stored in annotation
2170 pub fn draw_annotation(&self, rect: impl AsRef<Rect>, key: &str, value: &Data) -> &Self {
2171 let key = CString::new(key).unwrap();
2172 unsafe {
2173 self.native_mut().drawAnnotation(
2174 rect.as_ref().native(),
2175 key.as_ptr(),
2176 value.native_mut_force(),
2177 )
2178 }
2179 self
2180 }
2181
2182 /// Returns `true` if clip is empty; that is, nothing will draw.
2183 ///
2184 /// May do work when called; it should not be called more often than needed. However, once
2185 /// called, subsequent calls perform no work until clip changes.
2186 ///
2187 /// Returns `true` if clip is empty
2188 ///
2189 /// example: <https://fiddle.skia.org/c/@Canvas_isClipEmpty>
2190 pub fn is_clip_empty(&self) -> bool {
2191 unsafe { sb::C_SkCanvas_isClipEmpty(self.native()) }
2192 }
2193
2194 /// Returns `true` if clip is [`Rect`] and not empty.
2195 /// Returns `false` if the clip is empty, or if it is not [`Rect`].
2196 ///
2197 /// Returns `true` if clip is [`Rect`] and not empty
2198 ///
2199 /// example: <https://fiddle.skia.org/c/@Canvas_isClipRect>
2200 pub fn is_clip_rect(&self) -> bool {
2201 unsafe { sb::C_SkCanvas_isClipRect(self.native()) }
2202 }
2203
2204 /// Returns the current transform from local coordinates to the 'device', which for most
2205 /// purposes means pixels.
2206 ///
2207 /// Returns transformation from local coordinates to device / pixels.
2208 pub fn local_to_device(&self) -> M44 {
2209 M44::construct(|m| unsafe { sb::C_SkCanvas_getLocalToDevice(self.native(), m) })
2210 }
2211
2212 /// Throws away the 3rd row and column in the matrix, so be warned.
2213 pub fn local_to_device_as_3x3(&self) -> Matrix {
2214 self.local_to_device().to_m33()
2215 }
2216
2217 /// DEPRECATED
2218 /// Legacy version of [`Self::local_to_device()`], which strips away any Z information, and just
2219 /// returns a 3x3 version.
2220 ///
2221 /// Returns 3x3 version of [`Self::local_to_device()`]
2222 ///
2223 /// example: <https://fiddle.skia.org/c/@Canvas_getTotalMatrix>
2224 /// example: <https://fiddle.skia.org/c/@Clip>
2225 #[deprecated(
2226 since = "0.38.0",
2227 note = "use local_to_device() or local_to_device_as_3x3() instead"
2228 )]
2229 pub fn total_matrix(&self) -> Matrix {
2230 let mut matrix = Matrix::default();
2231 unsafe { sb::C_SkCanvas_getTotalMatrix(self.native(), matrix.native_mut()) };
2232 matrix
2233 }
2234
2235 //
2236 // internal helper
2237 //
2238
2239 pub(crate) fn own_from_native_ptr<'lt>(native: *mut SkCanvas) -> Option<OwnedCanvas<'lt>> {
2240 if !native.is_null() {
2241 Some(OwnedCanvas::<'lt>(
2242 ptr::NonNull::new(
2243 Self::borrow_from_native(unsafe { &*native }) as *const _ as *mut _
2244 )
2245 .unwrap(),
2246 PhantomData,
2247 ))
2248 } else {
2249 None
2250 }
2251 }
2252
2253 pub(crate) fn borrow_from_native(native: &SkCanvas) -> &Self {
2254 unsafe { transmute_ref(native) }
2255 }
2256}
2257
2258impl QuickReject<Rect> for Canvas {
2259 /// Returns `true` if [`Rect`] `rect`, transformed by [`Matrix`], can be quickly determined to
2260 /// be outside of clip. May return `false` even though rect is outside of clip.
2261 ///
2262 /// Use to check if an area to be drawn is clipped out, to skip subsequent draw calls.
2263 ///
2264 /// - `rect` [`Rect`] to compare with clip
2265 ///
2266 /// Returns `true` if `rect`, transformed by [`Matrix`], does not intersect clip
2267 ///
2268 /// example: <https://fiddle.skia.org/c/@Canvas_quickReject>
2269 fn quick_reject(&self, rect: &Rect) -> bool {
2270 unsafe { self.native().quickReject(rect.native()) }
2271 }
2272}
2273
2274impl QuickReject<Path> for Canvas {
2275 /// Returns `true` if `path`, transformed by [`Matrix`], can be quickly determined to be
2276 /// outside of clip. May return `false` even though `path` is outside of clip.
2277 ///
2278 /// Use to check if an area to be drawn is clipped out, to skip subsequent draw calls.
2279 ///
2280 /// - `path` [`Path`] to compare with clip
2281 ///
2282 /// Returns `true` if `path`, transformed by [`Matrix`], does not intersect clip
2283 ///
2284 /// example: <https://fiddle.skia.org/c/@Canvas_quickReject_2>
2285 fn quick_reject(&self, path: &Path) -> bool {
2286 unsafe { self.native().quickReject1(path.native()) }
2287 }
2288}
2289
2290pub trait SetMatrix {
2291 /// DEPRECATED -- use [`M44`] version
2292 #[deprecated(since = "0.38.0", note = "Use M44 version")]
2293 fn set_matrix(&self, matrix: &Matrix) -> &Self;
2294}
2295
2296impl SetMatrix for Canvas {
2297 /// DEPRECATED -- use [`M44`] version
2298 fn set_matrix(&self, matrix: &Matrix) -> &Self {
2299 unsafe { self.native_mut().setMatrix1(matrix.native()) }
2300 self
2301 }
2302}
2303
2304//
2305// Lattice
2306//
2307
2308pub mod lattice {
2309 use crate::{prelude::*, Color, IRect};
2310 use skia_bindings::{self as sb, SkCanvas_Lattice};
2311 use std::marker::PhantomData;
2312
2313 /// [`Lattice`] divides [`crate::Bitmap`] or [`crate::Image`] into a rectangular grid.
2314 /// Grid entries on even columns and even rows are fixed; these entries are
2315 /// always drawn at their original size if the destination is large enough.
2316 /// If the destination side is too small to hold the fixed entries, all fixed
2317 /// entries are proportionately scaled down to fit.
2318 /// The grid entries not on even columns and rows are scaled to fit the
2319 /// remaining space, if any.
2320 #[derive(Debug)]
2321 pub struct Lattice<'a> {
2322 /// x-axis values dividing bitmap
2323 pub x_divs: &'a [i32],
2324 /// y-axis values dividing bitmap
2325 pub y_divs: &'a [i32],
2326 /// array of fill types
2327 pub rect_types: Option<&'a [RectType]>,
2328 /// source bounds to draw from
2329 pub bounds: Option<IRect>,
2330 /// array of colors
2331 pub colors: Option<&'a [Color]>,
2332 }
2333
2334 #[derive(Debug)]
2335 pub(crate) struct Ref<'a> {
2336 pub native: SkCanvas_Lattice,
2337 pd: PhantomData<&'a Lattice<'a>>,
2338 }
2339
2340 impl Lattice<'_> {
2341 pub(crate) fn native(&self) -> Ref {
2342 if let Some(rect_types) = self.rect_types {
2343 let rect_count = (self.x_divs.len() + 1) * (self.y_divs.len() + 1);
2344 assert_eq!(rect_count, rect_types.len());
2345 // even though rect types may not include any FixedColor refs,
2346 // we expect the colors slice with a proper size here, this
2347 // saves us for going over the types array and looking for FixedColor
2348 // entries.
2349 assert_eq!(rect_count, self.colors.unwrap().len());
2350 }
2351
2352 let native = SkCanvas_Lattice {
2353 fXDivs: self.x_divs.as_ptr(),
2354 fYDivs: self.y_divs.as_ptr(),
2355 fRectTypes: self.rect_types.as_ptr_or_null(),
2356 fXCount: self.x_divs.len().try_into().unwrap(),
2357 fYCount: self.y_divs.len().try_into().unwrap(),
2358 fBounds: self.bounds.native().as_ptr_or_null(),
2359 fColors: self.colors.native().as_ptr_or_null(),
2360 };
2361 Ref {
2362 native,
2363 pd: PhantomData,
2364 }
2365 }
2366 }
2367
2368 /// Optional setting per rectangular grid entry to make it transparent,
2369 /// or to fill the grid entry with a color.
2370 pub use sb::SkCanvas_Lattice_RectType as RectType;
2371 variant_name!(RectType::FixedColor);
2372}
2373
2374#[derive(Debug)]
2375/// Stack helper class calls [`Canvas::restore_to_count()`] when [`AutoCanvasRestore`]
2376/// goes out of scope. Use this to guarantee that the canvas is restored to a known
2377/// state.
2378pub struct AutoRestoredCanvas<'a> {
2379 canvas: &'a Canvas,
2380 restore: SkAutoCanvasRestore,
2381}
2382
2383impl Deref for AutoRestoredCanvas<'_> {
2384 type Target = Canvas;
2385 fn deref(&self) -> &Self::Target {
2386 self.canvas
2387 }
2388}
2389
2390impl NativeAccess for AutoRestoredCanvas<'_> {
2391 type Native = SkAutoCanvasRestore;
2392
2393 fn native(&self) -> &SkAutoCanvasRestore {
2394 &self.restore
2395 }
2396
2397 fn native_mut(&mut self) -> &mut SkAutoCanvasRestore {
2398 &mut self.restore
2399 }
2400}
2401
2402impl Drop for AutoRestoredCanvas<'_> {
2403 /// Restores [`Canvas`] to saved state. Drop is called when container goes out of scope.
2404 fn drop(&mut self) {
2405 unsafe { sb::C_SkAutoCanvasRestore_destruct(self.native_mut()) }
2406 }
2407}
2408
2409impl AutoRestoredCanvas<'_> {
2410 /// Restores [`Canvas`] to saved state immediately. Subsequent calls and [`Self::drop()`] have
2411 /// no effect.
2412 pub fn restore(&mut self) {
2413 unsafe { sb::C_SkAutoCanvasRestore_restore(self.native_mut()) }
2414 }
2415}
2416
2417pub enum AutoCanvasRestore {}
2418
2419impl AutoCanvasRestore {
2420 // TODO: rename to save(), add a method to Canvas, perhaps named auto_restored()?
2421 /// Preserves [`Canvas::save()`] count. Optionally saves [`Canvas`] clip and [`Canvas`] matrix.
2422 ///
2423 /// - `canvas` [`Canvas`] to guard
2424 /// - `do_save` call [`Canvas::save()`]
2425 ///
2426 /// Returns utility to restore [`Canvas`] state on destructor
2427 pub fn guard(canvas: &Canvas, do_save: bool) -> AutoRestoredCanvas {
2428 let restore = construct(|acr| unsafe {
2429 sb::C_SkAutoCanvasRestore_Construct(acr, canvas.native_mut(), do_save)
2430 });
2431
2432 AutoRestoredCanvas { canvas, restore }
2433 }
2434}
2435
2436#[cfg(test)]
2437mod tests {
2438 use crate::{
2439 canvas::SaveLayerFlags, canvas::SaveLayerRec, surfaces, AlphaType, Canvas, ClipOp, Color,
2440 ColorType, ImageInfo, OwnedCanvas, Rect,
2441 };
2442
2443 #[test]
2444 fn test_raster_direct_creation_and_clear_in_memory() {
2445 let info = ImageInfo::new((2, 2), ColorType::RGBA8888, AlphaType::Unpremul, None);
2446 assert_eq!(8, info.min_row_bytes());
2447 let mut bytes: [u8; 8 * 2] = Default::default();
2448 {
2449 let canvas = Canvas::from_raster_direct(&info, bytes.as_mut(), None, None).unwrap();
2450 canvas.clear(Color::RED);
2451 }
2452
2453 assert_eq!(0xff, bytes[0]);
2454 assert_eq!(0x00, bytes[1]);
2455 assert_eq!(0x00, bytes[2]);
2456 assert_eq!(0xff, bytes[3]);
2457 }
2458
2459 #[test]
2460 fn test_raster_direct_n32_creation_and_clear_in_memory() {
2461 let mut pixels: [u32; 4] = Default::default();
2462 {
2463 let canvas = Canvas::from_raster_direct_n32((2, 2), pixels.as_mut(), None).unwrap();
2464 canvas.clear(Color::RED);
2465 }
2466
2467 // TODO: equals to 0xff0000ff on macOS, but why? Endianness should be the same.
2468 // assert_eq!(0xffff0000, pixels[0]);
2469 }
2470
2471 #[test]
2472 fn test_empty_canvas_creation() {
2473 let canvas = OwnedCanvas::default();
2474 drop(canvas)
2475 }
2476
2477 #[test]
2478 fn test_save_layer_rec_lifetimes() {
2479 let rect = Rect::default();
2480 {
2481 let _rec = SaveLayerRec::default()
2482 .flags(SaveLayerFlags::PRESERVE_LCD_TEXT)
2483 .bounds(&rect);
2484 }
2485 }
2486
2487 #[test]
2488 fn test_make_surface() {
2489 let mut pixels: [u32; 4] = Default::default();
2490 let canvas = Canvas::from_raster_direct_n32((2, 2), pixels.as_mut(), None).unwrap();
2491 let ii = canvas.image_info();
2492 let mut surface = canvas.new_surface(&ii, None).unwrap();
2493 dbg!(&canvas as *const _);
2494 drop(canvas);
2495
2496 let canvas = surface.canvas();
2497 dbg!(canvas as *const _);
2498 canvas.clear(Color::RED);
2499 }
2500
2501 #[test]
2502 fn clip_options_overloads() {
2503 let c = OwnedCanvas::default();
2504 // do_anti_alias
2505 c.clip_rect(Rect::default(), None, true);
2506 // clip_op
2507 c.clip_rect(Rect::default(), ClipOp::Difference, None);
2508 // both
2509 c.clip_rect(Rect::default(), ClipOp::Difference, true);
2510 }
2511
2512 /// Regression test for: <https://github.com/rust-skia/rust-skia/issues/427>
2513 #[test]
2514 fn test_local_and_device_clip_bounds() {
2515 let mut surface =
2516 surfaces::raster(&ImageInfo::new_n32_premul((100, 100), None), 0, None).unwrap();
2517 let _ = surface.canvas().device_clip_bounds();
2518 let _ = surface.canvas().local_clip_bounds();
2519 let _ = surface.canvas().local_to_device();
2520 }
2521}