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