• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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