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/gpu/ganesh/SkImageGanesh.h"
9
10 #include "include/core/SkAlphaType.h"
11 #include "include/core/SkBitmap.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkData.h"
14 #include "include/core/SkImage.h"
15 #include "include/core/SkImageInfo.h"
16 #include "include/core/SkPixmap.h"
17 #include "include/core/SkRefCnt.h"
18 #include "include/core/SkSamplingOptions.h"
19 #include "include/core/SkSize.h"
20 #include "include/core/SkYUVAInfo.h"
21 #include "include/core/SkYUVAPixmaps.h"
22 #include "include/gpu/GpuTypes.h"
23 #include "include/gpu/ganesh/GrBackendSurface.h"
24 #include "include/gpu/ganesh/GrContextThreadSafeProxy.h"
25 #include "include/gpu/ganesh/GrDirectContext.h"
26 #include "include/gpu/ganesh/GrRecordingContext.h"
27 #include "include/gpu/ganesh/GrTypes.h"
28 #include "include/gpu/ganesh/GrYUVABackendTextures.h"
29 #include "include/gpu/ganesh/GrExternalTextureGenerator.h"
30 #include "include/private/base/SkAssert.h"
31 #include "include/private/chromium/SkImageChromium.h"
32 #include "include/private/gpu/ganesh/GrImageContext.h"
33 #include "include/private/gpu/ganesh/GrTextureGenerator.h"
34 #include "include/private/gpu/ganesh/GrTypesPriv.h"
35 #include "src/core/SkAutoPixmapStorage.h"
36 #include "src/core/SkImageInfoPriv.h"
37 #include "src/gpu/GpuTypesPriv.h"
38 #include "src/gpu/RefCntedCallback.h"
39 #include "src/gpu/Swizzle.h"
40 #include "src/gpu/ganesh/GrBackendTextureImageGenerator.h"
41 #include "src/gpu/ganesh/GrBackendUtils.h"
42 #include "src/gpu/ganesh/GrCaps.h"
43 #include "src/gpu/ganesh/GrContextThreadSafeProxyPriv.h"
44 #include "src/gpu/ganesh/GrDirectContextPriv.h"
45 #include "src/gpu/ganesh/GrGpu.h"
46 #include "src/gpu/ganesh/GrGpuResourcePriv.h"
47 #include "src/gpu/ganesh/GrImageContextPriv.h"
48 #include "src/gpu/ganesh/GrProxyProvider.h"
49 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
50 #include "src/gpu/ganesh/GrSemaphore.h"
51 #include "src/gpu/ganesh/GrSurfaceProxy.h"
52 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
53 #include "src/gpu/ganesh/GrTexture.h"
54 #include "src/gpu/ganesh/GrTextureProxy.h"
55 #include "src/gpu/ganesh/GrYUVATextureProxies.h"
56 #include "src/gpu/ganesh/SkGr.h"
57 #include "src/gpu/ganesh/image/GrImageUtils.h"
58 #include "src/gpu/ganesh/image/SkImage_Ganesh.h"
59 #include "src/gpu/ganesh/image/SkImage_GaneshBase.h"
60 #include "src/gpu/ganesh/image/SkImage_GaneshYUVA.h"
61 #include "src/image/SkImage_Base.h"
62
63 #include <algorithm>
64 #include <memory>
65 #include <tuple>
66 #include <utility>
67
68 enum SkColorType : int;
69 enum class SkTextureCompressionType;
70
71 namespace SkImages {
72
MakeBackendTextureFromImage(GrDirectContext * direct,sk_sp<SkImage> image,GrBackendTexture * backendTexture,BackendTextureReleaseProc * releaseProc)73 bool MakeBackendTextureFromImage(GrDirectContext* direct,
74 sk_sp<SkImage> image,
75 GrBackendTexture* backendTexture,
76 BackendTextureReleaseProc* releaseProc) {
77 if (!image || !backendTexture || !releaseProc) {
78 return false;
79 }
80
81 auto [view, ct] = skgpu::ganesh::AsView(direct, image, skgpu::Mipmapped::kNo);
82 if (!view) {
83 return false;
84 }
85
86 // Flush any pending IO on the texture.
87 direct->priv().flushSurface(view.proxy());
88
89 GrTexture* texture = view.asTextureProxy()->peekTexture();
90 if (!texture) {
91 return false;
92 }
93 // We must make a copy of the image if the image is not unique, if the GrTexture owned by the
94 // image is not unique, or if the texture wraps an external object.
95 if (!image->unique() || !texture->unique() || texture->resourcePriv().refsWrappedObjects()) {
96 // onMakeSubset will always copy the image.
97 image = as_IB(image)->onMakeSubset(direct, image->bounds());
98 if (!image) {
99 return false;
100 }
101 return MakeBackendTextureFromImage(direct, std::move(image), backendTexture, releaseProc);
102 }
103
104 SkASSERT(!texture->resourcePriv().refsWrappedObjects());
105 SkASSERT(texture->unique());
106 SkASSERT(image->unique());
107
108 // Take a reference to the GrTexture and release the image.
109 sk_sp<GrTexture> textureRef = sk_ref_sp(texture);
110 view.reset();
111 image = nullptr;
112 SkASSERT(textureRef->unique());
113
114 // Steal the backend texture from the GrTexture, releasing the GrTexture in the process.
115 return GrTexture::StealBackendTexture(std::move(textureRef), backendTexture, releaseProc);
116 }
117
GetBackendTextureFromImage(const SkImage * img,GrBackendTexture * outTexture,bool flushPendingGrContextIO,GrSurfaceOrigin * origin)118 bool GetBackendTextureFromImage(const SkImage* img,
119 GrBackendTexture* outTexture,
120 bool flushPendingGrContextIO,
121 GrSurfaceOrigin* origin) {
122 if (!img) {
123 return false;
124 }
125 auto ib = as_IB(img);
126 if (ib->type() != SkImage_Base::Type::kGanesh) {
127 return false;
128 }
129 auto ig = static_cast<const SkImage_Ganesh*>(img);
130 return ig->getExistingBackendTexture(outTexture, flushPendingGrContextIO, origin);
131 }
132
TextureFromCompressedTexture(GrRecordingContext * context,const GrBackendTexture & backendTexture,GrSurfaceOrigin origin,SkAlphaType alphaType,sk_sp<SkColorSpace> colorSpace,TextureReleaseProc textureReleaseProc,ReleaseContext releaseContext)133 sk_sp<SkImage> TextureFromCompressedTexture(GrRecordingContext* context,
134 const GrBackendTexture& backendTexture,
135 GrSurfaceOrigin origin,
136 SkAlphaType alphaType,
137 sk_sp<SkColorSpace> colorSpace,
138 TextureReleaseProc textureReleaseProc,
139 ReleaseContext releaseContext) {
140 auto releaseHelper = skgpu::RefCntedCallback::Make(textureReleaseProc, releaseContext);
141
142 if (!context) {
143 return nullptr;
144 }
145
146 const GrCaps* caps = context->priv().caps();
147
148 if (!SkImage_GaneshBase::ValidateCompressedBackendTexture(caps, backendTexture, alphaType)) {
149 return nullptr;
150 }
151
152 GrProxyProvider* proxyProvider = context->priv().proxyProvider();
153 sk_sp<GrTextureProxy> proxy =
154 proxyProvider->wrapCompressedBackendTexture(backendTexture,
155 kBorrow_GrWrapOwnership,
156 GrWrapCacheable::kNo,
157 std::move(releaseHelper));
158 if (!proxy) {
159 return nullptr;
160 }
161
162 SkTextureCompressionType type =
163 GrBackendFormatToCompressionType(backendTexture.getBackendFormat());
164 SkColorType ct = skgpu::CompressionTypeToSkColorType(type);
165
166 GrSurfaceProxyView view(std::move(proxy), origin, skgpu::Swizzle::RGBA());
167 return sk_make_sp<SkImage_Ganesh>(sk_ref_sp(context),
168 kNeedNewImageUniqueID,
169 std::move(view),
170 SkColorInfo(ct, alphaType, std::move(colorSpace)));
171 }
172
new_wrapped_texture_common(GrRecordingContext * rContext,const GrBackendTexture & backendTex,GrColorType colorType,GrSurfaceOrigin origin,SkAlphaType at,sk_sp<SkColorSpace> colorSpace,GrWrapOwnership ownership,sk_sp<skgpu::RefCntedCallback> releaseHelper)173 static sk_sp<SkImage> new_wrapped_texture_common(GrRecordingContext* rContext,
174 const GrBackendTexture& backendTex,
175 GrColorType colorType,
176 GrSurfaceOrigin origin,
177 SkAlphaType at,
178 sk_sp<SkColorSpace> colorSpace,
179 GrWrapOwnership ownership,
180 sk_sp<skgpu::RefCntedCallback> releaseHelper) {
181 if (!backendTex.isValid() || backendTex.width() <= 0 || backendTex.height() <= 0) {
182 return nullptr;
183 }
184
185 GrProxyProvider* proxyProvider = rContext->priv().proxyProvider();
186 sk_sp<GrTextureProxy> proxy = proxyProvider->wrapBackendTexture(
187 backendTex, ownership, GrWrapCacheable::kNo, kRead_GrIOType, std::move(releaseHelper));
188 if (!proxy) {
189 return nullptr;
190 }
191
192 skgpu::Swizzle swizzle =
193 rContext->priv().caps()->getReadSwizzle(proxy->backendFormat(), colorType);
194 GrSurfaceProxyView view(std::move(proxy), origin, swizzle);
195 SkColorInfo info(GrColorTypeToSkColorType(colorType), at, std::move(colorSpace));
196 return sk_make_sp<SkImage_Ganesh>(
197 sk_ref_sp(rContext), kNeedNewImageUniqueID, std::move(view), std::move(info));
198 }
199
BorrowTextureFrom(GrRecordingContext * context,const GrBackendTexture & backendTexture,GrSurfaceOrigin origin,SkColorType colorType,SkAlphaType alphaType,sk_sp<SkColorSpace> colorSpace,TextureReleaseProc textureReleaseProc,ReleaseContext releaseContext)200 sk_sp<SkImage> BorrowTextureFrom(GrRecordingContext* context,
201 const GrBackendTexture& backendTexture,
202 GrSurfaceOrigin origin,
203 SkColorType colorType,
204 SkAlphaType alphaType,
205 sk_sp<SkColorSpace> colorSpace,
206 TextureReleaseProc textureReleaseProc,
207 ReleaseContext releaseContext) {
208 auto releaseHelper = skgpu::RefCntedCallback::Make(textureReleaseProc, releaseContext);
209
210 if (!context) {
211 return nullptr;
212 }
213
214 const GrCaps* caps = context->priv().caps();
215
216 GrColorType grColorType = SkColorTypeToGrColorType(colorType);
217 if (GrColorType::kUnknown == grColorType) {
218 return nullptr;
219 }
220
221 if (!SkImage_GaneshBase::ValidateBackendTexture(
222 caps, backendTexture, grColorType, colorType, alphaType, colorSpace)) {
223 return nullptr;
224 }
225
226 return new_wrapped_texture_common(context,
227 backendTexture,
228 grColorType,
229 origin,
230 alphaType,
231 std::move(colorSpace),
232 kBorrow_GrWrapOwnership,
233 std::move(releaseHelper));
234 }
235
AdoptTextureFrom(GrRecordingContext * context,const GrBackendTexture & backendTexture,GrSurfaceOrigin textureOrigin,SkColorType colorType)236 sk_sp<SkImage> AdoptTextureFrom(GrRecordingContext* context,
237 const GrBackendTexture& backendTexture,
238 GrSurfaceOrigin textureOrigin,
239 SkColorType colorType) {
240 return AdoptTextureFrom(
241 context, backendTexture, textureOrigin, colorType, kPremul_SkAlphaType, nullptr);
242 }
243
AdoptTextureFrom(GrRecordingContext * context,const GrBackendTexture & backendTexture,GrSurfaceOrigin textureOrigin,SkColorType colorType,SkAlphaType alphaType)244 sk_sp<SkImage> AdoptTextureFrom(GrRecordingContext* context,
245 const GrBackendTexture& backendTexture,
246 GrSurfaceOrigin textureOrigin,
247 SkColorType colorType,
248 SkAlphaType alphaType) {
249 return AdoptTextureFrom(context, backendTexture, textureOrigin, colorType, alphaType, nullptr);
250 }
251
AdoptTextureFrom(GrRecordingContext * context,const GrBackendTexture & backendTexture,GrSurfaceOrigin origin,SkColorType colorType,SkAlphaType alphaType,sk_sp<SkColorSpace> colorSpace)252 sk_sp<SkImage> AdoptTextureFrom(GrRecordingContext* context,
253 const GrBackendTexture& backendTexture,
254 GrSurfaceOrigin origin,
255 SkColorType colorType,
256 SkAlphaType alphaType,
257 sk_sp<SkColorSpace> colorSpace) {
258 auto dContext = GrAsDirectContext(context);
259 if (!dContext) {
260 // We have a DDL context and we don't support adopted textures for them.
261 return nullptr;
262 }
263
264 const GrCaps* caps = dContext->priv().caps();
265
266 GrColorType grColorType = SkColorTypeToGrColorType(colorType);
267 if (GrColorType::kUnknown == grColorType) {
268 return nullptr;
269 }
270
271 if (!SkImage_GaneshBase::ValidateBackendTexture(
272 caps, backendTexture, grColorType, colorType, alphaType, colorSpace)) {
273 return nullptr;
274 }
275
276 return new_wrapped_texture_common(dContext,
277 backendTexture,
278 grColorType,
279 origin,
280 alphaType,
281 std::move(colorSpace),
282 kAdopt_GrWrapOwnership,
283 nullptr);
284 }
285
TextureFromCompressedTextureData(GrDirectContext * direct,sk_sp<SkData> data,int width,int height,SkTextureCompressionType type,skgpu::Mipmapped mipmapped,GrProtected isProtected)286 sk_sp<SkImage> TextureFromCompressedTextureData(GrDirectContext* direct,
287 sk_sp<SkData> data,
288 int width,
289 int height,
290 SkTextureCompressionType type,
291 skgpu::Mipmapped mipmapped,
292 GrProtected isProtected) {
293 if (!direct || !data) {
294 return nullptr;
295 }
296
297 GrBackendFormat beFormat = direct->compressedBackendFormat(type);
298 if (!beFormat.isValid()) {
299 sk_sp<SkImage> tmp = RasterFromCompressedTextureData(std::move(data), width, height, type);
300 if (!tmp) {
301 return nullptr;
302 }
303 return TextureFromImage(direct, tmp, mipmapped);
304 }
305
306 GrProxyProvider* proxyProvider = direct->priv().proxyProvider();
307 sk_sp<GrTextureProxy> proxy = proxyProvider->createCompressedTextureProxy(
308 {width, height}, skgpu::Budgeted::kYes, mipmapped, isProtected, type, std::move(data));
309 if (!proxy) {
310 return nullptr;
311 }
312 GrSurfaceProxyView view(std::move(proxy));
313
314 SkColorType colorType = skgpu::CompressionTypeToSkColorType(type);
315
316 return sk_make_sp<SkImage_Ganesh>(sk_ref_sp(direct),
317 kNeedNewImageUniqueID,
318 std::move(view),
319 SkColorInfo(colorType, kOpaque_SkAlphaType, nullptr));
320 }
321
PromiseTextureFrom(sk_sp<GrContextThreadSafeProxy> threadSafeProxy,const GrBackendFormat & backendFormat,SkISize dimensions,skgpu::Mipmapped mipmapped,GrSurfaceOrigin origin,SkColorType colorType,SkAlphaType alphaType,sk_sp<SkColorSpace> colorSpace,PromiseImageTextureFulfillProc textureFulfillProc,PromiseImageTextureReleaseProc textureReleaseProc,PromiseImageTextureContext textureContext)322 sk_sp<SkImage> PromiseTextureFrom(sk_sp<GrContextThreadSafeProxy> threadSafeProxy,
323 const GrBackendFormat& backendFormat,
324 SkISize dimensions,
325 skgpu::Mipmapped mipmapped,
326 GrSurfaceOrigin origin,
327 SkColorType colorType,
328 SkAlphaType alphaType,
329 sk_sp<SkColorSpace> colorSpace,
330 PromiseImageTextureFulfillProc textureFulfillProc,
331 PromiseImageTextureReleaseProc textureReleaseProc,
332 PromiseImageTextureContext textureContext) {
333 // Our contract is that we will always call the release proc even on failure.
334 // We use the helper to convey the context, so we need to ensure make doesn't fail.
335 textureReleaseProc = textureReleaseProc ? textureReleaseProc : [](void*) {};
336 auto releaseHelper = skgpu::RefCntedCallback::Make(textureReleaseProc, textureContext);
337 SkImageInfo info = SkImageInfo::Make(dimensions, colorType, alphaType, colorSpace);
338 if (!SkImageInfoIsValid(info)) {
339 return nullptr;
340 }
341
342 if (!threadSafeProxy) {
343 return nullptr;
344 }
345
346 if (dimensions.isEmpty()) {
347 return nullptr;
348 }
349
350 GrColorType grColorType = SkColorTypeToGrColorType(colorType);
351 if (GrColorType::kUnknown == grColorType) {
352 return nullptr;
353 }
354
355 if (!threadSafeProxy->priv().caps()->areColorTypeAndFormatCompatible(grColorType,
356 backendFormat)) {
357 return nullptr;
358 }
359
360 auto proxy = SkImage_GaneshBase::MakePromiseImageLazyProxy(threadSafeProxy.get(),
361 dimensions,
362 backendFormat,
363 mipmapped,
364 textureFulfillProc,
365 std::move(releaseHelper));
366 if (!proxy) {
367 return nullptr;
368 }
369 skgpu::Swizzle swizzle =
370 threadSafeProxy->priv().caps()->getReadSwizzle(backendFormat, grColorType);
371 GrSurfaceProxyView view(std::move(proxy), origin, swizzle);
372 sk_sp<GrImageContext> ctx(GrImageContextPriv::MakeForPromiseImage(std::move(threadSafeProxy)));
373 return sk_make_sp<SkImage_Ganesh>(std::move(ctx),
374 kNeedNewImageUniqueID,
375 std::move(view),
376 SkColorInfo(colorType, alphaType, std::move(colorSpace)));
377 }
378
CrossContextTextureFromPixmap(GrDirectContext * dContext,const SkPixmap & originalPixmap,bool buildMips,bool limitToMaxTextureSize)379 sk_sp<SkImage> CrossContextTextureFromPixmap(GrDirectContext* dContext,
380 const SkPixmap& originalPixmap,
381 bool buildMips,
382 bool limitToMaxTextureSize) {
383 // Some backends or drivers don't support (safely) moving resources between contexts
384 if (!dContext || !dContext->priv().caps()->crossContextTextureSupport()) {
385 return RasterFromPixmapCopy(originalPixmap);
386 }
387
388 // If non-power-of-two mipmapping isn't supported, ignore the client's request
389 if (!dContext->priv().caps()->mipmapSupport()) {
390 buildMips = false;
391 }
392
393 const SkPixmap* pixmap = &originalPixmap;
394 SkAutoPixmapStorage resized;
395 int maxTextureSize = dContext->priv().caps()->maxTextureSize();
396 int maxDim = std::max(originalPixmap.width(), originalPixmap.height());
397 if (limitToMaxTextureSize && maxDim > maxTextureSize) {
398 float scale = static_cast<float>(maxTextureSize) / maxDim;
399 int newWidth = std::min(static_cast<int>(originalPixmap.width() * scale), maxTextureSize);
400 int newHeight = std::min(static_cast<int>(originalPixmap.height() * scale), maxTextureSize);
401 SkImageInfo info = originalPixmap.info().makeWH(newWidth, newHeight);
402 SkSamplingOptions sampling(SkFilterMode::kLinear);
403 if (!resized.tryAlloc(info) || !originalPixmap.scalePixels(resized, sampling)) {
404 return nullptr;
405 }
406 pixmap = &resized;
407 }
408 // Turn the pixmap into a GrTextureProxy
409 SkBitmap bmp;
410 bmp.installPixels(*pixmap);
411 skgpu::Mipmapped mipmapped = buildMips ? skgpu::Mipmapped::kYes : skgpu::Mipmapped::kNo;
412 auto [view, ct] = GrMakeUncachedBitmapProxyView(dContext, bmp, mipmapped);
413 if (!view) {
414 return RasterFromPixmapCopy(*pixmap);
415 }
416
417 sk_sp<GrTexture> texture = sk_ref_sp(view.proxy()->peekTexture());
418
419 // Flush any writes or uploads
420 dContext->priv().flushSurface(view.proxy());
421 GrGpu* gpu = dContext->priv().getGpu();
422
423 std::unique_ptr<GrSemaphore> sema = gpu->prepareTextureForCrossContextUsage(texture.get());
424
425 SkColorType skCT = GrColorTypeToSkColorType(ct);
426 auto gen = GrBackendTextureImageGenerator::Make(std::move(texture),
427 view.origin(),
428 std::move(sema),
429 skCT,
430 pixmap->alphaType(),
431 pixmap->info().refColorSpace());
432 return DeferredFromTextureGenerator(std::move(gen));
433 }
434
TextureFromImage(GrDirectContext * dContext,const SkImage * img,skgpu::Mipmapped mipmapped,skgpu::Budgeted budgeted)435 sk_sp<SkImage> TextureFromImage(GrDirectContext* dContext,
436 const SkImage* img,
437 skgpu::Mipmapped mipmapped,
438 skgpu::Budgeted budgeted) {
439 if (!dContext || !img) {
440 return nullptr;
441 }
442 auto ib = as_IB(img);
443 if (!dContext->priv().caps()->mipmapSupport() || ib->dimensions().area() <= 1) {
444 mipmapped = skgpu::Mipmapped::kNo;
445 }
446
447 if (ib->isGaneshBacked()) {
448 if (!ib->context()->priv().matches(dContext)) {
449 return nullptr;
450 }
451
452 if (mipmapped == skgpu::Mipmapped::kNo || ib->hasMipmaps()) {
453 return sk_ref_sp(const_cast<SkImage_Base*>(ib));
454 }
455 }
456 GrImageTexGenPolicy policy = budgeted == skgpu::Budgeted::kYes
457 ? GrImageTexGenPolicy::kNew_Uncached_Budgeted
458 : GrImageTexGenPolicy::kNew_Uncached_Unbudgeted;
459 // TODO: Don't flatten YUVA images here. Add mips to the planes instead.
460 auto [view, ct] = skgpu::ganesh::AsView(dContext, ib, mipmapped, policy);
461 if (!view) {
462 return nullptr;
463 }
464 SkASSERT(view.asTextureProxy());
465 SkASSERT(mipmapped == skgpu::Mipmapped::kNo ||
466 view.asTextureProxy()->mipmapped() == skgpu::Mipmapped::kYes);
467 SkColorInfo colorInfo(GrColorTypeToSkColorType(ct), ib->alphaType(), ib->refColorSpace());
468 return sk_make_sp<SkImage_Ganesh>(
469 sk_ref_sp(dContext), ib->uniqueID(), std::move(view), std::move(colorInfo));
470 }
471
TextureFromYUVATextures(GrRecordingContext * context,const GrYUVABackendTextures & yuvaTextures)472 sk_sp<SkImage> TextureFromYUVATextures(GrRecordingContext* context,
473 const GrYUVABackendTextures& yuvaTextures) {
474 return TextureFromYUVATextures(context, yuvaTextures, nullptr, nullptr, nullptr);
475 }
476
TextureFromYUVATextures(GrRecordingContext * context,const GrYUVABackendTextures & yuvaTextures,sk_sp<SkColorSpace> imageColorSpace,TextureReleaseProc textureReleaseProc,ReleaseContext releaseContext)477 sk_sp<SkImage> TextureFromYUVATextures(GrRecordingContext* context,
478 const GrYUVABackendTextures& yuvaTextures,
479 sk_sp<SkColorSpace> imageColorSpace,
480 TextureReleaseProc textureReleaseProc,
481 ReleaseContext releaseContext) {
482 auto releaseHelper = skgpu::RefCntedCallback::Make(textureReleaseProc, releaseContext);
483
484 GrProxyProvider* proxyProvider = context->priv().proxyProvider();
485 int numPlanes = yuvaTextures.yuvaInfo().numPlanes();
486 sk_sp<GrSurfaceProxy> proxies[SkYUVAInfo::kMaxPlanes];
487 for (int plane = 0; plane < numPlanes; ++plane) {
488 proxies[plane] = proxyProvider->wrapBackendTexture(yuvaTextures.texture(plane),
489 kBorrow_GrWrapOwnership,
490 GrWrapCacheable::kNo,
491 kRead_GrIOType,
492 releaseHelper);
493 if (!proxies[plane]) {
494 return {};
495 }
496 }
497 GrYUVATextureProxies yuvaProxies(
498 yuvaTextures.yuvaInfo(), proxies, yuvaTextures.textureOrigin());
499
500 if (!yuvaProxies.isValid()) {
501 return nullptr;
502 }
503
504 return sk_make_sp<SkImage_GaneshYUVA>(
505 sk_ref_sp(context), kNeedNewImageUniqueID, yuvaProxies, imageColorSpace);
506 }
507
TextureFromYUVAPixmaps(GrRecordingContext * context,const SkYUVAPixmaps & pixmaps,skgpu::Mipmapped buildMips,bool limitToMaxTextureSize)508 sk_sp<SkImage> TextureFromYUVAPixmaps(GrRecordingContext* context,
509 const SkYUVAPixmaps& pixmaps,
510 skgpu::Mipmapped buildMips,
511 bool limitToMaxTextureSize) {
512 return TextureFromYUVAPixmaps(context, pixmaps, buildMips, limitToMaxTextureSize, nullptr);
513 }
514
TextureFromYUVAPixmaps(GrRecordingContext * context,const SkYUVAPixmaps & pixmaps,skgpu::Mipmapped buildMips,bool limitToMaxTextureSize,sk_sp<SkColorSpace> imageColorSpace)515 sk_sp<SkImage> TextureFromYUVAPixmaps(GrRecordingContext* context,
516 const SkYUVAPixmaps& pixmaps,
517 skgpu::Mipmapped buildMips,
518 bool limitToMaxTextureSize,
519 sk_sp<SkColorSpace> imageColorSpace) {
520 if (!context) {
521 return nullptr;
522 }
523
524 if (!pixmaps.isValid()) {
525 return nullptr;
526 }
527
528 if (!context->priv().caps()->mipmapSupport()) {
529 buildMips = skgpu::Mipmapped::kNo;
530 }
531
532 // Resize the pixmaps if necessary.
533 int numPlanes = pixmaps.numPlanes();
534 int maxTextureSize = context->priv().caps()->maxTextureSize();
535 int maxDim = std::max(pixmaps.yuvaInfo().width(), pixmaps.yuvaInfo().height());
536
537 SkYUVAPixmaps tempPixmaps;
538 const SkYUVAPixmaps* pixmapsToUpload = &pixmaps;
539 // We assume no plane is larger than the image size (and at least one plane is as big).
540 if (maxDim > maxTextureSize) {
541 if (!limitToMaxTextureSize) {
542 return nullptr;
543 }
544 float scale = static_cast<float>(maxTextureSize) / maxDim;
545 SkISize newDimensions = {
546 std::min(static_cast<int>(pixmaps.yuvaInfo().width() * scale), maxTextureSize),
547 std::min(static_cast<int>(pixmaps.yuvaInfo().height() * scale), maxTextureSize)};
548 SkYUVAInfo newInfo = pixmaps.yuvaInfo().makeDimensions(newDimensions);
549 SkYUVAPixmapInfo newPixmapInfo(newInfo, pixmaps.dataType(), /*row bytes*/ nullptr);
550 tempPixmaps = SkYUVAPixmaps::Allocate(newPixmapInfo);
551 SkSamplingOptions sampling(SkFilterMode::kLinear);
552 if (!tempPixmaps.isValid()) {
553 return nullptr;
554 }
555 for (int i = 0; i < numPlanes; ++i) {
556 if (!pixmaps.plane(i).scalePixels(tempPixmaps.plane(i), sampling)) {
557 return nullptr;
558 }
559 }
560 pixmapsToUpload = &tempPixmaps;
561 }
562
563 // Convert to texture proxies.
564 GrSurfaceProxyView views[SkYUVAInfo::kMaxPlanes];
565 GrColorType pixmapColorTypes[SkYUVAInfo::kMaxPlanes];
566 for (int i = 0; i < numPlanes; ++i) {
567 // Turn the pixmap into a GrTextureProxy
568 SkBitmap bmp;
569 bmp.installPixels(pixmapsToUpload->plane(i));
570 std::tie(views[i], std::ignore) = GrMakeUncachedBitmapProxyView(context, bmp, buildMips);
571 if (!views[i]) {
572 return nullptr;
573 }
574 pixmapColorTypes[i] = SkColorTypeToGrColorType(bmp.colorType());
575 }
576
577 GrYUVATextureProxies yuvaProxies(pixmapsToUpload->yuvaInfo(), views, pixmapColorTypes);
578 SkASSERT(yuvaProxies.isValid());
579 return sk_make_sp<SkImage_GaneshYUVA>(sk_ref_sp(context),
580 kNeedNewImageUniqueID,
581 std::move(yuvaProxies),
582 std::move(imageColorSpace));
583 }
584
PromiseTextureFromYUVA(sk_sp<GrContextThreadSafeProxy> threadSafeProxy,const GrYUVABackendTextureInfo & backendTextureInfo,sk_sp<SkColorSpace> imageColorSpace,PromiseImageTextureFulfillProc textureFulfillProc,PromiseImageTextureReleaseProc textureReleaseProc,PromiseImageTextureContext textureContexts[])585 sk_sp<SkImage> PromiseTextureFromYUVA(sk_sp<GrContextThreadSafeProxy> threadSafeProxy,
586 const GrYUVABackendTextureInfo& backendTextureInfo,
587 sk_sp<SkColorSpace> imageColorSpace,
588 PromiseImageTextureFulfillProc textureFulfillProc,
589 PromiseImageTextureReleaseProc textureReleaseProc,
590 PromiseImageTextureContext textureContexts[]) {
591 if (!backendTextureInfo.isValid()) {
592 return nullptr;
593 }
594
595 SkISize planeDimensions[SkYUVAInfo::kMaxPlanes];
596 int n = backendTextureInfo.yuvaInfo().planeDimensions(planeDimensions);
597
598 // Our contract is that we will always call the release proc even on failure.
599 // We use the helper to convey the context, so we need to ensure make doesn't fail.
600 textureReleaseProc = textureReleaseProc ? textureReleaseProc : [](void*) {};
601 sk_sp<skgpu::RefCntedCallback> releaseHelpers[4];
602 for (int i = 0; i < n; ++i) {
603 releaseHelpers[i] = skgpu::RefCntedCallback::Make(textureReleaseProc, textureContexts[i]);
604 }
605
606 if (!threadSafeProxy) {
607 return nullptr;
608 }
609
610 SkAlphaType at =
611 backendTextureInfo.yuvaInfo().hasAlpha() ? kPremul_SkAlphaType : kOpaque_SkAlphaType;
612 SkImageInfo info = SkImageInfo::Make(
613 backendTextureInfo.yuvaInfo().dimensions(), SkImage_GaneshYUVA::kAssumedColorType, at,
614 imageColorSpace);
615 if (!SkImageInfoIsValid(info)) {
616 return nullptr;
617 }
618
619 // Make a lazy proxy for each plane
620 sk_sp<GrSurfaceProxy> proxies[4];
621 for (int i = 0; i < n; ++i) {
622 proxies[i] =
623 SkImage_GaneshBase::MakePromiseImageLazyProxy(threadSafeProxy.get(),
624 planeDimensions[i],
625 backendTextureInfo.planeFormat(i),
626 skgpu::Mipmapped::kNo,
627 textureFulfillProc,
628 std::move(releaseHelpers[i]));
629 if (!proxies[i]) {
630 return nullptr;
631 }
632 }
633 GrYUVATextureProxies yuvaTextureProxies(
634 backendTextureInfo.yuvaInfo(), proxies, backendTextureInfo.textureOrigin());
635 SkASSERT(yuvaTextureProxies.isValid());
636 sk_sp<GrImageContext> ctx(GrImageContextPriv::MakeForPromiseImage(std::move(threadSafeProxy)));
637 return sk_make_sp<SkImage_GaneshYUVA>(std::move(ctx),
638 kNeedNewImageUniqueID,
639 std::move(yuvaTextureProxies),
640 std::move(imageColorSpace));
641 }
642
643 } // namespace SkImages
644