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 | |
27 | sk_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 | |
101 | void GrYUVProvider::YUVGen_DataReleaseProc(void*, void* data) { |
102 | SkCachedData* cachedData = static_cast<SkCachedData*>(data); |
103 | SkASSERT(cachedData); |
104 | cachedData->unref(); |
105 | } |
106 | |
107 | GrSurfaceProxyView 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 | |