1 | use std::{fmt, ptr}; |
2 | |
3 | use skia_bindings::{self as sb, SkRefCntBase, SkSurface}; |
4 | |
5 | use crate::{ |
6 | gpu, prelude::*, Bitmap, Canvas, IPoint, IRect, ISize, Image, ImageInfo, Paint, Pixmap, Point, |
7 | SamplingOptions, SurfaceProps, |
8 | }; |
9 | |
10 | pub 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 |
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()`]. |
140 | pub use skia_bindings::SkSurface_ContentChangeMode as ContentChangeMode; |
141 | variant_name!(ContentChangeMode::Retain); |
142 | |
143 | #[cfg (feature = "gpu" )] |
144 | pub use skia_bindings::SkSurface_BackendHandleAccess as BackendHandleAccess; |
145 | #[cfg (feature = "gpu" )] |
146 | variant_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. |
154 | pub type Surface = RCHandle<SkSurface>; |
155 | require_type_equality!(sb::SkSurface_INHERITED, sb::SkRefCnt); |
156 | |
157 | impl NativeRefCountedBase for SkSurface { |
158 | type Base = SkRefCntBase; |
159 | } |
160 | |
161 | impl fmt::Debug for Surface { |
162 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
163 | f&mut DebugStruct<'_, '_>.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(name:"props" , &self.props()) |
168 | .finish() |
169 | } |
170 | } |
171 | |
172 | impl 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" )] |
258 | impl 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 | gpu::surfaces::wrap_ca_metal_layer( |
418 | context, |
419 | layer, |
420 | origin, |
421 | sample_cnt, |
422 | color_type, |
423 | color_space, |
424 | surface_props, |
425 | drawable, |
426 | ) |
427 | } |
428 | |
429 | /// Creates [`Surface`] from MTKView. |
430 | /// Returned [`Surface`] takes a reference on the `MTKView`. The ref on the layer will be |
431 | /// released when the [`Surface`] is destroyed. |
432 | /// |
433 | /// Only available when Metal API is enabled. |
434 | /// |
435 | /// Will grab the current drawable from the layer and use its texture as a `backend_rt` to |
436 | /// create a renderable surface. |
437 | /// |
438 | /// * `context` - GPU context |
439 | /// * `layer` - [`gpu::mtl::Handle`] (expected to be a `MTKView*`) |
440 | /// * `sample_cnt` - samples per pixel, or 0 to disable full scene anti-aliasing |
441 | /// * `color_space` - range of colors; may be `None` |
442 | /// * `surface_props` - LCD striping orientation and setting for device independent |
443 | /// fonts; may be `None` |
444 | /// |
445 | /// Returns: created [`Surface`], or `None` |
446 | #[deprecated (since = "0.65.0" , note = "Use gpu::surfaces::wrap_mtk_view" )] |
447 | #[allow (clippy::missing_safety_doc)] |
448 | #[cfg (feature = "metal" )] |
449 | pub unsafe fn from_mtk_view( |
450 | context: &mut gpu::RecordingContext, |
451 | mtk_view: gpu::mtl::Handle, |
452 | origin: gpu::SurfaceOrigin, |
453 | sample_count: impl Into<Option<usize>>, |
454 | color_type: crate::ColorType, |
455 | color_space: impl Into<Option<crate::ColorSpace>>, |
456 | surface_props: Option<&SurfaceProps>, |
457 | ) -> Option<Self> { |
458 | gpu::surfaces::wrap_mtk_view( |
459 | context, |
460 | mtk_view, |
461 | origin, |
462 | sample_count, |
463 | color_type, |
464 | color_space, |
465 | surface_props, |
466 | ) |
467 | } |
468 | } |
469 | |
470 | impl Surface { |
471 | /// Returns [`Surface`] without backing pixels. Drawing to [`Canvas`] returned from [`Surface`] |
472 | /// has no effect. Calling [`Self::image_snapshot()`] on returned [`Surface`] returns `None`. |
473 | /// |
474 | /// * `width` - one or greater |
475 | /// * `height` - one or greater |
476 | /// |
477 | /// Returns: [`Surface`] if width and height are positive; otherwise, `None` |
478 | /// |
479 | /// example: <https://fiddle.skia.org/c/@Surface_MakeNull> |
480 | #[deprecated (since = "0.64.0" , note = "use surfaces::null()" )] |
481 | pub fn new_null(size: impl Into<ISize>) -> Option<Self> { |
482 | surfaces::null(size) |
483 | } |
484 | |
485 | /// Returns pixel count in each row; may be zero or greater. |
486 | /// |
487 | /// Returns: number of pixel columns |
488 | pub fn width(&self) -> i32 { |
489 | unsafe { sb::C_SkSurface_width(self.native()) } |
490 | } |
491 | |
492 | /// Returns pixel row count; may be zero or greater. |
493 | /// |
494 | /// Returns: number of pixel rows |
495 | /// |
496 | pub fn height(&self) -> i32 { |
497 | unsafe { sb::C_SkSurface_height(self.native()) } |
498 | } |
499 | |
500 | /// Returns an [`ImageInfo`] describing the surface. |
501 | pub fn image_info(&mut self) -> ImageInfo { |
502 | let mut info = ImageInfo::default(); |
503 | unsafe { sb::C_SkSurface_imageInfo(self.native_mut(), info.native_mut()) }; |
504 | info |
505 | } |
506 | |
507 | /// Returns unique value identifying the content of [`Surface`]. Returned value changes |
508 | /// each time the content changes. Content is changed by drawing, or by calling |
509 | /// [`Self::notify_content_will_change()`]. |
510 | /// |
511 | /// Returns: unique content identifier |
512 | /// |
513 | /// example: <https://fiddle.skia.org/c/@Surface_notifyContentWillChange> |
514 | pub fn generation_id(&mut self) -> u32 { |
515 | unsafe { self.native_mut().generationID() } |
516 | } |
517 | |
518 | /// Notifies that [`Surface`] contents will be changed by code outside of Skia. |
519 | /// Subsequent calls to [`Self::generation_id()`] return a different value. |
520 | /// |
521 | /// example: <https://fiddle.skia.org/c/@Surface_notifyContentWillChange> |
522 | pub fn notify_content_will_change(&mut self, mode: ContentChangeMode) -> &mut Self { |
523 | unsafe { self.native_mut().notifyContentWillChange(mode) } |
524 | self |
525 | } |
526 | } |
527 | |
528 | #[cfg (not(feature = "gpu" ))] |
529 | impl Surface { |
530 | /// Returns the recording context being used by the [`Surface`]. |
531 | pub fn recording_context(&self) -> Option<gpu::RecordingContext> { |
532 | None |
533 | } |
534 | |
535 | /// Returns the recording context being used by the [`Surface`]. |
536 | pub fn direct_context(&self) -> Option<gpu::DirectContext> { |
537 | None |
538 | } |
539 | } |
540 | |
541 | #[cfg (feature = "gpu" )] |
542 | impl Surface { |
543 | /// Returns the recording context being used by the [`Surface`]. |
544 | /// |
545 | /// Returns: the recording context, if available; `None` otherwise |
546 | pub fn recording_context(&self) -> Option<gpu::RecordingContext> { |
547 | gpu::RecordingContext::from_unshared_ptr(unsafe { self.native().recordingContext() }) |
548 | } |
549 | |
550 | /// rust-skia helper, not in Skia |
551 | pub fn direct_context(&self) -> Option<gpu::DirectContext> { |
552 | self.recording_context() |
553 | .and_then(|mut ctx| ctx.as_direct_context()) |
554 | } |
555 | |
556 | /// Retrieves the back-end texture. If [`Surface`] has no back-end texture, `None` |
557 | /// is returned. |
558 | /// |
559 | /// The returned [`gpu::BackendTexture`] should be discarded if the [`Surface`] is drawn to or deleted. |
560 | /// |
561 | /// Returns: GPU texture reference; `None` on failure |
562 | #[deprecated (since = "0.64.0" , note = "use gpu::surfaces::get_backend_texture()" )] |
563 | pub fn get_backend_texture( |
564 | &mut self, |
565 | handle_access: BackendHandleAccess, |
566 | ) -> Option<gpu::BackendTexture> { |
567 | gpu::surfaces::get_backend_texture(self, handle_access) |
568 | } |
569 | |
570 | /// Retrieves the back-end render target. If [`Surface`] has no back-end render target, `None` |
571 | /// is returned. |
572 | /// |
573 | /// The returned [`gpu::BackendRenderTarget`] should be discarded if the [`Surface`] is drawn to |
574 | /// or deleted. |
575 | /// |
576 | /// Returns: GPU render target reference; `None` on failure |
577 | #[deprecated ( |
578 | since = "0.64.0" , |
579 | note = "use gpu::surfaces::get_backend_render_target()" |
580 | )] |
581 | pub fn get_backend_render_target( |
582 | &mut self, |
583 | handle_access: BackendHandleAccess, |
584 | ) -> Option<gpu::BackendRenderTarget> { |
585 | gpu::surfaces::get_backend_render_target(self, handle_access) |
586 | } |
587 | |
588 | // TODO: support variant with TextureReleaseProc and ReleaseContext |
589 | |
590 | /// If the surface was made via [`Self::from_backend_texture`] then it's backing texture may be |
591 | /// substituted with a different texture. The contents of the previous backing texture are |
592 | /// copied into the new texture. [`Canvas`] state is preserved. The original sample count is |
593 | /// used. The [`gpu::BackendFormat`] and dimensions of replacement texture must match that of |
594 | /// the original. |
595 | /// |
596 | /// * `backend_texture` - the new backing texture for the surface |
597 | pub fn replace_backend_texture( |
598 | &mut self, |
599 | backend_texture: &gpu::BackendTexture, |
600 | origin: gpu::SurfaceOrigin, |
601 | ) -> bool { |
602 | self.replace_backend_texture_with_mode(backend_texture, origin, ContentChangeMode::Retain) |
603 | } |
604 | |
605 | /// If the surface was made via [`Self::from_backend_texture()`] then it's backing texture may be |
606 | /// substituted with a different texture. The contents of the previous backing texture are |
607 | /// copied into the new texture. [`Canvas`] state is preserved. The original sample count is |
608 | /// used. The [`gpu::BackendFormat`] and dimensions of replacement texture must match that of |
609 | /// the original. |
610 | /// |
611 | /// * `backend_texture` - the new backing texture for the surface |
612 | /// * `mode` - Retain or discard current Content |
613 | pub fn replace_backend_texture_with_mode( |
614 | &mut self, |
615 | backend_texture: &gpu::BackendTexture, |
616 | origin: gpu::SurfaceOrigin, |
617 | mode: impl Into<Option<ContentChangeMode>>, |
618 | ) -> bool { |
619 | unsafe { |
620 | sb::C_SkSurface_replaceBackendTexture( |
621 | self.native_mut(), |
622 | backend_texture.native(), |
623 | origin, |
624 | mode.into().unwrap_or(ContentChangeMode::Retain), |
625 | ) |
626 | } |
627 | } |
628 | } |
629 | |
630 | impl Surface { |
631 | /// Returns [`Canvas`] that draws into [`Surface`]. Subsequent calls return the same [`Canvas`]. |
632 | /// [`Canvas`] returned is managed and owned by [`Surface`], and is deleted when [`Surface`] |
633 | /// is deleted. |
634 | /// |
635 | /// Returns: drawing [`Canvas`] for [`Surface`] |
636 | /// |
637 | /// example: <https://fiddle.skia.org/c/@Surface_getCanvas> |
638 | pub fn canvas(&mut self) -> &Canvas { |
639 | let canvas_ref = unsafe { &*self.native_mut().getCanvas() }; |
640 | Canvas::borrow_from_native(canvas_ref) |
641 | } |
642 | |
643 | // TODO: capabilities() |
644 | |
645 | // TODO: why is self mutable here? |
646 | |
647 | /// Returns a compatible [`Surface`], or `None`. Returned [`Surface`] contains |
648 | /// the same raster, GPU, or null properties as the original. Returned [`Surface`] |
649 | /// does not share the same pixels. |
650 | /// |
651 | /// Returns `None` if `image_info` width or height are zero, or if `image_info` |
652 | /// is incompatible with [`Surface`]. |
653 | /// |
654 | /// * `image_info` - width, height, [`crate::ColorType`], [`crate::AlphaType`], [`crate::ColorSpace`], |
655 | /// of [`Surface`]; width and height must be greater than zero |
656 | /// |
657 | /// Returns: compatible [`Surface`] or `None` |
658 | /// |
659 | /// example: <https://fiddle.skia.org/c/@Surface_makeSurface> |
660 | pub fn new_surface(&mut self, image_info: &ImageInfo) -> Option<Self> { |
661 | Self::from_ptr(unsafe { |
662 | sb::C_SkSurface_makeSurface(self.native_mut(), image_info.native()) |
663 | }) |
664 | } |
665 | |
666 | /// Calls [`Self::new_surface()`] with the same [`ImageInfo`] as this surface, but with the |
667 | /// specified width and height. |
668 | pub fn new_surface_with_dimensions(&mut self, dim: impl Into<ISize>) -> Option<Self> { |
669 | let dim = dim.into(); |
670 | Self::from_ptr(unsafe { |
671 | sb::C_SkSurface_makeSurface2(self.native_mut(), dim.width, dim.height) |
672 | }) |
673 | } |
674 | |
675 | /// Returns [`Image`] capturing [`Surface`] contents. Subsequent drawing to [`Surface`] contents |
676 | /// are not captured. [`Image`] allocation is accounted for if [`Surface`] was created with |
677 | /// [`gpu::Budgeted::Yes`]. |
678 | /// |
679 | /// Returns: [`Image`] initialized with [`Surface`] contents |
680 | /// |
681 | /// example: <https://fiddle.skia.org/c/@Surface_makeImageSnapshot> |
682 | pub fn image_snapshot(&mut self) -> Image { |
683 | Image::from_ptr(unsafe { |
684 | sb::C_SkSurface_makeImageSnapshot(self.native_mut(), ptr::null()) |
685 | }) |
686 | .unwrap() |
687 | } |
688 | |
689 | /// Returns an [`Image`] capturing the current [`Surface`] contents. However, the contents of the |
690 | /// [`Image`] are only valid as long as no other writes to the [`Surface`] occur. If writes to the |
691 | /// original [`Surface`] happen then contents of the [`Image`] are undefined. However, continued use |
692 | /// of the [`Image`] should not cause crashes or similar fatal behavior. |
693 | /// |
694 | /// This API is useful for cases where the client either immediately destroys the [`Surface`] |
695 | /// after the [`Image`] is created or knows they will destroy the [`Image`] before writing to the |
696 | /// [`Surface`] again. |
697 | /// |
698 | /// This API can be more performant than [`Self::image_snapshot()`] as it never does an internal copy |
699 | /// of the data assuming the user frees either the [`Image`] or [`Surface`] as described above. |
700 | pub fn make_temporary_image(&mut self) -> Option<Image> { |
701 | Image::from_ptr(unsafe { sb::C_SkSurface_makeTemporaryImage(self.native_mut()) }) |
702 | } |
703 | |
704 | // TODO: combine this function with image_snapshot and make bounds optional()? |
705 | |
706 | /// Like the no-parameter version, this returns an image of the current surface contents. |
707 | /// This variant takes a rectangle specifying the subset of the surface that is of interest. |
708 | /// These bounds will be sanitized before being used. |
709 | /// - If bounds extends beyond the surface, it will be trimmed to just the intersection of |
710 | /// it and the surface. |
711 | /// - If bounds does not intersect the surface, then this returns `None`. |
712 | /// - If bounds == the surface, then this is the same as calling the no-parameter variant. |
713 | /// |
714 | /// example: <https://fiddle.skia.org/c/@Surface_makeImageSnapshot_2> |
715 | pub fn image_snapshot_with_bounds(&mut self, bounds: impl AsRef<IRect>) -> Option<Image> { |
716 | Image::from_ptr(unsafe { |
717 | sb::C_SkSurface_makeImageSnapshot(self.native_mut(), bounds.as_ref().native()) |
718 | }) |
719 | } |
720 | |
721 | /// Draws [`Surface`] contents to canvas, with its top-left corner at `(offset.x, offset.y)`. |
722 | /// |
723 | /// If [`Paint`] paint is not `None`, apply [`crate::ColorFilter`], alpha, [`crate::ImageFilter`], and [`crate::BlendMode`]. |
724 | /// |
725 | /// * `canvas` - [`Canvas`] drawn into |
726 | /// * `offset.x` - horizontal offset in [`Canvas`] |
727 | /// * `offset.y` - vertical offset in [`Canvas`] |
728 | /// * `sampling` - what technique to use when sampling the surface pixels |
729 | /// * `paint` - [`Paint`] containing [`crate::BlendMode`], [`crate::ColorFilter`], [`crate::ImageFilter`], |
730 | /// and so on; or `None` |
731 | /// |
732 | /// example: <https://fiddle.skia.org/c/@Surface_draw> |
733 | pub fn draw( |
734 | &mut self, |
735 | canvas: &Canvas, |
736 | offset: impl Into<Point>, |
737 | sampling: impl Into<SamplingOptions>, |
738 | paint: Option<&Paint>, |
739 | ) { |
740 | let offset = offset.into(); |
741 | let sampling = sampling.into(); |
742 | unsafe { |
743 | self.native_mut().draw( |
744 | canvas.native_mut(), |
745 | offset.x, |
746 | offset.y, |
747 | sampling.native(), |
748 | paint.native_ptr_or_null(), |
749 | ) |
750 | } |
751 | } |
752 | |
753 | pub fn peek_pixels(&mut self) -> Option<Pixmap> { |
754 | let mut pm = Pixmap::default(); |
755 | unsafe { self.native_mut().peekPixels(pm.native_mut()) }.if_true_some(pm) |
756 | } |
757 | |
758 | // TODO: why is self mut? |
759 | |
760 | /// Copies [`crate::Rect`] of pixels to dst. |
761 | /// |
762 | /// Source [`crate::Rect`] corners are (`src.x`, `src.y`) and [`Surface`] `(width(), height())`. |
763 | /// Destination [`crate::Rect`] corners are `(0, 0)` and `(dst.width(), dst.height())`. |
764 | /// Copies each readable pixel intersecting both rectangles, without scaling, |
765 | /// converting to `dst_color_type()` and `dst_alpha_type()` if required. |
766 | /// |
767 | /// Pixels are readable when [`Surface`] is raster, or backed by a Ganesh GPU backend. Graphite |
768 | /// has deprecated this API in favor of the equivalent asynchronous API on |
769 | /// `skgpu::graphite::Context` (with an optional explicit synchonization). |
770 | /// |
771 | /// The destination pixel storage must be allocated by the caller. |
772 | /// |
773 | /// Pixel values are converted only if [`crate::ColorType`] and [`crate::AlphaType`] |
774 | /// do not match. Only pixels within both source and destination rectangles |
775 | /// are copied. dst contents outside [`crate::Rect`] intersection are unchanged. |
776 | /// |
777 | /// Pass negative values for `src.x` or `src.y` to offset pixels across or down destination. |
778 | /// |
779 | /// Does not copy, and returns `false` if: |
780 | /// - Source and destination rectangles do not intersect. |
781 | /// - [`Pixmap`] pixels could not be allocated. |
782 | /// - `dst.row_bytes()` is too small to contain one row of pixels. |
783 | /// |
784 | /// * `dst` - storage for pixels copied from [`Surface`] |
785 | /// * `src_x` - offset into readable pixels on x-axis; may be negative |
786 | /// * `src_y` - offset into readable pixels on y-axis; may be negative |
787 | /// |
788 | /// Returns: `true` if pixels were copied |
789 | /// |
790 | /// example: <https://fiddle.skia.org/c/@Surface_readPixels> |
791 | pub fn read_pixels_to_pixmap(&mut self, dst: &Pixmap, src: impl Into<IPoint>) -> bool { |
792 | let src = src.into(); |
793 | unsafe { self.native_mut().readPixels(dst.native(), src.x, src.y) } |
794 | } |
795 | |
796 | /// Copies [`crate::Rect`] of pixels from [`Canvas`] into `dst_pixels`. |
797 | /// |
798 | /// Source [`crate::Rect`] corners are (`src.x`, `src.y`) and [`Surface`] (width(), height()). |
799 | /// Destination [`crate::Rect`] corners are (0, 0) and (`dst_info`.width(), `dst_info`.height()). |
800 | /// Copies each readable pixel intersecting both rectangles, without scaling, |
801 | /// converting to `dst_info_color_type()` and `dst_info_alpha_type()` if required. |
802 | /// |
803 | /// Pixels are readable when [`Surface`] is raster, or backed by a Ganesh GPU backend. Graphite |
804 | /// has deprecated this API in favor of the equivalent asynchronous API on |
805 | /// `skgpu::graphite::Context` (with an optional explicit synchonization). |
806 | /// |
807 | /// The destination pixel storage must be allocated by the caller. |
808 | /// |
809 | /// Pixel values are converted only if [`crate::ColorType`] and [`crate::AlphaType`] |
810 | /// do not match. Only pixels within both source and destination rectangles |
811 | /// are copied. `dst_pixels` contents outside [`crate::Rect`] intersection are unchanged. |
812 | /// |
813 | /// Pass negative values for `src.x` or `src.y` to offset pixels across or down destination. |
814 | /// |
815 | /// Does not copy, and returns `false` if: |
816 | /// - Source and destination rectangles do not intersect. |
817 | /// - [`Surface`] pixels could not be converted to `dst_info.color_type()` or `dst_info.alpha_type()`. |
818 | /// - `dst_row_bytes` is too small to contain one row of pixels. |
819 | /// |
820 | /// * `dst_info` - width, height, [`crate::ColorType`], and [`crate::AlphaType`] of `dst_pixels` |
821 | /// * `dst_pixels` - storage for pixels; `dst_info.height()` times `dst_row_bytes`, or larger |
822 | /// * `dst_row_bytes` - size of one destination row; `dst_info.width()` times pixel size, or larger |
823 | /// * `src.x` - offset into readable pixels on x-axis; may be negative |
824 | /// * `src.y` - offset into readable pixels on y-axis; may be negative |
825 | /// |
826 | /// Returns: `true` if pixels were copied |
827 | pub fn read_pixels( |
828 | &mut self, |
829 | dst_info: &ImageInfo, |
830 | dst_pixels: &mut [u8], |
831 | dst_row_bytes: usize, |
832 | src: impl Into<IPoint>, |
833 | ) -> bool { |
834 | if !dst_info.valid_pixels(dst_row_bytes, dst_pixels) { |
835 | return false; |
836 | } |
837 | let src = src.into(); |
838 | unsafe { |
839 | self.native_mut().readPixels1( |
840 | dst_info.native(), |
841 | dst_pixels.as_mut_ptr() as _, |
842 | dst_row_bytes, |
843 | src.x, |
844 | src.y, |
845 | ) |
846 | } |
847 | } |
848 | |
849 | // TODO: why is self mut? |
850 | // TODO: why is Bitmap immutable? |
851 | |
852 | /// Copies [`crate::Rect`] of pixels from [`Surface`] into bitmap. |
853 | /// |
854 | /// Source [`crate::Rect`] corners are (`src.x`, `src.y`) and [`Surface`] (width(), height()). |
855 | /// Destination [`crate::Rect`] corners are `(0, 0)` and `(bitmap.width(), bitmap.height())`. |
856 | /// Copies each readable pixel intersecting both rectangles, without scaling, |
857 | /// converting to `bitmap.color_type()` and `bitmap.alpha_type()` if required. |
858 | /// |
859 | /// Pixels are readable when [`Surface`] is raster, or backed by a Ganesh GPU backend. Graphite |
860 | /// has deprecated this API in favor of the equivalent asynchronous API on |
861 | /// `skgpu::graphite::Context` (with an optional explicit synchonization). |
862 | /// |
863 | /// The destination pixel storage must be allocated by the caller. |
864 | /// |
865 | /// Pixel values are converted only if [`crate::ColorType`] and [`crate::AlphaType`] |
866 | /// do not match. Only pixels within both source and destination rectangles |
867 | /// are copied. dst contents outside [`crate::Rect`] intersection are unchanged. |
868 | /// |
869 | /// Pass negative values for `src.x` or `src.y` to offset pixels across or down destination. |
870 | /// |
871 | /// Does not copy, and returns `false` if: |
872 | /// - Source and destination rectangles do not intersect. |
873 | /// - [`Surface`] pixels could not be converted to `dst.color_type()` or `dst.alpha_type()`. |
874 | /// - dst pixels could not be allocated. |
875 | /// - `dst.row_bytes()` is too small to contain one row of pixels. |
876 | /// |
877 | /// * `dst` - storage for pixels copied from [`Surface`] |
878 | /// * `src.x` - offset into readable pixels on x-axis; may be negative |
879 | /// * `src.y` - offset into readable pixels on y-axis; may be negative |
880 | /// |
881 | /// Returns: `true` if pixels were copied |
882 | /// |
883 | /// example: <https://fiddle.skia.org/c/@Surface_readPixels_3> |
884 | pub fn read_pixels_to_bitmap(&mut self, bitmap: &Bitmap, src: impl Into<IPoint>) -> bool { |
885 | let src = src.into(); |
886 | unsafe { self.native_mut().readPixels2(bitmap.native(), src.x, src.y) } |
887 | } |
888 | |
889 | // TODO: AsyncReadResult, RescaleGamma (m79, m86) |
890 | // TODO: wrap asyncRescaleAndReadPixels (m76, m79, m89) |
891 | // TODO: wrap asyncRescaleAndReadPixelsYUV420 (m77, m79, m89) |
892 | // TODO: wrap asyncRescaleAndReadPixelsYUVA420 (m117) |
893 | |
894 | /// Copies [`crate::Rect`] of pixels from the src [`Pixmap`] to the [`Surface`]. |
895 | /// |
896 | /// Source [`crate::Rect`] corners are `(0, 0)` and `(src.width(), src.height())`. |
897 | /// Destination [`crate::Rect`] corners are `(`dst.x`, `dst.y`)` and |
898 | /// (`dst.x` + Surface width(), `dst.y` + Surface height()). |
899 | /// |
900 | /// Copies each readable pixel intersecting both rectangles, without scaling, |
901 | /// converting to [`Surface`] `color_type()` and [`Surface`] `alpha_type()` if required. |
902 | /// |
903 | /// * `src` - storage for pixels to copy to [`Surface`] |
904 | /// * `dst.x` - x-axis position relative to [`Surface`] to begin copy; may be negative |
905 | /// * `dst.y` - y-axis position relative to [`Surface`] to begin copy; may be negative |
906 | /// |
907 | /// example: <https://fiddle.skia.org/c/@Surface_writePixels> |
908 | pub fn write_pixels_from_pixmap(&mut self, src: &Pixmap, dst: impl Into<IPoint>) { |
909 | let dst = dst.into(); |
910 | unsafe { self.native_mut().writePixels(src.native(), dst.x, dst.y) } |
911 | } |
912 | |
913 | /// Copies [`crate::Rect`] of pixels from the src [`Bitmap`] to the [`Surface`]. |
914 | /// |
915 | /// Source [`crate::Rect`] corners are `(0, 0)` and `(src.width(), src.height())`. |
916 | /// Destination [`crate::Rect`] corners are `(`dst.x`, `dst.y`)` and |
917 | /// `(`dst.x` + Surface width(), `dst.y` + Surface height())`. |
918 | /// |
919 | /// Copies each readable pixel intersecting both rectangles, without scaling, |
920 | /// converting to [`Surface`] `color_type()` and [`Surface`] `alpha_type()` if required. |
921 | /// |
922 | /// * `src` - storage for pixels to copy to [`Surface`] |
923 | /// * `dst.x` - x-axis position relative to [`Surface`] to begin copy; may be negative |
924 | /// * `dst.y` - y-axis position relative to [`Surface`] to begin copy; may be negative |
925 | /// |
926 | /// example: <https://fiddle.skia.org/c/@Surface_writePixels_2> |
927 | pub fn write_pixels_from_bitmap(&mut self, bitmap: &Bitmap, dst: impl Into<IPoint>) { |
928 | let dst = dst.into(); |
929 | unsafe { |
930 | self.native_mut() |
931 | .writePixels1(bitmap.native(), dst.x, dst.y) |
932 | } |
933 | } |
934 | |
935 | /// Returns [`SurfaceProps`] for surface. |
936 | /// |
937 | /// Returns: LCD striping orientation and setting for device independent fonts |
938 | pub fn props(&self) -> &SurfaceProps { |
939 | SurfaceProps::from_native_ref(unsafe { &*sb::C_SkSurface_props(self.native()) }) |
940 | } |
941 | |
942 | // TODO: wait() |
943 | } |
944 | |
945 | pub use surfaces::BackendSurfaceAccess; |
946 | |
947 | impl Surface { |
948 | /// If a surface is GPU texture backed, is being drawn with MSAA, and there is a resolve |
949 | /// texture, this call will insert a resolve command into the stream of gpu commands. In order |
950 | /// for the resolve to actually have an effect, the work still needs to be flushed and submitted |
951 | /// to the GPU after recording the resolve command. If a resolve is not supported or the |
952 | /// [`Surface`] has no dirty work to resolve, then this call is a no-op. |
953 | /// |
954 | /// This call is most useful when the [`Surface`] is created by wrapping a single sampled gpu |
955 | /// texture, but asking Skia to render with MSAA. If the client wants to use the wrapped texture |
956 | /// outside of Skia, the only way to trigger a resolve is either to call this command or use |
957 | /// [`Self::flush()`]. |
958 | #[cfg (feature = "gpu" )] |
959 | #[deprecated (since = "0.65.0" , note = "Use gpu::surfaces::resolve_msaa" )] |
960 | pub fn resolve_msaa(&mut self) { |
961 | gpu::surfaces::resolve_msaa(self) |
962 | } |
963 | } |
964 | |
965 | #[cfg (test)] |
966 | mod tests { |
967 | use super::*; |
968 | |
969 | #[test ] |
970 | fn create() { |
971 | assert!(surfaces::raster_n32_premul((0, 0)).is_none()); |
972 | let surface = surfaces::raster_n32_premul((1, 1)).unwrap(); |
973 | assert_eq!(1, surface.native().ref_counted_base()._ref_cnt()) |
974 | } |
975 | |
976 | #[test ] |
977 | fn test_raster_direct() { |
978 | let image_info = ImageInfo::new( |
979 | (20, 20), |
980 | crate::ColorType::RGBA8888, |
981 | crate::AlphaType::Unpremul, |
982 | None, |
983 | ); |
984 | let min_row_bytes = image_info.min_row_bytes(); |
985 | let mut pixels = vec![0u8; image_info.compute_byte_size(min_row_bytes)]; |
986 | let mut surface = surfaces::wrap_pixels( |
987 | &image_info, |
988 | pixels.as_mut_slice(), |
989 | Some(min_row_bytes), |
990 | None, |
991 | ) |
992 | .unwrap(); |
993 | let paint = Paint::default(); |
994 | surface.canvas().draw_circle((10, 10), 10.0, &paint); |
995 | } |
996 | |
997 | #[test ] |
998 | fn test_drawing_owned_as_exclusive_ref_ergonomics() { |
999 | let mut surface = surfaces::raster_n32_premul((16, 16)).unwrap(); |
1000 | |
1001 | // option1: |
1002 | // - An &canvas can be drawn to. |
1003 | { |
1004 | let canvas = Canvas::new(ISize::new(16, 16), None).unwrap(); |
1005 | surface.draw(&canvas, (5.0, 5.0), SamplingOptions::default(), None); |
1006 | surface.draw(&canvas, (10.0, 10.0), SamplingOptions::default(), None); |
1007 | } |
1008 | |
1009 | // option2: |
1010 | // - A canvas from another surface can be drawn to. |
1011 | { |
1012 | let mut surface2 = surfaces::raster_n32_premul((16, 16)).unwrap(); |
1013 | let canvas = surface2.canvas(); |
1014 | surface.draw(canvas, (5.0, 5.0), SamplingOptions::default(), None); |
1015 | surface.draw(canvas, (10.0, 10.0), SamplingOptions::default(), None); |
1016 | } |
1017 | } |
1018 | } |
1019 | |