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/SkYUVAPixmaps.h"
13 #include "include/gpu/GrDirectContext.h"
14 #include "include/gpu/GrRecordingContext.h"
15 #include "include/gpu/GrYUVABackendTextures.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/GrDirectContextPriv.h"
21 #include "src/gpu/GrGpu.h"
22 #include "src/gpu/GrImageContextPriv.h"
23 #include "src/gpu/GrProxyProvider.h"
24 #include "src/gpu/GrRecordingContextPriv.h"
25 #include "src/gpu/SkGr.h"
26 #include "src/gpu/SurfaceFillContext.h"
27 #include "src/gpu/effects/GrBicubicEffect.h"
28 #include "src/gpu/effects/GrYUVtoRGBEffect.h"
29 #include "src/image/SkImage_Gpu.h"
30 #include "src/image/SkImage_GpuYUVA.h"
31
32 static constexpr auto kAssumedColorType = kRGBA_8888_SkColorType;
33
SkImage_GpuYUVA(sk_sp<GrImageContext> context,uint32_t uniqueID,GrYUVATextureProxies proxies,sk_sp<SkColorSpace> imageColorSpace)34 SkImage_GpuYUVA::SkImage_GpuYUVA(sk_sp<GrImageContext> context,
35 uint32_t uniqueID,
36 GrYUVATextureProxies proxies,
37 sk_sp<SkColorSpace> imageColorSpace)
38 : INHERITED(std::move(context),
39 SkImageInfo::Make(proxies.yuvaInfo().dimensions(),
40 kAssumedColorType,
41 // If an alpha channel is present we always use kPremul. This
42 // is because, although the planar data is always un-premul,
43 // the final interleaved RGBA sample produced in the shader
44 // is premul (and similar if flattened via asView).
45 proxies.yuvaInfo().hasAlpha() ? kPremul_SkAlphaType
46 : kOpaque_SkAlphaType,
47 std::move(imageColorSpace)),
48 uniqueID)
49 , fYUVAProxies(std::move(proxies)) {
50 // The caller should have checked this, just verifying.
51 SkASSERT(fYUVAProxies.isValid());
52 }
53
54 // For onMakeColorSpace()
SkImage_GpuYUVA(sk_sp<GrImageContext> context,const SkImage_GpuYUVA * image,sk_sp<SkColorSpace> targetCS)55 SkImage_GpuYUVA::SkImage_GpuYUVA(sk_sp<GrImageContext> context,
56 const SkImage_GpuYUVA* image,
57 sk_sp<SkColorSpace> targetCS)
58 : INHERITED(std::move(context),
59 image->imageInfo().makeColorSpace(std::move(targetCS)),
60 kNeedNewImageUniqueID)
61 , fYUVAProxies(image->fYUVAProxies)
62 // Since null fFromColorSpace means no GrColorSpaceXform, we turn a null
63 // image->refColorSpace() into an explicit SRGB.
64 , fFromColorSpace(image->colorSpace() ? image->refColorSpace() : SkColorSpace::MakeSRGB()) {
65 }
66
setupMipmapsForPlanes(GrRecordingContext * context) const67 bool SkImage_GpuYUVA::setupMipmapsForPlanes(GrRecordingContext* context) const {
68 if (!context || !fContext->priv().matches(context)) {
69 return false;
70 }
71 if (!context->priv().caps()->mipmapSupport()) {
72 // We succeed in this case by doing nothing.
73 return true;
74 }
75 int n = fYUVAProxies.yuvaInfo().numPlanes();
76 sk_sp<GrSurfaceProxy> newProxies[4];
77 for (int i = 0; i < n; ++i) {
78 auto* t = fYUVAProxies.proxy(i)->asTextureProxy();
79 if (t->mipmapped() == GrMipmapped::kNo && (t->width() > 1 || t->height() > 1)) {
80 auto newView = GrCopyBaseMipMapToView(context, fYUVAProxies.makeView(i));
81 if (!newView) {
82 return false;
83 }
84 SkASSERT(newView.swizzle() == fYUVAProxies.makeView(i).swizzle());
85 newProxies[i] = newView.detachProxy();
86 } else {
87 newProxies[i] = fYUVAProxies.refProxy(i);
88 }
89 }
90 fYUVAProxies = GrYUVATextureProxies(fYUVAProxies.yuvaInfo(),
91 newProxies,
92 fYUVAProxies.textureOrigin());
93 SkASSERT(fYUVAProxies.isValid());
94 return true;
95 }
96
97 //////////////////////////////////////////////////////////////////////////////////////////////////
98
onFlush(GrDirectContext * dContext,const GrFlushInfo & info) const99 GrSemaphoresSubmitted SkImage_GpuYUVA::onFlush(GrDirectContext* dContext,
100 const GrFlushInfo& info) const {
101 if (!fContext->priv().matches(dContext) || dContext->abandoned()) {
102 if (info.fSubmittedProc) {
103 info.fSubmittedProc(info.fSubmittedContext, false);
104 }
105 if (info.fFinishedProc) {
106 info.fFinishedProc(info.fFinishedContext);
107 }
108 return GrSemaphoresSubmitted::kNo;
109 }
110
111 GrSurfaceProxy* proxies[SkYUVAInfo::kMaxPlanes] = {};
112 size_t numProxies = fYUVAProxies.numPlanes();
113 for (size_t i = 0; i < numProxies; ++i) {
114 proxies[i] = fYUVAProxies.proxy(i);
115 }
116 return dContext->priv().flushSurfaces({proxies, numProxies},
117 SkSurface::BackendSurfaceAccess::kNoAccess,
118 info);
119 }
120
onHasMipmaps() const121 bool SkImage_GpuYUVA::onHasMipmaps() const { return fYUVAProxies.mipmapped() == GrMipmapped::kYes; }
122
onTextureSize() const123 size_t SkImage_GpuYUVA::onTextureSize() const {
124 size_t size = 0;
125 for (int i = 0; i < fYUVAProxies.numPlanes(); ++i) {
126 size += fYUVAProxies.proxy(i)->gpuMemorySize();
127 }
128 return size;
129 }
130
onMakeColorTypeAndColorSpace(SkColorType,sk_sp<SkColorSpace> targetCS,GrDirectContext * direct) const131 sk_sp<SkImage> SkImage_GpuYUVA::onMakeColorTypeAndColorSpace(SkColorType,
132 sk_sp<SkColorSpace> targetCS,
133 GrDirectContext* direct) const {
134 // We explicitly ignore color type changes, for now.
135
136 // we may need a mutex here but for now we expect usage to be in a single thread
137 if (fOnMakeColorSpaceTarget &&
138 SkColorSpace::Equals(targetCS.get(), fOnMakeColorSpaceTarget.get())) {
139 return fOnMakeColorSpaceResult;
140 }
141 sk_sp<SkImage> result = sk_sp<SkImage>(new SkImage_GpuYUVA(sk_ref_sp(direct), this, targetCS));
142 if (result) {
143 fOnMakeColorSpaceTarget = targetCS;
144 fOnMakeColorSpaceResult = result;
145 }
146 return result;
147 }
148
onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const149 sk_sp<SkImage> SkImage_GpuYUVA::onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const {
150 return sk_sp<SkImage>(new SkImage_GpuYUVA(fContext, this, std::move(newCS)));
151 }
152
onAsView(GrRecordingContext * rContext,GrMipmapped mipmapped,GrImageTexGenPolicy) const153 std::tuple<GrSurfaceProxyView, GrColorType> SkImage_GpuYUVA::onAsView(
154 GrRecordingContext* rContext,
155 GrMipmapped mipmapped,
156 GrImageTexGenPolicy) const {
157 if (!fContext->priv().matches(rContext)) {
158 return {};
159 }
160 auto sfc = rContext->priv().makeSFC(this->imageInfo(),
161 SkBackingFit::kExact,
162 /*sample count*/ 1,
163 mipmapped,
164 GrProtected::kNo,
165 kTopLeft_GrSurfaceOrigin,
166 SkBudgeted::kYes);
167 if (!sfc) {
168 return {};
169 }
170
171 const GrCaps& caps = *rContext->priv().caps();
172 auto fp = GrYUVtoRGBEffect::Make(fYUVAProxies, GrSamplerState::Filter::kNearest, caps);
173 if (fFromColorSpace) {
174 fp = GrColorSpaceXformEffect::Make(std::move(fp),
175 fFromColorSpace.get(), this->alphaType(),
176 this->colorSpace() , this->alphaType());
177 }
178 sfc->fillWithFP(std::move(fp));
179
180 return {sfc->readSurfaceView(), sfc->colorInfo().colorType()};
181 }
182
onAsFragmentProcessor(GrRecordingContext * context,SkSamplingOptions sampling,const SkTileMode tileModes[2],const SkMatrix & m,const SkRect * subset,const SkRect * domain) const183 std::unique_ptr<GrFragmentProcessor> SkImage_GpuYUVA::onAsFragmentProcessor(
184 GrRecordingContext* context,
185 SkSamplingOptions sampling,
186 const SkTileMode tileModes[2],
187 const SkMatrix& m,
188 const SkRect* subset,
189 const SkRect* domain) const {
190 if (!fContext->priv().matches(context)) {
191 return {};
192 }
193 auto wmx = SkTileModeToWrapMode(tileModes[0]);
194 auto wmy = SkTileModeToWrapMode(tileModes[1]);
195 GrSamplerState sampler(wmx, wmy, sampling.filter, sampling.mipmap);
196 if (sampler.mipmapped() == GrMipmapped::kYes && !this->setupMipmapsForPlanes(context)) {
197 sampler.setMipmapMode(GrSamplerState::MipmapMode::kNone);
198 }
199
200 const auto& yuvM = sampling.useCubic ? SkMatrix::I() : m;
201 auto fp = GrYUVtoRGBEffect::Make(fYUVAProxies,
202 sampler,
203 *context->priv().caps(),
204 yuvM,
205 subset,
206 domain);
207 if (sampling.useCubic) {
208 fp = GrBicubicEffect::Make(std::move(fp),
209 this->alphaType(),
210 m,
211 sampling.cubic,
212 GrBicubicEffect::Direction::kXY);
213 }
214 if (fFromColorSpace) {
215 fp = GrColorSpaceXformEffect::Make(std::move(fp),
216 fFromColorSpace.get(), this->alphaType(),
217 this->colorSpace() , this->alphaType());
218 }
219 return fp;
220 }
221
222 //////////////////////////////////////////////////////////////////////////////////////////////////
223
MakeFromYUVATextures(GrRecordingContext * context,const GrYUVABackendTextures & yuvaTextures,sk_sp<SkColorSpace> imageColorSpace,TextureReleaseProc textureReleaseProc,ReleaseContext releaseContext)224 sk_sp<SkImage> SkImage::MakeFromYUVATextures(GrRecordingContext* context,
225 const GrYUVABackendTextures& yuvaTextures,
226 sk_sp<SkColorSpace> imageColorSpace,
227 TextureReleaseProc textureReleaseProc,
228 ReleaseContext releaseContext) {
229 auto releaseHelper = GrRefCntedCallback::Make(textureReleaseProc, releaseContext);
230
231 GrProxyProvider* proxyProvider = context->priv().proxyProvider();
232 int numPlanes = yuvaTextures.yuvaInfo().numPlanes();
233 sk_sp<GrSurfaceProxy> proxies[SkYUVAInfo::kMaxPlanes];
234 for (int plane = 0; plane < numPlanes; ++plane) {
235 proxies[plane] = proxyProvider->wrapBackendTexture(yuvaTextures.texture(plane),
236 kBorrow_GrWrapOwnership,
237 GrWrapCacheable::kNo,
238 kRead_GrIOType,
239 releaseHelper);
240 if (!proxies[plane]) {
241 return {};
242 }
243 }
244 GrYUVATextureProxies yuvaProxies(yuvaTextures.yuvaInfo(),
245 proxies,
246 yuvaTextures.textureOrigin());
247
248 if (!yuvaProxies.isValid()) {
249 return nullptr;
250 }
251
252 return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(context),
253 kNeedNewImageUniqueID,
254 yuvaProxies,
255 imageColorSpace);
256 }
257
MakeFromYUVAPixmaps(GrRecordingContext * context,const SkYUVAPixmaps & pixmaps,GrMipmapped buildMips,bool limitToMaxTextureSize,sk_sp<SkColorSpace> imageColorSpace)258 sk_sp<SkImage> SkImage::MakeFromYUVAPixmaps(GrRecordingContext* context,
259 const SkYUVAPixmaps& pixmaps,
260 GrMipmapped buildMips,
261 bool limitToMaxTextureSize,
262 sk_sp<SkColorSpace> imageColorSpace) {
263 if (!context) {
264 return nullptr; // until we impl this for raster backend
265 }
266
267 if (!pixmaps.isValid()) {
268 return nullptr;
269 }
270
271 if (!context->priv().caps()->mipmapSupport()) {
272 buildMips = GrMipMapped::kNo;
273 }
274
275 // Resize the pixmaps if necessary.
276 int numPlanes = pixmaps.numPlanes();
277 int maxTextureSize = context->priv().caps()->maxTextureSize();
278 int maxDim = std::max(pixmaps.yuvaInfo().width(), pixmaps.yuvaInfo().height());
279
280 SkYUVAPixmaps tempPixmaps;
281 const SkYUVAPixmaps* pixmapsToUpload = &pixmaps;
282 // We assume no plane is larger than the image size (and at least one plane is as big).
283 if (maxDim > maxTextureSize) {
284 if (!limitToMaxTextureSize) {
285 return nullptr;
286 }
287 float scale = static_cast<float>(maxTextureSize)/maxDim;
288 SkISize newDimensions = {
289 std::min(static_cast<int>(pixmaps.yuvaInfo().width() *scale), maxTextureSize),
290 std::min(static_cast<int>(pixmaps.yuvaInfo().height()*scale), maxTextureSize)
291 };
292 SkYUVAInfo newInfo = pixmaps.yuvaInfo().makeDimensions(newDimensions);
293 SkYUVAPixmapInfo newPixmapInfo(newInfo, pixmaps.dataType(), /*row bytes*/ nullptr);
294 tempPixmaps = SkYUVAPixmaps::Allocate(newPixmapInfo);
295 SkSamplingOptions sampling(SkFilterMode::kLinear);
296 if (!tempPixmaps.isValid()) {
297 return nullptr;
298 }
299 for (int i = 0; i < numPlanes; ++i) {
300 if (!pixmaps.plane(i).scalePixels(tempPixmaps.plane(i), sampling)) {
301 return nullptr;
302 }
303 }
304 pixmapsToUpload = &tempPixmaps;
305 }
306
307 // Convert to texture proxies.
308 GrSurfaceProxyView views[SkYUVAInfo::kMaxPlanes];
309 GrColorType pixmapColorTypes[SkYUVAInfo::kMaxPlanes];
310 for (int i = 0; i < numPlanes; ++i) {
311 // Turn the pixmap into a GrTextureProxy
312 SkBitmap bmp;
313 bmp.installPixels(pixmapsToUpload->plane(i));
314 std::tie(views[i], std::ignore) = GrMakeUncachedBitmapProxyView(context, bmp, buildMips);
315 if (!views[i]) {
316 return nullptr;
317 }
318 pixmapColorTypes[i] = SkColorTypeToGrColorType(bmp.colorType());
319 }
320
321 GrYUVATextureProxies yuvaProxies(pixmapsToUpload->yuvaInfo(), views, pixmapColorTypes);
322 SkASSERT(yuvaProxies.isValid());
323 return sk_make_sp<SkImage_GpuYUVA>(sk_ref_sp(context),
324 kNeedNewImageUniqueID,
325 std::move(yuvaProxies),
326 std::move(imageColorSpace));
327 }
328
329 /////////////////////////////////////////////////////////////////////////////////////////////////
330
MakePromiseYUVATexture(sk_sp<GrContextThreadSafeProxy> threadSafeProxy,const GrYUVABackendTextureInfo & backendTextureInfo,sk_sp<SkColorSpace> imageColorSpace,PromiseImageTextureFulfillProc textureFulfillProc,PromiseImageTextureReleaseProc textureReleaseProc,PromiseImageTextureContext textureContexts[])331 sk_sp<SkImage> SkImage::MakePromiseYUVATexture(sk_sp<GrContextThreadSafeProxy> threadSafeProxy,
332 const GrYUVABackendTextureInfo& backendTextureInfo,
333 sk_sp<SkColorSpace> imageColorSpace,
334 PromiseImageTextureFulfillProc textureFulfillProc,
335 PromiseImageTextureReleaseProc textureReleaseProc,
336 PromiseImageTextureContext textureContexts[]) {
337 if (!backendTextureInfo.isValid()) {
338 return nullptr;
339 }
340
341 SkISize planeDimensions[SkYUVAInfo::kMaxPlanes];
342 int n = backendTextureInfo.yuvaInfo().planeDimensions(planeDimensions);
343
344 // Our contract is that we will always call the release proc even on failure.
345 // We use the helper to convey the context, so we need to ensure make doesn't fail.
346 textureReleaseProc = textureReleaseProc ? textureReleaseProc : [](void*) {};
347 sk_sp<GrRefCntedCallback> releaseHelpers[4];
348 for (int i = 0; i < n; ++i) {
349 releaseHelpers[i] = GrRefCntedCallback::Make(textureReleaseProc, textureContexts[i]);
350 }
351
352 if (!threadSafeProxy) {
353 return nullptr;
354 }
355
356 SkAlphaType at = backendTextureInfo.yuvaInfo().hasAlpha() ? kPremul_SkAlphaType
357 : kOpaque_SkAlphaType;
358 SkImageInfo info = SkImageInfo::Make(backendTextureInfo.yuvaInfo().dimensions(),
359 kAssumedColorType, at, imageColorSpace);
360 if (!SkImageInfoIsValid(info)) {
361 return nullptr;
362 }
363
364 // Make a lazy proxy for each plane and wrap in a view.
365 sk_sp<GrSurfaceProxy> proxies[4];
366 for (int i = 0; i < n; ++i) {
367 proxies[i] = SkImage_GpuBase::MakePromiseImageLazyProxy(threadSafeProxy.get(),
368 planeDimensions[i],
369 backendTextureInfo.planeFormat(i),
370 GrMipmapped::kNo,
371 textureFulfillProc,
372 std::move(releaseHelpers[i]));
373 if (!proxies[i]) {
374 return nullptr;
375 }
376 }
377 GrYUVATextureProxies yuvaTextureProxies(backendTextureInfo.yuvaInfo(),
378 proxies,
379 backendTextureInfo.textureOrigin());
380 SkASSERT(yuvaTextureProxies.isValid());
381 sk_sp<GrImageContext> ctx(GrImageContextPriv::MakeForPromiseImage(std::move(threadSafeProxy)));
382 return sk_make_sp<SkImage_GpuYUVA>(std::move(ctx),
383 kNeedNewImageUniqueID,
384 std::move(yuvaTextureProxies),
385 std::move(imageColorSpace));
386 }
387