1 | use skia_bindings as sb; |
2 | |
3 | use crate::{ |
4 | gpu::{ |
5 | BackendTexture, Budgeted, DirectContext, Mipmapped, Protected, RecordingContext, |
6 | SurfaceOrigin, YUVABackendTextures, |
7 | }, |
8 | prelude::*, |
9 | AlphaType, ColorSpace, ColorType, Data, IRect, ISize, Image, Pixmap, TextureCompressionType, |
10 | }; |
11 | #[allow (unused)] // docs only |
12 | use crate::{ImageInfo, Surface, YUVAInfo, YUVAPixmaps}; |
13 | |
14 | /// Creates GPU-backed [`Image`] from `backend_texture` associated with context. |
15 | /// Skia will assume ownership of the resource and will release it when no longer needed. |
16 | /// A non-null [`Image`] is returned if format of `backend_texture` is recognized and supported. |
17 | /// Recognized formats vary by GPU backend. |
18 | /// * `context` - GPU context |
19 | /// * `backend_texture` - texture residing on GPU |
20 | /// * `texture_origin` - origin of `backend_texture` |
21 | /// * `color_type` - color type of the resulting image |
22 | /// * `alpha_type` - alpha type of the resulting image |
23 | /// * `color_space` - range of colors; may be `None` |
24 | /// Returns: created [`Image`], or `None` |
25 | pub fn adopt_texture_from( |
26 | context: &mut RecordingContext, |
27 | backend_texture: &BackendTexture, |
28 | texture_origin: SurfaceOrigin, |
29 | color_type: ColorType, |
30 | alpha_type: impl Into<Option<AlphaType>>, |
31 | color_space: impl Into<Option<ColorSpace>>, |
32 | ) -> Option<Image> { |
33 | Image::from_ptr(unsafe { |
34 | sb::C_SkImages_AdoptTextureFrom( |
35 | context:context.native_mut(), |
36 | backendTexture:backend_texture.native(), |
37 | texture_origin, |
38 | colorType:color_type.into_native(), |
39 | alphaType:alpha_type.into().unwrap_or(AlphaType::Premul), |
40 | colorSpace:color_space.into().into_ptr_or_null(), |
41 | ) |
42 | }) |
43 | } |
44 | |
45 | /// Creates GPU-backed [`Image`] from the provided GPU texture associated with context. |
46 | /// GPU texture must stay valid and unchanged until `texture_release_proc` is called by Skia. |
47 | /// Skia will call `texture_release_proc` with the passed-in `release_context` when [`Image`] |
48 | /// is deleted or no longer refers to the texture. |
49 | /// A non-null [`Image`] is returned if format of `backend_texture` is recognized and supported. |
50 | /// Recognized formats vary by GPU backend. |
51 | /// Note: When using a DDL recording context, `texture_release_proc` will be called on the |
52 | /// GPU thread after the DDL is played back on the direct context. |
53 | /// * `context` - GPU context |
54 | /// * `backend_texture` - texture residing on GPU |
55 | /// * `color_space` - This describes the color space of this image's contents, as |
56 | /// seen after sampling. In general, if the format of the backend |
57 | /// texture is SRGB, some linear `color_space` should be supplied |
58 | /// (e.g., [`ColorSpace::new_srgb_linear()`]). If the format of the |
59 | /// backend texture is linear, then the `color_space` should include |
60 | /// a description of the transfer function as |
61 | /// well (e.g., [`ColorSpace::new_srgb()`]). |
62 | /// * `texture_release_proc` - function called when texture can be released |
63 | /// * `release_context` - state passed to `texture_release_proc` |
64 | /// Returns: created [`Image`], or `None` |
65 | // TODO: add variant with TextureReleaseProc |
66 | pub fn borrow_texture_from( |
67 | context: &mut RecordingContext, |
68 | backend_texture: &BackendTexture, |
69 | origin: SurfaceOrigin, |
70 | color_type: ColorType, |
71 | alpha_type: AlphaType, |
72 | color_space: impl Into<Option<ColorSpace>>, |
73 | ) -> Option<Image> { |
74 | Image::from_ptr(unsafe { |
75 | sb::C_SkImages_BorrowTextureFrom( |
76 | context:context.native_mut(), |
77 | backendTexture:backend_texture.native(), |
78 | origin, |
79 | colorType:color_type.into_native(), |
80 | alphaType:alpha_type, |
81 | colorSpace:color_space.into().into_ptr_or_null(), |
82 | ) |
83 | }) |
84 | } |
85 | |
86 | /// Creates a GPU-backed [`Image`] from pixmap. It is uploaded to GPU backend using context. |
87 | /// Created [`Image`] is available to other GPU contexts, and is available across thread |
88 | /// boundaries. All contexts must be in the same GPU share group, or otherwise |
89 | /// share resources. |
90 | /// When [`Image`] is no longer referenced, context releases texture memory |
91 | /// asynchronously. |
92 | /// [`ColorSpace`] of [`Image`] is determined by `pixmap.color_space()`. |
93 | /// [`Image`] is returned referring to GPU backend if context is not `None`, |
94 | /// format of data is recognized and supported, and if context supports moving |
95 | /// resources between contexts. Otherwise, pixmap pixel data is copied and [`Image`] |
96 | /// as returned in raster format if possible; `None` may be returned. |
97 | /// Recognized GPU formats vary by platform and GPU backend. |
98 | /// * `context` - GPU context |
99 | /// * `pixmap` - [`ImageInfo`], pixel address, and row bytes |
100 | /// * `build_mips` - create [`Image`] as mip map if `true` |
101 | /// * `limit_to_max_texture_size` - downscale image to GPU maximum texture size, if necessary |
102 | /// Returns: created [`Image`], or `None` |
103 | pub fn cross_context_texture_from_pixmap( |
104 | context: &mut DirectContext, |
105 | pixmap: &Pixmap, |
106 | build_mips: bool, |
107 | limit_to_max_texture_size: impl Into<Option<bool>>, |
108 | ) -> Option<Image> { |
109 | Image::from_ptr(unsafe { |
110 | sb::C_SkImages_CrossContextTextureFromPixmap( |
111 | context:context.native_mut(), |
112 | pixmap:pixmap.native(), |
113 | buildMips:build_mips, |
114 | limitToMaxTextureSize:limit_to_max_texture_size.into().unwrap_or(false), |
115 | ) |
116 | }) |
117 | } |
118 | |
119 | // TODO: TextureFromCompressedTexture |
120 | |
121 | /// Creates a GPU-backed [`Image`] from compressed data. |
122 | /// This method will return an [`Image`] representing the compressed data. |
123 | /// If the GPU doesn't support the specified compression method, the data |
124 | /// will be decompressed and then wrapped in a GPU-backed image. |
125 | /// Note: one can query the supported compression formats via |
126 | /// [`RecordingContext::compressed_backend_format`]. |
127 | /// * `context` - GPU context |
128 | /// * `data` - compressed data to store in [`Image`] |
129 | /// * `width` - width of full [`Image`] |
130 | /// * `height` - height of full [`Image`] |
131 | /// * `ty` - type of compression used |
132 | /// * `mipmapped` - does 'data' contain data for all the mipmap levels? |
133 | /// * `is_protected` - do the contents of 'data' require DRM protection (on Vulkan)? |
134 | /// Returns: created [`Image`], or `None` |
135 | pub fn texture_from_compressed_texture_data( |
136 | context: &mut DirectContext, |
137 | data: Data, |
138 | dimensions: impl Into<ISize>, |
139 | ty: TextureCompressionType, |
140 | mipmapped: impl Into<Option<Mipmapped>>, |
141 | is_protected: impl Into<Option<Protected>>, |
142 | ) -> Option<Image> { |
143 | let dimensions = dimensions.into(); |
144 | let mipmapped: skgpu_Mipmapped = mipmapped.into().unwrap_or(Mipmapped::No); |
145 | let is_protected: skgpu_Protected = is_protected.into().unwrap_or(Protected::No); |
146 | |
147 | Image::from_ptr(unsafe { |
148 | sb::C_SkImages_TextureFromCompressedTextureData( |
149 | context:context.native_mut(), |
150 | data:data.into_ptr(), |
151 | dimensions.width, |
152 | dimensions.height, |
153 | type_:ty, |
154 | mipmapped, |
155 | prot:is_protected, |
156 | ) |
157 | }) |
158 | } |
159 | |
160 | /// Returns [`Image`] backed by GPU texture associated with context. Returned [`Image`] is |
161 | /// compatible with [`Surface`] created with `dst_color_space`. The returned [`Image`] respects |
162 | /// mipmapped setting; if mipmapped equals [`Mipmapped::Yes`], the backing texture |
163 | /// allocates mip map levels. |
164 | /// The mipmapped parameter is effectively treated as `No` if MIP maps are not supported by the |
165 | /// GPU. |
166 | /// Returns original [`Image`] if the image is already texture-backed, the context matches, and |
167 | /// mipmapped is compatible with the backing GPU texture. skgpu::Budgeted is ignored in this |
168 | /// case. |
169 | /// Returns `None` if context is `None`, or if [`Image`] was created with another |
170 | /// [`DirectContext`]. |
171 | /// * `direct_context` - the [`DirectContext`] in play, if it exists |
172 | /// * `image` - a non-null pointer to an [`Image`]. |
173 | /// * `mipmapped` - Whether created [`Image`] texture must allocate mip map levels. |
174 | /// Defaults to `No`. |
175 | /// * `budgeted` - Whether to count a newly created texture for the returned image |
176 | /// counts against the context's budget. Defaults to `Yes`. |
177 | /// Returns: created [`Image`], or `None` |
178 | pub fn texture_from_image( |
179 | direct_context: &mut DirectContext, |
180 | image: &Image, |
181 | mipmapped: Mipmapped, |
182 | budgeted: Budgeted, |
183 | ) -> Option<Image> { |
184 | Image::from_ptr(unsafe { |
185 | sb::C_SkImages_TextureFromImage( |
186 | context:direct_context.native_mut(), |
187 | self_:image.native(), |
188 | mipmapped, |
189 | budgeted:budgeted.into_native(), |
190 | ) |
191 | }) |
192 | } |
193 | |
194 | /// Creates a GPU-backed [`Image`] from [`YUVAPixmaps`]. |
195 | /// The image will remain planar with each plane converted to a texture using the passed |
196 | /// [`RecordingContext`]. |
197 | /// [`YUVAPixmaps`] has a [`YUVAInfo`] which specifies the transformation from YUV to RGB. |
198 | /// The [`ColorSpace`] of the resulting RGB values is specified by `image_color_space`. This will |
199 | /// be the [`ColorSpace`] reported by the image and when drawn the RGB values will be converted |
200 | /// from this space into the destination space (if the destination is tagged). |
201 | /// Currently, this is only supported using the GPU backend and will fail if context is `None`. |
202 | /// [`YUVAPixmaps`] does not need to remain valid after this returns. |
203 | /// * `context` - GPU context |
204 | /// * `pixmaps` - The planes as pixmaps with supported [`YUVAInfo`] that |
205 | /// specifies conversion to RGB. |
206 | /// * `build_mips` - create internal YUVA textures as mip map if `k_yes`. This is |
207 | /// silently ignored if the context does not support mip maps. |
208 | /// * `limit_to_max_texture_size` - downscale image to GPU maximum texture size, if necessary |
209 | /// * `image_color_space` - range of colors of the resulting image; may be `None` |
210 | /// Returns: created [`Image`], or `None` |
211 | pub fn texture_from_yuva_pixmaps( |
212 | context: &mut RecordingContext, |
213 | yuva_pixmaps: &crate::YUVAPixmaps, |
214 | build_mips: impl Into<Option<Mipmapped>>, |
215 | limit_to_max_texture_size: impl Into<Option<bool>>, |
216 | image_color_space: impl Into<Option<ColorSpace>>, |
217 | ) -> Option<Image> { |
218 | Image::from_ptr(unsafe { |
219 | sb::C_SkImages_TextureFromYUVAPixmaps( |
220 | context:context.native_mut(), |
221 | pixmaps:yuva_pixmaps.native(), |
222 | buildMips:build_mips.into().unwrap_or(Mipmapped::No), |
223 | limitToMaxTextureSize:limit_to_max_texture_size.into().unwrap_or(false), |
224 | imageColorSpace:image_color_space.into().into_ptr_or_null(), |
225 | ) |
226 | }) |
227 | } |
228 | |
229 | /// Creates a GPU-backed [`Image`] from `YUV[A]` planar textures. This requires that the textures |
230 | /// stay valid for the lifetime of the image. The ReleaseContext can be used to know when it is |
231 | /// safe to either delete or overwrite the textures. If ReleaseProc is provided it is also called |
232 | /// before return on failure. |
233 | /// * `context` - GPU context |
234 | /// * `yuva_textures` - A set of textures containing YUVA data and a description of the |
235 | /// data and transformation to RGBA. |
236 | /// * `image_color_space` - range of colors of the resulting image after conversion to RGB; |
237 | /// may be `None` |
238 | /// * `texture_release_proc` - called when the backend textures can be released |
239 | /// * `release_context` - state passed to `texture_release_proc` |
240 | /// Returns: created [`Image`], or `None` |
241 | pub fn texture_from_yuva_textures( |
242 | context: &mut RecordingContext, |
243 | yuva_textures: &YUVABackendTextures, |
244 | image_color_space: impl Into<Option<ColorSpace>>, |
245 | ) -> Option<Image> { |
246 | Image::from_ptr(unsafe { |
247 | sb::C_SkImages_TextureFromYUVATextures( |
248 | context:context.native_mut(), |
249 | yuvaTextures:yuva_textures.native(), |
250 | imageColorSpace:image_color_space.into().into_ptr_or_null(), |
251 | ) |
252 | }) |
253 | } |
254 | |
255 | // TODO: PromiseTextureFrom |
256 | // TODO: PromiseTextureFromYUVA |
257 | |
258 | /// Retrieves the existing backend texture. If [`Image`] is not a Ganesh-backend texture image |
259 | /// or otherwise does not have such a texture, `false` is returned. Otherwise, returned will |
260 | /// be set to the image's texture. |
261 | /// |
262 | /// If `flush_pending_gr_context_io` is `true`, completes deferred I/O operations. |
263 | /// If origin in not `None`, copies location of content drawn into [`Image`]. |
264 | /// * `out_texture` - Will be set to the underlying texture of the image if non-null. |
265 | /// * `flush_pending_gr_context_io` - flag to flush outstanding requests |
266 | /// * `origin` - Will be set to the origin orientation of the image if non-null. |
267 | /// Returns: `None` if a Ganesh backend texture cannot be retrieved. |
268 | pub fn get_backend_texture_from_image( |
269 | image: &Image, |
270 | flush_pending_gr_context_io: bool, |
271 | ) -> Option<(BackendTexture, SurfaceOrigin)> { |
272 | let mut origin: GrSurfaceOrigin = SurfaceOrigin::TopLeft; |
273 | unsafeOption> { |
274 | let backend_texture: *mut GrBackendTexture = sb::C_SkImages_GetBackendTextureFromImage( |
275 | self_:image.native(), |
276 | flushPendingGrContextIO:flush_pending_gr_context_io, |
277 | &mut origin, |
278 | ); |
279 | BackendTexture::from_native_if_valid(backend_texture) |
280 | } |
281 | .map(|texture: RefHandle| (texture, origin)) |
282 | } |
283 | |
284 | // TODO: MakeBackendTextureFromImage |
285 | // TODO: GetBackendTextureFromImage (legacy name) |
286 | |
287 | /// Returns subset of this image as a texture-backed image. |
288 | /// |
289 | /// Returns `None` if any of the following are true: |
290 | /// - Subset is empty |
291 | /// - Subset is not contained inside the image's bounds |
292 | /// - Pixels in the source image could not be read or copied |
293 | /// - The source image is texture-backed and context does not match the source image's context. |
294 | /// |
295 | /// * `context` - the [`DirectContext`] to which the subset should be uploaded. |
296 | /// * `subset` - bounds of returned [`Image`] |
297 | /// Returns: the subsetted image, uploaded as a texture, or `None` |
298 | pub fn subset_texture_from( |
299 | context: &mut DirectContext, |
300 | image: &Image, |
301 | subset: impl AsRef<IRect>, |
302 | ) -> Option<Image> { |
303 | Image::from_ptr(unsafe { |
304 | sb::C_SkImages_SubsetTextureFrom( |
305 | context:context.native_mut(), |
306 | image:image.native(), |
307 | subset:subset.as_ref().native(), |
308 | ) |
309 | }) |
310 | } |
311 | |