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