• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/GrBitmapTextureMaker.h"
20 #include "src/gpu/GrClip.h"
21 #include "src/gpu/GrContextPriv.h"
22 #include "src/gpu/GrGpu.h"
23 #include "src/gpu/GrRecordingContextPriv.h"
24 #include "src/gpu/GrRenderTargetContext.h"
25 #include "src/gpu/GrTextureProducer.h"
26 #include "src/gpu/SkGr.h"
27 #include "src/gpu/effects/GrYUVtoRGBEffect.h"
28 #include "src/image/SkImage_Gpu.h"
29 #include "src/image/SkImage_GpuYUVA.h"
30 
31 static constexpr auto kAssumedColorType = kRGBA_8888_SkColorType;
32 
SkImage_GpuYUVA(sk_sp<GrContext> context,SkISize size,uint32_t uniqueID,SkYUVColorSpace colorSpace,GrSurfaceProxyView views[],GrColorType proxyColorTypes[],int numViews,const SkYUVAIndex yuvaIndices[4],GrSurfaceOrigin origin,sk_sp<SkColorSpace> imageColorSpace)33 SkImage_GpuYUVA::SkImage_GpuYUVA(sk_sp<GrContext> context, SkISize size, uint32_t uniqueID,
34                                  SkYUVColorSpace colorSpace, GrSurfaceProxyView views[],
35                                  GrColorType proxyColorTypes[], int numViews,
36                                  const SkYUVAIndex yuvaIndices[4], GrSurfaceOrigin origin,
37                                  sk_sp<SkColorSpace> imageColorSpace)
38         : INHERITED(std::move(context), size, uniqueID, kAssumedColorType,
39                     // If an alpha channel is present we always switch to kPremul. This is because,
40                     // although the planar data is always un-premul, the final interleaved RGB image
41                     // is/would-be premul.
42                     GetAlphaTypeFromYUVAIndices(yuvaIndices), std::move(imageColorSpace))
43         , fNumViews(numViews)
44         , fYUVColorSpace(colorSpace)
45         , fOrigin(origin) {
46     // The caller should have done this work, just verifying
47     SkDEBUGCODE(int textureCount;)
48     SkASSERT(SkYUVAIndex::AreValidIndices(yuvaIndices, &textureCount));
49     SkASSERT(textureCount == fNumViews);
50 
51     for (int i = 0; i < numViews; ++i) {
52         fViews[i] = std::move(views[i]);
53         fProxyColorTypes[i] = proxyColorTypes[i];
54     }
55     memcpy(fYUVAIndices, yuvaIndices, 4 * sizeof(SkYUVAIndex));
56 }
57 
58 // For onMakeColorSpace()
SkImage_GpuYUVA(const SkImage_GpuYUVA * image,sk_sp<SkColorSpace> targetCS)59 SkImage_GpuYUVA::SkImage_GpuYUVA(const SkImage_GpuYUVA* image, sk_sp<SkColorSpace> targetCS)
60         : INHERITED(image->fContext, image->dimensions(), kNeedNewImageUniqueID, 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         , fNumViews(image->fNumViews)
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 == fNumViews);
75 
76     if (image->fRGBView.proxy()) {
77         fRGBView = image->fRGBView;  // we ref in this case, not move
78     } else {
79         for (int i = 0; i < fNumViews; ++i) {
80             fViews[i] = image->fViews[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 
setupMipmapsForPlanes(GrRecordingContext * context) const87 bool SkImage_GpuYUVA::setupMipmapsForPlanes(GrRecordingContext* context) const {
88     // We shouldn't get here if the planes were already flattened to RGBA.
89     SkASSERT(fViews[0].proxy() && !fRGBView.proxy());
90     if (!context || !fContext->priv().matches(context)) {
91         return false;
92     }
93 
94     for (int i = 0; i < fNumViews; ++i) {
95         GrTextureProducer::CopyParams copyParams;
96         int mipCount = SkMipMap::ComputeLevelCount(fViews[i].proxy()->width(),
97                                                    fViews[i].proxy()->height());
98         if (mipCount && GrGpu::IsACopyNeededForMips(fContext->priv().caps(),
99                                                     fViews[i].asTextureProxy(),
100                                                     GrSamplerState::Filter::kMipMap,
101                                                     &copyParams)) {
102             auto mippedView = GrCopyBaseMipMapToTextureProxy(context, fViews[i].asTextureProxy(),
103                                                              fOrigin, fProxyColorTypes[i]);
104             if (!mippedView.proxy()) {
105                 return false;
106             }
107             fViews[i] = std::move(mippedView);
108         }
109     }
110     return true;
111 }
112 
113 //////////////////////////////////////////////////////////////////////////////////////////////////
114 
onFlush(GrContext * context,const GrFlushInfo & info)115 GrSemaphoresSubmitted SkImage_GpuYUVA::onFlush(GrContext* context, const GrFlushInfo& info) {
116     if (!context || !fContext->priv().matches(context) || fContext->abandoned()) {
117         return GrSemaphoresSubmitted::kNo;
118     }
119 
120     GrSurfaceProxy* proxies[4] = {fViews[0].proxy(), fViews[1].proxy(), fViews[2].proxy(),
121                                   fViews[3].proxy()};
122     int numProxies = fNumViews;
123     if (fRGBView.proxy()) {
124         // Either we've already flushed the flattening draw or the flattening is unflushed. In the
125         // latter case it should still be ok to just pass fRGBView proxy because it in turn depends
126         // on the planar proxies and will cause all of their work to flush as well.
127         proxies[0] = fRGBView.proxy();
128         numProxies = 1;
129     }
130     return context->priv().flushSurfaces(proxies, numProxies, info);
131 }
132 
peekProxy() const133 GrTextureProxy* SkImage_GpuYUVA::peekProxy() const { return fRGBView.asTextureProxy(); }
134 
flattenToRGB(GrRecordingContext * context) const135 void SkImage_GpuYUVA::flattenToRGB(GrRecordingContext* context) const {
136     if (fRGBView.proxy()) {
137         return;
138     }
139 
140     if (!context || !fContext->priv().matches(context)) {
141         return;
142     }
143 
144     // Needs to create a render target in order to draw to it for the yuv->rgb conversion.
145     auto renderTargetContext = GrRenderTargetContext::Make(
146             context, GrColorType::kRGBA_8888, this->refColorSpace(), SkBackingFit::kExact,
147             this->dimensions(), 1, GrMipMapped::kNo, GrProtected::kNo, fOrigin);
148     if (!renderTargetContext) {
149         return;
150     }
151 
152     sk_sp<GrColorSpaceXform> colorSpaceXform;
153     if (fFromColorSpace) {
154         colorSpaceXform = GrColorSpaceXform::Make(fFromColorSpace.get(), this->alphaType(),
155                                                   this->colorSpace(), this->alphaType());
156     }
157     const SkRect rect = SkRect::MakeIWH(this->width(), this->height());
158     if (!RenderYUVAToRGBA(fContext.get(), renderTargetContext.get(), rect, fYUVColorSpace,
159                           std::move(colorSpaceXform), fViews, fYUVAIndices)) {
160         return;
161     }
162 
163     fRGBView = renderTargetContext->readSurfaceView();
164     SkASSERT(fRGBView.origin() == fOrigin);
165     SkASSERT(fRGBView.swizzle() == GrSwizzle());
166     for (auto& v : fViews) {
167         v.reset();
168     }
169 }
170 
refMippedView(GrRecordingContext * context) const171 GrSurfaceProxyView SkImage_GpuYUVA::refMippedView(GrRecordingContext* context) const {
172     // if invalid or already has miplevels
173     this->flattenToRGB(context);
174     if (!fRGBView.proxy() || GrMipMapped::kYes == fRGBView.asTextureProxy()->mipMapped()) {
175         return fRGBView;
176     }
177 
178     // need to generate mips for the proxy
179     GrColorType srcColorType = SkColorTypeToGrColorType(this->colorType());
180     GrSurfaceProxyView mippedView = GrCopyBaseMipMapToTextureProxy(context, fRGBView.proxy(),
181                                                                    fRGBView.origin(), srcColorType);
182     if (mippedView.proxy()) {
183         fRGBView = std::move(mippedView);
184         return fRGBView;
185     }
186 
187     // failed to generate mips
188     return {};
189 }
190 
view(GrRecordingContext * context) const191 const GrSurfaceProxyView* SkImage_GpuYUVA::view(GrRecordingContext* context) const {
192     this->flattenToRGB(context);
193     if (!fRGBView.proxy()) {
194         return nullptr;
195     }
196     return &fRGBView;
197 }
198 
199 //////////////////////////////////////////////////////////////////////////////////////////////////
200 
onMakeColorTypeAndColorSpace(GrRecordingContext *,SkColorType,sk_sp<SkColorSpace> targetCS) const201 sk_sp<SkImage> SkImage_GpuYUVA::onMakeColorTypeAndColorSpace(
202         GrRecordingContext*, SkColorType, sk_sp<SkColorSpace> targetCS) const {
203     // We explicitly ignore color type changes, for now.
204 
205     // we may need a mutex here but for now we expect usage to be in a single thread
206     if (fOnMakeColorSpaceTarget &&
207         SkColorSpace::Equals(targetCS.get(), fOnMakeColorSpaceTarget.get())) {
208         return fOnMakeColorSpaceResult;
209     }
210     sk_sp<SkImage> result = sk_sp<SkImage>(new SkImage_GpuYUVA(this, targetCS));
211     if (result) {
212         fOnMakeColorSpaceTarget = targetCS;
213         fOnMakeColorSpaceResult = result;
214     }
215     return result;
216 }
217 
onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const218 sk_sp<SkImage> SkImage_GpuYUVA::onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const {
219     return sk_make_sp<SkImage_GpuYUVA>(fContext, this->dimensions(), kNeedNewImageUniqueID,
220                                        fYUVColorSpace, fViews, fProxyColorTypes, fNumViews,
221                                        fYUVAIndices, fOrigin, std::move(newCS));
222 }
223 
224 //////////////////////////////////////////////////////////////////////////////////////////////////
225 
MakeFromYUVATextures(GrContext * ctx,SkYUVColorSpace colorSpace,const GrBackendTexture yuvaTextures[],const SkYUVAIndex yuvaIndices[4],SkISize imageSize,GrSurfaceOrigin imageOrigin,sk_sp<SkColorSpace> imageColorSpace)226 sk_sp<SkImage> SkImage::MakeFromYUVATextures(GrContext* ctx,
227                                              SkYUVColorSpace colorSpace,
228                                              const GrBackendTexture yuvaTextures[],
229                                              const SkYUVAIndex yuvaIndices[4],
230                                              SkISize imageSize,
231                                              GrSurfaceOrigin imageOrigin,
232                                              sk_sp<SkColorSpace> imageColorSpace) {
233     int numTextures;
234     if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures)) {
235         return nullptr;
236     }
237 
238     GrSurfaceProxyView tempViews[4];
239     if (!SkImage_GpuBase::MakeTempTextureProxies(ctx, yuvaTextures, numTextures, yuvaIndices,
240                                                  imageOrigin, tempViews)) {
241         return nullptr;
242     }
243     GrColorType proxyColorTypes[4];
244     for (int i = 0; i < numTextures; ++i) {
245         proxyColorTypes[i] = ctx->priv().caps()->getYUVAColorTypeFromBackendFormat(
246                 yuvaTextures[i].getBackendFormat(), yuvaIndices[3].fIndex == i);
247     }
248 
249     return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(ctx), imageSize, kNeedNewImageUniqueID, colorSpace,
250                                        tempViews, proxyColorTypes, numTextures, yuvaIndices,
251                                        imageOrigin, imageColorSpace);
252 }
253 
MakeFromYUVAPixmaps(GrContext * context,SkYUVColorSpace yuvColorSpace,const SkPixmap yuvaPixmaps[],const SkYUVAIndex yuvaIndices[4],SkISize imageSize,GrSurfaceOrigin imageOrigin,bool buildMips,bool limitToMaxTextureSize,sk_sp<SkColorSpace> imageColorSpace)254 sk_sp<SkImage> SkImage::MakeFromYUVAPixmaps(GrContext* context, SkYUVColorSpace yuvColorSpace,
255                                             const SkPixmap yuvaPixmaps[],
256                                             const SkYUVAIndex yuvaIndices[4], SkISize imageSize,
257                                             GrSurfaceOrigin imageOrigin, bool buildMips,
258                                             bool limitToMaxTextureSize,
259                                             sk_sp<SkColorSpace> imageColorSpace) {
260     if (!context) {
261         return nullptr;  // until we impl this for raster backend
262     }
263 
264     int numPixmaps;
265     if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numPixmaps)) {
266         return nullptr;
267     }
268 
269     if (!context->priv().caps()->mipMapSupport()) {
270         buildMips = false;
271     }
272 
273     // Make proxies
274     GrSurfaceProxyView tempViews[4];
275     GrColorType proxyColorTypes[4];
276     for (int i = 0; i < numPixmaps; ++i) {
277         const SkPixmap* pixmap = &yuvaPixmaps[i];
278         SkAutoPixmapStorage resized;
279         int maxTextureSize = context->priv().caps()->maxTextureSize();
280         int maxDim = std::max(yuvaPixmaps[i].width(), yuvaPixmaps[i].height());
281         if (limitToMaxTextureSize && maxDim > maxTextureSize) {
282             float scale = static_cast<float>(maxTextureSize) / maxDim;
283             int newWidth = std::min(static_cast<int>(yuvaPixmaps[i].width() * scale), maxTextureSize);
284             int newHeight =
285                     std::min(static_cast<int>(yuvaPixmaps[i].height() * scale), maxTextureSize);
286             SkImageInfo info = yuvaPixmaps[i].info().makeWH(newWidth, newHeight);
287             if (!resized.tryAlloc(info) ||
288                 !yuvaPixmaps[i].scalePixels(resized, kLow_SkFilterQuality)) {
289                 return nullptr;
290             }
291             pixmap = &resized;
292         }
293         // Turn the pixmap into a GrTextureProxy
294         SkBitmap bmp;
295         bmp.installPixels(*pixmap);
296         GrBitmapTextureMaker bitmapMaker(context, bmp);
297         GrMipMapped mipMapped = buildMips ? GrMipMapped::kYes : GrMipMapped::kNo;
298         GrSurfaceProxyView view;
299         std::tie(tempViews[i], proxyColorTypes[i]) = bitmapMaker.view(mipMapped);
300         if (!tempViews[i]) {
301             return nullptr;
302         }
303     }
304 
305     return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(context), imageSize, kNeedNewImageUniqueID,
306                                        yuvColorSpace, tempViews, proxyColorTypes, numPixmaps,
307                                        yuvaIndices, imageOrigin, imageColorSpace);
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 =
357             SkImageInfo::Make(imageWidth, imageHeight, kAssumedColorType, 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     GrSurfaceProxyView views[4];
376     GrColorType proxyColorTypes[4];
377     for (int texIdx = 0; texIdx < numTextures; ++texIdx) {
378         GrColorType colorType = context->priv().caps()->getYUVAColorTypeFromBackendFormat(
379                 yuvaFormats[texIdx], yuvaIndices[3].fIndex == texIdx);
380         if (GrColorType::kUnknown == colorType) {
381             return nullptr;
382         }
383 
384         auto proxy = MakePromiseImageLazyProxy(
385                 context, yuvaSizes[texIdx].width(), yuvaSizes[texIdx].height(), colorType,
386                 yuvaFormats[texIdx], GrMipMapped::kNo, textureFulfillProc, textureReleaseProc,
387                 promiseDoneProc, textureContexts[texIdx], version);
388         ++proxiesCreated;
389         if (!proxy) {
390             return nullptr;
391         }
392         GrSwizzle swizzle = context->priv().caps()->getReadSwizzle(proxy->backendFormat(),
393                                                                    colorType);
394         views[texIdx] = GrSurfaceProxyView(std::move(proxy), imageOrigin, swizzle);
395         proxyColorTypes[texIdx] = colorType;
396     }
397 
398     return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(context), SkISize{imageWidth, imageHeight},
399                                        kNeedNewImageUniqueID, yuvColorSpace, views,
400                                        proxyColorTypes, numTextures, yuvaIndices, imageOrigin,
401                                        std::move(imageColorSpace));
402 }
403