xref: /aosp_15_r20/external/skia/src/gpu/graphite/ImageFactories.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2023 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkBitmap.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColorSpace.h"
11 #include "include/core/SkImage.h"
12 #include "include/core/SkSurface.h"
13 #include "include/core/SkYUVAInfo.h"
14 #include "include/core/SkYUVAPixmaps.h"
15 #include "include/gpu/GpuTypes.h"
16 #include "include/gpu/graphite/BackendTexture.h"
17 #include "include/gpu/graphite/Image.h"
18 #include "include/gpu/graphite/Recorder.h"
19 #include "include/gpu/graphite/Surface.h"
20 #include "include/gpu/graphite/YUVABackendTextures.h"
21 #include "include/private/base/SkMutex.h"
22 #include "src/core/SkImageFilterTypes.h"
23 #include "src/core/SkImageFilter_Base.h"
24 #include "src/gpu/RefCntedCallback.h"
25 #include "src/gpu/graphite/Caps.h"
26 #include "src/gpu/graphite/Image_Base_Graphite.h"
27 #include "src/gpu/graphite/Image_Graphite.h"
28 #include "src/gpu/graphite/Image_YUVA_Graphite.h"
29 #include "src/gpu/graphite/Log.h"
30 #include "src/gpu/graphite/RecorderPriv.h"
31 #include "src/gpu/graphite/ResourceProvider.h"
32 #include "src/gpu/graphite/Surface_Graphite.h"
33 #include "src/gpu/graphite/Texture.h"
34 #include "src/gpu/graphite/TextureProxy.h"
35 #include "src/gpu/graphite/TextureProxyView.h"
36 #include "src/gpu/graphite/TextureUtils.h"
37 #include "src/image/SkImage_Base.h"
38 #include "src/image/SkImage_Lazy.h"
39 #include "src/image/SkImage_Picture.h"
40 #include "src/image/SkImage_Raster.h"
41 
42 #include <string>
43 
44 namespace SkImages {
45 
46 using namespace skgpu::graphite;
47 
validate_backend_texture(const skgpu::graphite::Caps * caps,const skgpu::graphite::BackendTexture & texture,const SkColorInfo & info)48 static bool validate_backend_texture(const skgpu::graphite::Caps* caps,
49                                      const skgpu::graphite::BackendTexture& texture,
50                                      const SkColorInfo& info) {
51     if (!texture.isValid() || texture.dimensions().width() <= 0 ||
52         texture.dimensions().height() <= 0) {
53         return false;
54     }
55 
56     if (!SkColorInfoIsValid(info)) {
57         return false;
58     }
59 
60     if (!caps->isTexturable(texture.info())) {
61         return false;
62     }
63 
64     return caps->areColorTypeAndTextureInfoCompatible(info.colorType(), texture.info());
65 }
66 
WrapTexture(Recorder * recorder,const BackendTexture & backendTex,SkColorType ct,SkAlphaType at,sk_sp<SkColorSpace> cs,skgpu::Origin origin,GenerateMipmapsFromBase genMipmaps,TextureReleaseProc releaseP,ReleaseContext releaseC,std::string_view label)67 sk_sp<SkImage> WrapTexture(Recorder* recorder,
68                            const BackendTexture& backendTex,
69                            SkColorType ct,
70                            SkAlphaType at,
71                            sk_sp<SkColorSpace> cs,
72                            skgpu::Origin origin,
73                            GenerateMipmapsFromBase genMipmaps,
74                            TextureReleaseProc releaseP,
75                            ReleaseContext releaseC,
76                            std::string_view label) {
77     auto releaseHelper = skgpu::RefCntedCallback::Make(releaseP, releaseC);
78 
79     if (!recorder) {
80         return nullptr;
81     }
82 
83     const Caps* caps = recorder->priv().caps();
84 
85     SkColorInfo info(ct, at, std::move(cs));
86 
87     if (!validate_backend_texture(caps, backendTex, info)) {
88         return nullptr;
89     }
90 
91     if (label.empty()) {
92         label = "WrappedImage";
93     }
94 
95     sk_sp<Texture> texture =
96             recorder->priv().resourceProvider()->createWrappedTexture(backendTex, std::move(label));
97     if (!texture) {
98         SKGPU_LOG_W("Texture creation failed");
99         return nullptr;
100     }
101     texture->setReleaseCallback(std::move(releaseHelper));
102 
103     sk_sp<TextureProxy> proxy = TextureProxy::Wrap(std::move(texture));
104     SkASSERT(proxy);
105 
106     skgpu::Swizzle swizzle = caps->getReadSwizzle(ct, backendTex.info());
107     TextureProxyView view(std::move(proxy), swizzle, origin);
108 
109     if (genMipmaps == GenerateMipmapsFromBase::kYes) {
110         if (view.proxy()->mipmapped() == skgpu::Mipmapped::kNo) {
111             SKGPU_LOG_W("Failed SkImage:::WrapTexture because asked to generate mipmaps for "
112                         "nonmipmapped texture");
113             return nullptr;
114         }
115         if (!GenerateMipmaps(recorder, view.refProxy(), info)) {
116             SKGPU_LOG_W("Failed SkImage::WrapTexture. Could not generate mipmaps.");
117             return nullptr;
118         }
119     }
120 
121     return sk_make_sp<skgpu::graphite::Image>(view, info);
122 }
123 
WrapTexture(Recorder * recorder,const BackendTexture & backendTex,SkColorType ct,SkAlphaType at,sk_sp<SkColorSpace> cs,skgpu::Origin origin,TextureReleaseProc releaseP,ReleaseContext releaseC,std::string_view label)124 sk_sp<SkImage> WrapTexture(Recorder* recorder,
125                            const BackendTexture& backendTex,
126                            SkColorType ct,
127                            SkAlphaType at,
128                            sk_sp<SkColorSpace> cs,
129                            skgpu::Origin origin,
130                            TextureReleaseProc releaseP,
131                            ReleaseContext releaseC,
132                            std::string_view label) {
133     return WrapTexture(recorder,
134                        backendTex,
135                        ct,
136                        at,
137                        std::move(cs),
138                        origin,
139                        SkImages::GenerateMipmapsFromBase::kNo,
140                        releaseP,
141                        releaseC,
142                        std::move(label));
143 }
144 
WrapTexture(Recorder * recorder,const BackendTexture & backendTex,SkColorType ct,SkAlphaType at,sk_sp<SkColorSpace> cs,TextureReleaseProc releaseP,ReleaseContext releaseC,std::string_view label)145 sk_sp<SkImage> WrapTexture(Recorder* recorder,
146                            const BackendTexture& backendTex,
147                            SkColorType ct,
148                            SkAlphaType at,
149                            sk_sp<SkColorSpace> cs,
150                            TextureReleaseProc releaseP,
151                            ReleaseContext releaseC,
152                            std::string_view label) {
153     return WrapTexture(recorder,
154                        backendTex,
155                        ct,
156                        at,
157                        std::move(cs),
158                        skgpu::Origin::kTopLeft,
159                        SkImages::GenerateMipmapsFromBase::kNo,
160                        releaseP,
161                        releaseC,
162                        std::move(label));
163 }
164 
PromiseTextureFrom(Recorder * recorder,SkISize dimensions,const TextureInfo & textureInfo,const SkColorInfo & colorInfo,skgpu::Origin origin,Volatile isVolatile,GraphitePromiseTextureFulfillProc fulfillProc,GraphitePromiseImageReleaseProc imageReleaseProc,GraphitePromiseTextureReleaseProc textureReleaseProc,GraphitePromiseImageContext imageContext,std::string_view label)165 sk_sp<SkImage> PromiseTextureFrom(Recorder* recorder,
166                                   SkISize dimensions,
167                                   const TextureInfo& textureInfo,
168                                   const SkColorInfo& colorInfo,
169                                   skgpu::Origin origin,
170                                   Volatile isVolatile,
171                                   GraphitePromiseTextureFulfillProc fulfillProc,
172                                   GraphitePromiseImageReleaseProc imageReleaseProc,
173                                   GraphitePromiseTextureReleaseProc textureReleaseProc,
174                                   GraphitePromiseImageContext imageContext,
175                                   std::string_view label) {
176     // Our contract is that we will always call the _image_ release proc even on failure.
177     // We use the helper to convey the imageContext, so we need to ensure Make doesn't fail.
178     imageReleaseProc = imageReleaseProc ? imageReleaseProc : [](void*) {};
179     auto releaseHelper = skgpu::RefCntedCallback::Make(imageReleaseProc, imageContext);
180 
181     if (!recorder) {
182         SKGPU_LOG_W("Null Recorder");
183         return nullptr;
184     }
185 
186     const Caps* caps = recorder->priv().caps();
187 
188     SkImageInfo info = SkImageInfo::Make(dimensions, colorInfo);
189     if (!SkImageInfoIsValid(info)) {
190         SKGPU_LOG_W("Invalid SkImageInfo");
191         return nullptr;
192     }
193 
194     if (!caps->areColorTypeAndTextureInfoCompatible(colorInfo.colorType(), textureInfo)) {
195         SKGPU_LOG_W("Incompatible SkColorType and TextureInfo");
196         return nullptr;
197     }
198 
199     // Non-YUVA promise images use the 'imageContext' for both the release proc and fulfill proc.
200     sk_sp<TextureProxy> proxy = MakePromiseImageLazyProxy(caps,
201                                                           dimensions,
202                                                           textureInfo,
203                                                           isVolatile,
204                                                           std::move(releaseHelper),
205                                                           fulfillProc,
206                                                           imageContext,
207                                                           textureReleaseProc,
208                                                           std::move(label));
209     if (!proxy) {
210         return nullptr;
211     }
212 
213     skgpu::Swizzle swizzle = caps->getReadSwizzle(colorInfo.colorType(), textureInfo);
214     TextureProxyView view(std::move(proxy), swizzle, origin);
215     return sk_make_sp<Image>(view, colorInfo);
216 }
217 
PromiseTextureFrom(Recorder * recorder,SkISize dimensions,const TextureInfo & textureInfo,const SkColorInfo & colorInfo,Volatile isVolatile,GraphitePromiseTextureFulfillProc fulfillProc,GraphitePromiseImageReleaseProc imageReleaseProc,GraphitePromiseTextureReleaseProc textureReleaseProc,GraphitePromiseImageContext imageContext)218 sk_sp<SkImage> PromiseTextureFrom(Recorder* recorder,
219                                   SkISize dimensions,
220                                   const TextureInfo& textureInfo,
221                                   const SkColorInfo& colorInfo,
222                                   Volatile isVolatile,
223                                   GraphitePromiseTextureFulfillProc fulfillProc,
224                                   GraphitePromiseImageReleaseProc imageReleaseProc,
225                                   GraphitePromiseTextureReleaseProc textureReleaseProc,
226                                   GraphitePromiseImageContext imageContext) {
227     return PromiseTextureFrom(recorder,
228                               dimensions,
229                               textureInfo,
230                               colorInfo,
231                               skgpu::Origin::kTopLeft,
232                               isVolatile,
233                               fulfillProc,
234                               imageReleaseProc,
235                               textureReleaseProc,
236                               imageContext);
237 }
238 
PromiseTextureFromYUVA(skgpu::graphite::Recorder * recorder,const YUVABackendTextureInfo & backendTextureInfo,sk_sp<SkColorSpace> imageColorSpace,skgpu::graphite::Volatile isVolatile,GraphitePromiseTextureFulfillProc fulfillProc,GraphitePromiseImageReleaseProc imageReleaseProc,GraphitePromiseTextureReleaseProc textureReleaseProc,GraphitePromiseImageContext imageContext,GraphitePromiseTextureFulfillContext planeContexts[],std::string_view label)239 sk_sp<SkImage> PromiseTextureFromYUVA(skgpu::graphite::Recorder* recorder,
240                                       const YUVABackendTextureInfo& backendTextureInfo,
241                                       sk_sp<SkColorSpace> imageColorSpace,
242                                       skgpu::graphite::Volatile isVolatile,
243                                       GraphitePromiseTextureFulfillProc fulfillProc,
244                                       GraphitePromiseImageReleaseProc imageReleaseProc,
245                                       GraphitePromiseTextureReleaseProc textureReleaseProc,
246                                       GraphitePromiseImageContext imageContext,
247                                       GraphitePromiseTextureFulfillContext planeContexts[],
248                                       std::string_view label) {
249     // Our contract is that we will always call the _image_ release proc even on failure.
250     // We use the helper to convey the imageContext, so we need to ensure Make doesn't fail.
251     auto releaseHelper = skgpu::RefCntedCallback::Make(imageReleaseProc, imageContext);
252     if (!recorder) {
253         return nullptr;
254     }
255     // Precompute the dimensions for all promise texture planes
256     SkISize planeDimensions[SkYUVAInfo::kMaxPlanes];
257     if (!backendTextureInfo.yuvaInfo().planeDimensions(planeDimensions)) {
258         return nullptr;
259     }
260 
261     std::string labelStr(label);
262     if (labelStr.empty()) {
263         labelStr = "Wrapped_PromiseYUVPlane";
264     } else {
265         labelStr += "_PromiseYUVPlane";
266     }
267 
268     TextureProxyView planes[SkYUVAInfo::kMaxPlanes];
269     for (int i = 0; i < backendTextureInfo.numPlanes(); ++i) {
270         sk_sp<TextureProxy> lazyProxy = MakePromiseImageLazyProxy(
271                 recorder->priv().caps(),
272                 planeDimensions[i],
273                 backendTextureInfo.planeTextureInfo(i),
274                 isVolatile,
275                 releaseHelper,
276                 fulfillProc,
277                 planeContexts[i],
278                 textureReleaseProc,
279                 labelStr);
280         // Promise YUVA images assume the default rgba swizzle.
281         planes[i] = TextureProxyView(std::move(lazyProxy));
282     }
283     return Image_YUVA::Make(recorder->priv().caps(), backendTextureInfo.yuvaInfo(),
284                             SkSpan(planes), std::move(imageColorSpace));
285 }
286 
SubsetTextureFrom(skgpu::graphite::Recorder * recorder,const SkImage * img,const SkIRect & subset,SkImage::RequiredProperties props)287 sk_sp<SkImage> SubsetTextureFrom(skgpu::graphite::Recorder* recorder,
288                                  const SkImage* img,
289                                  const SkIRect& subset,
290                                  SkImage::RequiredProperties props) {
291     if (!recorder || !img) {
292         return nullptr;
293     }
294     auto subsetImg = img->makeSubset(recorder, subset, props);
295     return SkImages::TextureFromImage(recorder, subsetImg, props);
296 }
297 
MakeWithFilter(skgpu::graphite::Recorder * recorder,sk_sp<SkImage> src,const SkImageFilter * filter,const SkIRect & subset,const SkIRect & clipBounds,SkIRect * outSubset,SkIPoint * offset)298 sk_sp<SkImage> MakeWithFilter(skgpu::graphite::Recorder* recorder,
299                               sk_sp<SkImage> src,
300                               const SkImageFilter* filter,
301                               const SkIRect& subset,
302                               const SkIRect& clipBounds,
303                               SkIRect* outSubset,
304                               SkIPoint* offset) {
305     if (!recorder || !src || !filter) {
306         return nullptr;
307     }
308 
309     sk_sp<skif::Backend> backend = skif::MakeGraphiteBackend(recorder, {}, src->colorType());
310     return as_IFB(filter)->makeImageWithFilter(std::move(backend),
311                                                std::move(src),
312                                                subset,
313                                                clipBounds,
314                                                outSubset,
315                                                offset);
316 }
317 
generate_picture_texture(skgpu::graphite::Recorder * recorder,const SkImage_Picture * img,const SkImageInfo & info,SkImage::RequiredProperties requiredProps)318 static sk_sp<SkImage> generate_picture_texture(skgpu::graphite::Recorder* recorder,
319                                                const SkImage_Picture* img,
320                                                const SkImageInfo& info,
321                                                SkImage::RequiredProperties requiredProps) {
322     auto mm = requiredProps.fMipmapped ? skgpu::Mipmapped::kYes : skgpu::Mipmapped::kNo;
323     // Use a non-budgeted surface since the image wrapping the surface's texture will be owned by
324     // the client.
325     sk_sp<Surface> surface = Surface::Make(recorder,
326                                            info,
327                                            "LazySkImagePictureTexture",
328                                            skgpu::Budgeted::kNo,
329                                            mm,
330                                            SkBackingFit::kExact,
331                                            img->props());
332 
333     if (!surface) {
334         SKGPU_LOG_E("Failed to create Surface");
335         return nullptr;
336     }
337 
338     img->replay(surface->getCanvas());
339     // If the surface was created with mipmaps, they will be automatically generated when flushing
340     // the tasks when 'surface' goes out of scope.
341     return surface->asImage();
342 }
343 
344 /*
345  *  We only have 2 ways to create a Graphite-backed image.
346  *
347  *  1. Ask the generator to natively create one
348  *  2. Ask the generator to return RGB(A) data, which the GPU can convert
349  */
make_texture_image_from_lazy(skgpu::graphite::Recorder * recorder,const SkImage_Lazy * img,SkImage::RequiredProperties requiredProps)350 static sk_sp<SkImage> make_texture_image_from_lazy(skgpu::graphite::Recorder* recorder,
351                                                    const SkImage_Lazy* img,
352                                                    SkImage::RequiredProperties requiredProps) {
353     // 1. Ask the generator to natively create one.
354     {
355         if (img->type() == SkImage_Base::Type::kLazyPicture) {
356             sk_sp<SkImage> newImage =
357                     generate_picture_texture(recorder,
358                                              static_cast<const SkImage_Picture*>(img),
359                                              img->imageInfo(),
360                                              requiredProps);
361             if (newImage) {
362                 SkASSERT(as_IB(newImage)->isGraphiteBacked());
363                 return newImage;
364             }
365         }
366         // There is not an analog to GrTextureGenerator for Graphite yet, but if there was,
367         // we would want to call it here.
368     }
369 
370     // 2. Ask the generator to return a bitmap, which the GPU can convert.
371     {
372         SkBitmap bitmap;
373         if (img->getROPixels(nullptr, &bitmap, SkImage_Lazy::CachingHint::kDisallow_CachingHint)) {
374             return skgpu::graphite::MakeFromBitmap(recorder,
375                                                    img->imageInfo().colorInfo(),
376                                                    bitmap,
377                                                    nullptr,
378                                                    skgpu::Budgeted::kNo,
379                                                    requiredProps,
380                                                    "LazySkImageBitmapTexture");
381         }
382     }
383 
384     return nullptr;
385 }
386 
TextureFromImage(skgpu::graphite::Recorder * recorder,const SkImage * image,SkImage::RequiredProperties requiredProps)387 sk_sp<SkImage> TextureFromImage(skgpu::graphite::Recorder* recorder,
388                                 const SkImage* image,
389                                 SkImage::RequiredProperties requiredProps) {
390     if (!recorder || !image) {
391         return nullptr;
392     }
393     if (image->dimensions().area() <= 1) {
394         requiredProps.fMipmapped = false;
395     }
396 
397     auto ib = as_IB(image);
398     SkASSERT(!ib->isGaneshBacked());
399 
400     if (ib->isRasterBacked()) {
401         auto raster = static_cast<const SkImage_Raster*>(ib);
402         return skgpu::graphite::MakeFromBitmap(recorder,
403                                                raster->imageInfo().colorInfo(),
404                                                raster->bitmap(),
405                                                raster->refMips(),
406                                                skgpu::Budgeted::kNo,
407                                                requiredProps,
408                                                "RasterBitmapTexture");
409     }
410     if (ib->isLazyGenerated()) {
411         return make_texture_image_from_lazy(
412                 recorder, static_cast<const SkImage_Lazy*>(ib), requiredProps);
413     }
414     SkASSERT(ib->isGraphiteBacked());
415     return ib->makeSubset(recorder, ib->bounds(), requiredProps);
416 }
417 
TextureFromYUVAPixmaps(Recorder * recorder,const SkYUVAPixmaps & pixmaps,SkImage::RequiredProperties requiredProps,bool limitToMaxTextureSize,sk_sp<SkColorSpace> imageColorSpace,std::string_view label)418 sk_sp<SkImage> TextureFromYUVAPixmaps(Recorder* recorder,
419                                       const SkYUVAPixmaps& pixmaps,
420                                       SkImage::RequiredProperties requiredProps,
421                                       bool limitToMaxTextureSize,
422                                       sk_sp<SkColorSpace> imageColorSpace,
423                                       std::string_view label) {
424     if (!recorder) {
425         return nullptr;
426     }
427 
428     // Determine if we have to resize the pixmaps
429     const int maxTextureSize = recorder->priv().caps()->maxTextureSize();
430     const int maxDim = std::max(pixmaps.yuvaInfo().width(), pixmaps.yuvaInfo().height());
431 
432     SkYUVAPixmapInfo finalInfo = pixmaps.pixmapsInfo();
433     if (maxDim > maxTextureSize) {
434         if (!limitToMaxTextureSize) {
435             return nullptr;
436         }
437         float scale = static_cast<float>(maxTextureSize) / maxDim;
438         SkISize newDimensions = {
439                 std::min(static_cast<int>(pixmaps.yuvaInfo().width() * scale), maxTextureSize),
440                 std::min(static_cast<int>(pixmaps.yuvaInfo().height() * scale), maxTextureSize)};
441         finalInfo = SkYUVAPixmapInfo(pixmaps.yuvaInfo().makeDimensions(newDimensions),
442                                      pixmaps.dataType(),
443                                      /*rowBytes=*/nullptr);
444     }
445 
446     std::string labelStr(label);
447     if (labelStr.empty()) {
448         labelStr = "YUVRasterBitmapPlane";
449     } else {
450         labelStr += "_YUVBitmapPlane";
451     }
452 
453     auto mipmapped = requiredProps.fMipmapped ? skgpu::Mipmapped::kYes : skgpu::Mipmapped::kNo;
454     TextureProxyView planes[SkYUVAInfo::kMaxPlanes];
455     for (int i = 0; i < finalInfo.yuvaInfo().numPlanes(); ++i) {
456         SkBitmap bmp;
457         if (maxDim > maxTextureSize) {
458             // Rescale the data before uploading
459             if (!bmp.tryAllocPixels(finalInfo.planeInfo(i)) ||
460                 !pixmaps.plane(i).scalePixels(bmp.pixmap(), SkFilterMode::kLinear)) {
461                 return nullptr;
462             }
463         } else {
464             // Use original data to upload
465             if (!bmp.installPixels(pixmaps.plane(i))) {
466                 return nullptr;
467             }
468         }
469 
470         auto [view, _] = MakeBitmapProxyView(recorder, bmp, /*mipmapsIn=*/nullptr,
471                                              mipmapped,  skgpu::Budgeted::kNo,
472                                              labelStr);
473         planes[i] = std::move(view);
474     }
475     return Image_YUVA::Make(recorder->priv().caps(), finalInfo.yuvaInfo(),
476                             SkSpan(planes), std::move(imageColorSpace));
477 }
478 
TextureFromYUVATextures(Recorder * recorder,const YUVABackendTextures & yuvaTextures,sk_sp<SkColorSpace> imageColorSpace,TextureReleaseProc releaseP,ReleaseContext releaseC,std::string_view label)479 sk_sp<SkImage> TextureFromYUVATextures(Recorder* recorder,
480                                        const YUVABackendTextures& yuvaTextures,
481                                        sk_sp<SkColorSpace> imageColorSpace,
482                                        TextureReleaseProc releaseP,
483                                        ReleaseContext releaseC,
484                                        std::string_view label) {
485     auto releaseHelper = skgpu::RefCntedCallback::Make(releaseP, releaseC);
486     if (!recorder) {
487         return nullptr;
488     }
489 
490     std::string labelStr(label);
491     if (labelStr.empty()) {
492         labelStr = "Wrapped_YUVPlane";
493     } else {
494         labelStr += "_YUVPlane";
495     }
496 
497     TextureProxyView planes[SkYUVAInfo::kMaxPlanes];
498     for (int i = 0; i < yuvaTextures.yuvaInfo().numPlanes(); ++i) {
499         sk_sp<Texture> texture = recorder->priv().resourceProvider()->createWrappedTexture(
500                 yuvaTextures.planeTexture(i), labelStr);
501         if (!texture) {
502             SKGPU_LOG_W("Failed to wrap backend texture for YUVA plane %d", i);
503             return nullptr;
504         }
505         texture->setReleaseCallback(releaseHelper);
506         planes[i] = TextureProxyView(TextureProxy::Wrap(std::move(texture)));
507     }
508 
509     return Image_YUVA::Make(recorder->priv().caps(), yuvaTextures.yuvaInfo(),
510                             SkSpan(planes), std::move(imageColorSpace));
511 }
512 
TextureFromYUVAImages(Recorder * recorder,const SkYUVAInfo & yuvaInfo,SkSpan<const sk_sp<SkImage>> images,sk_sp<SkColorSpace> imageColorSpace)513 sk_sp<SkImage> TextureFromYUVAImages(Recorder* recorder,
514                                      const SkYUVAInfo& yuvaInfo,
515                                      SkSpan<const sk_sp<SkImage>> images,
516                                      sk_sp<SkColorSpace> imageColorSpace) {
517     // This factory is just a view of the images, so does not actually trigger any work on the
518     // recorder. It is just used to provide the Caps.
519     return Image_YUVA::WrapImages(recorder->priv().caps(), yuvaInfo, images, imageColorSpace);
520 }
521 
522 }  // namespace SkImages
523