Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
YUVUtils.cpp
Go to the documentation of this file.
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
9
14#include "include/core/SkData.h"
22#include "src/core/SkYUVMath.h"
27
28#ifdef SK_GRAPHITE
33#endif
34
35namespace {
36
37static SkPMColor convert_yuva_to_rgba(const float mtx[20], uint8_t yuva[4]) {
38 uint8_t y = yuva[0];
39 uint8_t u = yuva[1];
40 uint8_t v = yuva[2];
41 uint8_t a = yuva[3];
42
43 uint8_t r = SkTPin(SkScalarRoundToInt(mtx[ 0]*y + mtx[ 1]*u + mtx[ 2]*v + mtx[ 4]*255), 0, 255);
44 uint8_t g = SkTPin(SkScalarRoundToInt(mtx[ 5]*y + mtx[ 6]*u + mtx[ 7]*v + mtx[ 9]*255), 0, 255);
45 uint8_t b = SkTPin(SkScalarRoundToInt(mtx[10]*y + mtx[11]*u + mtx[12]*v + mtx[14]*255), 0, 255);
46
47 return SkPremultiplyARGBInline(a, r, g, b);
48}
49
50static uint8_t look_up(SkPoint normPt, const SkPixmap& pmap, SkColorChannel channel) {
51 SkASSERT(normPt.x() > 0 && normPt.x() < 1.0f);
52 SkASSERT(normPt.y() > 0 && normPt.y() < 1.0f);
53 int x = SkScalarFloorToInt(normPt.x() * pmap.width());
54 int y = SkScalarFloorToInt(normPt.y() * pmap.height());
55
56 auto ii = pmap.info().makeColorType(kRGBA_8888_SkColorType).makeWH(1, 1);
57 uint32_t pixel;
58 SkAssertResult(pmap.readPixels(ii, &pixel, sizeof(pixel), x, y));
59 int shift = static_cast<int>(channel) * 8;
60 return static_cast<uint8_t>((pixel >> shift) & 0xff);
61}
62
63class Generator : public SkImageGenerator {
64public:
66 : SkImageGenerator(SkImageInfo::Make(pixmaps.yuvaInfo().dimensions(),
67 kN32_SkColorType,
69 std::move(cs)))
70 , fPixmaps(std::move(pixmaps)) {}
71
72protected:
73 bool onGetPixels(const SkImageInfo& info,
74 void* pixels,
75 size_t rowBytes,
76 const Options&) override {
77 if (kUnknown_SkColorType == fFlattened.colorType()) {
78 fFlattened.allocPixels(info);
79 SkASSERT(info == this->getInfo());
80
81 float mtx[20];
82 SkColorMatrix_YUV2RGB(fPixmaps.yuvaInfo().yuvColorSpace(), mtx);
83 SkYUVAInfo::YUVALocations yuvaLocations = fPixmaps.toYUVALocations();
85
86 SkMatrix om = fPixmaps.yuvaInfo().originMatrix();
87 SkAssertResult(om.invert(&om));
88 float normX = 1.f/info.width();
89 float normY = 1.f/info.height();
90 if (SkEncodedOriginSwapsWidthHeight(fPixmaps.yuvaInfo().origin())) {
91 using std::swap;
92 swap(normX, normY);
93 }
94 for (int y = 0; y < info.height(); ++y) {
95 for (int x = 0; x < info.width(); ++x) {
96 SkPoint xy1 {(x + 0.5f),
97 (y + 0.5f)};
98 xy1 = om.mapPoint(xy1);
99 xy1.fX *= normX;
100 xy1.fY *= normY;
101
102 uint8_t yuva[4] = {0, 0, 0, 255};
103
104 for (auto c : {SkYUVAInfo::YUVAChannels::kY,
107 const auto& pmap = fPixmaps.plane(yuvaLocations[c].fPlane);
108 yuva[c] = look_up(xy1, pmap, yuvaLocations[c].fChannel);
109 }
110 auto [aPlane, aChan] = yuvaLocations[SkYUVAInfo::YUVAChannels::kA];
111 if (aPlane >= 0) {
112 const auto& pmap = fPixmaps.plane(aPlane);
113 yuva[3] = look_up(xy1, pmap, aChan);
114 }
115
116 // Making premul here.
117 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba(mtx, yuva);
118 }
119 }
120 }
121
122 return fFlattened.readPixels(info, pixels, rowBytes, 0, 0);
123 }
124
126 SkYUVAPixmapInfo* info) const override {
127 *info = fPixmaps.pixmapsInfo();
128 return info->isValid();
129 }
130
131 bool onGetYUVAPlanes(const SkYUVAPixmaps& pixmaps) override {
132 SkASSERT(pixmaps.yuvaInfo() == fPixmaps.yuvaInfo());
133 for (int i = 0; i < pixmaps.numPlanes(); ++i) {
134 SkASSERT(fPixmaps.plane(i).colorType() == pixmaps.plane(i).colorType());
135 SkASSERT(fPixmaps.plane(i).dimensions() == pixmaps.plane(i).dimensions());
136 SkASSERT(fPixmaps.plane(i).rowBytes() == pixmaps.plane(i).rowBytes());
137 fPixmaps.plane(i).readPixels(pixmaps.plane(i));
138 }
139 return true;
140 }
141
142private:
143 SkYUVAPixmaps fPixmaps;
144 SkBitmap fFlattened;
145};
146
147} // anonymous namespace
148
149namespace sk_gpu_test {
150
151std::tuple<std::array<sk_sp<SkImage>, SkYUVAInfo::kMaxPlanes>, SkYUVAInfo>
152MakeYUVAPlanesAsA8(SkImage* src,
155 GrRecordingContext* rContext) {
156 float rgbToYUV[20];
157 SkColorMatrix_RGB2YUV(cs, rgbToYUV);
158
162 int n = SkYUVAInfo::PlaneDimensions(src->dimensions(),
163 config,
164 ss,
166 dims);
167 std::array<sk_sp<SkImage>, 4> planes;
168 for (int i = 0; i < n; ++i) {
170 sk_sp<SkSurface> surf;
171 if (rContext) {
172 surf = SkSurfaces::RenderTarget(rContext, skgpu::Budgeted::kYes, info, 1, nullptr);
173 } else {
174 surf = SkSurfaces::Raster(info);
175 }
176 if (!surf) {
177 return {};
178 }
179
181 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 float m[20] = {};
185 std::copy_n(rgbToYUV + 5*i, 5, m + 15);
186 paint.setColorFilter(SkColorFilters::Matrix(m));
187 surf->getCanvas()->drawImageRect(src,
188 SkRect::Make(dims[i]),
190 &paint);
191 planes[i] = surf->makeImageSnapshot();
192 if (!planes[i]) {
193 return {};
194 }
195 }
196 SkYUVAInfo info(src->dimensions(), config, ss, cs);
197 return {planes, info};
198}
199
200std::unique_ptr<LazyYUVImage> LazyYUVImage::Make(sk_sp<SkData> data,
201 skgpu::Mipmapped mipmapped,
203 std::unique_ptr<LazyYUVImage> image(new LazyYUVImage());
204 if (image->reset(std::move(data), mipmapped, std::move(cs))) {
205 return image;
206 } else {
207 return nullptr;
208 }
209}
210
211std::unique_ptr<LazyYUVImage> LazyYUVImage::Make(SkYUVAPixmaps pixmaps,
212 skgpu::Mipmapped mipmapped,
214 std::unique_ptr<LazyYUVImage> image(new LazyYUVImage());
215 if (image->reset(std::move(pixmaps), mipmapped, std::move(cs))) {
216 return image;
217 } else {
218 return nullptr;
219 }
220}
221
223 if (this->ensureYUVImage(rContext, type)) {
224 size_t idx = static_cast<size_t>(type);
225 SkASSERT(idx < std::size(fYUVImage));
226 return fYUVImage[idx];
227 } else {
228 return nullptr;
229 }
230}
231
232#if defined(SK_GRAPHITE)
234 if (this->ensureYUVImage(recorder, type)) {
235 size_t idx = static_cast<size_t>(type);
236 SkASSERT(idx < std::size(fYUVImage));
237 return fYUVImage[idx];
238 } else {
239 return nullptr;
240 }
241}
242#endif
243
244bool LazyYUVImage::reset(sk_sp<SkData> data, skgpu::Mipmapped mipmapped, sk_sp<SkColorSpace> cs) {
245 fMipmapped = mipmapped;
247 if (!codec) {
248 return false;
249 }
250
251 SkYUVAPixmapInfo yuvaPixmapInfo;
252 if (!codec->queryYUVAInfo(SkYUVAPixmapInfo::SupportedDataTypes::All(), &yuvaPixmapInfo)) {
253 return false;
254 }
255 fPixmaps = SkYUVAPixmaps::Allocate(yuvaPixmapInfo);
256 if (!fPixmaps.isValid()) {
257 return false;
258 }
259
260 if (!codec->getYUVAPlanes(fPixmaps)) {
261 return false;
262 }
263
264 fColorSpace = std::move(cs);
265
266 // The SkPixmap data is fully configured now for MakeFromYUVAPixmaps once we get a GrContext
267 return true;
268}
269
270bool LazyYUVImage::reset(SkYUVAPixmaps pixmaps,
271 skgpu::Mipmapped mipmapped,
273 if (!pixmaps.isValid()) {
274 return false;
275 }
276 fMipmapped = mipmapped;
277 if (pixmaps.ownsStorage()) {
278 fPixmaps = std::move(pixmaps);
279 } else {
280 fPixmaps = SkYUVAPixmaps::MakeCopy(std::move(pixmaps));
281 }
282 fColorSpace = std::move(cs);
283 // The SkPixmap data is fully configured now for MakeFromYUVAPixmaps once we get a GrContext
284 return true;
285}
286
287bool LazyYUVImage::ensureYUVImage(GrRecordingContext* rContext, Type type) {
288 size_t idx = static_cast<size_t>(type);
289 SkASSERT(idx < std::size(fYUVImage));
290 if (fYUVImage[idx] && fYUVImage[idx]->isValid(rContext)) {
291 return true; // Have already made a YUV image valid for this context.
292 }
293 // Try to make a new YUV image for this context.
294 switch (type) {
296 if (!rContext || rContext->abandoned()) {
297 return false;
298 }
299 fYUVImage[idx] = SkImages::TextureFromYUVAPixmaps(rContext,
300 fPixmaps,
301 fMipmapped,
302 /*limit to max tex size*/ false,
303 fColorSpace);
304 break;
306 // Make sure the generator has ownership of its backing planes.
307 auto generator = std::make_unique<Generator>(fPixmaps, fColorSpace);
308 fYUVImage[idx] = SkImages::DeferredFromGenerator(std::move(generator));
309 break;
310 }
312 if (!rContext || rContext->abandoned()) {
313 return false;
314 }
315 if (auto direct = rContext->asDirectContext()) {
318 for (int i = 0; i < fPixmaps.numPlanes(); ++i) {
319 mbets[i] = sk_gpu_test::ManagedBackendTexture::MakeFromPixmap(
320 direct,
321 fPixmaps.plane(i),
322 fMipmapped,
323 skgpu::Renderable::kNo,
324 skgpu::Protected::kNo);
325 if (mbets[i]) {
326 textures[i] = mbets[i]->texture();
327 } else {
328 return false;
329 }
330 }
331 GrYUVABackendTextures yuvaTextures(fPixmaps.yuvaInfo(),
332 textures,
334 if (!yuvaTextures.isValid()) {
335 return false;
336 }
337 void* planeRelContext =
338 sk_gpu_test::ManagedBackendTexture::MakeYUVAReleaseContext(mbets);
339 fYUVImage[idx] = SkImages::TextureFromYUVATextures(
340 direct,
341 yuvaTextures,
342 fColorSpace,
343 sk_gpu_test::ManagedBackendTexture::ReleaseProc,
344 planeRelContext);
345 }
346 break;
348 // Not supported in Ganesh
349 return false;
350 }
351 return fYUVImage[idx] != nullptr;
352}
353
354#if defined(SK_GRAPHITE)
355using BackendTexture = skgpu::graphite::BackendTexture;
357using YUVABackendTextures = skgpu::graphite::YUVABackendTextures;
358
359bool LazyYUVImage::ensureYUVImage(Recorder* recorder, Type type) {
360 size_t idx = static_cast<size_t>(type);
361 SkASSERT(idx < std::size(fYUVImage));
362 if (fYUVImage[idx] && as_IB(fYUVImage[idx])->isGraphiteBacked()) {
363 return true; // Have already made a YUV image suitable for Graphite.
364 }
365 // Try to make a new Graphite YUV image
366 switch (type) {
368 if (!recorder) {
369 return false;
370 }
371 fYUVImage[idx] =
373 fPixmaps,
374 {fMipmapped == skgpu::Mipmapped::kYes},
375 /*limitToMaxTextureSize=*/false,
376 fColorSpace);
377 break;
379 // Make sure the generator has ownership of its backing planes.
380 auto generator = std::make_unique<Generator>(fPixmaps, fColorSpace);
381 fYUVImage[idx] = SkImages::DeferredFromGenerator(std::move(generator));
382 break;
383 }
384 case Type::kFromTextures: {
385 if (!recorder) {
386 return false;
387 }
388
390 BackendTexture textures[SkYUVAInfo::kMaxPlanes];
391 for (int i = 0; i < fPixmaps.numPlanes(); ++i) {
392 // MakeFromPixmap will handle generating the upper mipmap levels if necessary.
393 mbets[i] = sk_gpu_test::ManagedGraphiteTexture::MakeFromPixmap(
394 recorder,
395 fPixmaps.plane(i),
396 fMipmapped,
397 skgpu::Renderable::kNo,
398 skgpu::Protected::kNo);
399 if (mbets[i]) {
400 textures[i] = mbets[i]->texture();
401 } else {
402 return false;
403 }
404 }
405 YUVABackendTextures yuvaTextures(recorder,
406 fPixmaps.yuvaInfo(),
407 textures);
408 if (!yuvaTextures.isValid()) {
409 return false;
410 }
411 void* imageRelContext =
412 sk_gpu_test::ManagedGraphiteTexture::MakeYUVAReleaseContext(mbets);
413 fYUVImage[idx] = SkImages::TextureFromYUVATextures(
414 recorder,
415 yuvaTextures,
416 fColorSpace,
417 sk_gpu_test::ManagedGraphiteTexture::ImageReleaseProc,
418 imageRelContext);
419 break;
420 }
421 case Type::kFromImages: {
422 if (!recorder) {
423 return false;
424 }
425
427
429 GenerateMipmapsFromBase genMipmaps = GenerateMipmapsFromBase::kNo;
430 if (fMipmapped == skgpu::Mipmapped::kYes) {
431 genMipmaps = GenerateMipmapsFromBase::kYes;
432 }
433
434 for (int i = 0; i < fPixmaps.numPlanes(); ++i) {
435 const auto& plane = fPixmaps.plane(i);
437 if (fMipmapped == skgpu::Mipmapped::kYes) {
438 mbet = ManagedGraphiteTexture::MakeUnInit(recorder,
439 plane.info(),
440 skgpu::Mipmapped::kYes,
441 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 int levelCnt = SkMipmap::ComputeLevelCount(plane.dimensions());
448 skia_private::TArray<SkAutoPixmapStorage> levelStorage(levelCnt);
449 skia_private::TArray<SkPixmap> levels(levelCnt + 1);
450 levels.push_back(plane);
451 for (int l = 0; l < levelCnt; ++l) {
452 SkISize dims = SkMipmap::ComputeLevelSize(plane.dimensions(), l);
454 level.alloc(plane.info().makeDimensions(dims));
455 level.erase(SK_ColorRED);
456 levels.push_back(level);
457 levelStorage.push_back(std::move(level));
458 }
459 if (!mbet || !recorder->updateBackendTexture(mbet->texture(),
460 levels.data(),
461 levels.size())) {
462 return false;
463 }
464 } else {
465 mbet = ManagedGraphiteTexture::MakeFromPixmap(recorder,
466 plane,
467 skgpu::Mipmapped::kNo,
468 skgpu::Renderable::kNo);
469 if (!mbet) {
470 return false;
471 }
472 }
473 planeImgs[i] = SkImages::WrapTexture(recorder,
474 mbet->texture(),
475 plane.colorType(),
476 plane.alphaType(),
477 fColorSpace,
479 genMipmaps,
480 ManagedGraphiteTexture::ImageReleaseProc,
481 mbet->releaseContext());
482 }
483
484 fYUVImage[idx] = SkImages::TextureFromYUVAImages(
485 recorder,
486 fPixmaps.yuvaInfo(),
487 planeImgs,
488 fColorSpace);
489 break;
490 }
491 }
492 return fYUVImage[idx] != nullptr;
493}
494#endif
495
496} // namespace sk_gpu_test
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
@ kTopLeft_GrSurfaceOrigin
Definition GrTypes.h:148
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition SkAlphaType.h:29
#define SkAssertResult(cond)
Definition SkAssert.h:123
#define SkASSERT(cond)
Definition SkAssert.h:116
static SkPMColor SkPremultiplyARGBInline(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition SkColorType.h:24
@ kUnknown_SkColorType
uninitialized
Definition SkColorType.h:20
uint32_t SkPMColor
Definition SkColor.h:205
SkColorChannel
Definition SkColor.h:228
constexpr SkColor SK_ColorRED
Definition SkColor.h:126
@ kTopLeft_SkEncodedOrigin
static bool SkEncodedOriginSwapsWidthHeight(SkEncodedOrigin origin)
SkYUVColorSpace
Definition SkImageInfo.h:68
static SkImage_Base * as_IB(SkImage *image)
static std::unique_ptr< SkEncoder > Make(SkWStream *dst, const SkPixmap *src, const SkYUVAPixmaps *srcYUVA, const SkColorSpace *srcYUVAColorSpace, const SkJpegEncoder::Options &options)
void swap(sk_sp< T > &a, sk_sp< T > &b)
Definition SkRefCnt.h:341
#define SkScalarRoundToInt(x)
Definition SkScalar.h:37
#define SkScalarFloorToInt(x)
Definition SkScalar.h:35
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
Definition SkTPin.h:19
void SkColorMatrix_RGB2YUV(SkYUVColorSpace cs, float m[20])
void SkColorMatrix_YUV2RGB(SkYUVColorSpace cs, float m[20])
virtual GrDirectContext * asDirectContext()
bool abandoned() override
static std::unique_ptr< SkImageGenerator > MakeFromEncodedCodec(sk_sp< SkData >, std::optional< SkAlphaType >=std::nullopt)
static sk_sp< SkColorFilter > Matrix(const SkColorMatrix &)
const SkImageInfo & getInfo() const
virtual bool onGetYUVAPlanes(const SkYUVAPixmaps &)
virtual bool onQueryYUVAInfo(const SkYUVAPixmapInfo::SupportedDataTypes &, SkYUVAPixmapInfo *) const
virtual bool onGetPixels(const SkImageInfo &, void *, size_t, const Options &)
SkPoint mapPoint(SkPoint pt) const
Definition SkMatrix.h:1374
bool invert(SkMatrix *inverse) const
Definition SkMatrix.h:1206
static SkISize ComputeLevelSize(int baseWidth, int baseHeight, int level)
Definition SkMipmap.cpp:168
static int ComputeLevelCount(int baseWidth, int baseHeight)
Definition SkMipmap.cpp:134
size_t rowBytes() const
Definition SkPixmap.h:145
int width() const
Definition SkPixmap.h:160
bool readPixels(const SkImageInfo &dstInfo, void *dstPixels, size_t dstRowBytes) const
Definition SkPixmap.h:592
SkColorType colorType() const
Definition SkPixmap.h:173
const SkImageInfo & info() const
Definition SkPixmap.h:135
int height() const
Definition SkPixmap.h:166
SkISize dimensions() const
Definition SkPixmap.h:171
@ kY_U_V_A
Plane 0: Y, Plane 1: U, Plane 2: V, Plane 3: A.
@ kY_U_V
Plane 0: Y, Plane 1: U, Plane 2: V.
static constexpr int kMaxPlanes
Definition SkYUVAInfo.h:98
std::array< YUVALocation, kYUVAChannelCount > YUVALocations
Definition SkYUVAInfo.h:32
static int PlaneDimensions(SkISize imageDimensions, PlaneConfig, Subsampling, SkEncodedOrigin, SkISize planeDimensions[kMaxPlanes])
static constexpr SupportedDataTypes All()
const SkYUVAInfo & yuvaInfo() const
const SkPixmap & plane(int i) const
static SkYUVAPixmaps Allocate(const SkYUVAPixmapInfo &yuvaPixmapInfo)
static SkYUVAPixmaps MakeCopy(const SkYUVAPixmaps &src)
int numPlanes() const
bool ownsStorage() const
bool isValid() const
sk_sp< SkImage > refImage(GrRecordingContext *rContext, Type)
Definition YUVUtils.cpp:222
static std::unique_ptr< LazyYUVImage > Make(sk_sp< SkData > data, skgpu::Mipmapped=skgpu::Mipmapped::kNo, sk_sp< SkColorSpace >=nullptr)
Definition YUVUtils.cpp:200
bool updateBackendTexture(const BackendTexture &, const SkPixmap srcData[], int numLevels)
Definition Recorder.cpp:311
const Paint & paint
std::vector< std::shared_ptr< FakeTexture > > textures
sk_sp< SkImage > image
Definition examples.cpp:29
static bool b
struct MyStruct a[10]
double y
double x
SK_API sk_sp< SkImage > TextureFromYUVAPixmaps(GrRecordingContext *context, const SkYUVAPixmaps &pixmaps, skgpu::Mipmapped buildMips, bool limitToMaxTextureSize, sk_sp< SkColorSpace > imageColorSpace)
SK_API sk_sp< SkImage > DeferredFromGenerator(std::unique_ptr< SkImageGenerator > imageGenerator)
SK_API sk_sp< SkImage > TextureFromYUVATextures(GrRecordingContext *context, const GrYUVABackendTextures &yuvaTextures, sk_sp< SkColorSpace > imageColorSpace, TextureReleaseProc textureReleaseProc=nullptr, ReleaseContext releaseContext=nullptr)
SK_API sk_sp< SkImage > TextureFromYUVAImages(skgpu::graphite::Recorder *recorder, const SkYUVAInfo &yuvaInfo, SkSpan< const sk_sp< SkImage > > images, sk_sp< SkColorSpace > imageColorSpace)
GenerateMipmapsFromBase
Definition Image.h:31
SK_API sk_sp< SkImage > WrapTexture(skgpu::graphite::Recorder *, const skgpu::graphite::BackendTexture &, SkColorType colorType, SkAlphaType alphaType, sk_sp< SkColorSpace > colorSpace, skgpu::Origin origin, GenerateMipmapsFromBase generateMipmapsFromBase, TextureReleaseProc=nullptr, ReleaseContext=nullptr)
SK_API sk_sp< SkSurface > Raster(const SkImageInfo &imageInfo, size_t rowBytes, const SkSurfaceProps *surfaceProps)
SK_API sk_sp< SkSurface > RenderTarget(GrRecordingContext *context, skgpu::Budgeted budgeted, const SkImageInfo &imageInfo, int sampleCount, GrSurfaceOrigin surfaceOrigin, const SkSurfaceProps *surfaceProps, bool shouldCreateWithMips=false, bool isProtected=false)
std::function< UnitlessTime(int)> Generator
Mipmapped
Definition GpuTypes.h:53
Definition ref_ptr.h:256
SkImageInfo makeWH(int newWidth, int newHeight) const
SkImageInfo makeColorType(SkColorType newColorType) const
static SkImageInfo MakeA8(int width, int height)
float fX
x-axis value
constexpr float y() const
constexpr float x() const
static SkRect Make(const SkISize &size)
Definition SkRect.h:669
static bool AreValidLocations(const SkYUVAInfo::YUVALocations &locations, int *numPlanes=nullptr)