• 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 "GrClip.h"
13 #include "GrContext.h"
14 #include "GrContextPriv.h"
15 #include "GrGpu.h"
16 #include "GrRecordingContext.h"
17 #include "GrRecordingContextPriv.h"
18 #include "GrRenderTargetContext.h"
19 #include "GrTexture.h"
20 #include "GrTextureProducer.h"
21 #include "SkAutoPixmapStorage.h"
22 #include "SkGr.h"
23 #include "SkImage_Gpu.h"
24 #include "SkImage_GpuYUVA.h"
25 #include "SkMipMap.h"
26 #include "SkScopeExit.h"
27 #include "SkYUVASizeInfo.h"
28 #include "effects/GrYUVtoRGBEffect.h"
29 
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)30 SkImage_GpuYUVA::SkImage_GpuYUVA(sk_sp<GrContext> context, int width, int height, uint32_t uniqueID,
31                                  SkYUVColorSpace colorSpace, sk_sp<GrTextureProxy> proxies[],
32                                  int numProxies, const SkYUVAIndex yuvaIndices[4],
33                                  GrSurfaceOrigin origin, sk_sp<SkColorSpace> imageColorSpace)
34         : INHERITED(std::move(context), width, height, uniqueID,
35                     // If an alpha channel is present we always switch to kPremul. This is because,
36                     // although the planar data is always un-premul, the final interleaved RGB image
37                     // is/would-be premul.
38                     GetAlphaTypeFromYUVAIndices(yuvaIndices), imageColorSpace)
39         , fNumProxies(numProxies)
40         , fYUVColorSpace(colorSpace)
41         , fOrigin(origin) {
42     // The caller should have done this work, just verifying
43     SkDEBUGCODE(int textureCount;)
44     SkASSERT(SkYUVAIndex::AreValidIndices(yuvaIndices, &textureCount));
45     SkASSERT(textureCount == fNumProxies);
46 
47     for (int i = 0; i < numProxies; ++i) {
48         fProxies[i] = std::move(proxies[i]);
49     }
50     memcpy(fYUVAIndices, yuvaIndices, 4*sizeof(SkYUVAIndex));
51 }
52 
53 // For onMakeColorSpace()
SkImage_GpuYUVA(const SkImage_GpuYUVA * image,sk_sp<SkColorSpace> targetCS)54 SkImage_GpuYUVA::SkImage_GpuYUVA(const SkImage_GpuYUVA* image, sk_sp<SkColorSpace> targetCS)
55     : INHERITED(image->fContext, image->width(), image->height(), kNeedNewImageUniqueID,
56                 // If an alpha channel is present we always switch to kPremul. This is because,
57                 // although the planar data is always un-premul, the final interleaved RGB image
58                 // is/would-be premul.
59                 GetAlphaTypeFromYUVAIndices(image->fYUVAIndices), image->fColorSpace)
60     , fNumProxies(image->fNumProxies)
61     , fYUVColorSpace(image->fYUVColorSpace)
62     , fOrigin(image->fOrigin)
63     , fTargetColorSpace(targetCS) {
64         // The caller should have done this work, just verifying
65     SkDEBUGCODE(int textureCount;)
66         SkASSERT(SkYUVAIndex::AreValidIndices(image->fYUVAIndices, &textureCount));
67     SkASSERT(textureCount == fNumProxies);
68 
69     for (int i = 0; i < fNumProxies; ++i) {
70         fProxies[i] = image->fProxies[i];  // we ref in this case, not move
71     }
72     memcpy(fYUVAIndices, image->fYUVAIndices, 4 * sizeof(SkYUVAIndex));
73 }
74 
~SkImage_GpuYUVA()75 SkImage_GpuYUVA::~SkImage_GpuYUVA() {}
76 
onImageInfo() const77 SkImageInfo SkImage_GpuYUVA::onImageInfo() const {
78     // Note: this is the imageInfo for the flattened image, not the YUV planes
79     return SkImageInfo::Make(this->width(), this->height(), kRGBA_8888_SkColorType,
80                              fAlphaType, fTargetColorSpace ? fTargetColorSpace : fColorSpace);
81 }
82 
setupMipmapsForPlanes(GrRecordingContext * context) const83 bool SkImage_GpuYUVA::setupMipmapsForPlanes(GrRecordingContext* context) const {
84     if (!context || !fContext->priv().matches(context)) {
85         return false;
86     }
87 
88     for (int i = 0; i < fNumProxies; ++i) {
89         GrTextureProducer::CopyParams copyParams;
90         int mipCount = SkMipMap::ComputeLevelCount(fProxies[i]->width(), fProxies[i]->height());
91         if (mipCount && GrGpu::IsACopyNeededForMips(fContext->priv().caps(),
92                                                     fProxies[i].get(),
93                                                     GrSamplerState::Filter::kMipMap,
94                                                     &copyParams)) {
95             auto mippedProxy = GrCopyBaseMipMapToTextureProxy(context, fProxies[i].get());
96             if (!mippedProxy) {
97                 return false;
98             }
99             fProxies[i] = mippedProxy;
100         }
101     }
102     return true;
103 }
104 
105 //////////////////////////////////////////////////////////////////////////////////////////////////
106 
peekProxy() const107 GrTextureProxy* SkImage_GpuYUVA::peekProxy() const {
108     return fRGBProxy.get();
109 }
110 
asTextureProxyRef(GrRecordingContext * context) const111 sk_sp<GrTextureProxy> SkImage_GpuYUVA::asTextureProxyRef(GrRecordingContext* context) const {
112     if (fRGBProxy) {
113         return fRGBProxy;
114     }
115 
116     if (!context || !fContext->priv().matches(context)) {
117         return nullptr;
118     }
119 
120     const GrBackendFormat format =
121         fContext->priv().caps()->getBackendFormatFromColorType(kRGBA_8888_SkColorType);
122 
123     // Needs to create a render target in order to draw to it for the yuv->rgb conversion.
124     sk_sp<GrRenderTargetContext> renderTargetContext(
125         context->priv().makeDeferredRenderTargetContext(
126             format, SkBackingFit::kExact, this->width(), this->height(),
127             kRGBA_8888_GrPixelConfig, fColorSpace, 1, GrMipMapped::kNo, fOrigin));
128     if (!renderTargetContext) {
129         return nullptr;
130     }
131 
132     auto colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(), fAlphaType,
133                                                     fTargetColorSpace.get(), fAlphaType);
134     const SkRect rect = SkRect::MakeIWH(this->width(), this->height());
135     if (!RenderYUVAToRGBA(fContext.get(), renderTargetContext.get(), rect, fYUVColorSpace,
136                           std::move(colorSpaceXform), fProxies, fYUVAIndices)) {
137         return nullptr;
138     }
139 
140     fRGBProxy = renderTargetContext->asTextureProxyRef();
141     return fRGBProxy;
142 }
143 
asMippedTextureProxyRef(GrRecordingContext * context) const144 sk_sp<GrTextureProxy> SkImage_GpuYUVA::asMippedTextureProxyRef(GrRecordingContext* context) const {
145     if (!context || !fContext->priv().matches(context)) {
146         return nullptr;
147     }
148 
149     // if invalid or already has miplevels
150     auto proxy = this->asTextureProxyRef(context);
151     if (!proxy || GrMipMapped::kYes == fRGBProxy->mipMapped()) {
152         return proxy;
153     }
154 
155     // need to generate mips for the proxy
156     if (auto mippedProxy = GrCopyBaseMipMapToTextureProxy(context, proxy.get())) {
157         fRGBProxy = mippedProxy;
158         return mippedProxy;
159     }
160 
161     // failed to generate mips
162     return nullptr;
163 }
164 
165 //////////////////////////////////////////////////////////////////////////////////////////////////
166 
onMakeColorTypeAndColorSpace(GrRecordingContext *,SkColorType,sk_sp<SkColorSpace> targetCS) const167 sk_sp<SkImage> SkImage_GpuYUVA::onMakeColorTypeAndColorSpace(GrRecordingContext*,
168                                                              SkColorType,
169                                                              sk_sp<SkColorSpace> targetCS) const {
170     // We explicitly ignore color type changes, for now.
171 
172     // we may need a mutex here but for now we expect usage to be in a single thread
173     if (fOnMakeColorSpaceTarget &&
174         SkColorSpace::Equals(targetCS.get(), fOnMakeColorSpaceTarget.get())) {
175         return fOnMakeColorSpaceResult;
176     }
177     sk_sp<SkImage> result = sk_sp<SkImage>(new SkImage_GpuYUVA(this, targetCS));
178     if (result) {
179         fOnMakeColorSpaceTarget = targetCS;
180         fOnMakeColorSpaceResult = result;
181     }
182     return result;
183 }
184 
185 //////////////////////////////////////////////////////////////////////////////////////////////////
186 
MakeFromYUVATextures(GrContext * ctx,SkYUVColorSpace colorSpace,const GrBackendTexture yuvaTextures[],const SkYUVAIndex yuvaIndices[4],SkISize imageSize,GrSurfaceOrigin imageOrigin,sk_sp<SkColorSpace> imageColorSpace)187 sk_sp<SkImage> SkImage::MakeFromYUVATextures(GrContext* ctx,
188                                              SkYUVColorSpace colorSpace,
189                                              const GrBackendTexture yuvaTextures[],
190                                              const SkYUVAIndex yuvaIndices[4],
191                                              SkISize imageSize,
192                                              GrSurfaceOrigin imageOrigin,
193                                              sk_sp<SkColorSpace> imageColorSpace) {
194     int numTextures;
195     if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures)) {
196         return nullptr;
197     }
198 
199     sk_sp<GrTextureProxy> tempTextureProxies[4];
200     if (!SkImage_GpuBase::MakeTempTextureProxies(ctx, yuvaTextures, numTextures, yuvaIndices,
201                                                  imageOrigin, tempTextureProxies)) {
202         return nullptr;
203     }
204 
205     return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(ctx), imageSize.width(), imageSize.height(),
206                                        kNeedNewImageUniqueID, colorSpace, tempTextureProxies,
207                                        numTextures, yuvaIndices, imageOrigin, imageColorSpace);
208 }
209 
MakeFromYUVAPixmaps(GrContext * context,SkYUVColorSpace yuvColorSpace,const SkPixmap yuvaPixmaps[],const SkYUVAIndex yuvaIndices[4],SkISize imageSize,GrSurfaceOrigin imageOrigin,bool buildMips,bool limitToMaxTextureSize,sk_sp<SkColorSpace> imageColorSpace)210 sk_sp<SkImage> SkImage::MakeFromYUVAPixmaps(
211         GrContext* context, SkYUVColorSpace yuvColorSpace, const SkPixmap yuvaPixmaps[],
212         const SkYUVAIndex yuvaIndices[4], SkISize imageSize, GrSurfaceOrigin imageOrigin,
213         bool buildMips, bool limitToMaxTextureSize, sk_sp<SkColorSpace> imageColorSpace) {
214     int numPixmaps;
215     if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numPixmaps)) {
216         return nullptr;
217     }
218 
219     // Make proxies
220     GrProxyProvider* proxyProvider = context->priv().proxyProvider();
221     sk_sp<GrTextureProxy> tempTextureProxies[4];
222     for (int i = 0; i < numPixmaps; ++i) {
223         const SkPixmap* pixmap = &yuvaPixmaps[i];
224         SkAutoPixmapStorage resized;
225         int maxTextureSize = context->priv().caps()->maxTextureSize();
226         int maxDim = SkTMax(yuvaPixmaps[i].width(), yuvaPixmaps[i].height());
227         if (limitToMaxTextureSize && maxDim > maxTextureSize) {
228             float scale = static_cast<float>(maxTextureSize) / maxDim;
229             int newWidth = SkTMin(static_cast<int>(yuvaPixmaps[i].width() * scale),
230                                   maxTextureSize);
231             int newHeight = SkTMin(static_cast<int>(yuvaPixmaps[i].height() * scale),
232                                    maxTextureSize);
233             SkImageInfo info = yuvaPixmaps[i].info().makeWH(newWidth, newHeight);
234             if (!resized.tryAlloc(info) ||
235                 !yuvaPixmaps[i].scalePixels(resized, kLow_SkFilterQuality)) {
236                 return nullptr;
237             }
238             pixmap = &resized;
239         }
240         // Turn the pixmap into a GrTextureProxy
241         if (buildMips) {
242             SkBitmap bmp;
243             bmp.installPixels(*pixmap);
244             tempTextureProxies[i] = proxyProvider->createMipMapProxyFromBitmap(bmp);
245         }
246         if (!tempTextureProxies[i]) {
247             if (SkImageInfoIsValid(pixmap->info())) {
248                 ATRACE_ANDROID_FRAMEWORK("Upload Texture [%ux%u]",
249                                          pixmap->width(), pixmap->height());
250                 // We don't need a release proc on the data in pixmap since we know we are in a
251                 // GrContext that has a resource provider. Thus the createTextureProxy call will
252                 // immediately upload the data.
253                 sk_sp<SkImage> image = SkImage::MakeFromRaster(*pixmap, nullptr, nullptr);
254                 tempTextureProxies[i] =
255                         proxyProvider->createTextureProxy(std::move(image), kNone_GrSurfaceFlags, 1,
256                                                           SkBudgeted::kYes, SkBackingFit::kExact);
257             }
258         }
259 
260         if (!tempTextureProxies[i]) {
261             return nullptr;
262         }
263     }
264 
265     return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(context), imageSize.width(), imageSize.height(),
266                                        kNeedNewImageUniqueID, yuvColorSpace, tempTextureProxies,
267                                        numPixmaps, yuvaIndices, imageOrigin, imageColorSpace);
268 }
269 
270 
271 /////////////////////////////////////////////////////////////////////////////////////////////////
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[])272 sk_sp<SkImage> SkImage_GpuYUVA::MakePromiseYUVATexture(
273         GrContext* context,
274         SkYUVColorSpace yuvColorSpace,
275         const GrBackendFormat yuvaFormats[],
276         const SkISize yuvaSizes[],
277         const SkYUVAIndex yuvaIndices[4],
278         int imageWidth,
279         int imageHeight,
280         GrSurfaceOrigin imageOrigin,
281         sk_sp<SkColorSpace> imageColorSpace,
282         PromiseImageTextureFulfillProc textureFulfillProc,
283         PromiseImageTextureReleaseProc textureReleaseProc,
284         PromiseImageTextureDoneProc promiseDoneProc,
285         PromiseImageTextureContext textureContexts[]) {
286     int numTextures;
287     bool valid = SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures);
288 
289     // The contract here is that if 'promiseDoneProc' is passed in it should always be called,
290     // even if creation of the SkImage fails. Once we call MakePromiseImageLazyProxy it takes
291     // responsibility for calling the done proc.
292     if (!promiseDoneProc) {
293         return nullptr;
294     }
295     int proxiesCreated = 0;
296     SkScopeExit callDone([promiseDoneProc, textureContexts, numTextures, &proxiesCreated]() {
297         for (int i = proxiesCreated; i < numTextures; ++i) {
298             promiseDoneProc(textureContexts[i]);
299         }
300     });
301 
302     if (!valid) {
303         return nullptr;
304     }
305 
306     if (!context) {
307         return nullptr;
308     }
309 
310     if (imageWidth <= 0 || imageHeight <= 0) {
311         return nullptr;
312     }
313 
314     SkAlphaType at = (-1 != yuvaIndices[SkYUVAIndex::kA_Index].fIndex) ? kPremul_SkAlphaType
315                                                                        : kOpaque_SkAlphaType;
316     SkImageInfo info = SkImageInfo::Make(imageWidth, imageHeight, kRGBA_8888_SkColorType,
317                                          at, imageColorSpace);
318     if (!SkImageInfoIsValid(info)) {
319         return nullptr;
320     }
321 
322     // verify sizes with expected texture count
323     for (int i = 0; i < numTextures; ++i) {
324         if (yuvaSizes[i].isEmpty()) {
325             return nullptr;
326         }
327     }
328     for (int i = numTextures; i < SkYUVASizeInfo::kMaxCount; ++i) {
329         if (!yuvaSizes[i].isEmpty()) {
330             return nullptr;
331         }
332     }
333 
334     // Get lazy proxies
335     sk_sp<GrTextureProxy> proxies[4];
336     for (int texIdx = 0; texIdx < numTextures; ++texIdx) {
337         GrPixelConfig config =
338                 context->priv().caps()->getYUVAConfigFromBackendFormat(yuvaFormats[texIdx]);
339         if (config == kUnknown_GrPixelConfig) {
340             return nullptr;
341         }
342         proxies[texIdx] = MakePromiseImageLazyProxy(
343                 context, yuvaSizes[texIdx].width(), yuvaSizes[texIdx].height(), imageOrigin, config,
344                 yuvaFormats[texIdx], GrMipMapped::kNo, textureFulfillProc, textureReleaseProc,
345                 promiseDoneProc, textureContexts[texIdx]);
346         ++proxiesCreated;
347         if (!proxies[texIdx]) {
348             return nullptr;
349         }
350     }
351 
352     return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(context), imageWidth, imageHeight,
353                                        kNeedNewImageUniqueID, yuvColorSpace, proxies, numTextures,
354                                        yuvaIndices, imageOrigin, std::move(imageColorSpace));
355 }
356