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/private/GrRecordingContext.h"
15#include "src/core/SkAutoPixmapStorage.h"
16#include "src/core/SkMipMap.h"
17#include "src/core/SkScopeExit.h"
18#include "src/gpu/GrBitmapTextureMaker.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/GrTexture.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
31static constexpr auto kAssumedColorType = kRGBA_8888_SkColorType;
32
33SkImage_GpuYUVA::SkImage_GpuYUVA(sk_sp<GrContext> context,
34 SkISize size,
35 uint32_t uniqueID,
36 SkYUVColorSpace colorSpace,
37 GrSurfaceProxyView views[],
38 int numViews,
39 const SkYUVAIndex yuvaIndices[4],
40 GrSurfaceOrigin origin,
41 sk_sp<SkColorSpace> imageColorSpace)
42 : INHERITED(std::move(context),
43 size,
44 uniqueID,
45 kAssumedColorType,
46 // If an alpha channel is present we always switch to kPremul. This is because,
47 // although the planar data is always un-premul, the final interleaved RGB image
48 // is/would-be premul.
49 GetAlphaTypeFromYUVAIndices(yuvaIndices),
50 std::move(imageColorSpace))
51 , fNumViews(numViews)
52 , fYUVColorSpace(colorSpace)
53 , fOrigin(origin) {
54 // The caller should have done this work, just verifying
55 SkDEBUGCODE(int textureCount;)
56 SkASSERT(SkYUVAIndex::AreValidIndices(yuvaIndices, &textureCount));
57 SkASSERT(textureCount == fNumViews);
58
59 for (int i = 0; i < numViews; ++i) {
60 fViews[i] = std::move(views[i]);
61 }
62 memcpy(fYUVAIndices, yuvaIndices, 4 * sizeof(SkYUVAIndex));
63}
64
65// For onMakeColorSpace()
66SkImage_GpuYUVA::SkImage_GpuYUVA(const SkImage_GpuYUVA* image, sk_sp<SkColorSpace> targetCS)
67 : INHERITED(image->fContext, image->dimensions(), kNeedNewImageUniqueID, kAssumedColorType,
68 // If an alpha channel is present we always switch to kPremul. This is because,
69 // although the planar data is always un-premul, the final interleaved RGB image
70 // is/would-be premul.
71 GetAlphaTypeFromYUVAIndices(image->fYUVAIndices), std::move(targetCS))
72 , fNumViews(image->fNumViews)
73 , fYUVColorSpace(image->fYUVColorSpace)
74 , fOrigin(image->fOrigin)
75 // Since null fFromColorSpace means no GrColorSpaceXform, we turn a null
76 // image->refColorSpace() into an explicit SRGB.
77 , fFromColorSpace(image->colorSpace() ? image->refColorSpace() : SkColorSpace::MakeSRGB()) {
78 // The caller should have done this work, just verifying
79 SkDEBUGCODE(int textureCount;)
80 SkASSERT(SkYUVAIndex::AreValidIndices(image->fYUVAIndices, &textureCount));
81 SkASSERT(textureCount == fNumViews);
82
83 if (image->fRGBView.proxy()) {
84 fRGBView = image->fRGBView; // we ref in this case, not move
85 } else {
86 for (int i = 0; i < fNumViews; ++i) {
87 fViews[i] = image->fViews[i]; // we ref in this case, not move
88 }
89 }
90 memcpy(fYUVAIndices, image->fYUVAIndices, 4 * sizeof(SkYUVAIndex));
91}
92
93bool SkImage_GpuYUVA::setupMipmapsForPlanes(GrRecordingContext* context) const {
94 // We shouldn't get here if the planes were already flattened to RGBA.
95 SkASSERT(fViews[0].proxy() && !fRGBView.proxy());
96 if (!context || !fContext->priv().matches(context)) {
97 return false;
98 }
99
100 for (int i = 0; i < fNumViews; ++i) {
101 int mipCount = SkMipMap::ComputeLevelCount(fViews[i].proxy()->width(),
102 fViews[i].proxy()->height());
103 if (mipCount && GrGpu::IsACopyNeededForMips(fContext->priv().caps(),
104 fViews[i].asTextureProxy(),
105 GrSamplerState::Filter::kMipMap)) {
106 auto mippedView = GrCopyBaseMipMapToView(context, fViews[i]);
107 if (!mippedView) {
108 return false;
109 }
110 fViews[i] = std::move(mippedView);
111 }
112 }
113 return true;
114}
115
116//////////////////////////////////////////////////////////////////////////////////////////////////
117
118GrSemaphoresSubmitted SkImage_GpuYUVA::onFlush(GrContext* context, const GrFlushInfo& info) {
119 if (!context || !fContext->priv().matches(context) || fContext->abandoned()) {
120 return GrSemaphoresSubmitted::kNo;
121 }
122
123 GrSurfaceProxy* proxies[4] = {fViews[0].proxy(), fViews[1].proxy(), fViews[2].proxy(),
124 fViews[3].proxy()};
125 int numProxies = fNumViews;
126 if (fRGBView.proxy()) {
127 // Either we've already flushed the flattening draw or the flattening is unflushed. In the
128 // latter case it should still be ok to just pass fRGBView proxy because it in turn depends
129 // on the planar proxies and will cause all of their work to flush as well.
130 proxies[0] = fRGBView.proxy();
131 numProxies = 1;
132 }
133 return context->priv().flushSurfaces(proxies, numProxies, info);
134}
135
136GrTextureProxy* SkImage_GpuYUVA::peekProxy() const { return fRGBView.asTextureProxy(); }
137
138void SkImage_GpuYUVA::flattenToRGB(GrRecordingContext* context) const {
139 if (fRGBView.proxy()) {
140 return;
141 }
142
143 if (!context || !fContext->priv().matches(context)) {
144 return;
145 }
146
147 // Needs to create a render target in order to draw to it for the yuv->rgb conversion.
148 auto renderTargetContext = GrRenderTargetContext::Make(
149 context, GrColorType::kRGBA_8888, this->refColorSpace(), SkBackingFit::kExact,
150 this->dimensions(), 1, GrMipMapped::kNo, GrProtected::kNo, fOrigin);
151 if (!renderTargetContext) {
152 return;
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), fViews, fYUVAIndices)) {
163 return;
164 }
165
166 fRGBView = renderTargetContext->readSurfaceView();
167 SkASSERT(fRGBView.origin() == fOrigin);
168 SkASSERT(fRGBView.swizzle() == GrSwizzle());
169 for (auto& v : fViews) {
170 v.reset();
171 }
172}
173
174GrSurfaceProxyView SkImage_GpuYUVA::refMippedView(GrRecordingContext* context) const {
175 // if invalid or already has miplevels
176 this->flattenToRGB(context);
177 if (!fRGBView || fRGBView.asTextureProxy()->mipMapped() == GrMipMapped::kYes) {
178 return fRGBView;
179 }
180
181 // need to generate mips for the proxy
182 auto mippedView = GrCopyBaseMipMapToView(context, fRGBView);
183 if (!mippedView) {
184 return {};
185 }
186
187 fRGBView = std::move(mippedView);
188 return fRGBView;
189}
190
191const 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
201sk_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
218sk_sp<SkImage> SkImage_GpuYUVA::onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const {
219 return sk_make_sp<SkImage_GpuYUVA>(fContext, this->dimensions(), kNeedNewImageUniqueID,
220 fYUVColorSpace, fViews, fNumViews, fYUVAIndices, fOrigin,
221 std::move(newCS));
222}
223
224//////////////////////////////////////////////////////////////////////////////////////////////////
225
226sk_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
244 return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(ctx), imageSize, kNeedNewImageUniqueID, colorSpace,
245 tempViews, numTextures, yuvaIndices, imageOrigin,
246 imageColorSpace);
247}
248
249sk_sp<SkImage> SkImage::MakeFromYUVAPixmaps(GrContext* context, SkYUVColorSpace yuvColorSpace,
250 const SkPixmap yuvaPixmaps[],
251 const SkYUVAIndex yuvaIndices[4], SkISize imageSize,
252 GrSurfaceOrigin imageOrigin, bool buildMips,
253 bool limitToMaxTextureSize,
254 sk_sp<SkColorSpace> imageColorSpace) {
255 if (!context) {
256 return nullptr; // until we impl this for raster backend
257 }
258
259 int numPixmaps;
260 if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numPixmaps)) {
261 return nullptr;
262 }
263
264 if (!context->priv().caps()->mipMapSupport()) {
265 buildMips = false;
266 }
267
268 // Make proxies
269 GrSurfaceProxyView tempViews[4];
270 for (int i = 0; i < numPixmaps; ++i) {
271 const SkPixmap* pixmap = &yuvaPixmaps[i];
272 SkAutoPixmapStorage resized;
273 int maxTextureSize = context->priv().caps()->maxTextureSize();
274 int maxDim = std::max(yuvaPixmaps[i].width(), yuvaPixmaps[i].height());
275 if (limitToMaxTextureSize && maxDim > maxTextureSize) {
276 float scale = static_cast<float>(maxTextureSize) / maxDim;
277 int newWidth = std::min(static_cast<int>(yuvaPixmaps[i].width() * scale), maxTextureSize);
278 int newHeight =
279 std::min(static_cast<int>(yuvaPixmaps[i].height() * scale), maxTextureSize);
280 SkImageInfo info = yuvaPixmaps[i].info().makeWH(newWidth, newHeight);
281 if (!resized.tryAlloc(info) ||
282 !yuvaPixmaps[i].scalePixels(resized, kLow_SkFilterQuality)) {
283 return nullptr;
284 }
285 pixmap = &resized;
286 }
287 // Turn the pixmap into a GrTextureProxy
288 SkBitmap bmp;
289 bmp.installPixels(*pixmap);
290 GrBitmapTextureMaker bitmapMaker(context, bmp, GrImageTexGenPolicy::kNew_Uncached_Budgeted);
291 GrMipMapped mipMapped = buildMips ? GrMipMapped::kYes : GrMipMapped::kNo;
292 GrSurfaceProxyView view;
293 tempViews[i] = bitmapMaker.view(mipMapped);
294 if (!tempViews[i]) {
295 return nullptr;
296 }
297 }
298
299 return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(context), imageSize, kNeedNewImageUniqueID,
300 yuvColorSpace, tempViews, numPixmaps, yuvaIndices,
301 imageOrigin, imageColorSpace);
302}
303
304/////////////////////////////////////////////////////////////////////////////////////////////////
305sk_sp<SkImage> SkImage_GpuYUVA::MakePromiseYUVATexture(
306 GrContext* context,
307 SkYUVColorSpace yuvColorSpace,
308 const GrBackendFormat yuvaFormats[],
309 const SkISize yuvaSizes[],
310 const SkYUVAIndex yuvaIndices[4],
311 int imageWidth,
312 int imageHeight,
313 GrSurfaceOrigin imageOrigin,
314 sk_sp<SkColorSpace> imageColorSpace,
315 PromiseImageTextureFulfillProc textureFulfillProc,
316 PromiseImageTextureReleaseProc textureReleaseProc,
317 PromiseImageTextureDoneProc promiseDoneProc,
318 PromiseImageTextureContext textureContexts[],
319 PromiseImageApiVersion version) {
320 int numTextures;
321 bool valid = SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures);
322
323 // The contract here is that if 'promiseDoneProc' is passed in it should always be called,
324 // even if creation of the SkImage fails. Once we call MakePromiseImageLazyProxy it takes
325 // responsibility for calling the done proc.
326 if (!promiseDoneProc) {
327 return nullptr;
328 }
329 int proxiesCreated = 0;
330 SkScopeExit callDone([promiseDoneProc, textureContexts, numTextures, &proxiesCreated]() {
331 for (int i = proxiesCreated; i < numTextures; ++i) {
332 promiseDoneProc(textureContexts[i]);
333 }
334 });
335
336 if (!valid) {
337 return nullptr;
338 }
339
340 if (!context) {
341 return nullptr;
342 }
343
344 if (imageWidth <= 0 || imageHeight <= 0) {
345 return nullptr;
346 }
347
348 SkAlphaType at = (-1 != yuvaIndices[SkYUVAIndex::kA_Index].fIndex) ? kPremul_SkAlphaType
349 : kOpaque_SkAlphaType;
350 SkImageInfo info =
351 SkImageInfo::Make(imageWidth, imageHeight, kAssumedColorType, at, imageColorSpace);
352 if (!SkImageInfoIsValid(info)) {
353 return nullptr;
354 }
355
356 // verify sizes with expected texture count
357 for (int i = 0; i < numTextures; ++i) {
358 if (yuvaSizes[i].isEmpty()) {
359 return nullptr;
360 }
361 }
362 for (int i = numTextures; i < SkYUVASizeInfo::kMaxCount; ++i) {
363 if (!yuvaSizes[i].isEmpty()) {
364 return nullptr;
365 }
366 }
367
368 // Get lazy proxies
369 GrSurfaceProxyView views[4];
370 for (int texIdx = 0; texIdx < numTextures; ++texIdx) {
371 auto proxy = MakePromiseImageLazyProxy(
372 context, yuvaSizes[texIdx].width(), yuvaSizes[texIdx].height(),
373 yuvaFormats[texIdx], GrMipMapped::kNo, textureFulfillProc, textureReleaseProc,
374 promiseDoneProc, textureContexts[texIdx], version);
375 ++proxiesCreated;
376 if (!proxy) {
377 return nullptr;
378 }
379 views[texIdx] = GrSurfaceProxyView(std::move(proxy), imageOrigin, GrSwizzle("rgba"));
380 }
381
382 return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(context), SkISize{imageWidth, imageHeight},
383 kNeedNewImageUniqueID, yuvColorSpace, views, numTextures,
384 yuvaIndices, imageOrigin, std::move(imageColorSpace));
385}
386