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