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