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 ©Params)) {
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