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 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()`]. |
136 | pub use skia_bindings::SkSurface_ContentChangeMode as ContentChangeMode; |
137 | variant_name!(ContentChangeMode::Retain); |
138 | |
139 | #[cfg (feature = "gpu" )] |
140 | pub use skia_bindings::SkSurface_BackendHandleAccess as BackendHandleAccess; |
141 | #[cfg (feature = "gpu" )] |
142 | variant_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. |
150 | pub type Surface = RCHandle<SkSurface>; |
151 | require_type_equality!(sb::SkSurface_INHERITED, sb::SkRefCnt); |
152 | |
153 | impl NativeRefCountedBase for SkSurface { |
154 | type Base = SkRefCntBase; |
155 | } |
156 | |
157 | impl 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 | |
168 | impl 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" )] |
251 | impl 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 | |
458 | impl 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" ))] |
516 | impl 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" )] |
529 | impl 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 | |
617 | impl 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 | |
907 | pub use surfaces::BackendSurfaceAccess; |
908 | |
909 | impl 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)] |
928 | mod 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 | |