skia_safe/core/surface.rs
1use std::{fmt, ptr};
2
3use skia_bindings::{self as sb, SkRefCntBase, SkSurface};
4
5use crate::{
6 Bitmap, Canvas, IPoint, IRect, ISize, Image, ImageInfo, Paint, Pixmap, Point, SamplingOptions,
7 SurfaceProps, gpu, prelude::*,
8};
9
10pub mod surfaces {
11 use skia_bindings::{self as sb};
12
13 use crate::{ISize, ImageInfo, Surface, SurfaceProps, prelude::*};
14
15 pub use sb::SkSurfaces_BackendSurfaceAccess as BackendSurfaceAccess;
16 variant_name!(BackendSurfaceAccess::Present);
17
18 /// Returns [`Surface`] without backing pixels. Drawing to [`crate::Canvas`] returned from
19 /// [`Surface`] has no effect. Calling [`Surface::image_snapshot()`] on returned [`Surface`]
20 /// returns `None`.
21 ///
22 /// * `width` - one or greater
23 /// * `height` - one or greater
24 ///
25 /// Returns: [`Surface`] if width and height are positive; otherwise, `None`
26 ///
27 /// example: <https://fiddle.skia.org/c/@Surface_MakeNull>
28 pub fn null(size: impl Into<ISize>) -> Option<Surface> {
29 let size = size.into();
30 Surface::from_ptr(unsafe { sb::C_SkSurfaces_Null(size.width, size.height) })
31 }
32
33 /// Allocates raster [`Surface`]. [`crate::Canvas`] returned by [`Surface`] draws directly into
34 /// pixels. Allocates and zeroes pixel memory. Pixel memory size is height times width times
35 /// four. Pixel memory is deleted when [`Surface`] is deleted.
36 ///
37 /// Internally, sets [`ImageInfo`] to width, height, native color type, and
38 /// [`crate::AlphaType::Premul`].
39 ///
40 /// [`Surface`] is returned if width and height are greater than zero.
41 ///
42 /// Use to create [`Surface`] that matches [`crate::PMColor`], the native pixel arrangement on
43 /// the platform. [`Surface`] drawn to output device skips converting its pixel format.
44 ///
45 /// * `width` - pixel column count; must be greater than zero
46 /// * `height` - pixel row count; must be greater than zero
47 /// * `surface_props` - LCD striping orientation and setting for device independent fonts; may
48 /// be `None`
49 ///
50 /// Returns: [`Surface`] if all parameters are valid; otherwise, `None`
51 pub fn raster_n32_premul(size: impl Into<ISize>) -> Option<Surface> {
52 raster(&ImageInfo::new_n32_premul(size, None), None, None)
53 }
54
55 /// Allocates raster [`Surface`]. [`crate::Canvas`] returned by [`Surface`] draws directly into
56 /// pixels. Allocates and zeroes pixel memory. Pixel memory size is `image_info.height()` times
57 /// `row_bytes`, or times `image_info.min_row_bytes()` if `row_bytes` is zero. Pixel memory is
58 /// deleted when [`Surface`] is deleted.
59 ///
60 /// [`Surface`] is returned if all parameters are valid. Valid parameters include: info
61 /// dimensions are greater than zero; info contains [`crate::ColorType`] and
62 /// [`crate::AlphaType`] supported by raster surface; `row_bytes` is large enough to contain
63 /// info width pixels of [`crate::ColorType`], or is zero.
64 ///
65 /// If `row_bytes` is zero, a suitable value will be chosen internally.
66 ///
67 /// * `image_info` - width, height, [`crate::ColorType`], [`crate::AlphaType`],
68 /// [`crate::ColorSpace`], of raster surface; width and height must be
69 /// greater than zero
70 /// * `row_bytes` - interval from one [`Surface`] row to the next; may be zero
71 /// * `surface_props` - LCD striping orientation and setting for device independent fonts; may
72 /// be `None`
73 ///
74 /// Returns: [`Surface`] if all parameters are valid; otherwise, `None`
75 pub fn raster(
76 image_info: &ImageInfo,
77 row_bytes: impl Into<Option<usize>>,
78 surface_props: Option<&SurfaceProps>,
79 ) -> Option<Surface> {
80 Surface::from_ptr(unsafe {
81 sb::C_SkSurfaces_Raster(
82 image_info.native(),
83 row_bytes.into().unwrap_or_default(),
84 surface_props.native_ptr_or_null(),
85 )
86 })
87 }
88
89 /// Allocates raster [`Surface`]. [`crate::Canvas`] returned by [`Surface`] draws directly into
90 /// pixels.
91 ///
92 /// [`Surface`] is returned if all parameters are valid. Valid parameters include: info
93 /// dimensions are greater than zero; info contains [`crate::ColorType`] and
94 /// [`crate::AlphaType`] supported by raster surface; pixels is not `None`; `row_bytes` is large
95 /// enough to contain info width pixels of [`crate::ColorType`].
96 ///
97 /// Pixel buffer size should be info height times computed `row_bytes`. Pixels are not
98 /// initialized. To access pixels after drawing, [`Surface::peek_pixels()`] or
99 /// [`Surface::read_pixels()`].
100 ///
101 /// * `image_info` - width, height, [`crate::ColorType`], [`crate::AlphaType`],
102 /// [`crate::ColorSpace`], of raster surface; width and height must be
103 /// greater than zero
104 /// * `pixels` - pointer to destination pixels buffer
105 /// * `row_bytes` - interval from one [`Surface`] row to the next
106 /// * `surface_props` - LCD striping orientation and setting for device independent fonts; may
107 /// be `None`
108 ///
109 /// Returns: [`Surface`] if all parameters are valid; otherwise, `None`
110 pub fn wrap_pixels<'pixels>(
111 image_info: &ImageInfo,
112 pixels: &'pixels mut [u8],
113 row_bytes: impl Into<Option<usize>>,
114 surface_props: Option<&SurfaceProps>,
115 ) -> Option<Borrows<'pixels, Surface>> {
116 let row_bytes = row_bytes
117 .into()
118 .unwrap_or_else(|| image_info.min_row_bytes());
119
120 if pixels.len() < image_info.compute_byte_size(row_bytes) {
121 return None;
122 };
123
124 Surface::from_ptr(unsafe {
125 sb::C_SkSurfaces_WrapPixels(
126 image_info.native(),
127 pixels.as_mut_ptr() as _,
128 row_bytes,
129 surface_props.native_ptr_or_null(),
130 )
131 })
132 .map(move |surface| surface.borrows(pixels))
133 }
134
135 // TODO: WrapPixels(&Pixmap)
136 // TODO: WrapPixelsReleaseProc()?
137}
138
139/// ContentChangeMode members are parameters to [`Surface::notify_content_will_change()`].
140pub use skia_bindings::SkSurface_ContentChangeMode as ContentChangeMode;
141variant_name!(ContentChangeMode::Retain);
142
143#[cfg(feature = "gpu")]
144pub use skia_bindings::SkSurface_BackendHandleAccess as BackendHandleAccess;
145#[cfg(feature = "gpu")]
146variant_name!(BackendHandleAccess::FlushWrite);
147
148/// [`Surface`] is responsible for managing the pixels that a canvas draws into. The pixels can be
149/// allocated either in CPU memory (a raster surface) or on the GPU (a `RenderTarget` surface).
150/// [`Surface`] takes care of allocating a [`Canvas`] that will draw into the surface. Call
151/// `surface_get_canvas()` to use that canvas (but don't delete it, it is owned by the surface).
152/// [`Surface`] always has non-zero dimensions. If there is a request for a new surface, and either
153/// of the requested dimensions are zero, then `None` will be returned.
154pub type Surface = RCHandle<SkSurface>;
155require_type_equality!(sb::SkSurface_INHERITED, sb::SkRefCnt);
156
157impl NativeRefCountedBase for SkSurface {
158 type Base = SkRefCntBase;
159}
160
161impl fmt::Debug for Surface {
162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163 f.debug_struct("Surface")
164 // self must be mutable (this goes through Canvas).
165 // .field("image_info", &self.image_info())
166 // .field("generation_id", &self.generation_id())
167 .field("props", &self.props())
168 .finish()
169 }
170}
171
172impl Surface {
173 /// Allocates raster [`Surface`]. [`Canvas`] returned by [`Surface`] draws directly into pixels.
174 ///
175 /// [`Surface`] is returned if all parameters are valid.
176 /// Valid parameters include:
177 /// info dimensions are greater than zero;
178 /// info contains [`crate::ColorType`] and [`crate::AlphaType`] supported by raster surface;
179 /// pixels is not `None`;
180 /// `row_bytes` is large enough to contain info width pixels of [`crate::ColorType`].
181 ///
182 /// Pixel buffer size should be info height times computed `row_bytes`.
183 /// Pixels are not initialized.
184 /// To access pixels after drawing, [`Self::peek_pixels()`] or [`Self::read_pixels()`].
185 ///
186 /// * `image_info` - width, height, [`crate::ColorType`], [`crate::AlphaType`], [`crate::ColorSpace`],
187 /// of raster surface; width and height must be greater than zero
188 /// * `pixels` - pointer to destination pixels buffer
189 /// * `row_bytes` - interval from one [`Surface`] row to the next
190 /// * `surface_props` - LCD striping orientation and setting for device independent fonts;
191 /// may be `None`
192 ///
193 /// Returns: [`Surface`] if all parameters are valid; otherwise, `None`
194 #[deprecated(since = "0.64.0", note = "use surfaces::wrap_pixels()")]
195 pub fn new_raster_direct<'pixels>(
196 image_info: &ImageInfo,
197 pixels: &'pixels mut [u8],
198 row_bytes: impl Into<Option<usize>>,
199 surface_props: Option<&SurfaceProps>,
200 ) -> Option<Borrows<'pixels, Surface>> {
201 surfaces::wrap_pixels(image_info, pixels, row_bytes, surface_props)
202 }
203
204 /// Allocates raster [`Surface`]. [`Canvas`] returned by [`Surface`] draws directly into pixels.
205 /// Allocates and zeroes pixel memory. Pixel memory size is `image_info.height()` times
206 /// `row_bytes`, or times `image_info.min_row_bytes()` if `row_bytes` is zero.
207 /// Pixel memory is deleted when [`Surface`] is deleted.
208 ///
209 /// [`Surface`] is returned if all parameters are valid.
210 /// Valid parameters include:
211 /// info dimensions are greater than zero;
212 /// info contains [`crate::ColorType`] and [`crate::AlphaType`] supported by raster surface;
213 /// `row_bytes` is large enough to contain info width pixels of [`crate::ColorType`], or is zero.
214 ///
215 /// If `row_bytes` is zero, a suitable value will be chosen internally.
216 ///
217 /// * `image_info` - width, height, [`crate::ColorType`], [`crate::AlphaType`], [`crate::ColorSpace`],
218 /// of raster surface; width and height must be greater than zero
219 /// * `row_bytes` - interval from one [`Surface`] row to the next; may be zero
220 /// * `surface_props` - LCD striping orientation and setting for device independent fonts;
221 /// may be `None`
222 ///
223 /// Returns: [`Surface`] if all parameters are valid; otherwise, `None`
224 #[deprecated(since = "0.64.0", note = "use surfaces::raster()")]
225 pub fn new_raster(
226 image_info: &ImageInfo,
227 row_bytes: impl Into<Option<usize>>,
228 surface_props: Option<&SurfaceProps>,
229 ) -> Option<Self> {
230 surfaces::raster(image_info, row_bytes, surface_props)
231 }
232
233 /// Allocates raster [`Surface`]. [`Canvas`] returned by [`Surface`] draws directly into pixels.
234 /// Allocates and zeroes pixel memory. Pixel memory size is height times width times
235 /// four. Pixel memory is deleted when [`Surface`] is deleted.
236 ///
237 /// Internally, sets [`ImageInfo`] to width, height, native color type, and
238 /// [`crate::AlphaType::Premul`].
239 ///
240 /// [`Surface`] is returned if width and height are greater than zero.
241 ///
242 /// Use to create [`Surface`] that matches [`crate::PMColor`], the native pixel arrangement on
243 /// the platform. [`Surface`] drawn to output device skips converting its pixel format.
244 ///
245 /// * `width` - pixel column count; must be greater than zero
246 /// * `height` - pixel row count; must be greater than zero
247 /// * `surface_props` - LCD striping orientation and setting for device independent
248 /// fonts; may be `None`
249 ///
250 /// Returns: [`Surface`] if all parameters are valid; otherwise, `None`
251 #[deprecated(since = "0.64.0", note = "use surfaces::raster_n32_premul()")]
252 pub fn new_raster_n32_premul(size: impl Into<ISize>) -> Option<Self> {
253 surfaces::raster_n32_premul(size)
254 }
255}
256
257#[cfg(feature = "gpu")]
258impl Surface {
259 /// Wraps a GPU-backed texture into [`Surface`]. Caller must ensure the texture is
260 /// valid for the lifetime of returned [`Surface`]. If `sample_cnt` greater than zero,
261 /// creates an intermediate MSAA [`Surface`] which is used for drawing `backend_texture`.
262 ///
263 /// [`Surface`] is returned if all parameters are valid. `backend_texture` is valid if
264 /// its pixel configuration agrees with `color_space` and context; for instance, if
265 /// `backend_texture` has an sRGB configuration, then context must support sRGB,
266 /// and `color_space` must be present. Further, `backend_texture` width and height must
267 /// not exceed context capabilities, and the context must be able to support
268 /// back-end textures.
269 ///
270 /// * `context` - GPU context
271 /// * `backend_texture` - texture residing on GPU
272 /// * `sample_cnt` - samples per pixel, or 0 to disable full scene anti-aliasing
273 /// * `color_space` - range of colors; may be `None`
274 /// * `surface_props` - LCD striping orientation and setting for device independent
275 /// fonts; may be `None`
276 ///
277 /// Returns: [`Surface`] if all parameters are valid; otherwise, `None`
278 #[deprecated(since = "0.64.0", note = "use gpu::surfaces::wrap_backend_texture()")]
279 pub fn from_backend_texture(
280 context: &mut gpu::RecordingContext,
281 backend_texture: &gpu::BackendTexture,
282 origin: gpu::SurfaceOrigin,
283 sample_cnt: impl Into<Option<usize>>,
284 color_type: crate::ColorType,
285 color_space: impl Into<Option<crate::ColorSpace>>,
286 surface_props: Option<&SurfaceProps>,
287 ) -> Option<Self> {
288 gpu::surfaces::wrap_backend_texture(
289 context,
290 backend_texture,
291 origin,
292 sample_cnt,
293 color_type,
294 color_space,
295 surface_props,
296 )
297 }
298
299 /// Wraps a GPU-backed buffer into [`Surface`]. Caller must ensure `backend_render_target`
300 /// is valid for the lifetime of returned [`Surface`].
301 ///
302 /// [`Surface`] is returned if all parameters are valid. `backend_render_target` is valid if
303 /// its pixel configuration agrees with `color_space` and context; for instance, if
304 /// `backend_render_target` has an sRGB configuration, then context must support sRGB,
305 /// and `color_space` must be present. Further, `backend_render_target` width and height must
306 /// not exceed context capabilities, and the context must be able to support
307 /// back-end render targets.
308 ///
309 /// * `context` - GPU context
310 /// * `backend_render_target` - GPU intermediate memory buffer
311 /// * `color_space` - range of colors
312 /// * `surface_props` - LCD striping orientation and setting for device independent
313 /// fonts; may be `None`
314 ///
315 /// Returns: [`Surface`] if all parameters are valid; otherwise, `None`
316 #[deprecated(
317 since = "0.64.0",
318 note = "use gpu::surfaces::wrap_backend_render_target()"
319 )]
320 pub fn from_backend_render_target(
321 context: &mut gpu::RecordingContext,
322 backend_render_target: &gpu::BackendRenderTarget,
323 origin: gpu::SurfaceOrigin,
324 color_type: crate::ColorType,
325 color_space: impl Into<Option<crate::ColorSpace>>,
326 surface_props: Option<&SurfaceProps>,
327 ) -> Option<Self> {
328 gpu::surfaces::wrap_backend_render_target(
329 context,
330 backend_render_target,
331 origin,
332 color_type,
333 color_space,
334 surface_props,
335 )
336 }
337
338 /// Returns [`Surface`] on GPU indicated by context. Allocates memory for
339 /// pixels, based on the width, height, and [`crate::ColorType`] in [`ImageInfo`]. budgeted
340 /// selects whether allocation for pixels is tracked by context. `image_info`
341 /// describes the pixel format in [`crate::ColorType`], and transparency in
342 /// [`crate::AlphaType`], and color matching in [`crate::ColorSpace`].
343 ///
344 /// `sample_count` requests the number of samples per pixel.
345 /// Pass zero to disable multi-sample anti-aliasing. The request is rounded
346 /// up to the next supported count, or rounded down if it is larger than the
347 /// maximum supported count.
348 ///
349 /// `surface_origin` pins either the top-left or the bottom-left corner to the origin.
350 ///
351 /// `should_create_with_mips` hints that [`Image`] returned by [`Image::image_snapshot`] is mip map.
352 ///
353 /// * `context` - GPU context
354 /// * `image_info` - width, height, [`crate::ColorType`], [`crate::AlphaType`], [`crate::ColorSpace`];
355 /// width, or height, or both, may be zero
356 /// * `sample_count` - samples per pixel, or 0 to disable full scene anti-aliasing
357 /// * `surface_props` - LCD striping orientation and setting for device independent
358 /// fonts; may be `None`
359 /// * `should_create_with_mips` - hint that [`Surface`] will host mip map images
360 ///
361 /// Returns: [`Surface`] if all parameters are valid; otherwise, `None`
362 #[deprecated(since = "0.64.0", note = "use gpu::surfaces::render_target()")]
363 pub fn new_render_target(
364 context: &mut gpu::RecordingContext,
365 budgeted: gpu::Budgeted,
366 image_info: &ImageInfo,
367 sample_count: impl Into<Option<usize>>,
368 surface_origin: impl Into<Option<gpu::SurfaceOrigin>>,
369 surface_props: Option<&SurfaceProps>,
370 should_create_with_mips: impl Into<Option<bool>>,
371 ) -> Option<Self> {
372 gpu::surfaces::render_target(
373 context,
374 budgeted,
375 image_info,
376 sample_count,
377 surface_origin,
378 surface_props,
379 should_create_with_mips,
380 None,
381 )
382 }
383
384 /// Creates [`Surface`] from CAMetalLayer.
385 /// Returned [`Surface`] takes a reference on the CAMetalLayer. The ref on the layer will be
386 /// released when the [`Surface`] is destroyed.
387 ///
388 /// Only available when Metal API is enabled.
389 ///
390 /// Will grab the current drawable from the layer and use its texture as a `backend_rt` to
391 /// create a renderable surface.
392 ///
393 /// * `context` - GPU context
394 /// * `layer` - [`gpu::mtl::Handle`] (expected to be a CAMetalLayer*)
395 /// * `sample_cnt` - samples per pixel, or 0 to disable full scene anti-aliasing
396 /// * `color_space` - range of colors; may be `None`
397 /// * `surface_props` - LCD striping orientation and setting for device independent
398 /// fonts; may be `None`
399 /// * `drawable` - Pointer to drawable to be filled in when this surface is
400 /// instantiated; may not be `None`
401 ///
402 /// Returns: created [`Surface`], or `None`
403 #[deprecated(since = "0.65.0", note = "Use gpu::surfaces::wrap_ca_metal_layer")]
404 #[allow(clippy::missing_safety_doc)]
405 #[allow(clippy::too_many_arguments)]
406 #[cfg(feature = "metal")]
407 pub unsafe fn from_ca_metal_layer(
408 context: &mut gpu::RecordingContext,
409 layer: gpu::mtl::Handle,
410 origin: gpu::SurfaceOrigin,
411 sample_cnt: impl Into<Option<usize>>,
412 color_type: crate::ColorType,
413 color_space: impl Into<Option<crate::ColorSpace>>,
414 surface_props: Option<&SurfaceProps>,
415 drawable: *mut gpu::mtl::Handle,
416 ) -> Option<Self> {
417 unsafe {
418 gpu::surfaces::wrap_ca_metal_layer(
419 context,
420 layer,
421 origin,
422 sample_cnt,
423 color_type,
424 color_space,
425 surface_props,
426 drawable,
427 )
428 }
429 }
430
431 /// Creates [`Surface`] from MTKView.
432 /// Returned [`Surface`] takes a reference on the `MTKView`. The ref on the layer will be
433 /// released when the [`Surface`] is destroyed.
434 ///
435 /// Only available when Metal API is enabled.
436 ///
437 /// Will grab the current drawable from the layer and use its texture as a `backend_rt` to
438 /// create a renderable surface.
439 ///
440 /// * `context` - GPU context
441 /// * `layer` - [`gpu::mtl::Handle`] (expected to be a `MTKView*`)
442 /// * `sample_cnt` - samples per pixel, or 0 to disable full scene anti-aliasing
443 /// * `color_space` - range of colors; may be `None`
444 /// * `surface_props` - LCD striping orientation and setting for device independent
445 /// fonts; may be `None`
446 ///
447 /// Returns: created [`Surface`], or `None`
448 #[deprecated(since = "0.65.0", note = "Use gpu::surfaces::wrap_mtk_view")]
449 #[allow(clippy::missing_safety_doc)]
450 #[cfg(feature = "metal")]
451 pub unsafe fn from_mtk_view(
452 context: &mut gpu::RecordingContext,
453 mtk_view: gpu::mtl::Handle,
454 origin: gpu::SurfaceOrigin,
455 sample_count: impl Into<Option<usize>>,
456 color_type: crate::ColorType,
457 color_space: impl Into<Option<crate::ColorSpace>>,
458 surface_props: Option<&SurfaceProps>,
459 ) -> Option<Self> {
460 unsafe {
461 gpu::surfaces::wrap_mtk_view(
462 context,
463 mtk_view,
464 origin,
465 sample_count,
466 color_type,
467 color_space,
468 surface_props,
469 )
470 }
471 }
472}
473
474impl Surface {
475 /// Returns [`Surface`] without backing pixels. Drawing to [`Canvas`] returned from [`Surface`]
476 /// has no effect. Calling [`Self::image_snapshot()`] on returned [`Surface`] returns `None`.
477 ///
478 /// * `width` - one or greater
479 /// * `height` - one or greater
480 ///
481 /// Returns: [`Surface`] if width and height are positive; otherwise, `None`
482 ///
483 /// example: <https://fiddle.skia.org/c/@Surface_MakeNull>
484 #[deprecated(since = "0.64.0", note = "use surfaces::null()")]
485 pub fn new_null(size: impl Into<ISize>) -> Option<Self> {
486 surfaces::null(size)
487 }
488
489 /// Returns pixel count in each row; may be zero or greater.
490 ///
491 /// Returns: number of pixel columns
492 pub fn width(&self) -> i32 {
493 unsafe { sb::C_SkSurface_width(self.native()) }
494 }
495
496 /// Returns pixel row count; may be zero or greater.
497 ///
498 /// Returns: number of pixel rows
499 ///
500 pub fn height(&self) -> i32 {
501 unsafe { sb::C_SkSurface_height(self.native()) }
502 }
503
504 /// Returns an [`ImageInfo`] describing the surface.
505 pub fn image_info(&self) -> ImageInfo {
506 let mut info = ImageInfo::default();
507 unsafe { sb::C_SkSurface_imageInfo(self.native(), info.native_mut()) };
508 info
509 }
510
511 /// Returns unique value identifying the content of [`Surface`]. Returned value changes
512 /// each time the content changes. Content is changed by drawing, or by calling
513 /// [`Self::notify_content_will_change()`].
514 ///
515 /// Returns: unique content identifier
516 ///
517 /// example: <https://fiddle.skia.org/c/@Surface_notifyContentWillChange>
518 pub fn generation_id(&mut self) -> u32 {
519 unsafe { self.native_mut().generationID() }
520 }
521
522 /// Notifies that [`Surface`] contents will be changed by code outside of Skia.
523 /// Subsequent calls to [`Self::generation_id()`] return a different value.
524 ///
525 /// example: <https://fiddle.skia.org/c/@Surface_notifyContentWillChange>
526 pub fn notify_content_will_change(&mut self, mode: ContentChangeMode) -> &mut Self {
527 unsafe { self.native_mut().notifyContentWillChange(mode) }
528 self
529 }
530}
531
532#[cfg(not(feature = "gpu"))]
533impl Surface {
534 /// Returns the recording context being used by the [`Surface`].
535 pub fn recording_context(&self) -> Option<gpu::RecordingContext> {
536 None
537 }
538
539 /// Returns the recording context being used by the [`Surface`].
540 pub fn direct_context(&self) -> Option<gpu::DirectContext> {
541 None
542 }
543}
544
545#[cfg(feature = "gpu")]
546impl Surface {
547 /// Returns the recording context being used by the [`Surface`].
548 ///
549 /// Returns: the recording context, if available; `None` otherwise
550 pub fn recording_context(&self) -> Option<gpu::RecordingContext> {
551 gpu::RecordingContext::from_unshared_ptr(unsafe { self.native().recordingContext() })
552 }
553
554 /// rust-skia helper, not in Skia
555 pub fn direct_context(&self) -> Option<gpu::DirectContext> {
556 self.recording_context()
557 .and_then(|mut ctx| ctx.as_direct_context())
558 }
559
560 /// Retrieves the back-end texture. If [`Surface`] has no back-end texture, `None`
561 /// is returned.
562 ///
563 /// The returned [`gpu::BackendTexture`] should be discarded if the [`Surface`] is drawn to or deleted.
564 ///
565 /// Returns: GPU texture reference; `None` on failure
566 #[deprecated(since = "0.64.0", note = "use gpu::surfaces::get_backend_texture()")]
567 pub fn get_backend_texture(
568 &mut self,
569 handle_access: BackendHandleAccess,
570 ) -> Option<gpu::BackendTexture> {
571 gpu::surfaces::get_backend_texture(self, handle_access)
572 }
573
574 /// Retrieves the back-end render target. If [`Surface`] has no back-end render target, `None`
575 /// is returned.
576 ///
577 /// The returned [`gpu::BackendRenderTarget`] should be discarded if the [`Surface`] is drawn to
578 /// or deleted.
579 ///
580 /// Returns: GPU render target reference; `None` on failure
581 #[deprecated(
582 since = "0.64.0",
583 note = "use gpu::surfaces::get_backend_render_target()"
584 )]
585 pub fn get_backend_render_target(
586 &mut self,
587 handle_access: BackendHandleAccess,
588 ) -> Option<gpu::BackendRenderTarget> {
589 gpu::surfaces::get_backend_render_target(self, handle_access)
590 }
591
592 // TODO: support variant with TextureReleaseProc and ReleaseContext
593
594 /// If the surface was made via [`Self::from_backend_texture`] then it's backing texture may be
595 /// substituted with a different texture. The contents of the previous backing texture are
596 /// copied into the new texture. [`Canvas`] state is preserved. The original sample count is
597 /// used. The [`gpu::BackendFormat`] and dimensions of replacement texture must match that of
598 /// the original.
599 ///
600 /// * `backend_texture` - the new backing texture for the surface
601 pub fn replace_backend_texture(
602 &mut self,
603 backend_texture: &gpu::BackendTexture,
604 origin: gpu::SurfaceOrigin,
605 ) -> bool {
606 self.replace_backend_texture_with_mode(backend_texture, origin, ContentChangeMode::Retain)
607 }
608
609 /// If the surface was made via [`Self::from_backend_texture()`] then it's backing texture may be
610 /// substituted with a different texture. The contents of the previous backing texture are
611 /// copied into the new texture. [`Canvas`] state is preserved. The original sample count is
612 /// used. The [`gpu::BackendFormat`] and dimensions of replacement texture must match that of
613 /// the original.
614 ///
615 /// * `backend_texture` - the new backing texture for the surface
616 /// * `mode` - Retain or discard current Content
617 pub fn replace_backend_texture_with_mode(
618 &mut self,
619 backend_texture: &gpu::BackendTexture,
620 origin: gpu::SurfaceOrigin,
621 mode: impl Into<Option<ContentChangeMode>>,
622 ) -> bool {
623 unsafe {
624 sb::C_SkSurface_replaceBackendTexture(
625 self.native_mut(),
626 backend_texture.native(),
627 origin,
628 mode.into().unwrap_or(ContentChangeMode::Retain),
629 )
630 }
631 }
632}
633
634impl Surface {
635 /// Returns [`Canvas`] that draws into [`Surface`]. Subsequent calls return the same [`Canvas`].
636 /// [`Canvas`] returned is managed and owned by [`Surface`], and is deleted when [`Surface`]
637 /// is deleted.
638 ///
639 /// Returns: drawing [`Canvas`] for [`Surface`]
640 ///
641 /// example: <https://fiddle.skia.org/c/@Surface_getCanvas>
642 pub fn canvas(&mut self) -> &Canvas {
643 let canvas_ref = unsafe { &*self.native_mut().getCanvas() };
644 Canvas::borrow_from_native(canvas_ref)
645 }
646
647 // TODO: capabilities()
648
649 // TODO: why is self mutable here?
650
651 /// Returns a compatible [`Surface`], or `None`. Returned [`Surface`] contains
652 /// the same raster, GPU, or null properties as the original. Returned [`Surface`]
653 /// does not share the same pixels.
654 ///
655 /// Returns `None` if `image_info` width or height are zero, or if `image_info`
656 /// is incompatible with [`Surface`].
657 ///
658 /// * `image_info` - width, height, [`crate::ColorType`], [`crate::AlphaType`], [`crate::ColorSpace`],
659 /// of [`Surface`]; width and height must be greater than zero
660 ///
661 /// Returns: compatible [`Surface`] or `None`
662 ///
663 /// example: <https://fiddle.skia.org/c/@Surface_makeSurface>
664 pub fn new_surface(&mut self, image_info: &ImageInfo) -> Option<Self> {
665 Self::from_ptr(unsafe {
666 sb::C_SkSurface_makeSurface(self.native_mut(), image_info.native())
667 })
668 }
669
670 /// Calls [`Self::new_surface()`] with the same [`ImageInfo`] as this surface, but with the
671 /// specified width and height.
672 pub fn new_surface_with_dimensions(&mut self, dim: impl Into<ISize>) -> Option<Self> {
673 let dim = dim.into();
674 Self::from_ptr(unsafe {
675 sb::C_SkSurface_makeSurface2(self.native_mut(), dim.width, dim.height)
676 })
677 }
678
679 /// Returns [`Image`] capturing [`Surface`] contents. Subsequent drawing to [`Surface`] contents
680 /// are not captured. [`Image`] allocation is accounted for if [`Surface`] was created with
681 /// [`gpu::Budgeted::Yes`].
682 ///
683 /// Returns: [`Image`] initialized with [`Surface`] contents
684 ///
685 /// example: <https://fiddle.skia.org/c/@Surface_makeImageSnapshot>
686 pub fn image_snapshot(&mut self) -> Image {
687 Image::from_ptr(unsafe {
688 sb::C_SkSurface_makeImageSnapshot(self.native_mut(), ptr::null())
689 })
690 .unwrap()
691 }
692
693 /// Returns an [`Image`] capturing the current [`Surface`] contents. However, the contents of the
694 /// [`Image`] are only valid as long as no other writes to the [`Surface`] occur. If writes to the
695 /// original [`Surface`] happen then contents of the [`Image`] are undefined. However, continued use
696 /// of the [`Image`] should not cause crashes or similar fatal behavior.
697 ///
698 /// This API is useful for cases where the client either immediately destroys the [`Surface`]
699 /// after the [`Image`] is created or knows they will destroy the [`Image`] before writing to the
700 /// [`Surface`] again.
701 ///
702 /// This API can be more performant than [`Self::image_snapshot()`] as it never does an internal copy
703 /// of the data assuming the user frees either the [`Image`] or [`Surface`] as described above.
704 pub fn make_temporary_image(&mut self) -> Option<Image> {
705 Image::from_ptr(unsafe { sb::C_SkSurface_makeTemporaryImage(self.native_mut()) })
706 }
707
708 // TODO: combine this function with image_snapshot and make bounds optional()?
709
710 /// Like the no-parameter version, this returns an image of the current surface contents.
711 /// This variant takes a rectangle specifying the subset of the surface that is of interest.
712 /// These bounds will be sanitized before being used.
713 /// - If bounds extends beyond the surface, it will be trimmed to just the intersection of
714 /// it and the surface.
715 /// - If bounds does not intersect the surface, then this returns `None`.
716 /// - If bounds == the surface, then this is the same as calling the no-parameter variant.
717 ///
718 /// example: <https://fiddle.skia.org/c/@Surface_makeImageSnapshot_2>
719 pub fn image_snapshot_with_bounds(&mut self, bounds: impl AsRef<IRect>) -> Option<Image> {
720 Image::from_ptr(unsafe {
721 sb::C_SkSurface_makeImageSnapshot(self.native_mut(), bounds.as_ref().native())
722 })
723 }
724
725 /// Draws [`Surface`] contents to canvas, with its top-left corner at `(offset.x, offset.y)`.
726 ///
727 /// If [`Paint`] paint is not `None`, apply [`crate::ColorFilter`], alpha, [`crate::ImageFilter`], and [`crate::BlendMode`].
728 ///
729 /// * `canvas` - [`Canvas`] drawn into
730 /// * `offset.x` - horizontal offset in [`Canvas`]
731 /// * `offset.y` - vertical offset in [`Canvas`]
732 /// * `sampling` - what technique to use when sampling the surface pixels
733 /// * `paint` - [`Paint`] containing [`crate::BlendMode`], [`crate::ColorFilter`], [`crate::ImageFilter`],
734 /// and so on; or `None`
735 ///
736 /// example: <https://fiddle.skia.org/c/@Surface_draw>
737 pub fn draw(
738 &mut self,
739 canvas: &Canvas,
740 offset: impl Into<Point>,
741 sampling: impl Into<SamplingOptions>,
742 paint: Option<&Paint>,
743 ) {
744 let offset = offset.into();
745 let sampling = sampling.into();
746 unsafe {
747 self.native_mut().draw(
748 canvas.native_mut(),
749 offset.x,
750 offset.y,
751 sampling.native(),
752 paint.native_ptr_or_null(),
753 )
754 }
755 }
756
757 pub fn peek_pixels(&mut self) -> Option<Pixmap> {
758 let mut pm = Pixmap::default();
759 unsafe { self.native_mut().peekPixels(pm.native_mut()) }.then_some(pm)
760 }
761
762 // TODO: why is self mut?
763
764 /// Copies [`crate::Rect`] of pixels to dst.
765 ///
766 /// Source [`crate::Rect`] corners are (`src.x`, `src.y`) and [`Surface`] `(width(), height())`.
767 /// Destination [`crate::Rect`] corners are `(0, 0)` and `(dst.width(), dst.height())`.
768 /// Copies each readable pixel intersecting both rectangles, without scaling,
769 /// converting to `dst_color_type()` and `dst_alpha_type()` if required.
770 ///
771 /// Pixels are readable when [`Surface`] is raster, or backed by a Ganesh GPU backend. Graphite
772 /// has deprecated this API in favor of the equivalent asynchronous API on
773 /// `skgpu::graphite::Context` (with an optional explicit synchonization).
774 ///
775 /// The destination pixel storage must be allocated by the caller.
776 ///
777 /// Pixel values are converted only if [`crate::ColorType`] and [`crate::AlphaType`]
778 /// do not match. Only pixels within both source and destination rectangles
779 /// are copied. dst contents outside [`crate::Rect`] intersection are unchanged.
780 ///
781 /// Pass negative values for `src.x` or `src.y` to offset pixels across or down destination.
782 ///
783 /// Does not copy, and returns `false` if:
784 /// - Source and destination rectangles do not intersect.
785 /// - [`Pixmap`] pixels could not be allocated.
786 /// - `dst.row_bytes()` is too small to contain one row of pixels.
787 ///
788 /// * `dst` - storage for pixels copied from [`Surface`]
789 /// * `src_x` - offset into readable pixels on x-axis; may be negative
790 /// * `src_y` - offset into readable pixels on y-axis; may be negative
791 ///
792 /// Returns: `true` if pixels were copied
793 ///
794 /// example: <https://fiddle.skia.org/c/@Surface_readPixels>
795 pub fn read_pixels_to_pixmap(&mut self, dst: &Pixmap, src: impl Into<IPoint>) -> bool {
796 let src = src.into();
797 unsafe { self.native_mut().readPixels(dst.native(), src.x, src.y) }
798 }
799
800 /// Copies [`crate::Rect`] of pixels from [`Canvas`] into `dst_pixels`.
801 ///
802 /// Source [`crate::Rect`] corners are (`src.x`, `src.y`) and [`Surface`] (width(), height()).
803 /// Destination [`crate::Rect`] corners are (0, 0) and (`dst_info`.width(), `dst_info`.height()).
804 /// Copies each readable pixel intersecting both rectangles, without scaling,
805 /// converting to `dst_info_color_type()` and `dst_info_alpha_type()` if required.
806 ///
807 /// Pixels are readable when [`Surface`] is raster, or backed by a Ganesh GPU backend. Graphite
808 /// has deprecated this API in favor of the equivalent asynchronous API on
809 /// `skgpu::graphite::Context` (with an optional explicit synchonization).
810 ///
811 /// The destination pixel storage must be allocated by the caller.
812 ///
813 /// Pixel values are converted only if [`crate::ColorType`] and [`crate::AlphaType`]
814 /// do not match. Only pixels within both source and destination rectangles
815 /// are copied. `dst_pixels` contents outside [`crate::Rect`] intersection are unchanged.
816 ///
817 /// Pass negative values for `src.x` or `src.y` to offset pixels across or down destination.
818 ///
819 /// Does not copy, and returns `false` if:
820 /// - Source and destination rectangles do not intersect.
821 /// - [`Surface`] pixels could not be converted to `dst_info.color_type()` or `dst_info.alpha_type()`.
822 /// - `dst_row_bytes` is too small to contain one row of pixels.
823 ///
824 /// * `dst_info` - width, height, [`crate::ColorType`], and [`crate::AlphaType`] of `dst_pixels`
825 /// * `dst_pixels` - storage for pixels; `dst_info.height()` times `dst_row_bytes`, or larger
826 /// * `dst_row_bytes` - size of one destination row; `dst_info.width()` times pixel size, or larger
827 /// * `src.x` - offset into readable pixels on x-axis; may be negative
828 /// * `src.y` - offset into readable pixels on y-axis; may be negative
829 ///
830 /// Returns: `true` if pixels were copied
831 pub fn read_pixels(
832 &mut self,
833 dst_info: &ImageInfo,
834 dst_pixels: &mut [u8],
835 dst_row_bytes: usize,
836 src: impl Into<IPoint>,
837 ) -> bool {
838 if !dst_info.valid_pixels(dst_row_bytes, dst_pixels) {
839 return false;
840 }
841 let src = src.into();
842 unsafe {
843 self.native_mut().readPixels1(
844 dst_info.native(),
845 dst_pixels.as_mut_ptr() as _,
846 dst_row_bytes,
847 src.x,
848 src.y,
849 )
850 }
851 }
852
853 // TODO: why is self mut?
854 // TODO: why is Bitmap immutable?
855
856 /// Copies [`crate::Rect`] of pixels from [`Surface`] into bitmap.
857 ///
858 /// Source [`crate::Rect`] corners are (`src.x`, `src.y`) and [`Surface`] (width(), height()).
859 /// Destination [`crate::Rect`] corners are `(0, 0)` and `(bitmap.width(), bitmap.height())`.
860 /// Copies each readable pixel intersecting both rectangles, without scaling,
861 /// converting to `bitmap.color_type()` and `bitmap.alpha_type()` if required.
862 ///
863 /// Pixels are readable when [`Surface`] is raster, or backed by a Ganesh GPU backend. Graphite
864 /// has deprecated this API in favor of the equivalent asynchronous API on
865 /// `skgpu::graphite::Context` (with an optional explicit synchonization).
866 ///
867 /// The destination pixel storage must be allocated by the caller.
868 ///
869 /// Pixel values are converted only if [`crate::ColorType`] and [`crate::AlphaType`]
870 /// do not match. Only pixels within both source and destination rectangles
871 /// are copied. dst contents outside [`crate::Rect`] intersection are unchanged.
872 ///
873 /// Pass negative values for `src.x` or `src.y` to offset pixels across or down destination.
874 ///
875 /// Does not copy, and returns `false` if:
876 /// - Source and destination rectangles do not intersect.
877 /// - [`Surface`] pixels could not be converted to `dst.color_type()` or `dst.alpha_type()`.
878 /// - dst pixels could not be allocated.
879 /// - `dst.row_bytes()` is too small to contain one row of pixels.
880 ///
881 /// * `dst` - storage for pixels copied from [`Surface`]
882 /// * `src.x` - offset into readable pixels on x-axis; may be negative
883 /// * `src.y` - offset into readable pixels on y-axis; may be negative
884 ///
885 /// Returns: `true` if pixels were copied
886 ///
887 /// example: <https://fiddle.skia.org/c/@Surface_readPixels_3>
888 pub fn read_pixels_to_bitmap(&mut self, bitmap: &Bitmap, src: impl Into<IPoint>) -> bool {
889 let src = src.into();
890 unsafe { self.native_mut().readPixels2(bitmap.native(), src.x, src.y) }
891 }
892
893 // TODO: AsyncReadResult, RescaleGamma (m79, m86)
894 // TODO: wrap asyncRescaleAndReadPixels (m76, m79, m89)
895 // TODO: wrap asyncRescaleAndReadPixelsYUV420 (m77, m79, m89)
896 // TODO: wrap asyncRescaleAndReadPixelsYUVA420 (m117)
897
898 /// Copies [`crate::Rect`] of pixels from the src [`Pixmap`] to the [`Surface`].
899 ///
900 /// Source [`crate::Rect`] corners are `(0, 0)` and `(src.width(), src.height())`.
901 /// Destination [`crate::Rect`] corners are `(`dst.x`, `dst.y`)` and
902 /// (`dst.x` + Surface width(), `dst.y` + Surface height()).
903 ///
904 /// Copies each readable pixel intersecting both rectangles, without scaling,
905 /// converting to [`Surface`] `color_type()` and [`Surface`] `alpha_type()` if required.
906 ///
907 /// * `src` - storage for pixels to copy to [`Surface`]
908 /// * `dst.x` - x-axis position relative to [`Surface`] to begin copy; may be negative
909 /// * `dst.y` - y-axis position relative to [`Surface`] to begin copy; may be negative
910 ///
911 /// example: <https://fiddle.skia.org/c/@Surface_writePixels>
912 pub fn write_pixels_from_pixmap(&mut self, src: &Pixmap, dst: impl Into<IPoint>) {
913 let dst = dst.into();
914 unsafe { self.native_mut().writePixels(src.native(), dst.x, dst.y) }
915 }
916
917 /// Copies [`crate::Rect`] of pixels from the src [`Bitmap`] to the [`Surface`].
918 ///
919 /// Source [`crate::Rect`] corners are `(0, 0)` and `(src.width(), src.height())`.
920 /// Destination [`crate::Rect`] corners are `(`dst.x`, `dst.y`)` and
921 /// `(`dst.x` + Surface width(), `dst.y` + Surface height())`.
922 ///
923 /// Copies each readable pixel intersecting both rectangles, without scaling,
924 /// converting to [`Surface`] `color_type()` and [`Surface`] `alpha_type()` if required.
925 ///
926 /// * `src` - storage for pixels to copy to [`Surface`]
927 /// * `dst.x` - x-axis position relative to [`Surface`] to begin copy; may be negative
928 /// * `dst.y` - y-axis position relative to [`Surface`] to begin copy; may be negative
929 ///
930 /// example: <https://fiddle.skia.org/c/@Surface_writePixels_2>
931 pub fn write_pixels_from_bitmap(&mut self, bitmap: &Bitmap, dst: impl Into<IPoint>) {
932 let dst = dst.into();
933 unsafe {
934 self.native_mut()
935 .writePixels1(bitmap.native(), dst.x, dst.y)
936 }
937 }
938
939 /// Returns [`SurfaceProps`] for surface.
940 ///
941 /// Returns: LCD striping orientation and setting for device independent fonts
942 pub fn props(&self) -> &SurfaceProps {
943 SurfaceProps::from_native_ref(unsafe { &*sb::C_SkSurface_props(self.native()) })
944 }
945
946 // TODO: wait()
947}
948
949pub use surfaces::BackendSurfaceAccess;
950
951impl Surface {
952 /// If a surface is GPU texture backed, is being drawn with MSAA, and there is a resolve
953 /// texture, this call will insert a resolve command into the stream of gpu commands. In order
954 /// for the resolve to actually have an effect, the work still needs to be flushed and submitted
955 /// to the GPU after recording the resolve command. If a resolve is not supported or the
956 /// [`Surface`] has no dirty work to resolve, then this call is a no-op.
957 ///
958 /// This call is most useful when the [`Surface`] is created by wrapping a single sampled gpu
959 /// texture, but asking Skia to render with MSAA. If the client wants to use the wrapped texture
960 /// outside of Skia, the only way to trigger a resolve is either to call this command or use
961 /// [`Self::flush()`].
962 #[cfg(feature = "gpu")]
963 #[deprecated(since = "0.65.0", note = "Use gpu::surfaces::resolve_msaa")]
964 pub fn resolve_msaa(&mut self) {
965 gpu::surfaces::resolve_msaa(self)
966 }
967}
968
969#[cfg(test)]
970mod tests {
971 use super::*;
972
973 #[test]
974 fn create() {
975 assert!(surfaces::raster_n32_premul((0, 0)).is_none());
976 let surface = surfaces::raster_n32_premul((1, 1)).unwrap();
977 assert_eq!(1, surface.native().ref_counted_base()._ref_cnt())
978 }
979
980 #[test]
981 fn test_raster_direct() {
982 let image_info = ImageInfo::new(
983 (20, 20),
984 crate::ColorType::RGBA8888,
985 crate::AlphaType::Unpremul,
986 None,
987 );
988 let min_row_bytes = image_info.min_row_bytes();
989 let mut pixels = vec![0u8; image_info.compute_byte_size(min_row_bytes)];
990 let mut surface = surfaces::wrap_pixels(
991 &image_info,
992 pixels.as_mut_slice(),
993 Some(min_row_bytes),
994 None,
995 )
996 .unwrap();
997 let paint = Paint::default();
998 surface.canvas().draw_circle((10, 10), 10.0, &paint);
999 }
1000
1001 #[test]
1002 fn test_drawing_owned_as_exclusive_ref_ergonomics() {
1003 let mut surface = surfaces::raster_n32_premul((16, 16)).unwrap();
1004
1005 // option1:
1006 // - An &canvas can be drawn to.
1007 {
1008 let canvas = Canvas::new(ISize::new(16, 16), None).unwrap();
1009 surface.draw(&canvas, (5.0, 5.0), SamplingOptions::default(), None);
1010 surface.draw(&canvas, (10.0, 10.0), SamplingOptions::default(), None);
1011 }
1012
1013 // option2:
1014 // - A canvas from another surface can be drawn to.
1015 {
1016 let mut surface2 = surfaces::raster_n32_premul((16, 16)).unwrap();
1017 let canvas = surface2.canvas();
1018 surface.draw(canvas, (5.0, 5.0), SamplingOptions::default(), None);
1019 surface.draw(canvas, (10.0, 10.0), SamplingOptions::default(), None);
1020 }
1021 }
1022}