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