1 /*
2  * Copyright 2018 Google Inc.
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 <cstddef>
9 #include <cstring>
10 #include <type_traits>
11 
12 #include "include/core/SkYUVASizeInfo.h"
13 #include "include/gpu/GrContext.h"
14 #include "include/gpu/GrTexture.h"
15 #include "include/private/GrRecordingContext.h"
16 #include "src/core/SkAutoPixmapStorage.h"
17 #include "src/core/SkMipMap.h"
18 #include "src/core/SkScopeExit.h"
19 #include "src/gpu/GrClip.h"
20 #include "src/gpu/GrContextPriv.h"
21 #include "src/gpu/GrGpu.h"
22 #include "src/gpu/GrRecordingContextPriv.h"
23 #include "src/gpu/GrRenderTargetContext.h"
24 #include "src/gpu/GrTextureProducer.h"
25 #include "src/gpu/SkGr.h"
26 #include "src/gpu/effects/GrYUVtoRGBEffect.h"
27 #include "src/image/SkImage_Gpu.h"
28 #include "src/image/SkImage_GpuYUVA.h"
29 
30 static constexpr auto kAssumedColorType = kRGBA_8888_SkColorType;
31 
SkImage_GpuYUVA(sk_sp<GrContext> context,int width,int height,uint32_t uniqueID,SkYUVColorSpace colorSpace,sk_sp<GrTextureProxy> proxies[],GrColorType proxyColorTypes[],int numProxies,const SkYUVAIndex yuvaIndices[4],GrSurfaceOrigin origin,sk_sp<SkColorSpace> imageColorSpace)32 SkImage_GpuYUVA::SkImage_GpuYUVA(sk_sp<GrContext> context, int width, int height, uint32_t uniqueID,
33                                  SkYUVColorSpace colorSpace, sk_sp<GrTextureProxy> proxies[],
34                                  GrColorType proxyColorTypes[], int numProxies,
35                                  const SkYUVAIndex yuvaIndices[4], GrSurfaceOrigin origin,
36                                  sk_sp<SkColorSpace> imageColorSpace)
37         : INHERITED(std::move(context), width, height, uniqueID, kAssumedColorType,
38                     // If an alpha channel is present we always switch to kPremul. This is because,
39                     // although the planar data is always un-premul, the final interleaved RGB image
40                     // is/would-be premul.
41                     GetAlphaTypeFromYUVAIndices(yuvaIndices), std::move(imageColorSpace))
42         , fNumProxies(numProxies)
43         , fYUVColorSpace(colorSpace)
44         , fOrigin(origin) {
45     // The caller should have done this work, just verifying
46     SkDEBUGCODE(int textureCount;)
47     SkASSERT(SkYUVAIndex::AreValidIndices(yuvaIndices, &textureCount));
48     SkASSERT(textureCount == fNumProxies);
49 
50     for (int i = 0; i < numProxies; ++i) {
51         fProxies[i] = std::move(proxies[i]);
52         fProxyColorTypes[i] = proxyColorTypes[i];
53     }
54     memcpy(fYUVAIndices, yuvaIndices, 4*sizeof(SkYUVAIndex));
55 }
56 
57 // For onMakeColorSpace()
SkImage_GpuYUVA(const SkImage_GpuYUVA * image,sk_sp<SkColorSpace> targetCS)58 SkImage_GpuYUVA::SkImage_GpuYUVA(const SkImage_GpuYUVA* image, sk_sp<SkColorSpace> targetCS)
59         : INHERITED(image->fContext, image->width(), image->height(), kNeedNewImageUniqueID,
60                     kAssumedColorType,
61                     // If an alpha channel is present we always switch to kPremul. This is because,
62                     // although the planar data is always un-premul, the final interleaved RGB image
63                     // is/would-be premul.
64                     GetAlphaTypeFromYUVAIndices(image->fYUVAIndices), std::move(targetCS))
65         , fNumProxies(image->fNumProxies)
66         , fYUVColorSpace(image->fYUVColorSpace)
67         , fOrigin(image->fOrigin)
68         // Since null fFromColorSpace means no GrColorSpaceXform, we turn a null
69         // image->refColorSpace() into an explicit SRGB.
70         , fFromColorSpace(image->colorSpace() ? image->refColorSpace() : SkColorSpace::MakeSRGB()) {
71     // The caller should have done this work, just verifying
72     SkDEBUGCODE(int textureCount;)
73         SkASSERT(SkYUVAIndex::AreValidIndices(image->fYUVAIndices, &textureCount));
74     SkASSERT(textureCount == fNumProxies);
75 
76     if (image->fRGBProxy) {
77         fRGBProxy = image->fRGBProxy;  // we ref in this case, not move
78     } else {
79         for (int i = 0; i < fNumProxies; ++i) {
80             fProxies[i] = image->fProxies[i];  // we ref in this case, not move
81             fProxyColorTypes[i] = image->fProxyColorTypes[i];
82         }
83     }
84     memcpy(fYUVAIndices, image->fYUVAIndices, 4 * sizeof(SkYUVAIndex));
85 }
86 
~SkImage_GpuYUVA()87 SkImage_GpuYUVA::~SkImage_GpuYUVA() {}
88 
setupMipmapsForPlanes(GrRecordingContext * context) const89 bool SkImage_GpuYUVA::setupMipmapsForPlanes(GrRecordingContext* context) const {
90     // We shouldn't get here if the planes were already flattened to RGBA.
91     SkASSERT(fProxies[0] && !fRGBProxy);
92     if (!context || !fContext->priv().matches(context)) {
93         return false;
94     }
95 
96     for (int i = 0; i < fNumProxies; ++i) {
97         GrTextureProducer::CopyParams copyParams;
98         int mipCount = SkMipMap::ComputeLevelCount(fProxies[i]->width(), fProxies[i]->height());
99         if (mipCount && GrGpu::IsACopyNeededForMips(fContext->priv().caps(),
100                                                     fProxies[i].get(),
101                                                     GrSamplerState::Filter::kMipMap,
102                                                     &copyParams)) {
103             auto mippedProxy = GrCopyBaseMipMapToTextureProxy(context, fProxies[i].get(),
104                                                               fProxyColorTypes[i]);
105             if (!mippedProxy) {
106                 return false;
107             }
108             fProxies[i] = mippedProxy;
109         }
110     }
111     return true;
112 }
113 
114 //////////////////////////////////////////////////////////////////////////////////////////////////
115 
onFlush(GrContext * context,const GrFlushInfo & info)116 GrSemaphoresSubmitted SkImage_GpuYUVA::onFlush(GrContext* context, const GrFlushInfo& info) {
117     if (!context || !fContext->priv().matches(context) || fContext->abandoned()) {
118         return GrSemaphoresSubmitted::kNo;
119     }
120 
121     GrSurfaceProxy* proxies[4] = {fProxies[0].get(), fProxies[1].get(),
122                                   fProxies[2].get(), fProxies[3].get()};
123     int numProxies = fNumProxies;
124     if (fRGBProxy) {
125         // Either we've already flushed the flattening draw or the flattening is unflushed. In the
126         // latter case it should still be ok to just pass fRGBProxy because it in turn depends on
127         // the planar proxies and will cause all of their work to flush as well.
128         proxies[0] = fRGBProxy.get();
129         numProxies = 1;
130     }
131     return context->priv().flushSurfaces(proxies, numProxies, info);
132 }
133 
peekProxy() const134 GrTextureProxy* SkImage_GpuYUVA::peekProxy() const {
135     return fRGBProxy.get();
136 }
137 
asTextureProxyRef(GrRecordingContext * context) const138 sk_sp<GrTextureProxy> SkImage_GpuYUVA::asTextureProxyRef(GrRecordingContext* context) const {
139     if (fRGBProxy) {
140         return fRGBProxy;
141     }
142 
143     if (!context || !fContext->priv().matches(context)) {
144         return nullptr;
145     }
146 
147     // Needs to create a render target in order to draw to it for the yuv->rgb conversion.
148     auto renderTargetContext = context->priv().makeDeferredRenderTargetContext(
149             SkBackingFit::kExact, this->width(), this->height(), GrColorType::kRGBA_8888,
150             this->refColorSpace(), 1, GrMipMapped::kNo, fOrigin);
151     if (!renderTargetContext) {
152         return nullptr;
153     }
154 
155     sk_sp<GrColorSpaceXform> colorSpaceXform;
156     if (fFromColorSpace) {
157         colorSpaceXform = GrColorSpaceXform::Make(fFromColorSpace.get(), this->alphaType(),
158                                                   this->colorSpace(), this->alphaType());
159     }
160     const SkRect rect = SkRect::MakeIWH(this->width(), this->height());
161     if (!RenderYUVAToRGBA(fContext.get(), renderTargetContext.get(), rect, fYUVColorSpace,
162                           std::move(colorSpaceXform), fProxies, fYUVAIndices)) {
163         return nullptr;
164     }
165 
166     fRGBProxy = renderTargetContext->asTextureProxyRef();
167     for (auto& p : fProxies) {
168         p.reset();
169     }
170     return fRGBProxy;
171 }
172 
asMippedTextureProxyRef(GrRecordingContext * context) const173 sk_sp<GrTextureProxy> SkImage_GpuYUVA::asMippedTextureProxyRef(GrRecordingContext* context) const {
174     if (!context || !fContext->priv().matches(context)) {
175         return nullptr;
176     }
177 
178     // if invalid or already has miplevels
179     auto proxy = this->asTextureProxyRef(context);
180     if (!proxy || GrMipMapped::kYes == fRGBProxy->mipMapped()) {
181         return proxy;
182     }
183 
184     // need to generate mips for the proxy
185     GrColorType srcColorType = SkColorTypeToGrColorType(this->colorType());
186     if (auto mippedProxy = GrCopyBaseMipMapToTextureProxy(context, proxy.get(), srcColorType)) {
187         fRGBProxy = mippedProxy;
188         return mippedProxy;
189     }
190 
191     // failed to generate mips
192     return nullptr;
193 }
194 
195 //////////////////////////////////////////////////////////////////////////////////////////////////
196 
onMakeColorTypeAndColorSpace(GrRecordingContext *,SkColorType,sk_sp<SkColorSpace> targetCS) const197 sk_sp<SkImage> SkImage_GpuYUVA::onMakeColorTypeAndColorSpace(GrRecordingContext*,
198                                                              SkColorType,
199                                                              sk_sp<SkColorSpace> targetCS) const {
200     // We explicitly ignore color type changes, for now.
201 
202     // we may need a mutex here but for now we expect usage to be in a single thread
203     if (fOnMakeColorSpaceTarget &&
204         SkColorSpace::Equals(targetCS.get(), fOnMakeColorSpaceTarget.get())) {
205         return fOnMakeColorSpaceResult;
206     }
207     sk_sp<SkImage> result = sk_sp<SkImage>(new SkImage_GpuYUVA(this, targetCS));
208     if (result) {
209         fOnMakeColorSpaceTarget = targetCS;
210         fOnMakeColorSpaceResult = result;
211     }
212     return result;
213 }
214 
onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const215 sk_sp<SkImage> SkImage_GpuYUVA::onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const {
216     return sk_make_sp<SkImage_GpuYUVA>(fContext, this->width(), this->height(),
217                                        kNeedNewImageUniqueID, fYUVColorSpace, fProxies,
218                                        fProxyColorTypes, fNumProxies, fYUVAIndices, fOrigin,
219                                        std::move(newCS));
220 }
221 
222 //////////////////////////////////////////////////////////////////////////////////////////////////
223 
MakeFromYUVATextures(GrContext * ctx,SkYUVColorSpace colorSpace,const GrBackendTexture yuvaTextures[],const SkYUVAIndex yuvaIndices[4],SkISize imageSize,GrSurfaceOrigin imageOrigin,sk_sp<SkColorSpace> imageColorSpace)224 sk_sp<SkImage> SkImage::MakeFromYUVATextures(GrContext* ctx,
225                                              SkYUVColorSpace colorSpace,
226                                              const GrBackendTexture yuvaTextures[],
227                                              const SkYUVAIndex yuvaIndices[4],
228                                              SkISize imageSize,
229                                              GrSurfaceOrigin imageOrigin,
230                                              sk_sp<SkColorSpace> imageColorSpace) {
231     int numTextures;
232     if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures)) {
233         return nullptr;
234     }
235 
236     sk_sp<GrTextureProxy> tempTextureProxies[4];
237     if (!SkImage_GpuBase::MakeTempTextureProxies(ctx, yuvaTextures, numTextures, yuvaIndices,
238                                                  imageOrigin, tempTextureProxies)) {
239         return nullptr;
240     }
241     GrColorType proxyColorTypes[4];
242     for (int i = 0; i < numTextures; ++i) {
243         proxyColorTypes[i] = ctx->priv().caps()->getYUVAColorTypeFromBackendFormat(
244                 yuvaTextures[i].getBackendFormat(), yuvaIndices[3].fIndex == i);
245     }
246 
247     return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(ctx), imageSize.width(), imageSize.height(),
248                                        kNeedNewImageUniqueID, colorSpace, tempTextureProxies,
249                                        proxyColorTypes, numTextures, yuvaIndices, imageOrigin,
250                                        imageColorSpace);
251 }
252 
MakeFromYUVAPixmaps(GrContext * context,SkYUVColorSpace yuvColorSpace,const SkPixmap yuvaPixmaps[],const SkYUVAIndex yuvaIndices[4],SkISize imageSize,GrSurfaceOrigin imageOrigin,bool buildMips,bool limitToMaxTextureSize,sk_sp<SkColorSpace> imageColorSpace)253 sk_sp<SkImage> SkImage::MakeFromYUVAPixmaps(
254         GrContext* context, SkYUVColorSpace yuvColorSpace, const SkPixmap yuvaPixmaps[],
255         const SkYUVAIndex yuvaIndices[4], SkISize imageSize, GrSurfaceOrigin imageOrigin,
256         bool buildMips, bool limitToMaxTextureSize, sk_sp<SkColorSpace> imageColorSpace) {
257     if (!context) {
258         return nullptr; // until we impl this for raster backend
259     }
260 
261     int numPixmaps;
262     if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numPixmaps)) {
263         return nullptr;
264     }
265 
266     if (!context->priv().caps()->mipMapSupport()) {
267         buildMips = false;
268     }
269 
270     // Make proxies
271     GrProxyProvider* proxyProvider = context->priv().proxyProvider();
272     sk_sp<GrTextureProxy> tempTextureProxies[4];
273     GrColorType proxyColorTypes[4];
274     for (int i = 0; i < numPixmaps; ++i) {
275         const SkPixmap* pixmap = &yuvaPixmaps[i];
276         SkAutoPixmapStorage resized;
277         int maxTextureSize = context->priv().caps()->maxTextureSize();
278         int maxDim = SkTMax(yuvaPixmaps[i].width(), yuvaPixmaps[i].height());
279         if (limitToMaxTextureSize && maxDim > maxTextureSize) {
280             float scale = static_cast<float>(maxTextureSize) / maxDim;
281             int newWidth = SkTMin(static_cast<int>(yuvaPixmaps[i].width() * scale),
282                                   maxTextureSize);
283             int newHeight = SkTMin(static_cast<int>(yuvaPixmaps[i].height() * scale),
284                                    maxTextureSize);
285             SkImageInfo info = yuvaPixmaps[i].info().makeWH(newWidth, newHeight);
286             if (!resized.tryAlloc(info) ||
287                 !yuvaPixmaps[i].scalePixels(resized, kLow_SkFilterQuality)) {
288                 return nullptr;
289             }
290             pixmap = &resized;
291         }
292         // Turn the pixmap into a GrTextureProxy
293         SkBitmap bmp;
294         bmp.installPixels(*pixmap);
295         GrMipMapped mipMapped = buildMips ? GrMipMapped::kYes : GrMipMapped::kNo;
296         tempTextureProxies[i] = proxyProvider->createProxyFromBitmap(bmp, mipMapped);
297         if (!tempTextureProxies[i]) {
298             return nullptr;
299         }
300         proxyColorTypes[i] = SkColorTypeToGrColorType(bmp.colorType());
301     }
302 
303     return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(context), imageSize.width(), imageSize.height(),
304                                        kNeedNewImageUniqueID, yuvColorSpace, tempTextureProxies,
305                                        proxyColorTypes, numPixmaps, yuvaIndices, imageOrigin,
306                                        imageColorSpace);
307 }
308 
309 
310 /////////////////////////////////////////////////////////////////////////////////////////////////
MakePromiseYUVATexture(GrContext * context,SkYUVColorSpace yuvColorSpace,const GrBackendFormat yuvaFormats[],const SkISize yuvaSizes[],const SkYUVAIndex yuvaIndices[4],int imageWidth,int imageHeight,GrSurfaceOrigin imageOrigin,sk_sp<SkColorSpace> imageColorSpace,PromiseImageTextureFulfillProc textureFulfillProc,PromiseImageTextureReleaseProc textureReleaseProc,PromiseImageTextureDoneProc promiseDoneProc,PromiseImageTextureContext textureContexts[],PromiseImageApiVersion version)311 sk_sp<SkImage> SkImage_GpuYUVA::MakePromiseYUVATexture(
312         GrContext* context,
313         SkYUVColorSpace yuvColorSpace,
314         const GrBackendFormat yuvaFormats[],
315         const SkISize yuvaSizes[],
316         const SkYUVAIndex yuvaIndices[4],
317         int imageWidth,
318         int imageHeight,
319         GrSurfaceOrigin imageOrigin,
320         sk_sp<SkColorSpace> imageColorSpace,
321         PromiseImageTextureFulfillProc textureFulfillProc,
322         PromiseImageTextureReleaseProc textureReleaseProc,
323         PromiseImageTextureDoneProc promiseDoneProc,
324         PromiseImageTextureContext textureContexts[],
325         PromiseImageApiVersion version) {
326     int numTextures;
327     bool valid = SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures);
328 
329     // The contract here is that if 'promiseDoneProc' is passed in it should always be called,
330     // even if creation of the SkImage fails. Once we call MakePromiseImageLazyProxy it takes
331     // responsibility for calling the done proc.
332     if (!promiseDoneProc) {
333         return nullptr;
334     }
335     int proxiesCreated = 0;
336     SkScopeExit callDone([promiseDoneProc, textureContexts, numTextures, &proxiesCreated]() {
337         for (int i = proxiesCreated; i < numTextures; ++i) {
338             promiseDoneProc(textureContexts[i]);
339         }
340     });
341 
342     if (!valid) {
343         return nullptr;
344     }
345 
346     if (!context) {
347         return nullptr;
348     }
349 
350     if (imageWidth <= 0 || imageHeight <= 0) {
351         return nullptr;
352     }
353 
354     SkAlphaType at = (-1 != yuvaIndices[SkYUVAIndex::kA_Index].fIndex) ? kPremul_SkAlphaType
355                                                                        : kOpaque_SkAlphaType;
356     SkImageInfo info = SkImageInfo::Make(imageWidth, imageHeight, kAssumedColorType,
357                                          at, imageColorSpace);
358     if (!SkImageInfoIsValid(info)) {
359         return nullptr;
360     }
361 
362     // verify sizes with expected texture count
363     for (int i = 0; i < numTextures; ++i) {
364         if (yuvaSizes[i].isEmpty()) {
365             return nullptr;
366         }
367     }
368     for (int i = numTextures; i < SkYUVASizeInfo::kMaxCount; ++i) {
369         if (!yuvaSizes[i].isEmpty()) {
370             return nullptr;
371         }
372     }
373 
374     // Get lazy proxies
375     sk_sp<GrTextureProxy> proxies[4];
376     GrColorType proxyColorTypes[4];
377     for (int texIdx = 0; texIdx < numTextures; ++texIdx) {
378         GrColorType colorType = context->priv().caps()->getYUVAColorTypeFromBackendFormat(
379                                                                 yuvaFormats[texIdx],
380                                                                 yuvaIndices[3].fIndex == texIdx);
381         if (GrColorType::kUnknown == colorType) {
382             return nullptr;
383         }
384 
385         proxies[texIdx] = MakePromiseImageLazyProxy(
386                 context, yuvaSizes[texIdx].width(), yuvaSizes[texIdx].height(), imageOrigin,
387                 colorType, yuvaFormats[texIdx], GrMipMapped::kNo, textureFulfillProc,
388                 textureReleaseProc, promiseDoneProc, textureContexts[texIdx], version);
389         ++proxiesCreated;
390         if (!proxies[texIdx]) {
391             return nullptr;
392         }
393         proxyColorTypes[texIdx] = colorType;
394     }
395 
396     return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(context), imageWidth, imageHeight,
397                                        kNeedNewImageUniqueID, yuvColorSpace, proxies,
398                                        proxyColorTypes, numTextures, yuvaIndices, imageOrigin,
399                                        std::move(imageColorSpace));
400 }
401