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