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