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