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