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[],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 int numProxies, const SkYUVAIndex yuvaIndices[4],
35 GrSurfaceOrigin origin, sk_sp<SkColorSpace> imageColorSpace)
36 : INHERITED(std::move(context), width, height, uniqueID, kAssumedColorType,
37 // If an alpha channel is present we always switch to kPremul. This is because,
38 // although the planar data is always un-premul, the final interleaved RGB image
39 // is/would-be premul.
40 GetAlphaTypeFromYUVAIndices(yuvaIndices), std::move(imageColorSpace))
41 , fNumProxies(numProxies)
42 , fYUVColorSpace(colorSpace)
43 , fOrigin(origin) {
44 // The caller should have done this work, just verifying
45 SkDEBUGCODE(int textureCount;)
46 SkASSERT(SkYUVAIndex::AreValidIndices(yuvaIndices, &textureCount));
47 SkASSERT(textureCount == fNumProxies);
48
49 for (int i = 0; i < numProxies; ++i) {
50 fProxies[i] = std::move(proxies[i]);
51 }
52 memcpy(fYUVAIndices, yuvaIndices, 4*sizeof(SkYUVAIndex));
53 }
54
55 // For onMakeColorSpace()
SkImage_GpuYUVA(const SkImage_GpuYUVA * image,sk_sp<SkColorSpace> targetCS)56 SkImage_GpuYUVA::SkImage_GpuYUVA(const SkImage_GpuYUVA* image, sk_sp<SkColorSpace> targetCS)
57 : INHERITED(image->fContext, image->width(), image->height(), kNeedNewImageUniqueID,
58 kAssumedColorType,
59 // If an alpha channel is present we always switch to kPremul. This is because,
60 // although the planar data is always un-premul, the final interleaved RGB image
61 // is/would-be premul.
62 GetAlphaTypeFromYUVAIndices(image->fYUVAIndices), std::move(targetCS))
63 , fNumProxies(image->fNumProxies)
64 , fYUVColorSpace(image->fYUVColorSpace)
65 , fOrigin(image->fOrigin)
66 // Since null fFromColorSpace means no GrColorSpaceXform, we turn a null
67 // image->refColorSpace() into an explicit SRGB.
68 , fFromColorSpace(image->colorSpace() ? image->refColorSpace() : SkColorSpace::MakeSRGB()) {
69 // The caller should have done this work, just verifying
70 SkDEBUGCODE(int textureCount;)
71 SkASSERT(SkYUVAIndex::AreValidIndices(image->fYUVAIndices, &textureCount));
72 SkASSERT(textureCount == fNumProxies);
73
74 if (image->fRGBProxy) {
75 fRGBProxy = image->fRGBProxy; // we ref in this case, not move
76 } else {
77 for (int i = 0; i < fNumProxies; ++i) {
78 fProxies[i] = image->fProxies[i]; // we ref in this case, not move
79 }
80 }
81 memcpy(fYUVAIndices, image->fYUVAIndices, 4 * sizeof(SkYUVAIndex));
82 }
83
~SkImage_GpuYUVA()84 SkImage_GpuYUVA::~SkImage_GpuYUVA() {}
85
setupMipmapsForPlanes(GrRecordingContext * context) const86 bool SkImage_GpuYUVA::setupMipmapsForPlanes(GrRecordingContext* context) const {
87 // We shouldn't get here if the planes were already flattened to RGBA.
88 SkASSERT(fProxies[0] && !fRGBProxy);
89 if (!context || !fContext->priv().matches(context)) {
90 return false;
91 }
92
93 for (int i = 0; i < fNumProxies; ++i) {
94 GrTextureProducer::CopyParams copyParams;
95 int mipCount = SkMipMap::ComputeLevelCount(fProxies[i]->width(), fProxies[i]->height());
96 if (mipCount && GrGpu::IsACopyNeededForMips(fContext->priv().caps(),
97 fProxies[i].get(),
98 GrSamplerState::Filter::kMipMap,
99 ©Params)) {
100 auto mippedProxy = GrCopyBaseMipMapToTextureProxy(context, fProxies[i].get());
101 if (!mippedProxy) {
102 return false;
103 }
104 fProxies[i] = mippedProxy;
105 }
106 }
107 return true;
108 }
109
110 //////////////////////////////////////////////////////////////////////////////////////////////////
111
onFlush(GrContext * context,const GrFlushInfo & info)112 GrSemaphoresSubmitted SkImage_GpuYUVA::onFlush(GrContext* context, const GrFlushInfo& info) {
113 if (!context || !fContext->priv().matches(context) || fContext->abandoned()) {
114 return GrSemaphoresSubmitted::kNo;
115 }
116
117 GrSurfaceProxy* proxies[4] = {fProxies[0].get(), fProxies[1].get(),
118 fProxies[2].get(), fProxies[3].get()};
119 int numProxies = fNumProxies;
120 if (fRGBProxy) {
121 // Either we've already flushed the flattening draw or the flattening is unflushed. In the
122 // latter case it should still be ok to just pass fRGBProxy because it in turn depends on
123 // the planar proxies and will cause all of their work to flush as well.
124 proxies[0] = fRGBProxy.get();
125 numProxies = 1;
126 }
127 return context->priv().flushSurfaces(proxies, numProxies, info);
128 }
129
peekProxy() const130 GrTextureProxy* SkImage_GpuYUVA::peekProxy() const {
131 return fRGBProxy.get();
132 }
133
asTextureProxyRef(GrRecordingContext * context) const134 sk_sp<GrTextureProxy> SkImage_GpuYUVA::asTextureProxyRef(GrRecordingContext* context) const {
135 if (fRGBProxy) {
136 return fRGBProxy;
137 }
138
139 if (!context || !fContext->priv().matches(context)) {
140 return nullptr;
141 }
142
143 // Needs to create a render target in order to draw to it for the yuv->rgb conversion.
144 sk_sp<GrRenderTargetContext> renderTargetContext(
145 context->priv().makeDeferredRenderTargetContext(
146 SkBackingFit::kExact, this->width(), this->height(), GrColorType::kRGBA_8888,
147 this->refColorSpace(), 1, GrMipMapped::kNo, fOrigin));
148 if (!renderTargetContext) {
149 return nullptr;
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), fProxies, fYUVAIndices)) {
160 return nullptr;
161 }
162
163 fRGBProxy = renderTargetContext->asTextureProxyRef();
164 for (auto& p : fProxies) {
165 p.reset();
166 }
167 return fRGBProxy;
168 }
169
asMippedTextureProxyRef(GrRecordingContext * context) const170 sk_sp<GrTextureProxy> SkImage_GpuYUVA::asMippedTextureProxyRef(GrRecordingContext* context) const {
171 if (!context || !fContext->priv().matches(context)) {
172 return nullptr;
173 }
174
175 // if invalid or already has miplevels
176 auto proxy = this->asTextureProxyRef(context);
177 if (!proxy || GrMipMapped::kYes == fRGBProxy->mipMapped()) {
178 return proxy;
179 }
180
181 // need to generate mips for the proxy
182 if (auto mippedProxy = GrCopyBaseMipMapToTextureProxy(context, proxy.get())) {
183 fRGBProxy = mippedProxy;
184 return mippedProxy;
185 }
186
187 // failed to generate mips
188 return nullptr;
189 }
190
191 //////////////////////////////////////////////////////////////////////////////////////////////////
192
onMakeColorTypeAndColorSpace(GrRecordingContext *,SkColorType,sk_sp<SkColorSpace> targetCS) const193 sk_sp<SkImage> SkImage_GpuYUVA::onMakeColorTypeAndColorSpace(GrRecordingContext*,
194 SkColorType,
195 sk_sp<SkColorSpace> targetCS) const {
196 // We explicitly ignore color type changes, for now.
197
198 // we may need a mutex here but for now we expect usage to be in a single thread
199 if (fOnMakeColorSpaceTarget &&
200 SkColorSpace::Equals(targetCS.get(), fOnMakeColorSpaceTarget.get())) {
201 return fOnMakeColorSpaceResult;
202 }
203 sk_sp<SkImage> result = sk_sp<SkImage>(new SkImage_GpuYUVA(this, targetCS));
204 if (result) {
205 fOnMakeColorSpaceTarget = targetCS;
206 fOnMakeColorSpaceResult = result;
207 }
208 return result;
209 }
210
onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const211 sk_sp<SkImage> SkImage_GpuYUVA::onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const {
212 return sk_make_sp<SkImage_GpuYUVA>(fContext, this->width(), this->height(),
213 kNeedNewImageUniqueID, fYUVColorSpace, fProxies, fNumProxies,
214 fYUVAIndices, fOrigin, std::move(newCS));
215 }
216
217 //////////////////////////////////////////////////////////////////////////////////////////////////
218
MakeFromYUVATextures(GrContext * ctx,SkYUVColorSpace colorSpace,const GrBackendTexture yuvaTextures[],const SkYUVAIndex yuvaIndices[4],SkISize imageSize,GrSurfaceOrigin imageOrigin,sk_sp<SkColorSpace> imageColorSpace)219 sk_sp<SkImage> SkImage::MakeFromYUVATextures(GrContext* ctx,
220 SkYUVColorSpace colorSpace,
221 const GrBackendTexture yuvaTextures[],
222 const SkYUVAIndex yuvaIndices[4],
223 SkISize imageSize,
224 GrSurfaceOrigin imageOrigin,
225 sk_sp<SkColorSpace> imageColorSpace) {
226 int numTextures;
227 if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures)) {
228 return nullptr;
229 }
230
231 sk_sp<GrTextureProxy> tempTextureProxies[4];
232 if (!SkImage_GpuBase::MakeTempTextureProxies(ctx, yuvaTextures, numTextures, yuvaIndices,
233 imageOrigin, tempTextureProxies)) {
234 return nullptr;
235 }
236
237 return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(ctx), imageSize.width(), imageSize.height(),
238 kNeedNewImageUniqueID, colorSpace, tempTextureProxies,
239 numTextures, yuvaIndices, imageOrigin, imageColorSpace);
240 }
241
MakeFromYUVAPixmaps(GrContext * context,SkYUVColorSpace yuvColorSpace,const SkPixmap yuvaPixmaps[],const SkYUVAIndex yuvaIndices[4],SkISize imageSize,GrSurfaceOrigin imageOrigin,bool buildMips,bool limitToMaxTextureSize,sk_sp<SkColorSpace> imageColorSpace)242 sk_sp<SkImage> SkImage::MakeFromYUVAPixmaps(
243 GrContext* context, SkYUVColorSpace yuvColorSpace, const SkPixmap yuvaPixmaps[],
244 const SkYUVAIndex yuvaIndices[4], SkISize imageSize, GrSurfaceOrigin imageOrigin,
245 bool buildMips, bool limitToMaxTextureSize, sk_sp<SkColorSpace> imageColorSpace) {
246 if (!context) {
247 return nullptr; // until we impl this for raster backend
248 }
249
250 int numPixmaps;
251 if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numPixmaps)) {
252 return nullptr;
253 }
254
255 if (!context->priv().caps()->mipMapSupport()) {
256 buildMips = false;
257 }
258
259 // Make proxies
260 GrProxyProvider* proxyProvider = context->priv().proxyProvider();
261 sk_sp<GrTextureProxy> tempTextureProxies[4];
262 for (int i = 0; i < numPixmaps; ++i) {
263 const SkPixmap* pixmap = &yuvaPixmaps[i];
264 SkAutoPixmapStorage resized;
265 int maxTextureSize = context->priv().caps()->maxTextureSize();
266 int maxDim = SkTMax(yuvaPixmaps[i].width(), yuvaPixmaps[i].height());
267 if (limitToMaxTextureSize && maxDim > maxTextureSize) {
268 float scale = static_cast<float>(maxTextureSize) / maxDim;
269 int newWidth = SkTMin(static_cast<int>(yuvaPixmaps[i].width() * scale),
270 maxTextureSize);
271 int newHeight = SkTMin(static_cast<int>(yuvaPixmaps[i].height() * scale),
272 maxTextureSize);
273 SkImageInfo info = yuvaPixmaps[i].info().makeWH(newWidth, newHeight);
274 if (!resized.tryAlloc(info) ||
275 !yuvaPixmaps[i].scalePixels(resized, kLow_SkFilterQuality)) {
276 return nullptr;
277 }
278 pixmap = &resized;
279 }
280 // Turn the pixmap into a GrTextureProxy
281 SkBitmap bmp;
282 bmp.installPixels(*pixmap);
283 GrMipMapped mipMapped = buildMips ? GrMipMapped::kYes : GrMipMapped::kNo;
284 tempTextureProxies[i] = proxyProvider->createProxyFromBitmap(bmp, mipMapped);
285 if (!tempTextureProxies[i]) {
286 return nullptr;
287 }
288 }
289
290 return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(context), imageSize.width(), imageSize.height(),
291 kNeedNewImageUniqueID, yuvColorSpace, tempTextureProxies,
292 numPixmaps, yuvaIndices, imageOrigin, imageColorSpace);
293 }
294
295
296 /////////////////////////////////////////////////////////////////////////////////////////////////
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)297 sk_sp<SkImage> SkImage_GpuYUVA::MakePromiseYUVATexture(
298 GrContext* context,
299 SkYUVColorSpace yuvColorSpace,
300 const GrBackendFormat yuvaFormats[],
301 const SkISize yuvaSizes[],
302 const SkYUVAIndex yuvaIndices[4],
303 int imageWidth,
304 int imageHeight,
305 GrSurfaceOrigin imageOrigin,
306 sk_sp<SkColorSpace> imageColorSpace,
307 PromiseImageTextureFulfillProc textureFulfillProc,
308 PromiseImageTextureReleaseProc textureReleaseProc,
309 PromiseImageTextureDoneProc promiseDoneProc,
310 PromiseImageTextureContext textureContexts[],
311 PromiseImageApiVersion version) {
312 int numTextures;
313 bool valid = SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures);
314
315 // The contract here is that if 'promiseDoneProc' is passed in it should always be called,
316 // even if creation of the SkImage fails. Once we call MakePromiseImageLazyProxy it takes
317 // responsibility for calling the done proc.
318 if (!promiseDoneProc) {
319 return nullptr;
320 }
321 int proxiesCreated = 0;
322 SkScopeExit callDone([promiseDoneProc, textureContexts, numTextures, &proxiesCreated]() {
323 for (int i = proxiesCreated; i < numTextures; ++i) {
324 promiseDoneProc(textureContexts[i]);
325 }
326 });
327
328 if (!valid) {
329 return nullptr;
330 }
331
332 if (!context) {
333 return nullptr;
334 }
335
336 if (imageWidth <= 0 || imageHeight <= 0) {
337 return nullptr;
338 }
339
340 SkAlphaType at = (-1 != yuvaIndices[SkYUVAIndex::kA_Index].fIndex) ? kPremul_SkAlphaType
341 : kOpaque_SkAlphaType;
342 SkImageInfo info = SkImageInfo::Make(imageWidth, imageHeight, kAssumedColorType,
343 at, imageColorSpace);
344 if (!SkImageInfoIsValid(info)) {
345 return nullptr;
346 }
347
348 // verify sizes with expected texture count
349 for (int i = 0; i < numTextures; ++i) {
350 if (yuvaSizes[i].isEmpty()) {
351 return nullptr;
352 }
353 }
354 for (int i = numTextures; i < SkYUVASizeInfo::kMaxCount; ++i) {
355 if (!yuvaSizes[i].isEmpty()) {
356 return nullptr;
357 }
358 }
359
360 // Get lazy proxies
361 sk_sp<GrTextureProxy> proxies[4];
362 for (int texIdx = 0; texIdx < numTextures; ++texIdx) {
363 GrColorType colorType = context->priv().caps()->getYUVAColorTypeFromBackendFormat(
364 yuvaFormats[texIdx],
365 yuvaIndices[3].fIndex == texIdx);
366 if (GrColorType::kUnknown == colorType) {
367 return nullptr;
368 }
369
370 proxies[texIdx] = MakePromiseImageLazyProxy(
371 context, yuvaSizes[texIdx].width(), yuvaSizes[texIdx].height(), imageOrigin,
372 colorType, yuvaFormats[texIdx], GrMipMapped::kNo, textureFulfillProc,
373 textureReleaseProc, promiseDoneProc, textureContexts[texIdx], version);
374 ++proxiesCreated;
375 if (!proxies[texIdx]) {
376 return nullptr;
377 }
378 }
379
380 return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(context), imageWidth, imageHeight,
381 kNeedNewImageUniqueID, yuvColorSpace, proxies, numTextures,
382 yuvaIndices, imageOrigin, std::move(imageColorSpace));
383 }
384