Skip to main content

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}