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