• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2   * Copyright 2015 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 "src/gpu/GrYUVProvider.h"
9  
10  #include "include/core/SkRefCnt.h"
11  #include "include/core/SkYUVAIndex.h"
12  #include "include/private/GrRecordingContext.h"
13  #include "src/core/SkAutoMalloc.h"
14  #include "src/core/SkCachedData.h"
15  #include "src/core/SkResourceCache.h"
16  #include "src/core/SkYUVPlanesCache.h"
17  #include "src/gpu/GrCaps.h"
18  #include "src/gpu/GrClip.h"
19  #include "src/gpu/GrColorSpaceXform.h"
20  #include "src/gpu/GrProxyProvider.h"
21  #include "src/gpu/GrRecordingContextPriv.h"
22  #include "src/gpu/GrRenderTargetContext.h"
23  #include "src/gpu/GrTextureProxy.h"
24  #include "src/gpu/effects/GrYUVtoRGBEffect.h"
25  
getPlanes(SkYUVASizeInfo * size,SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],SkYUVColorSpace * colorSpace,const void * constPlanes[SkYUVASizeInfo::kMaxCount])26  sk_sp<SkCachedData> GrYUVProvider::getPlanes(SkYUVASizeInfo* size,
27                                               SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
28                                               SkYUVColorSpace* colorSpace,
29                                               const void* constPlanes[SkYUVASizeInfo::kMaxCount]) {
30      sk_sp<SkCachedData> data;
31      SkYUVPlanesCache::Info yuvInfo;
32      data.reset(SkYUVPlanesCache::FindAndRef(this->onGetID(), &yuvInfo));
33  
34      void* planes[SkYUVASizeInfo::kMaxCount];
35  
36      if (data.get()) {
37          planes[0] = (void*)data->data(); // we should always have at least one plane
38  
39          for (int i = 1; i < SkYUVASizeInfo::kMaxCount; ++i) {
40              if (!yuvInfo.fSizeInfo.fWidthBytes[i]) {
41                  SkASSERT(!yuvInfo.fSizeInfo.fWidthBytes[i] &&
42                           !yuvInfo.fSizeInfo.fSizes[i].fHeight);
43                  planes[i] = nullptr;
44                  continue;
45              }
46  
47              planes[i] = (uint8_t*)planes[i-1] + (yuvInfo.fSizeInfo.fWidthBytes[i-1] *
48                                                   yuvInfo.fSizeInfo.fSizes[i-1].fHeight);
49          }
50      } else {
51          // Fetch yuv plane sizes for memory allocation.
52          if (!this->onQueryYUVA8(&yuvInfo.fSizeInfo, yuvInfo.fYUVAIndices, &yuvInfo.fColorSpace)) {
53              return nullptr;
54          }
55  
56          // Allocate the memory for YUVA
57          size_t totalSize(0);
58          for (int i = 0; i < SkYUVASizeInfo::kMaxCount; i++) {
59              SkASSERT((yuvInfo.fSizeInfo.fWidthBytes[i] && yuvInfo.fSizeInfo.fSizes[i].fHeight) ||
60                       (!yuvInfo.fSizeInfo.fWidthBytes[i] && !yuvInfo.fSizeInfo.fSizes[i].fHeight));
61  
62              totalSize += yuvInfo.fSizeInfo.fWidthBytes[i] * yuvInfo.fSizeInfo.fSizes[i].fHeight;
63          }
64  
65          data.reset(SkResourceCache::NewCachedData(totalSize));
66  
67          planes[0] = data->writable_data();
68  
69          for (int i = 1; i < SkYUVASizeInfo::kMaxCount; ++i) {
70              if (!yuvInfo.fSizeInfo.fWidthBytes[i]) {
71                  SkASSERT(!yuvInfo.fSizeInfo.fWidthBytes[i] &&
72                           !yuvInfo.fSizeInfo.fSizes[i].fHeight);
73                  planes[i] = nullptr;
74                  continue;
75              }
76  
77              planes[i] = (uint8_t*)planes[i-1] + (yuvInfo.fSizeInfo.fWidthBytes[i-1] *
78                                                   yuvInfo.fSizeInfo.fSizes[i-1].fHeight);
79          }
80  
81          // Get the YUV planes.
82          if (!this->onGetYUVA8Planes(yuvInfo.fSizeInfo, yuvInfo.fYUVAIndices, planes)) {
83              return nullptr;
84          }
85  
86          // Decoding is done, cache the resulting YUV planes
87          SkYUVPlanesCache::Add(this->onGetID(), data.get(), &yuvInfo);
88      }
89  
90      *size = yuvInfo.fSizeInfo;
91      memcpy(yuvaIndices, yuvInfo.fYUVAIndices, sizeof(yuvInfo.fYUVAIndices));
92      *colorSpace = yuvInfo.fColorSpace;
93      constPlanes[0] = planes[0];
94      constPlanes[1] = planes[1];
95      constPlanes[2] = planes[2];
96      constPlanes[3] = planes[3];
97      return data;
98  }
99  
YUVGen_DataReleaseProc(const void *,void * data)100  void GrYUVProvider::YUVGen_DataReleaseProc(const void*, void* data) {
101      SkCachedData* cachedData = static_cast<SkCachedData*>(data);
102      SkASSERT(cachedData);
103      cachedData->unref();
104  }
105  
refAsTextureProxy(GrRecordingContext * ctx,const GrSurfaceDesc & desc,GrColorType colorType,SkColorSpace * srcColorSpace,SkColorSpace * dstColorSpace)106  sk_sp<GrTextureProxy> GrYUVProvider::refAsTextureProxy(GrRecordingContext* ctx,
107                                                         const GrSurfaceDesc& desc,
108                                                         GrColorType colorType,
109                                                         SkColorSpace* srcColorSpace,
110                                                         SkColorSpace* dstColorSpace) {
111      SkYUVASizeInfo yuvSizeInfo;
112      SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount];
113      SkYUVColorSpace yuvColorSpace;
114      const void* planes[SkYUVASizeInfo::kMaxCount];
115  
116      sk_sp<SkCachedData> dataStorage = this->getPlanes(&yuvSizeInfo, yuvaIndices,
117                                                        &yuvColorSpace, planes);
118      if (!dataStorage) {
119          return nullptr;
120      }
121  
122      sk_sp<GrTextureProxy> yuvTextureProxies[SkYUVASizeInfo::kMaxCount];
123      for (int i = 0; i < SkYUVASizeInfo::kMaxCount; ++i) {
124          if (yuvSizeInfo.fSizes[i].isEmpty()) {
125              SkASSERT(!yuvSizeInfo.fWidthBytes[i]);
126              continue;
127          }
128  
129          int componentWidth  = yuvSizeInfo.fSizes[i].fWidth;
130          int componentHeight = yuvSizeInfo.fSizes[i].fHeight;
131          // If the sizes of the components are not all the same we choose to create exact-match
132          // textures for the smaller ones rather than add a texture domain to the draw.
133          // TODO: revisit this decision to improve texture reuse?
134          SkBackingFit fit =
135                  (componentWidth  != yuvSizeInfo.fSizes[0].fWidth) ||
136                  (componentHeight != yuvSizeInfo.fSizes[0].fHeight)
137                      ? SkBackingFit::kExact : SkBackingFit::kApprox;
138  
139          SkImageInfo imageInfo = SkImageInfo::MakeA8(componentWidth, componentHeight);
140          SkPixmap pixmap(imageInfo, planes[i], yuvSizeInfo.fWidthBytes[i]);
141          SkCachedData* dataStoragePtr = dataStorage.get();
142          // We grab a ref to cached yuv data. When the SkImage we create below goes away it will call
143          // the YUVGen_DataReleaseProc which will release this ref.
144          // DDL TODO: Currently we end up creating a lazy proxy that will hold onto a ref to the
145          // SkImage in its lambda. This means that we'll keep the ref on the YUV data around for the
146          // life time of the proxy and not just upload. For non-DDL draws we should look into
147          // releasing this SkImage after uploads (by deleting the lambda after instantiation).
148          dataStoragePtr->ref();
149          sk_sp<SkImage> yuvImage = SkImage::MakeFromRaster(pixmap, YUVGen_DataReleaseProc,
150                                                            dataStoragePtr);
151  
152          auto proxyProvider = ctx->priv().proxyProvider();
153          yuvTextureProxies[i] =
154                  proxyProvider->createTextureProxy(yuvImage, 1, SkBudgeted::kYes, fit);
155  
156          SkASSERT(yuvTextureProxies[i]->width() == yuvSizeInfo.fSizes[i].fWidth);
157          SkASSERT(yuvTextureProxies[i]->height() == yuvSizeInfo.fSizes[i].fHeight);
158      }
159  
160      // TODO: investigate preallocating mip maps here
161      sk_sp<GrRenderTargetContext> renderTargetContext(ctx->priv().makeDeferredRenderTargetContext(
162              SkBackingFit::kExact, desc.fWidth, desc.fHeight, colorType, nullptr, 1,
163              GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin));
164      if (!renderTargetContext) {
165          return nullptr;
166      }
167  
168      GrPaint paint;
169      auto yuvToRgbProcessor = GrYUVtoRGBEffect::Make(yuvTextureProxies, yuvaIndices, yuvColorSpace,
170                                                      GrSamplerState::Filter::kNearest);
171      paint.addColorFragmentProcessor(std::move(yuvToRgbProcessor));
172  
173      // If the caller expects the pixels in a different color space than the one from the image,
174      // apply a color conversion to do this.
175      std::unique_ptr<GrFragmentProcessor> colorConversionProcessor =
176              GrColorSpaceXformEffect::Make(srcColorSpace, kOpaque_SkAlphaType,
177                                            dstColorSpace, kOpaque_SkAlphaType);
178      if (colorConversionProcessor) {
179          paint.addColorFragmentProcessor(std::move(colorConversionProcessor));
180      }
181  
182      paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
183      const SkRect r = SkRect::MakeIWH(yuvSizeInfo.fSizes[0].fWidth,
184                                       yuvSizeInfo.fSizes[0].fHeight);
185  
186      SkMatrix m = SkEncodedOriginToMatrix(yuvSizeInfo.fOrigin, r.width(), r.height());
187      renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, m, r);
188  
189      return renderTargetContext->asTextureProxyRef();
190  }
191