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