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