1use skia_bindings as sb;
2
3use 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
12use 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`
25pub 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
66pub 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`
103pub 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`
135pub 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`
178pub 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`
211pub 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`
241pub 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.
268pub 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`
298pub 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