/src/skia/tools/gpu/YUVUtils.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2019 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 "tools/gpu/YUVUtils.h" |
9 | | |
10 | | #include "include/core/SkBitmap.h" |
11 | | #include "include/core/SkCanvas.h" |
12 | | #include "include/core/SkColorFilter.h" |
13 | | #include "include/core/SkColorPriv.h" |
14 | | #include "include/core/SkData.h" |
15 | | #include "include/core/SkSurface.h" |
16 | | #include "include/gpu/GrRecordingContext.h" |
17 | | #include "include/gpu/GrYUVABackendTextures.h" |
18 | | #include "include/gpu/ganesh/SkImageGanesh.h" |
19 | | #include "include/gpu/ganesh/SkSurfaceGanesh.h" |
20 | | #include "src/codec/SkCodecImageGenerator.h" |
21 | | #include "src/core/SkYUVAInfoLocation.h" |
22 | | #include "src/core/SkYUVMath.h" |
23 | | #include "src/gpu/ganesh/GrDirectContextPriv.h" |
24 | | #include "src/gpu/ganesh/GrRecordingContextPriv.h" |
25 | | #include "src/image/SkImage_Base.h" |
26 | | #include "tools/gpu/ManagedBackendTexture.h" |
27 | | |
28 | | #ifdef SK_GRAPHITE |
29 | | #include "include/gpu/graphite/Image.h" |
30 | | #include "include/gpu/graphite/YUVABackendTextures.h" |
31 | | #include "include/private/base/SkTArray.h" |
32 | | #include "src/core/SkAutoPixmapStorage.h" |
33 | | #endif |
34 | | |
35 | | namespace { |
36 | | |
37 | 0 | static SkPMColor convert_yuva_to_rgba(const float mtx[20], uint8_t yuva[4]) { |
38 | 0 | uint8_t y = yuva[0]; |
39 | 0 | uint8_t u = yuva[1]; |
40 | 0 | uint8_t v = yuva[2]; |
41 | 0 | uint8_t a = yuva[3]; |
42 | |
|
43 | 0 | uint8_t r = SkTPin(SkScalarRoundToInt(mtx[ 0]*y + mtx[ 1]*u + mtx[ 2]*v + mtx[ 4]*255), 0, 255); |
44 | 0 | uint8_t g = SkTPin(SkScalarRoundToInt(mtx[ 5]*y + mtx[ 6]*u + mtx[ 7]*v + mtx[ 9]*255), 0, 255); |
45 | 0 | uint8_t b = SkTPin(SkScalarRoundToInt(mtx[10]*y + mtx[11]*u + mtx[12]*v + mtx[14]*255), 0, 255); |
46 | |
|
47 | 0 | return SkPremultiplyARGBInline(a, r, g, b); |
48 | 0 | } |
49 | | |
50 | 0 | static uint8_t look_up(SkPoint normPt, const SkPixmap& pmap, SkColorChannel channel) { |
51 | 0 | SkASSERT(normPt.x() > 0 && normPt.x() < 1.0f); |
52 | 0 | SkASSERT(normPt.y() > 0 && normPt.y() < 1.0f); |
53 | 0 | int x = SkScalarFloorToInt(normPt.x() * pmap.width()); |
54 | 0 | int y = SkScalarFloorToInt(normPt.y() * pmap.height()); |
55 | |
|
56 | 0 | auto ii = pmap.info().makeColorType(kRGBA_8888_SkColorType).makeWH(1, 1); |
57 | 0 | uint32_t pixel; |
58 | 0 | SkAssertResult(pmap.readPixels(ii, &pixel, sizeof(pixel), x, y)); |
59 | 0 | int shift = static_cast<int>(channel) * 8; |
60 | 0 | return static_cast<uint8_t>((pixel >> shift) & 0xff); |
61 | 0 | } Unexecuted instantiation: YUVUtils.cpp:(anonymous namespace)::look_up(SkPoint, SkPixmap const&, SkColorChannel) Unexecuted instantiation: YUVUtils.cpp:(anonymous namespace)::look_up(SkPoint, SkPixmap const&, SkColorChannel) |
62 | | |
63 | | class Generator : public SkImageGenerator { |
64 | | public: |
65 | | Generator(SkYUVAPixmaps pixmaps, sk_sp<SkColorSpace> cs) |
66 | | : SkImageGenerator(SkImageInfo::Make(pixmaps.yuvaInfo().dimensions(), |
67 | | kN32_SkColorType, |
68 | | kPremul_SkAlphaType, |
69 | | std::move(cs))) |
70 | 0 | , fPixmaps(std::move(pixmaps)) {} |
71 | | |
72 | | protected: |
73 | | bool onGetPixels(const SkImageInfo& info, |
74 | | void* pixels, |
75 | | size_t rowBytes, |
76 | 0 | const Options&) override { |
77 | 0 | if (kUnknown_SkColorType == fFlattened.colorType()) { |
78 | 0 | fFlattened.allocPixels(info); |
79 | 0 | SkASSERT(info == this->getInfo()); |
80 | |
|
81 | 0 | float mtx[20]; |
82 | 0 | SkColorMatrix_YUV2RGB(fPixmaps.yuvaInfo().yuvColorSpace(), mtx); |
83 | 0 | SkYUVAInfo::YUVALocations yuvaLocations = fPixmaps.toYUVALocations(); |
84 | 0 | SkASSERT(SkYUVAInfo::YUVALocation::AreValidLocations(yuvaLocations)); |
85 | |
|
86 | 0 | SkMatrix om = fPixmaps.yuvaInfo().originMatrix(); |
87 | 0 | SkAssertResult(om.invert(&om)); |
88 | 0 | float normX = 1.f/info.width(); |
89 | 0 | float normY = 1.f/info.height(); |
90 | 0 | if (SkEncodedOriginSwapsWidthHeight(fPixmaps.yuvaInfo().origin())) { |
91 | 0 | using std::swap; |
92 | 0 | swap(normX, normY); |
93 | 0 | } |
94 | 0 | for (int y = 0; y < info.height(); ++y) { |
95 | 0 | for (int x = 0; x < info.width(); ++x) { |
96 | 0 | SkPoint xy1 {(x + 0.5f), |
97 | 0 | (y + 0.5f)}; |
98 | 0 | xy1 = om.mapPoint(xy1); |
99 | 0 | xy1.fX *= normX; |
100 | 0 | xy1.fY *= normY; |
101 | |
|
102 | 0 | uint8_t yuva[4] = {0, 0, 0, 255}; |
103 | |
|
104 | 0 | for (auto c : {SkYUVAInfo::YUVAChannels::kY, |
105 | 0 | SkYUVAInfo::YUVAChannels::kU, |
106 | 0 | SkYUVAInfo::YUVAChannels::kV}) { |
107 | 0 | const auto& pmap = fPixmaps.plane(yuvaLocations[c].fPlane); |
108 | 0 | yuva[c] = look_up(xy1, pmap, yuvaLocations[c].fChannel); |
109 | 0 | } |
110 | 0 | auto [aPlane, aChan] = yuvaLocations[SkYUVAInfo::YUVAChannels::kA]; |
111 | 0 | if (aPlane >= 0) { |
112 | 0 | const auto& pmap = fPixmaps.plane(aPlane); |
113 | 0 | yuva[3] = look_up(xy1, pmap, aChan); |
114 | 0 | } |
115 | | |
116 | | // Making premul here. |
117 | 0 | *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba(mtx, yuva); |
118 | 0 | } |
119 | 0 | } |
120 | 0 | } |
121 | |
|
122 | 0 | return fFlattened.readPixels(info, pixels, rowBytes, 0, 0); |
123 | 0 | } Unexecuted instantiation: YUVUtils.cpp:(anonymous namespace)::Generator::onGetPixels(SkImageInfo const&, void*, unsigned long, SkImageGenerator::Options const&) Unexecuted instantiation: YUVUtils.cpp:(anonymous namespace)::Generator::onGetPixels(SkImageInfo const&, void*, unsigned long, SkImageGenerator::Options const&) |
124 | | |
125 | | bool onQueryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes& types, |
126 | 0 | SkYUVAPixmapInfo* info) const override { |
127 | 0 | *info = fPixmaps.pixmapsInfo(); |
128 | 0 | return info->isValid(); |
129 | 0 | } |
130 | | |
131 | 0 | bool onGetYUVAPlanes(const SkYUVAPixmaps& pixmaps) override { |
132 | 0 | SkASSERT(pixmaps.yuvaInfo() == fPixmaps.yuvaInfo()); |
133 | 0 | for (int i = 0; i < pixmaps.numPlanes(); ++i) { |
134 | 0 | SkASSERT(fPixmaps.plane(i).colorType() == pixmaps.plane(i).colorType()); |
135 | 0 | SkASSERT(fPixmaps.plane(i).dimensions() == pixmaps.plane(i).dimensions()); |
136 | 0 | SkASSERT(fPixmaps.plane(i).rowBytes() == pixmaps.plane(i).rowBytes()); |
137 | 0 | fPixmaps.plane(i).readPixels(pixmaps.plane(i)); |
138 | 0 | } |
139 | 0 | return true; |
140 | 0 | } Unexecuted instantiation: YUVUtils.cpp:(anonymous namespace)::Generator::onGetYUVAPlanes(SkYUVAPixmaps const&) Unexecuted instantiation: YUVUtils.cpp:(anonymous namespace)::Generator::onGetYUVAPlanes(SkYUVAPixmaps const&) |
141 | | |
142 | | private: |
143 | | SkYUVAPixmaps fPixmaps; |
144 | | SkBitmap fFlattened; |
145 | | }; |
146 | | |
147 | | } // anonymous namespace |
148 | | |
149 | | namespace sk_gpu_test { |
150 | | |
151 | | std::tuple<std::array<sk_sp<SkImage>, SkYUVAInfo::kMaxPlanes>, SkYUVAInfo> |
152 | | MakeYUVAPlanesAsA8(SkImage* src, |
153 | | SkYUVColorSpace cs, |
154 | | SkYUVAInfo::Subsampling ss, |
155 | 0 | GrRecordingContext* rContext) { |
156 | 0 | float rgbToYUV[20]; |
157 | 0 | SkColorMatrix_RGB2YUV(cs, rgbToYUV); |
158 | |
|
159 | 0 | SkYUVAInfo::PlaneConfig config = src->isOpaque() ? SkYUVAInfo::PlaneConfig::kY_U_V |
160 | 0 | : SkYUVAInfo::PlaneConfig::kY_U_V_A; |
161 | 0 | SkISize dims[SkYUVAInfo::kMaxPlanes]; |
162 | 0 | int n = SkYUVAInfo::PlaneDimensions(src->dimensions(), |
163 | 0 | config, |
164 | 0 | ss, |
165 | 0 | kTopLeft_SkEncodedOrigin, |
166 | 0 | dims); |
167 | 0 | std::array<sk_sp<SkImage>, 4> planes; |
168 | 0 | for (int i = 0; i < n; ++i) { |
169 | 0 | SkImageInfo info = SkImageInfo::MakeA8(dims[i]); |
170 | 0 | sk_sp<SkSurface> surf; |
171 | 0 | if (rContext) { |
172 | 0 | surf = SkSurfaces::RenderTarget(rContext, skgpu::Budgeted::kYes, info, 1, nullptr); |
173 | 0 | } else { |
174 | 0 | surf = SkSurfaces::Raster(info); |
175 | 0 | } |
176 | 0 | if (!surf) { |
177 | 0 | return {}; |
178 | 0 | } |
179 | | |
180 | 0 | SkPaint paint; |
181 | 0 | paint.setBlendMode(SkBlendMode::kSrc); |
182 | | |
183 | | // Make a matrix with the ith row of rgbToYUV copied to the A row since we're drawing to A8. |
184 | 0 | float m[20] = {}; |
185 | 0 | std::copy_n(rgbToYUV + 5*i, 5, m + 15); |
186 | 0 | paint.setColorFilter(SkColorFilters::Matrix(m)); |
187 | 0 | surf->getCanvas()->drawImageRect(src, |
188 | 0 | SkRect::Make(dims[i]), |
189 | 0 | SkSamplingOptions(SkFilterMode::kLinear), |
190 | 0 | &paint); |
191 | 0 | planes[i] = surf->makeImageSnapshot(); |
192 | 0 | if (!planes[i]) { |
193 | 0 | return {}; |
194 | 0 | } |
195 | 0 | } |
196 | 0 | SkYUVAInfo info(src->dimensions(), config, ss, cs); |
197 | 0 | return {planes, info}; |
198 | 0 | } |
199 | | |
200 | | std::unique_ptr<LazyYUVImage> LazyYUVImage::Make(sk_sp<SkData> data, |
201 | | skgpu::Mipmapped mipmapped, |
202 | 0 | sk_sp<SkColorSpace> cs) { |
203 | 0 | std::unique_ptr<LazyYUVImage> image(new LazyYUVImage()); |
204 | 0 | if (image->reset(std::move(data), mipmapped, std::move(cs))) { |
205 | 0 | return image; |
206 | 0 | } else { |
207 | 0 | return nullptr; |
208 | 0 | } |
209 | 0 | } |
210 | | |
211 | | std::unique_ptr<LazyYUVImage> LazyYUVImage::Make(SkYUVAPixmaps pixmaps, |
212 | | skgpu::Mipmapped mipmapped, |
213 | 0 | sk_sp<SkColorSpace> cs) { |
214 | 0 | std::unique_ptr<LazyYUVImage> image(new LazyYUVImage()); |
215 | 0 | if (image->reset(std::move(pixmaps), mipmapped, std::move(cs))) { |
216 | 0 | return image; |
217 | 0 | } else { |
218 | 0 | return nullptr; |
219 | 0 | } |
220 | 0 | } |
221 | | |
222 | 0 | sk_sp<SkImage> LazyYUVImage::refImage(GrRecordingContext* rContext, Type type) { |
223 | 0 | if (this->ensureYUVImage(rContext, type)) { |
224 | 0 | size_t idx = static_cast<size_t>(type); |
225 | 0 | SkASSERT(idx < std::size(fYUVImage)); |
226 | 0 | return fYUVImage[idx]; |
227 | 0 | } else { |
228 | 0 | return nullptr; |
229 | 0 | } |
230 | 0 | } Unexecuted instantiation: sk_gpu_test::LazyYUVImage::refImage(GrRecordingContext*, sk_gpu_test::LazyYUVImage::Type) Unexecuted instantiation: sk_gpu_test::LazyYUVImage::refImage(GrRecordingContext*, sk_gpu_test::LazyYUVImage::Type) |
231 | | |
232 | | #if defined(SK_GRAPHITE) |
233 | 0 | sk_sp<SkImage> LazyYUVImage::refImage(skgpu::graphite::Recorder* recorder, Type type) { |
234 | 0 | if (this->ensureYUVImage(recorder, type)) { |
235 | 0 | size_t idx = static_cast<size_t>(type); |
236 | 0 | SkASSERT(idx < std::size(fYUVImage)); |
237 | 0 | return fYUVImage[idx]; |
238 | 0 | } else { |
239 | 0 | return nullptr; |
240 | 0 | } |
241 | 0 | } Unexecuted instantiation: sk_gpu_test::LazyYUVImage::refImage(skgpu::graphite::Recorder*, sk_gpu_test::LazyYUVImage::Type) Unexecuted instantiation: sk_gpu_test::LazyYUVImage::refImage(skgpu::graphite::Recorder*, sk_gpu_test::LazyYUVImage::Type) |
242 | | #endif |
243 | | |
244 | 0 | bool LazyYUVImage::reset(sk_sp<SkData> data, skgpu::Mipmapped mipmapped, sk_sp<SkColorSpace> cs) { |
245 | 0 | fMipmapped = mipmapped; |
246 | 0 | auto codec = SkCodecImageGenerator::MakeFromEncodedCodec(data); |
247 | 0 | if (!codec) { |
248 | 0 | return false; |
249 | 0 | } |
250 | | |
251 | 0 | SkYUVAPixmapInfo yuvaPixmapInfo; |
252 | 0 | if (!codec->queryYUVAInfo(SkYUVAPixmapInfo::SupportedDataTypes::All(), &yuvaPixmapInfo)) { |
253 | 0 | return false; |
254 | 0 | } |
255 | 0 | fPixmaps = SkYUVAPixmaps::Allocate(yuvaPixmapInfo); |
256 | 0 | if (!fPixmaps.isValid()) { |
257 | 0 | return false; |
258 | 0 | } |
259 | | |
260 | 0 | if (!codec->getYUVAPlanes(fPixmaps)) { |
261 | 0 | return false; |
262 | 0 | } |
263 | | |
264 | 0 | fColorSpace = std::move(cs); |
265 | | |
266 | | // The SkPixmap data is fully configured now for MakeFromYUVAPixmaps once we get a GrContext |
267 | 0 | return true; |
268 | 0 | } |
269 | | |
270 | | bool LazyYUVImage::reset(SkYUVAPixmaps pixmaps, |
271 | | skgpu::Mipmapped mipmapped, |
272 | 0 | sk_sp<SkColorSpace> cs) { |
273 | 0 | if (!pixmaps.isValid()) { |
274 | 0 | return false; |
275 | 0 | } |
276 | 0 | fMipmapped = mipmapped; |
277 | 0 | if (pixmaps.ownsStorage()) { |
278 | 0 | fPixmaps = std::move(pixmaps); |
279 | 0 | } else { |
280 | 0 | fPixmaps = SkYUVAPixmaps::MakeCopy(std::move(pixmaps)); |
281 | 0 | } |
282 | 0 | fColorSpace = std::move(cs); |
283 | | // The SkPixmap data is fully configured now for MakeFromYUVAPixmaps once we get a GrContext |
284 | 0 | return true; |
285 | 0 | } |
286 | | |
287 | 0 | bool LazyYUVImage::ensureYUVImage(GrRecordingContext* rContext, Type type) { |
288 | 0 | size_t idx = static_cast<size_t>(type); |
289 | 0 | SkASSERT(idx < std::size(fYUVImage)); |
290 | 0 | if (fYUVImage[idx] && fYUVImage[idx]->isValid(rContext)) { |
291 | 0 | return true; // Have already made a YUV image valid for this context. |
292 | 0 | } |
293 | | // Try to make a new YUV image for this context. |
294 | 0 | switch (type) { |
295 | 0 | case Type::kFromPixmaps: |
296 | 0 | if (!rContext || rContext->abandoned()) { |
297 | 0 | return false; |
298 | 0 | } |
299 | 0 | fYUVImage[idx] = SkImages::TextureFromYUVAPixmaps(rContext, |
300 | 0 | fPixmaps, |
301 | 0 | fMipmapped, |
302 | 0 | /*limit to max tex size*/ false, |
303 | 0 | fColorSpace); |
304 | 0 | break; |
305 | 0 | case Type::kFromGenerator: { |
306 | | // Make sure the generator has ownership of its backing planes. |
307 | 0 | auto generator = std::make_unique<Generator>(fPixmaps, fColorSpace); |
308 | 0 | fYUVImage[idx] = SkImages::DeferredFromGenerator(std::move(generator)); |
309 | 0 | break; |
310 | 0 | } |
311 | 0 | case Type::kFromTextures: |
312 | 0 | if (!rContext || rContext->abandoned()) { |
313 | 0 | return false; |
314 | 0 | } |
315 | 0 | if (auto direct = rContext->asDirectContext()) { |
316 | 0 | sk_sp<sk_gpu_test::ManagedBackendTexture> mbets[SkYUVAInfo::kMaxPlanes]; |
317 | 0 | GrBackendTexture textures[SkYUVAInfo::kMaxPlanes]; |
318 | 0 | for (int i = 0; i < fPixmaps.numPlanes(); ++i) { |
319 | 0 | mbets[i] = sk_gpu_test::ManagedBackendTexture::MakeFromPixmap( |
320 | 0 | direct, |
321 | 0 | fPixmaps.plane(i), |
322 | 0 | fMipmapped, |
323 | 0 | skgpu::Renderable::kNo, |
324 | 0 | skgpu::Protected::kNo); |
325 | 0 | if (mbets[i]) { |
326 | 0 | textures[i] = mbets[i]->texture(); |
327 | 0 | } else { |
328 | 0 | return false; |
329 | 0 | } |
330 | 0 | } |
331 | 0 | GrYUVABackendTextures yuvaTextures(fPixmaps.yuvaInfo(), |
332 | 0 | textures, |
333 | 0 | kTopLeft_GrSurfaceOrigin); |
334 | 0 | if (!yuvaTextures.isValid()) { |
335 | 0 | return false; |
336 | 0 | } |
337 | 0 | void* planeRelContext = |
338 | 0 | sk_gpu_test::ManagedBackendTexture::MakeYUVAReleaseContext(mbets); |
339 | 0 | fYUVImage[idx] = SkImages::TextureFromYUVATextures( |
340 | 0 | direct, |
341 | 0 | yuvaTextures, |
342 | 0 | fColorSpace, |
343 | 0 | sk_gpu_test::ManagedBackendTexture::ReleaseProc, |
344 | 0 | planeRelContext); |
345 | 0 | } |
346 | 0 | break; |
347 | 0 | case Type::kFromImages: |
348 | | // Not supported in Ganesh |
349 | 0 | return false; |
350 | 0 | } |
351 | 0 | return fYUVImage[idx] != nullptr; |
352 | 0 | } Unexecuted instantiation: sk_gpu_test::LazyYUVImage::ensureYUVImage(GrRecordingContext*, sk_gpu_test::LazyYUVImage::Type) Unexecuted instantiation: sk_gpu_test::LazyYUVImage::ensureYUVImage(GrRecordingContext*, sk_gpu_test::LazyYUVImage::Type) |
353 | | |
354 | | #if defined(SK_GRAPHITE) |
355 | | using BackendTexture = skgpu::graphite::BackendTexture; |
356 | | using Recorder = skgpu::graphite::Recorder; |
357 | | using YUVABackendTextures = skgpu::graphite::YUVABackendTextures; |
358 | | |
359 | 0 | bool LazyYUVImage::ensureYUVImage(Recorder* recorder, Type type) { |
360 | 0 | size_t idx = static_cast<size_t>(type); |
361 | 0 | SkASSERT(idx < std::size(fYUVImage)); |
362 | 0 | if (fYUVImage[idx] && as_IB(fYUVImage[idx])->isGraphiteBacked()) { |
363 | 0 | return true; // Have already made a YUV image suitable for Graphite. |
364 | 0 | } |
365 | | // Try to make a new Graphite YUV image |
366 | 0 | switch (type) { |
367 | 0 | case Type::kFromPixmaps: |
368 | 0 | if (!recorder) { |
369 | 0 | return false; |
370 | 0 | } |
371 | 0 | fYUVImage[idx] = |
372 | 0 | SkImages::TextureFromYUVAPixmaps(recorder, |
373 | 0 | fPixmaps, |
374 | 0 | {fMipmapped == skgpu::Mipmapped::kYes}, |
375 | 0 | /*limitToMaxTextureSize=*/false, |
376 | 0 | fColorSpace); |
377 | 0 | break; |
378 | 0 | case Type::kFromGenerator: { |
379 | | // Make sure the generator has ownership of its backing planes. |
380 | 0 | auto generator = std::make_unique<Generator>(fPixmaps, fColorSpace); |
381 | 0 | fYUVImage[idx] = SkImages::DeferredFromGenerator(std::move(generator)); |
382 | 0 | break; |
383 | 0 | } |
384 | 0 | case Type::kFromTextures: { |
385 | 0 | if (!recorder) { |
386 | 0 | return false; |
387 | 0 | } |
388 | | |
389 | 0 | sk_sp<sk_gpu_test::ManagedGraphiteTexture> mbets[SkYUVAInfo::kMaxPlanes]; |
390 | 0 | BackendTexture textures[SkYUVAInfo::kMaxPlanes]; |
391 | 0 | for (int i = 0; i < fPixmaps.numPlanes(); ++i) { |
392 | | // MakeFromPixmap will handle generating the upper mipmap levels if necessary. |
393 | 0 | mbets[i] = sk_gpu_test::ManagedGraphiteTexture::MakeFromPixmap( |
394 | 0 | recorder, |
395 | 0 | fPixmaps.plane(i), |
396 | 0 | fMipmapped, |
397 | 0 | skgpu::Renderable::kNo, |
398 | 0 | skgpu::Protected::kNo); |
399 | 0 | if (mbets[i]) { |
400 | 0 | textures[i] = mbets[i]->texture(); |
401 | 0 | } else { |
402 | 0 | return false; |
403 | 0 | } |
404 | 0 | } |
405 | 0 | YUVABackendTextures yuvaTextures(recorder, |
406 | 0 | fPixmaps.yuvaInfo(), |
407 | 0 | textures); |
408 | 0 | if (!yuvaTextures.isValid()) { |
409 | 0 | return false; |
410 | 0 | } |
411 | 0 | void* imageRelContext = |
412 | 0 | sk_gpu_test::ManagedGraphiteTexture::MakeYUVAReleaseContext(mbets); |
413 | 0 | fYUVImage[idx] = SkImages::TextureFromYUVATextures( |
414 | 0 | recorder, |
415 | 0 | yuvaTextures, |
416 | 0 | fColorSpace, |
417 | 0 | sk_gpu_test::ManagedGraphiteTexture::ImageReleaseProc, |
418 | 0 | imageRelContext); |
419 | 0 | break; |
420 | 0 | } |
421 | 0 | case Type::kFromImages: { |
422 | 0 | if (!recorder) { |
423 | 0 | return false; |
424 | 0 | } |
425 | | |
426 | 0 | sk_sp<SkImage> planeImgs[SkYUVAInfo::kMaxPlanes]; |
427 | |
|
428 | 0 | using SkImages::GenerateMipmapsFromBase; |
429 | 0 | GenerateMipmapsFromBase genMipmaps = GenerateMipmapsFromBase::kNo; |
430 | 0 | if (fMipmapped == skgpu::Mipmapped::kYes) { |
431 | 0 | genMipmaps = GenerateMipmapsFromBase::kYes; |
432 | 0 | } |
433 | |
|
434 | 0 | for (int i = 0; i < fPixmaps.numPlanes(); ++i) { |
435 | 0 | const auto& plane = fPixmaps.plane(i); |
436 | 0 | sk_sp<ManagedGraphiteTexture> mbet; |
437 | 0 | if (fMipmapped == skgpu::Mipmapped::kYes) { |
438 | 0 | mbet = ManagedGraphiteTexture::MakeUnInit(recorder, |
439 | 0 | plane.info(), |
440 | 0 | skgpu::Mipmapped::kYes, |
441 | 0 | skgpu::Renderable::kNo); |
442 | | // We allocate a full mip set because updateBackendTexture requires it. However, |
443 | | // the non-base levels are cleared to red. We rely on SkImages::WrapTexture |
444 | | // to actually generate the contents from the base level for each plane on the |
445 | | // GPU. This exercises the case where the client wants a mipmapped YUV image but |
446 | | // only provides the base level contents. |
447 | 0 | int levelCnt = SkMipmap::ComputeLevelCount(plane.dimensions()); |
448 | 0 | skia_private::TArray<SkAutoPixmapStorage> levelStorage(levelCnt); |
449 | 0 | skia_private::TArray<SkPixmap> levels(levelCnt + 1); |
450 | 0 | levels.push_back(plane); |
451 | 0 | for (int l = 0; l < levelCnt; ++l) { |
452 | 0 | SkISize dims = SkMipmap::ComputeLevelSize(plane.dimensions(), l); |
453 | 0 | SkAutoPixmapStorage level; |
454 | 0 | level.alloc(plane.info().makeDimensions(dims)); |
455 | 0 | level.erase(SK_ColorRED); |
456 | 0 | levels.push_back(level); |
457 | 0 | levelStorage.push_back(std::move(level)); |
458 | 0 | } |
459 | 0 | if (!mbet || !recorder->updateBackendTexture(mbet->texture(), |
460 | 0 | levels.data(), |
461 | 0 | levels.size())) { |
462 | 0 | return false; |
463 | 0 | } |
464 | 0 | } else { |
465 | 0 | mbet = ManagedGraphiteTexture::MakeFromPixmap(recorder, |
466 | 0 | plane, |
467 | 0 | skgpu::Mipmapped::kNo, |
468 | 0 | skgpu::Renderable::kNo); |
469 | 0 | if (!mbet) { |
470 | 0 | return false; |
471 | 0 | } |
472 | 0 | } |
473 | 0 | planeImgs[i] = SkImages::WrapTexture(recorder, |
474 | 0 | mbet->texture(), |
475 | 0 | plane.colorType(), |
476 | 0 | plane.alphaType(), |
477 | 0 | fColorSpace, |
478 | 0 | skgpu::Origin::kTopLeft, |
479 | 0 | genMipmaps, |
480 | 0 | ManagedGraphiteTexture::ImageReleaseProc, |
481 | 0 | mbet->releaseContext()); |
482 | 0 | } |
483 | | |
484 | 0 | fYUVImage[idx] = SkImages::TextureFromYUVAImages( |
485 | 0 | recorder, |
486 | 0 | fPixmaps.yuvaInfo(), |
487 | 0 | planeImgs, |
488 | 0 | fColorSpace); |
489 | 0 | break; |
490 | 0 | } |
491 | 0 | } |
492 | 0 | return fYUVImage[idx] != nullptr; |
493 | 0 | } Unexecuted instantiation: sk_gpu_test::LazyYUVImage::ensureYUVImage(skgpu::graphite::Recorder*, sk_gpu_test::LazyYUVImage::Type) Unexecuted instantiation: sk_gpu_test::LazyYUVImage::ensureYUVImage(skgpu::graphite::Recorder*, sk_gpu_test::LazyYUVImage::Type) |
494 | | #endif |
495 | | |
496 | | } // namespace sk_gpu_test |