Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
wacky_yuv_formats.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2018 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 "gm/gm.h"
16#include "include/core/SkFont.h"
24#include "include/core/SkPath.h"
27#include "include/core/SkRect.h"
30#include "include/core/SkSize.h"
37#include "include/gpu/GrTypes.h"
45#include "src/base/SkHalf.h"
47#include "src/core/SkYUVMath.h"
50#include "tools/DecodeUtils.h"
51#include "tools/ToolUtils.h"
53#include "tools/gpu/YUVUtils.h"
54
55#include <math.h>
56#include <string.h>
57#include <initializer_list>
58#include <memory>
59#include <utility>
60#include <vector>
61
62static const int kTileWidthHeight = 128;
63static const int kLabelWidth = 64;
64static const int kLabelHeight = 32;
65static const int kSubsetPadding = 8;
66static const int kPad = 1;
67
69
71 // 4:2:0 formats, 24 bpp
72 kP016_YUVFormat, // 16-bit Y plane + 2x2 down sampled interleaved U/V plane (2 textures)
73 // 4:2:0 formats, "15 bpp" (but really 24 bpp)
74 kP010_YUVFormat, // same as kP016 except "10 bpp". Note that it is the same memory layout
75 // except that the bottom 6 bits are zeroed out (2 textures)
76 // TODO: we're cheating a bit w/ P010 and just treating it as unorm 16. This means its
77 // fully saturated values are 65504 rather than 65535 (that is just .9995 out of 1.0 though).
78
79 // This is laid out the same as kP016 and kP010 but uses F16 unstead of U16. In this case
80 // the 10 bits/channel vs 16 bits/channel distinction isn't relevant.
82
83 // 4:4:4 formats, 64 bpp
84 kY416_YUVFormat, // 16-bit AVYU values all interleaved (1 texture)
85
86 // 4:4:4 formats, 32 bpp
87 kAYUV_YUVFormat, // 8-bit YUVA values all interleaved (1 texture)
88 kY410_YUVFormat, // AVYU w/ 10bpp for YUV and 2 for A all interleaved (1 texture)
89
90 // 4:2:0 formats, 12 bpp
91 kNV12_YUVFormat, // 8-bit Y plane + 2x2 down sampled interleaved U/V planes (2 textures)
92 kNV21_YUVFormat, // same as kNV12 but w/ U/V reversed in the interleaved texture (2 textures)
93
94 kI420_YUVFormat, // 8-bit Y plane + separate 2x2 down sampled U and V planes (3 textures)
95 kYV12_YUVFormat, // 8-bit Y plane + separate 2x2 down sampled V and U planes (3 textures)
96
98};
99
100// Does the YUVFormat contain a slot for alpha? If not an external alpha plane is required for
101// transparency.
103 switch (format) {
104 case kP016_YUVFormat: return false;
105 case kP010_YUVFormat: return false;
106 case kP016F_YUVFormat: return false;
107 case kY416_YUVFormat: return true;
108 case kAYUV_YUVFormat: return true;
109 case kY410_YUVFormat: return true;
110 case kNV12_YUVFormat: return false;
111 case kNV21_YUVFormat: return false;
112 case kI420_YUVFormat: return false;
113 case kYV12_YUVFormat: return false;
114 }
116}
117
119public:
120 YUVAPlanarConfig(YUVFormat format, bool opaque, SkEncodedOrigin origin) : fOrigin(origin) {
121 switch (format) {
122 case kP016_YUVFormat:
123 case kP010_YUVFormat:
124 case kP016F_YUVFormat:
125 case kNV12_YUVFormat:
126 if (opaque) {
127 fPlaneConfig = SkYUVAInfo::PlaneConfig::kY_UV;
128 fSubsampling = SkYUVAInfo::Subsampling::k420;
129 } else {
131 fSubsampling = SkYUVAInfo::Subsampling::k420;
132 }
133 break;
134 case kY416_YUVFormat:
135 case kY410_YUVFormat:
136 if (opaque) {
137 fPlaneConfig = SkYUVAInfo::PlaneConfig::kUYV;
138 fSubsampling = SkYUVAInfo::Subsampling::k444;
139 } else {
140 fPlaneConfig = SkYUVAInfo::PlaneConfig::kUYVA;
141 fSubsampling = SkYUVAInfo::Subsampling::k444;
142 }
143 break;
144 case kAYUV_YUVFormat:
145 if (opaque) {
146 fPlaneConfig = SkYUVAInfo::PlaneConfig::kYUV;
147 fSubsampling = SkYUVAInfo::Subsampling::k444;
148 } else {
149 fPlaneConfig = SkYUVAInfo::PlaneConfig::kYUVA;
150 fSubsampling = SkYUVAInfo::Subsampling::k444;
151 }
152 break;
153 case kNV21_YUVFormat:
154 if (opaque) {
155 fPlaneConfig = SkYUVAInfo::PlaneConfig::kY_VU;
156 fSubsampling = SkYUVAInfo::Subsampling::k420;
157 } else {
159 fSubsampling = SkYUVAInfo::Subsampling::k420;
160 }
161 break;
162 case kI420_YUVFormat:
163 if (opaque) {
164 fPlaneConfig = SkYUVAInfo::PlaneConfig::kY_U_V;
165 fSubsampling = SkYUVAInfo::Subsampling::k420;
166 } else {
168 fSubsampling = SkYUVAInfo::Subsampling::k420;
169 }
170 break;
171 case kYV12_YUVFormat:
172 if (opaque) {
173 fPlaneConfig = SkYUVAInfo::PlaneConfig::kY_V_U;
174 fSubsampling = SkYUVAInfo::Subsampling::k420;
175 } else {
177 fSubsampling = SkYUVAInfo::Subsampling::k420;
178 }
179 break;
180 }
181 }
182
183 int numPlanes() const { return SkYUVAInfo::NumPlanes(fPlaneConfig); }
184
186 SkYUVColorSpace yuvColorSpace,
187 const SkBitmap bitmaps[],
188 int numBitmaps) const;
189
190private:
191 SkYUVAInfo::PlaneConfig fPlaneConfig;
192 SkYUVAInfo::Subsampling fSubsampling;
193 SkEncodedOrigin fOrigin;
194};
195
197 SkYUVColorSpace yuvColorSpace,
198 const SkBitmap bitmaps[],
199 int numBitmaps) const {
200 SkYUVAInfo info(dimensions, fPlaneConfig, fSubsampling, yuvColorSpace, fOrigin);
202 int n = info.numPlanes();
203 if (numBitmaps < n) {
204 return {};
205 }
206 for (int i = 0; i < n; ++i) {
207 pmaps[i] = bitmaps[i].pixmap();
208 }
210}
211
212// All the planes we need to construct the various YUV formats
213struct PlaneData {
218 SkBitmap fUQuarter; // 2x2 downsampled U channel
219 SkBitmap fVQuarter; // 2x2 downsampled V channel
220
222 SkBitmap fQuarter; // 2x2 downsampled YUVA
223};
224
245
246// Add a portion of a circle to 'path'. The points 'o1' and 'o2' are on the border of the circle
247// and have tangents 'v1' and 'v2'.
248static void add_arc(SkPath* path,
249 const SkPoint& o1, const SkVector& v1,
250 const SkPoint& o2, const SkVector& v2,
251 SkTDArray<SkRect>* circles, bool takeLongWayRound) {
252
253 SkVector v3 = { -v1.fY, v1.fX };
254 SkVector v4 = { v2.fY, -v2.fX };
255
256 SkScalar t = ((o2.fX - o1.fX) * v4.fY - (o2.fY - o1.fY) * v4.fX) / v3.cross(v4);
257 SkPoint center = { o1.fX + t * v3.fX, o1.fY + t * v3.fY };
258
259 SkRect r = { center.fX - t, center.fY - t, center.fX + t, center.fY + t };
260
261 if (circles) {
262 circles->push_back(r);
263 }
264
265 SkVector startV = o1 - center, endV = o2 - center;
266 startV.normalize();
267 endV.normalize();
268
269 SkScalar startDeg = SkRadiansToDegrees(SkScalarATan2(startV.fY, startV.fX));
270 SkScalar endDeg = SkRadiansToDegrees(SkScalarATan2(endV.fY, endV.fX));
271
272 startDeg += 360.0f;
273 startDeg = fmodf(startDeg, 360.0f);
274
275 endDeg += 360.0f;
276 endDeg = fmodf(endDeg, 360.0f);
277
278 if (endDeg < startDeg) {
279 endDeg += 360.0f;
280 }
281
282 SkScalar sweepDeg = SkTAbs(endDeg - startDeg);
283 if (!takeLongWayRound) {
284 sweepDeg = sweepDeg - 360;
285 }
286
287 path->arcTo(r, startDeg, sweepDeg, false);
288}
289
290static SkPath create_splat(const SkPoint& o, SkScalar innerRadius, SkScalar outerRadius,
291 SkScalar ratio, int numLobes, SkTDArray<SkRect>* circles) {
292 if (numLobes <= 1) {
293 return SkPath();
294 }
295
296 SkPath p;
297
298 int numDivisions = 2 * numLobes;
299 SkScalar fullLobeDegrees = 360.0f / numLobes;
300 SkScalar outDegrees = ratio * fullLobeDegrees / (ratio + 1.0f);
301 SkScalar innerDegrees = fullLobeDegrees / (ratio + 1.0f);
302 SkMatrix outerStep, innerStep;
303 outerStep.setRotate(outDegrees);
304 innerStep.setRotate(innerDegrees);
305 SkVector curV = SkVector::Make(0.0f, 1.0f);
306
307 if (circles) {
308 circles->push_back(SkRect::MakeLTRB(o.fX - innerRadius, o.fY - innerRadius,
309 o.fX + innerRadius, o.fY + innerRadius));
310 }
311
312 p.moveTo(o.fX + innerRadius * curV.fX, o.fY + innerRadius * curV.fY);
313
314 for (int i = 0; i < numDivisions; ++i) {
315
316 SkVector nextV;
317 if (0 == (i % 2)) {
318 nextV = outerStep.mapVector(curV.fX, curV.fY);
319
320 SkPoint top = SkPoint::Make(o.fX + outerRadius * curV.fX,
321 o.fY + outerRadius * curV.fY);
322 SkPoint nextTop = SkPoint::Make(o.fX + outerRadius * nextV.fX,
323 o.fY + outerRadius * nextV.fY);
324
325 p.lineTo(top);
326 add_arc(&p, top, curV, nextTop, nextV, circles, true);
327 } else {
328 nextV = innerStep.mapVector(curV.fX, curV.fY);
329
330 SkPoint bot = SkPoint::Make(o.fX + innerRadius * curV.fX,
331 o.fY + innerRadius * curV.fY);
332 SkPoint nextBot = SkPoint::Make(o.fX + innerRadius * nextV.fX,
333 o.fY + innerRadius * nextV.fY);
334
335 p.lineTo(bot);
336 add_arc(&p, bot, curV, nextBot, nextV, nullptr, false);
337 }
338
339 curV = nextV;
340 }
341
342 p.close();
343
344 return p;
345}
346
348 const SkTDArray<SkRect>& circles, bool opaque, bool padWithRed) {
349 const SkColor kGreen = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 178, 240, 104));
350 const SkColor kBlue = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 173, 167, 252));
351 const SkColor kYellow = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 255, 221, 117));
352 const SkColor kMagenta = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 255, 60, 217));
353 const SkColor kCyan = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 45, 237, 205));
354
355 int widthHeight = kTileWidthHeight + (padWithRed ? 2 * kSubsetPadding : 0);
356
357 SkImageInfo ii = SkImageInfo::Make(widthHeight, widthHeight,
359
360 SkBitmap bm;
361 bm.allocPixels(ii);
362
363 std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirect(ii,
364 bm.getPixels(),
365 bm.rowBytes());
366 if (padWithRed) {
367 canvas->clear(SK_ColorRED);
368 canvas->translate(kSubsetPadding, kSubsetPadding);
370 }
371 canvas->clear(opaque ? kGreen : SK_ColorTRANSPARENT);
372
374 paint.setAntiAlias(false); // serialize-8888 doesn't seem to work well w/ partial transparency
375 paint.setColor(kBlue);
376
377 canvas->drawPath(path, paint);
378
379 paint.setBlendMode(SkBlendMode::kSrc);
380 for (int i = 0; i < circles.size(); ++i) {
382 switch (i % 3) {
383 case 0: color = kYellow; break;
384 case 1: color = kMagenta; break;
385 default: color = kCyan; break;
386 }
387 paint.setColor(color);
388 paint.setAlpha(opaque ? 0xFF : 0x40);
389 SkRect r = circles[i];
390 r.inset(r.width()/4, r.height()/4);
391 canvas->drawOval(r, paint);
392 }
393
394 return bm;
395}
396
397static void convert_rgba_to_yuva(const float mtx[20], SkColor col, uint8_t yuv[4]) {
398 const uint8_t r = SkColorGetR(col);
399 const uint8_t g = SkColorGetG(col);
400 const uint8_t b = SkColorGetB(col);
401
402 yuv[0] = SkTPin(SkScalarRoundToInt(mtx[ 0]*r + mtx[ 1]*g + mtx[ 2]*b + mtx[ 4]*255), 0, 255);
403 yuv[1] = SkTPin(SkScalarRoundToInt(mtx[ 5]*r + mtx[ 6]*g + mtx[ 7]*b + mtx[ 9]*255), 0, 255);
404 yuv[2] = SkTPin(SkScalarRoundToInt(mtx[10]*r + mtx[11]*g + mtx[12]*b + mtx[14]*255), 0, 255);
405 yuv[3] = SkColorGetA(col);
406}
407
408static void extract_planes(const SkBitmap& origBM,
409 SkYUVColorSpace yuvColorSpace,
410 SkEncodedOrigin origin,
411 PlaneData* planes) {
412 SkImageInfo ii = origBM.info();
414 ii = ii.makeWH(ii.height(), ii.width());
415 }
416 SkBitmap orientedBM;
417 orientedBM.allocPixels(ii);
418 SkCanvas canvas(orientedBM);
419 SkMatrix matrix = SkEncodedOriginToMatrix(origin, origBM.width(), origBM.height());
420 SkAssertResult(matrix.invert(&matrix));
421 canvas.concat(matrix);
422 canvas.drawImage(origBM.asImage(), 0, 0);
423
424 if (yuvColorSpace == kIdentity_SkYUVColorSpace) {
425 // To test the identity color space we use JPEG YUV planes
426 yuvColorSpace = kJPEG_SkYUVColorSpace;
427 }
428
429 SkASSERT(!(ii.width() % 2));
430 SkASSERT(!(ii.height() % 2));
431 planes->fYFull.allocPixels(
433 planes->fUFull.allocPixels(
435 planes->fVFull.allocPixels(
438 planes->fUQuarter.allocPixels(SkImageInfo::Make(ii.width()/2, ii.height()/2,
440 planes->fVQuarter.allocPixels(SkImageInfo::Make(ii.width()/2, ii.height()/2,
442
443 planes->fFull.allocPixels(
445 planes->fQuarter.allocPixels(SkImageInfo::Make(ii.width()/2, ii.height()/2,
447
448 float mtx[20];
449 SkColorMatrix_RGB2YUV(yuvColorSpace, mtx);
450
451 SkColor4f* dst = (SkColor4f *) planes->fFull.getAddr(0, 0);
452 for (int y = 0; y < orientedBM.height(); ++y) {
453 for (int x = 0; x < orientedBM.width(); ++x) {
454 SkColor col = orientedBM.getColor(x, y);
455
456 uint8_t yuva[4];
457
458 convert_rgba_to_yuva(mtx, col, yuva);
459
460 *planes->fYFull.getAddr8(x, y) = yuva[0];
461 *planes->fUFull.getAddr8(x, y) = yuva[1];
462 *planes->fVFull.getAddr8(x, y) = yuva[2];
463 *planes->fAFull.getAddr8(x, y) = yuva[3];
464
465 // TODO: render in F32 rather than converting here
466 dst->fR = yuva[0] / 255.0f;
467 dst->fG = yuva[1] / 255.0f;
468 dst->fB = yuva[2] / 255.0f;
469 dst->fA = yuva[3] / 255.0f;
470 ++dst;
471 }
472 }
473
474 dst = (SkColor4f *) planes->fQuarter.getAddr(0, 0);
475 for (int y = 0; y < orientedBM.height()/2; ++y) {
476 for (int x = 0; x < orientedBM.width()/2; ++x) {
477 uint32_t yAccum = 0, uAccum = 0, vAccum = 0, aAccum = 0;
478
479 yAccum += *planes->fYFull.getAddr8(2*x, 2*y);
480 yAccum += *planes->fYFull.getAddr8(2*x+1, 2*y);
481 yAccum += *planes->fYFull.getAddr8(2*x, 2*y+1);
482 yAccum += *planes->fYFull.getAddr8(2*x+1, 2*y+1);
483
484 uAccum += *planes->fUFull.getAddr8(2*x, 2*y);
485 uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y);
486 uAccum += *planes->fUFull.getAddr8(2*x, 2*y+1);
487 uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y+1);
488
489 *planes->fUQuarter.getAddr8(x, y) = uAccum / 4.0f;
490
491 vAccum += *planes->fVFull.getAddr8(2*x, 2*y);
492 vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y);
493 vAccum += *planes->fVFull.getAddr8(2*x, 2*y+1);
494 vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y+1);
495
496 *planes->fVQuarter.getAddr8(x, y) = vAccum / 4.0f;
497
498 aAccum += *planes->fAFull.getAddr8(2*x, 2*y);
499 aAccum += *planes->fAFull.getAddr8(2*x+1, 2*y);
500 aAccum += *planes->fAFull.getAddr8(2*x, 2*y+1);
501 aAccum += *planes->fAFull.getAddr8(2*x+1, 2*y+1);
502
503 // TODO: render in F32 rather than converting here
504 dst->fR = yAccum / (4.0f * 255.0f);
505 dst->fG = uAccum / (4.0f * 255.0f);
506 dst->fB = vAccum / (4.0f * 255.0f);
507 dst->fA = aAccum / (4.0f * 255.0f);
508 ++dst;
509 }
510 }
511}
512
513// Create a 2x2 downsampled SkBitmap. It is stored in an RG texture. It can optionally be
514// uv (i.e., NV12) or vu (i.e., NV21).
516 const SkBitmap& quarterU,
517 const SkBitmap& quarterV,
518 bool uv) {
520
522 fullY.height()/2,
525
526 for (int y = 0; y < fullY.height()/2; ++y) {
527 for (int x = 0; x < fullY.width()/2; ++x) {
528 uint8_t u8 = *quarterU.getAddr8(x, y);
529 uint8_t v8 = *quarterV.getAddr8(x, y);
530
531 if (uv) {
532 *result.getAddr16(x, y) = (v8 << 8) | u8;
533 } else {
534 *result.getAddr16(x, y) = (u8 << 8) | v8;
535 }
536 }
537 }
538
539 return result;
540}
541
542// Create some flavor of a 16bits/channel bitmap from a RGBA_F32 source
543static SkBitmap make_16(const SkBitmap& src, SkColorType dstCT,
544 std::function<void(uint16_t* dstPixel, const float* srcPixel)> convert) {
545 SkASSERT(src.colorType() == kRGBA_F32_SkColorType);
546
548
550
551 for (int y = 0; y < src.height(); ++y) {
552 for (int x = 0; x < src.width(); ++x) {
553 const float* srcPixel = (const float*) src.getAddr(x, y);
554 uint16_t* dstPixel = (uint16_t*) result.getAddr(x, y);
555
556 convert(dstPixel, srcPixel);
557 }
558 }
559
560 return result;
561}
562
563static uint16_t flt_2_uint16(float flt) { return SkScalarRoundToInt(flt * 65535.0f); }
564
565// Recombine the separate planes into some YUV format. Returns the number of planes.
566static int create_YUV(const PlaneData& planes,
567 YUVFormat yuvFormat,
568 SkBitmap resultBMs[],
569 bool opaque) {
570 int nextLayer = 0;
571
572 switch (yuvFormat) {
573 case kY416_YUVFormat: {
574 resultBMs[nextLayer++] = make_16(planes.fFull, kR16G16B16A16_unorm_SkColorType,
575 [] (uint16_t* dstPixel, const float* srcPixel) {
576 dstPixel[0] = flt_2_uint16(srcPixel[1]); // U
577 dstPixel[1] = flt_2_uint16(srcPixel[0]); // Y
578 dstPixel[2] = flt_2_uint16(srcPixel[2]); // V
579 dstPixel[3] = flt_2_uint16(srcPixel[3]); // A
580 });
581 break;
582 }
583 case kAYUV_YUVFormat: {
584 SkBitmap yuvaFull;
585
586 yuvaFull.allocPixels(SkImageInfo::Make(planes.fYFull.width(), planes.fYFull.height(),
588
589 for (int y = 0; y < planes.fYFull.height(); ++y) {
590 for (int x = 0; x < planes.fYFull.width(); ++x) {
591
592 uint8_t Y = *planes.fYFull.getAddr8(x, y);
593 uint8_t U = *planes.fUFull.getAddr8(x, y);
594 uint8_t V = *planes.fVFull.getAddr8(x, y);
595 uint8_t A = *planes.fAFull.getAddr8(x, y);
596
597 // NOT premul!
598 // V and Y swapped to match RGBA layout
599 SkColor c = SkColorSetARGB(A, V, U, Y);
600 *yuvaFull.getAddr32(x, y) = c;
601 }
602 }
603
604 resultBMs[nextLayer++] = yuvaFull;
605 break;
606 }
607 case kY410_YUVFormat: {
608 SkBitmap yuvaFull;
609 uint32_t Y, U, V;
610 uint8_t A;
611
612 yuvaFull.allocPixels(SkImageInfo::Make(planes.fYFull.width(), planes.fYFull.height(),
615
616 for (int y = 0; y < planes.fYFull.height(); ++y) {
617 for (int x = 0; x < planes.fYFull.width(); ++x) {
618
619 Y = SkScalarRoundToInt((*planes.fYFull.getAddr8(x, y) / 255.0f) * 1023.0f);
620 U = SkScalarRoundToInt((*planes.fUFull.getAddr8(x, y) / 255.0f) * 1023.0f);
621 V = SkScalarRoundToInt((*planes.fVFull.getAddr8(x, y) / 255.0f) * 1023.0f);
622 A = SkScalarRoundToInt((*planes.fAFull.getAddr8(x, y) / 255.0f) * 3.0f);
623
624 // NOT premul!
625 *yuvaFull.getAddr32(x, y) = (A << 30) | (V << 20) | (Y << 10) | (U << 0);
626 }
627 }
628
629 resultBMs[nextLayer++] = yuvaFull;
630 break;
631 }
632 case kP016_YUVFormat: // fall through
633 case kP010_YUVFormat: {
634 resultBMs[nextLayer++] = make_16(planes.fFull, kA16_unorm_SkColorType,
635 [tenBitsPP = (yuvFormat == kP010_YUVFormat)]
636 (uint16_t* dstPixel, const float* srcPixel) {
637 uint16_t val16 = flt_2_uint16(srcPixel[0]);
638 dstPixel[0] = tenBitsPP ? (val16 & 0xFFC0)
639 : val16;
640 });
641 resultBMs[nextLayer++] = make_16(planes.fQuarter, kR16G16_unorm_SkColorType,
642 [tenBitsPP = (yuvFormat == kP010_YUVFormat)]
643 (uint16_t* dstPixel, const float* srcPixel) {
644 uint16_t u16 = flt_2_uint16(srcPixel[1]);
645 uint16_t v16 = flt_2_uint16(srcPixel[2]);
646 dstPixel[0] = tenBitsPP ? (u16 & 0xFFC0) : u16;
647 dstPixel[1] = tenBitsPP ? (v16 & 0xFFC0) : v16;
648 });
649 if (!opaque) {
650 resultBMs[nextLayer++] = make_16(planes.fFull, kA16_unorm_SkColorType,
651 [tenBitsPP = (yuvFormat == kP010_YUVFormat)]
652 (uint16_t* dstPixel, const float* srcPixel) {
653 uint16_t val16 = flt_2_uint16(srcPixel[3]);
654 dstPixel[0] = tenBitsPP ? (val16 & 0xFFC0)
655 : val16;
656 });
657 }
658 return nextLayer;
659 }
660 case kP016F_YUVFormat: {
661 resultBMs[nextLayer++] = make_16(planes.fFull, kA16_float_SkColorType,
662 [] (uint16_t* dstPixel, const float* srcPixel) {
663 dstPixel[0] = SkFloatToHalf(srcPixel[0]);
664 });
665 resultBMs[nextLayer++] = make_16(planes.fQuarter, kR16G16_float_SkColorType,
666 [] (uint16_t* dstPixel, const float* srcPixel) {
667 dstPixel[0] = SkFloatToHalf(srcPixel[1]);
668 dstPixel[1] = SkFloatToHalf(srcPixel[2]);
669 });
670 if (!opaque) {
671 resultBMs[nextLayer++] = make_16(planes.fFull, kA16_float_SkColorType,
672 [] (uint16_t* dstPixel, const float* srcPixel) {
673 dstPixel[0] = SkFloatToHalf(srcPixel[3]);
674 });
675 }
676 return nextLayer;
677 }
678 case kNV12_YUVFormat: {
679 SkBitmap uvQuarter = make_quarter_2_channel(planes.fYFull,
680 planes.fUQuarter,
681 planes.fVQuarter, true);
682 resultBMs[nextLayer++] = planes.fYFull;
683 resultBMs[nextLayer++] = uvQuarter;
684 break;
685 }
686 case kNV21_YUVFormat: {
687 SkBitmap vuQuarter = make_quarter_2_channel(planes.fYFull,
688 planes.fUQuarter,
689 planes.fVQuarter, false);
690 resultBMs[nextLayer++] = planes.fYFull;
691 resultBMs[nextLayer++] = vuQuarter;
692 break;
693 }
694 case kI420_YUVFormat:
695 resultBMs[nextLayer++] = planes.fYFull;
696 resultBMs[nextLayer++] = planes.fUQuarter;
697 resultBMs[nextLayer++] = planes.fVQuarter;
698 break;
699 case kYV12_YUVFormat:
700 resultBMs[nextLayer++] = planes.fYFull;
701 resultBMs[nextLayer++] = planes.fVQuarter;
702 resultBMs[nextLayer++] = planes.fUQuarter;
703 break;
704 }
705
706 if (!opaque && !has_alpha_channel(yuvFormat)) {
707 resultBMs[nextLayer++] = planes.fAFull;
708 }
709 return nextLayer;
710}
711
712static void draw_col_label(SkCanvas* canvas, int x, int yuvColorSpace, bool opaque) {
713 static const char* kYUVColorSpaceNames[] = {"JPEG", "601", "709F", "709L",
714 "2020_8F", "2020_8L", "2020_10L", "2020_12F",
715 "FCCL", "SMPTE240L","YDZDXL", "GBRL",
716 "YCGCO_8F", "YCGCO_8L", "YCGCO_10F","YCGCO_12F",
717 "YCGCO_12L","Identity"};
718 static_assert(std::size(kYUVColorSpaceNames) == std::size(color_space_array));
719
722 font.setEdging(SkFont::Edging::kAlias);
723
724 SkRect textRect;
725 SkString colLabel;
726
727 colLabel.printf("%s", kYUVColorSpaceNames[yuvColorSpace]);
728 font.measureText(colLabel.c_str(), colLabel.size(), SkTextEncoding::kUTF8, &textRect);
729 int y = textRect.height();
730
731 SkTextUtils::DrawString(canvas, colLabel.c_str(), x, y, font, paint, SkTextUtils::kCenter_Align);
732
733 colLabel.printf("%s", opaque ? "Opaque" : "Transparent");
734
735 font.measureText(colLabel.c_str(), colLabel.size(), SkTextEncoding::kUTF8, &textRect);
736 y += textRect.height();
737
738 SkTextUtils::DrawString(canvas, colLabel.c_str(), x, y, font, paint, SkTextUtils::kCenter_Align);
739}
740
741static void draw_row_label(SkCanvas* canvas, int y, int yuvFormat) {
742 static const char* kYUVFormatNames[] = {
743 "P016", "P010", "P016F", "Y416", "AYUV", "Y410", "NV12", "NV21", "I420", "YV12"
744 };
745 static_assert(std::size(kYUVFormatNames) == kLast_YUVFormat + 1);
746
749 font.setEdging(SkFont::Edging::kAlias);
750
751 SkRect textRect;
752 SkString rowLabel;
753
754 rowLabel.printf("%s", kYUVFormatNames[yuvFormat]);
755 font.measureText(rowLabel.c_str(), rowLabel.size(), SkTextEncoding::kUTF8, &textRect);
756 y += kTileWidthHeight/2 + textRect.height()/2;
757
758 canvas->drawString(rowLabel, 0, y, font, paint);
759}
760
762 static const float kJPEGConversionMatrix[20] = {
763 1.0f, 0.0f, 1.402f, 0.0f, -180.0f/255,
764 1.0f, -0.344136f, -0.714136f, 0.0f, 136.0f/255,
765 1.0f, 1.772f, 0.0f, 0.0f, -227.6f/255,
766 0.0f, 0.0f, 0.0f, 1.0f, 0.0f
767 };
768
769 return SkColorFilters::Matrix(kJPEGConversionMatrix);
770}
771
772namespace skiagm {
773
774// This GM creates an opaque and transparent bitmap, extracts the planes and then recombines
775// them into various YUV formats. It then renders the results in the grid:
776//
777// JPEG 601 709 Identity
778// Transparent Opaque Transparent Opaque Transparent Opaque Transparent Opaque
779// originals
780// P016
781// P010
782// P016F
783// Y416
784// AYUV
785// Y410
786// NV12
787// NV21
788// I420
789// YV12
790class WackyYUVFormatsGM : public GM {
791public:
793
794 WackyYUVFormatsGM(bool useTargetColorSpace, bool useSubset, bool useCubicSampling, Type type)
795 : fUseTargetColorSpace(useTargetColorSpace)
796 , fUseSubset(useSubset)
797 , fUseCubicSampling(useCubicSampling)
798 , fImageType(type) {
799 this->setBGColor(0xFFCCCCCC);
800 }
801
802protected:
803 SkString getName() const override {
804 SkString name("wacky_yuv_formats");
805 if (fUseTargetColorSpace) {
806 name += "_cs";
807 }
808 if (fUseSubset) {
809 name += "_domain";
810 }
811 if (fUseCubicSampling) {
812 name += "_cubic";
813 }
814 switch (fImageType) {
815 case Type::kFromPixmaps:
816 name += "_frompixmaps";
817 break;
818 case Type::kFromTextures:
819 break;
820 case Type::kFromGenerator:
821 name += "_imggen";
822 break;
823 case Type::kFromImages:
824 name += "_fromimages";
825 break;
826 }
827
828 return name;
829 }
830
831 SkISize getISize() override {
832 int numCols = 2 * (std::size(color_space_array)); // opacity x #-color-spaces
833 int numRows = 1 + (kLast_YUVFormat + 1); // original + #-yuv-formats
834 int wh = SkScalarCeilToInt(kTileWidthHeight * (fUseSubset ? 1.5f : 1.f));
835 return SkISize::Make(kLabelWidth + numCols * (wh + kPad),
836 kLabelHeight + numRows * (wh + kPad));
837 }
838
840 SkPoint origin = { kTileWidthHeight/2.0f, kTileWidthHeight/2.0f };
841 float outerRadius = kTileWidthHeight/2.0f - 20.0f;
842 float innerRadius = 20.0f;
843
844 {
845 // transparent
846 SkTDArray<SkRect> circles;
847 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 5, &circles);
848 fOriginalBMs[0] = make_bitmap(kRGBA_8888_SkColorType, path, circles, false, fUseSubset);
849 }
850
851 {
852 // opaque
853 SkTDArray<SkRect> circles;
854 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 7, &circles);
855 fOriginalBMs[1] = make_bitmap(kRGBA_8888_SkColorType, path, circles, true, fUseSubset);
856 }
857
858 if (fUseTargetColorSpace) {
859 fTargetColorSpace = SkColorSpace::MakeSRGB()->makeColorSpin();
860 }
861 }
862
863 bool createImages(GrDirectContext* dContext, Recorder* recorder) {
864 int origin = 0;
865 for (bool opaque : { false, true }) {
866 for (size_t cs = 0; cs < std::size(color_space_array); ++cs) {
867 PlaneData planes;
868 extract_planes(fOriginalBMs[opaque],
870 static_cast<SkEncodedOrigin>(origin + 1), // valid origins are 1...8
871 &planes);
872
873 for (int f = kP016_YUVFormat; f <= kLast_YUVFormat; ++f) {
874 auto format = static_cast<YUVFormat>(f);
875 SkBitmap resultBMs[4];
876
877 int numPlanes = create_YUV(planes, format, resultBMs, opaque);
878 const YUVAPlanarConfig planarConfig(format,
879 opaque,
880 static_cast<SkEncodedOrigin>(origin + 1));
881 SkYUVAPixmaps pixmaps =
882 planarConfig.makeYUVAPixmaps(fOriginalBMs[opaque].dimensions(),
884 resultBMs,
885 numPlanes);
886 auto lazyYUV = sk_gpu_test::LazyYUVImage::Make(std::move(pixmaps));
887#if defined(SK_GRAPHITE)
888 if (recorder) {
889 fImages[opaque][cs][format] = lazyYUV->refImage(recorder, fImageType);
890 } else
891#endif
892 {
893 fImages[opaque][cs][format] = lazyYUV->refImage(dContext, fImageType);
894 }
895 }
896 origin = (origin + 1) % 8;
897 }
898 }
899
900 if (dContext) {
901 // Some backends (e.g., Vulkan) require all work be completed for backend textures
902 // before they are deleted. Since we don't know when we'll next have access to a
903 // direct context, flush all the work now.
904 dContext->flush();
905 dContext->submit(GrSyncCpu::kYes);
906 }
907
908 return true;
909 }
910
912 auto dContext = GrAsDirectContext(canvas->recordingContext());
913 auto recorder = canvas->recorder();
914 this->createBitmaps();
915
916 if (dContext && dContext->abandoned()) {
917 // This isn't a GpuGM so a null 'context' is okay but an abandoned context
918 // if forbidden.
919 return DrawResult::kSkip;
920 }
921
922 // Only the generator is expected to work with the CPU backend.
923 if (fImageType != Type::kFromGenerator && !dContext && !recorder) {
924 return DrawResult::kSkip;
925 }
926
927 if (!this->createImages(dContext, recorder)) {
928 *errorMsg = "Failed to create YUV images";
929 return DrawResult::kFail;
930 }
931
932 return DrawResult::kOk;
933 }
934
935 void onGpuTeardown() override {
936 for (int i = 0; i < 2; ++i) {
937 for (size_t j = 0; j < std::size(color_space_array); ++j) {
938 for (int k = 0; k <= kLast_YUVFormat; ++k) {
939 fImages[i][j][k] = nullptr;
940 }
941 }
942 }
943 }
944
945 void onDraw(SkCanvas* canvas) override {
946 auto direct = GrAsDirectContext(canvas->recordingContext());
947#if defined(SK_GRAPHITE)
948 auto recorder = canvas->recorder();
949#endif
950
951 float cellWidth = kTileWidthHeight, cellHeight = kTileWidthHeight;
952 if (fUseSubset) {
953 cellWidth *= 1.5f;
954 cellHeight *= 1.5f;
955 }
956
957 SkRect srcRect = SkRect::Make(fOriginalBMs[0].dimensions());
958 SkRect dstRect = SkRect::MakeXYWH(kLabelWidth, 0.f, srcRect.width(), srcRect.height());
959
961 if (fUseSubset) {
963 // Draw a larger rectangle to ensure bilerp filtering would normally read outside the
964 // srcRect and hit the red pixels, if strict constraint weren't used.
965 dstRect.fRight = kLabelWidth + 1.5f * srcRect.width();
966 dstRect.fBottom = 1.5f * srcRect.height();
968 }
969
970 SkSamplingOptions sampling = fUseCubicSampling
973 for (size_t cs = kJPEG_SkYUVColorSpace; cs < std::size(color_space_array); ++cs) {
976 // The identity color space needs post processing to appear correctly
977 paint.setColorFilter(yuv_to_rgb_colorfilter());
978 }
979
980 for (int opaque : { 0, 1 }) {
981 dstRect.offsetTo(dstRect.fLeft, kLabelHeight);
982
983 draw_col_label(canvas, dstRect.fLeft + cellWidth / 2, cs, opaque);
984
985 canvas->drawImageRect(fOriginalBMs[opaque].asImage(), srcRect, dstRect,
986 SkSamplingOptions(), nullptr, constraint);
987 dstRect.offset(0.f, cellHeight + kPad);
988
990 draw_row_label(canvas, dstRect.fTop, format);
991 if (fUseTargetColorSpace && fImages[opaque][cs][format]) {
992 // Making a CS-specific version of a kIdentity_SkYUVColorSpace YUV image
993 // doesn't make a whole lot of sense. The colorSpace conversion will
994 // operate on the YUV components rather than the RGB components.
995 sk_sp<SkImage> csImage;
996#if defined(SK_GRAPHITE)
997 if (recorder) {
998 csImage = fImages[opaque][cs][format]->makeColorSpace(
999 recorder, fTargetColorSpace, {});
1000 } else
1001#endif
1002 {
1003 csImage = fImages[opaque][cs][format]->makeColorSpace(
1004 direct, fTargetColorSpace);
1005 }
1006 canvas->drawImageRect(csImage, srcRect, dstRect, sampling,
1007 &paint, constraint);
1008 } else {
1009 canvas->drawImageRect(fImages[opaque][cs][format], srcRect, dstRect,
1010 sampling, &paint, constraint);
1011 }
1012 dstRect.offset(0.f, cellHeight + kPad);
1013 }
1014
1015 dstRect.offset(cellWidth + kPad, 0.f);
1016 }
1017 }
1018 }
1019
1020private:
1021 SkBitmap fOriginalBMs[2];
1022 sk_sp<SkImage> fImages[2][std::size(color_space_array)][kLast_YUVFormat + 1];
1023 bool fUseTargetColorSpace;
1024 bool fUseSubset;
1025 bool fUseCubicSampling;
1026 Type fImageType;
1027 sk_sp<SkColorSpace> fTargetColorSpace;
1028
1029 using INHERITED = GM;
1030};
1031
1032//////////////////////////////////////////////////////////////////////////////
1033
1034DEF_GM(return new WackyYUVFormatsGM(/*useTargetColorSpace=*/false,
1035 /*useSubset=*/false,
1036 /*useCubicSampling=*/false,
1037 WackyYUVFormatsGM::Type::kFromTextures);)
1038DEF_GM(return new WackyYUVFormatsGM(/*useTargetColorSpace=*/false,
1039 /*useSubset=*/true,
1040 /*useCubicSampling=*/false,
1041 WackyYUVFormatsGM::Type::kFromTextures);)
1042DEF_GM(return new WackyYUVFormatsGM(/*useTargetColorSpace=*/true,
1043 /*useSubset=*/false,
1044 /*useCubicSampling=*/false,
1045 WackyYUVFormatsGM::Type::kFromTextures);)
1046DEF_GM(return new WackyYUVFormatsGM(/*useTargetColorSpace=*/false,
1047 /*useSubset=*/false,
1048 /*useCubicSampling=*/true,
1049 WackyYUVFormatsGM::Type::kFromTextures);)
1050DEF_GM(return new WackyYUVFormatsGM(/*useTargetColorSpace=*/false,
1051 /*useSubset=*/false,
1052 /*useCubicSampling=*/false,
1053 WackyYUVFormatsGM::Type::kFromGenerator);)
1054DEF_GM(return new WackyYUVFormatsGM(/*useTargetColorSpace=*/false,
1055 /*useSubset=*/false,
1056 /*useCubicSampling=*/false,
1057 WackyYUVFormatsGM::Type::kFromPixmaps);)
1058#if defined(SK_GRAPHITE)
1059DEF_GM(return new WackyYUVFormatsGM(/*useTargetColorSpace=*/false,
1060 /*useSubset=*/false,
1061 /*useCubicSampling=*/false,
1062 WackyYUVFormatsGM::Type::kFromImages);)
1063#endif
1064
1065class YUVMakeColorSpaceGM : public GM {
1066public:
1068 this->setBGColor(0xFFCCCCCC);
1069 }
1070
1071protected:
1072 SkString getName() const override { return SkString("yuv_make_color_space"); }
1073
1074 SkISize getISize() override {
1075 int numCols = 4; // (transparent, opaque) x (untagged, tagged)
1076 int numRows = 5; // original, YUV, subset, makeNonTextureImage, readPixels
1077 return SkISize::Make(numCols * (kTileWidthHeight + kPad) + kPad,
1078 numRows * (kTileWidthHeight + kPad) + kPad);
1079 }
1080
1082 SkPoint origin = { kTileWidthHeight/2.0f, kTileWidthHeight/2.0f };
1083 float outerRadius = kTileWidthHeight/2.0f - 20.0f;
1084 float innerRadius = 20.0f;
1085
1086 {
1087 // transparent
1088 SkTDArray<SkRect> circles;
1089 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 5, &circles);
1090 fOriginalBMs[0] = make_bitmap(kN32_SkColorType, path, circles, false, false);
1091 }
1092
1093 {
1094 // opaque
1095 SkTDArray<SkRect> circles;
1096 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 7, &circles);
1097 fOriginalBMs[1] = make_bitmap(kN32_SkColorType, path, circles, true, false);
1098 }
1099
1100 fTargetColorSpace = SkColorSpace::MakeSRGB()->makeColorSpin();
1101 }
1102
1104 for (bool opaque : { false, true }) {
1105 PlaneData planes;
1106 extract_planes(fOriginalBMs[opaque],
1109 &planes);
1110
1111 SkBitmap resultBMs[4];
1112
1113 create_YUV(planes, kAYUV_YUVFormat, resultBMs, opaque);
1114
1116
1117 auto yuvaPixmaps = planarConfig.makeYUVAPixmaps(fOriginalBMs[opaque].dimensions(),
1119 resultBMs,
1120 std::size(resultBMs));
1121
1122 int i = 0;
1123 for (sk_sp<SkColorSpace> cs : {sk_sp<SkColorSpace>(nullptr),
1125 auto lazyYUV = sk_gpu_test::LazyYUVImage::Make(
1126 yuvaPixmaps, skgpu::Mipmapped::kNo, std::move(cs));
1127 fImages[opaque][i++] =
1128 lazyYUV->refImage(context, sk_gpu_test::LazyYUVImage::Type::kFromTextures);
1129 }
1130 }
1131
1132 // Some backends (e.g., Vulkan) require all work be completed for backend textures before
1133 // they are deleted. Since we don't know when we'll next have access to a direct context,
1134 // flush all the work now.
1135 context->flush();
1136 context->submit(GrSyncCpu::kYes);
1137
1138 return true;
1139 }
1140
1142 auto dContext = GrAsDirectContext(canvas->recordingContext());
1143 if (!dContext || dContext->abandoned()) {
1144 *errorMsg = "DirectContext required to create YUV images";
1145 return DrawResult::kSkip;
1146 }
1147
1148 this->createBitmaps();
1149 if (!this->createImages(dContext)) {
1150 *errorMsg = "Failed to create YUV images";
1151 return DrawResult::kFail;
1152 }
1153
1154 return DrawResult::kOk;
1155 }
1156
1157 void onGpuTeardown() override {
1158 fImages[0][0] = fImages[0][1] = fImages[1][0] = fImages[1][1] = nullptr;
1159 }
1160
1161 DrawResult onDraw(SkCanvas* canvas, SkString* msg) override {
1162 SkASSERT(fImages[0][0] && fImages[0][1] && fImages[1][0] && fImages[1][1]);
1163
1164 auto dContext = GrAsDirectContext(canvas->recordingContext());
1165 if (!dContext) {
1166 *msg = "YUV ColorSpace image creation requires a direct context.";
1167 return DrawResult::kSkip;
1168 }
1169
1170 int x = kPad;
1171 for (int tagged : { 0, 1 }) {
1172 for (int opaque : { 0, 1 }) {
1173 int y = kPad;
1174
1175 auto raster = fOriginalBMs[opaque].asImage()->makeColorSpace(
1176 nullptr, fTargetColorSpace);
1177 canvas->drawImage(raster, x, y);
1178 y += kTileWidthHeight + kPad;
1179
1180 if (fImages[opaque][tagged]) {
1181 auto yuv = fImages[opaque][tagged]->makeColorSpace(dContext, fTargetColorSpace);
1182 SkASSERT(yuv);
1183 SkASSERT(SkColorSpace::Equals(yuv->colorSpace(), fTargetColorSpace.get()));
1184 canvas->drawImage(yuv, x, y);
1185 y += kTileWidthHeight + kPad;
1186
1188 auto subset = SkImages::SubsetTextureFrom(dContext, yuv.get(), bounds);
1189 SkASSERT(subset);
1190 canvas->drawImage(subset, x, y);
1191 y += kTileWidthHeight + kPad;
1192
1193 auto nonTexture = yuv->makeNonTextureImage();
1194 SkASSERT(nonTexture);
1195 canvas->drawImage(nonTexture, x, y);
1196 y += kTileWidthHeight + kPad;
1197
1198 SkBitmap readBack;
1199 readBack.allocPixels(yuv->imageInfo());
1200 SkAssertResult(yuv->readPixels(dContext, readBack.pixmap(), 0, 0));
1201 canvas->drawImage(readBack.asImage(), x, y);
1202 }
1203 x += kTileWidthHeight + kPad;
1204 }
1205 }
1206 return DrawResult::kOk;
1207 }
1208
1209private:
1210 SkBitmap fOriginalBMs[2];
1211 sk_sp<SkImage> fImages[2][2];
1212 sk_sp<SkColorSpace> fTargetColorSpace;
1213
1214 using INHERITED = GM;
1215};
1216
1217DEF_GM(return new YUVMakeColorSpaceGM();)
1218
1219} // namespace skiagm
1220
1221///////////////
1222
1225#include "tools/Resources.h"
1226
1227static void draw_diff(SkCanvas* canvas, SkScalar x, SkScalar y,
1228 const SkImage* a, const SkImage* b) {
1229 auto sh = SkShaders::Blend(SkBlendMode::kDifference,
1230 a->makeShader(SkSamplingOptions()),
1231 b->makeShader(SkSamplingOptions()));
1232 SkPaint paint;
1233 paint.setShader(sh);
1234 canvas->save();
1235 canvas->translate(x, y);
1236 canvas->drawRect(SkRect::MakeWH(a->width(), a->height()), paint);
1237
1238 SkColorMatrix cm;
1239 cm.setScale(64, 64, 64);
1240 paint.setShader(sh->makeWithColorFilter(SkColorFilters::Matrix(cm)));
1241 canvas->translate(0, a->height());
1242 canvas->drawRect(SkRect::MakeWH(a->width(), a->height()), paint);
1243
1244 canvas->restore();
1245}
1246
1247// Exercises SkColorMatrix_RGB2YUV for yuv colorspaces, showing the planes, and the
1248// resulting (recombined) images (gpu only for now).
1249//
1251 sk_sp<SkImage> fOrig;
1252
1253public:
1255
1256protected:
1257 SkString getName() const override { return SkString("yuv_splitter"); }
1258
1259 SkISize getISize() override { return SkISize::Make(1280, 768); }
1260
1261 void onOnceBeforeDraw() override {
1262 fOrig = ToolUtils::GetResourceAsImage("images/mandrill_256.png");
1263 }
1264
1265 void onDraw(SkCanvas* canvas) override {
1266 canvas->translate(fOrig->width(), 0);
1267 canvas->save();
1269 std::array<sk_sp<SkImage>, SkYUVAInfo::kMaxPlanes> planes;
1270 for (auto cs : {kRec709_SkYUVColorSpace,
1274 std::tie(planes, info) = sk_gpu_test::MakeYUVAPlanesAsA8(fOrig.get(),
1275 cs,
1277 /*recording context*/ nullptr);
1278 SkPixmap pixmaps[4];
1279 for (int i = 0; i < info.numPlanes(); ++i) {
1280 planes[i]->peekPixels(&pixmaps[i]);
1281 }
1282 auto yuvaPixmaps = SkYUVAPixmaps::FromExternalPixmaps(info, pixmaps);
1284 yuvaPixmaps,
1285 skgpu::Mipmapped::kNo,
1286 /* limit to max tex size */ false,
1287 /* color space */ nullptr);
1288 if (img) {
1289 canvas->drawImage(img, 0, 0);
1290 draw_diff(canvas, 0, fOrig->height(), fOrig.get(), img.get());
1291 }
1292 canvas->translate(fOrig->width(), 0);
1293 }
1294 canvas->restore();
1295 canvas->translate(-fOrig->width(), 0);
1296 int y = 0;
1297 for (int i = 0; i < info.numPlanes(); ++i) {
1298 canvas->drawImage(planes[i], 0, y);
1299 y += planes[i]->height();
1300 }
1301 }
1302
1303private:
1304 using INHERITED = GM;
1305};
1306DEF_GM( return new YUVSplitterGM; )
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
static GrDirectContext * GrAsDirectContext(GrContext_Base *base)
SkColor4f color
static sk_sp< SkImage > make_bitmap()
kUnpremul_SkAlphaType
static const uint64_t kGreen
static const uint64_t kBlue
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition SkAlphaType.h:29
#define SkAssertResult(cond)
Definition SkAssert.h:123
#define SkUNREACHABLE
Definition SkAssert.h:135
#define SkASSERT(cond)
Definition SkAssert.h:116
@ kDifference
rc = s + d - 2*(min(s*da, d*sa)), ra = kSrcOver
SkColorType
Definition SkColorType.h:19
@ kR16G16B16A16_unorm_SkColorType
pixel with a little endian uint16_t for red, green, blue
Definition SkColorType.h:50
@ kR8G8_unorm_SkColorType
pixel with a uint8_t for red and green
Definition SkColorType.h:43
@ kA16_unorm_SkColorType
pixel with a little endian uint16_t for alpha
Definition SkColorType.h:48
@ kGray_8_SkColorType
pixel with grayscale level in 8-bit byte
Definition SkColorType.h:35
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition SkColorType.h:24
@ kA16_float_SkColorType
pixel with a half float for alpha
Definition SkColorType.h:45
@ kRGBA_F32_SkColorType
pixel using C float for red, green, blue, alpha; in 128-bit word
Definition SkColorType.h:40
@ kRGBA_1010102_SkColorType
10 bits for red, green, blue; 2 bits for alpha; in 32-bit word
Definition SkColorType.h:27
@ kR16G16_unorm_SkColorType
pixel with a little endian uint16_t for red and green
Definition SkColorType.h:49
@ kR16G16_float_SkColorType
pixel with a half float for red and green
Definition SkColorType.h:46
#define SkColorGetR(color)
Definition SkColor.h:65
#define SkColorGetG(color)
Definition SkColor.h:69
uint32_t SkColor
Definition SkColor.h:37
constexpr SkColor SK_ColorTRANSPARENT
Definition SkColor.h:99
constexpr SkColor SK_ColorRED
Definition SkColor.h:126
static constexpr SkColor SkColorSetARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
Definition SkColor.h:49
#define SkColorGetA(color)
Definition SkColor.h:61
#define SkColorGetB(color)
Definition SkColor.h:73
SkEncodedOrigin
@ kTopLeft_SkEncodedOrigin
static bool SkEncodedOriginSwapsWidthHeight(SkEncodedOrigin origin)
static SkMatrix SkEncodedOriginToMatrix(SkEncodedOrigin origin, int w, int h)
@ kUTF8
uses bytes to represent UTF-8 or ASCII
static SkColorType colorType(AImageDecoder *decoder, const AImageDecoderHeaderInfo *headerInfo)
SkYUVColorSpace
Definition SkImageInfo.h:68
@ kRec709_Full_SkYUVColorSpace
describes HDTV range
Definition SkImageInfo.h:71
@ kYCgCo_10bit_Full_SkYUVColorSpace
Definition SkImageInfo.h:89
@ kYCgCo_12bit_Full_SkYUVColorSpace
Definition SkImageInfo.h:91
@ kYCgCo_12bit_Limited_SkYUVColorSpace
Definition SkImageInfo.h:92
@ kGBR_Limited_SkYUVColorSpace
Definition SkImageInfo.h:86
@ kBT2020_8bit_Limited_SkYUVColorSpace
Definition SkImageInfo.h:74
@ kBT2020_SkYUVColorSpace
@ kJPEG_SkYUVColorSpace
Definition SkImageInfo.h:98
@ kYCgCo_8bit_Limited_SkYUVColorSpace
Definition SkImageInfo.h:88
@ kRec601_SkYUVColorSpace
Definition SkImageInfo.h:99
@ kFCC_Limited_SkYUVColorSpace
Definition SkImageInfo.h:80
@ kYCgCo_8bit_Full_SkYUVColorSpace
describes YCgCo matrix
Definition SkImageInfo.h:87
@ kBT2020_8bit_Full_SkYUVColorSpace
describes UHDTV range, non-constant-luminance
Definition SkImageInfo.h:73
@ kRec601_Limited_SkYUVColorSpace
describes SDTV range
Definition SkImageInfo.h:70
@ kRec709_Limited_SkYUVColorSpace
Definition SkImageInfo.h:72
@ kSMPTE240_Limited_SkYUVColorSpace
Definition SkImageInfo.h:82
@ kBT2020_12bit_Full_SkYUVColorSpace
Definition SkImageInfo.h:77
@ kRec709_SkYUVColorSpace
@ kIdentity_SkYUVColorSpace
maps Y->R, U->G, V->B
Definition SkImageInfo.h:93
@ kBT2020_10bit_Limited_SkYUVColorSpace
Definition SkImageInfo.h:76
@ kYDZDX_Limited_SkYUVColorSpace
Definition SkImageInfo.h:84
@ kJPEG_Full_SkYUVColorSpace
describes full range
Definition SkImageInfo.h:69
static SkV4 v4(SkV3 v, SkScalar w)
Definition SkM44.cpp:329
#define INHERITED(method,...)
#define SkScalarATan2(y, x)
Definition SkScalar.h:50
#define SkScalarRoundToInt(x)
Definition SkScalar.h:37
#define SkScalarCeilToInt(x)
Definition SkScalar.h:36
#define SkRadiansToDegrees(radians)
Definition SkScalar.h:78
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
Definition SkTPin.h:19
static T SkTAbs(T value)
Definition SkTemplates.h:43
void SkColorMatrix_RGB2YUV(SkYUVColorSpace cs, float m[20])
static const SkScalar Y
static SkScalar center(float pos0, float pos1)
Vec2Value v2
constexpr int kPad
bool submit(GrSyncCpu sync=GrSyncCpu::kNo)
GrSemaphoresSubmitted flush(const GrFlushInfo &info)
void allocPixels(const SkImageInfo &info, size_t rowBytes)
Definition SkBitmap.cpp:258
sk_sp< SkImage > asImage() const
Definition SkBitmap.cpp:645
SkColor getColor(int x, int y) const
Definition SkBitmap.h:874
uint8_t * getAddr8(int x, int y) const
Definition SkBitmap.h:1270
int width() const
Definition SkBitmap.h:149
const SkPixmap & pixmap() const
Definition SkBitmap.h:133
void * getAddr(int x, int y) const
Definition SkBitmap.cpp:406
size_t rowBytes() const
Definition SkBitmap.h:238
void * getPixels() const
Definition SkBitmap.h:283
const SkImageInfo & info() const
Definition SkBitmap.h:139
int height() const
Definition SkBitmap.h:158
uint32_t * getAddr32(int x, int y) const
Definition SkBitmap.h:1260
void drawRect(const SkRect &rect, const SkPaint &paint)
void restore()
Definition SkCanvas.cpp:465
void translate(SkScalar dx, SkScalar dy)
virtual GrRecordingContext * recordingContext() const
virtual skgpu::graphite::Recorder * recorder() const
SrcRectConstraint
Definition SkCanvas.h:1541
@ kStrict_SrcRectConstraint
sample only inside bounds; slower
Definition SkCanvas.h:1542
@ kFast_SrcRectConstraint
sample outside bounds; faster
Definition SkCanvas.h:1543
void drawImageRect(const SkImage *, const SkRect &src, const SkRect &dst, const SkSamplingOptions &, const SkPaint *, SrcRectConstraint)
int save()
Definition SkCanvas.cpp:451
void concat(const SkMatrix &matrix)
void drawString(const char str[], SkScalar x, SkScalar y, const SkFont &font, const SkPaint &paint)
Definition SkCanvas.h:1803
static std::unique_ptr< SkCanvas > MakeRasterDirect(const SkImageInfo &info, void *pixels, size_t rowBytes, const SkSurfaceProps *props=nullptr)
void drawImage(const SkImage *image, SkScalar left, SkScalar top)
Definition SkCanvas.h:1528
static sk_sp< SkColorFilter > Matrix(const SkColorMatrix &)
void setScale(float rScale, float gScale, float bScale, float aScale=1.0f)
static bool Equals(const SkColorSpace *, const SkColorSpace *)
static sk_sp< SkColorSpace > MakeSRGB()
sk_sp< SkColorSpace > makeColorSpin() const
static constexpr SkFontStyle Bold()
Definition SkFontStyle.h:69
@ kAlias
no transparent pixels on glyph edges
virtual sk_sp< SkImage > makeColorSpace(GrDirectContext *direct, sk_sp< SkColorSpace > target) const =0
SkMatrix & setRotate(SkScalar degrees, SkScalar px, SkScalar py)
Definition SkMatrix.cpp:452
void mapVector(SkScalar dx, SkScalar dy, SkVector *result) const
Definition SkMatrix.h:1524
void printf(const char format[],...) SK_PRINTF_LIKE(2
Definition SkString.cpp:534
size_t size() const
Definition SkString.h:131
const char * c_str() const
Definition SkString.h:133
int size() const
Definition SkTDArray.h:138
void push_back(const T &v)
Definition SkTDArray.h:219
static void DrawString(SkCanvas *canvas, const char text[], SkScalar x, SkScalar y, const SkFont &font, const SkPaint &paint, Align align=kLeft_Align)
Definition SkTextUtils.h:34
@ 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.
@ kYUVA
Plane 0: YUVA.
@ kY_V_U_A
Plane 0: Y, Plane 1: V, Plane 2: U, Plane 3: A.
@ kUYVA
Plane 0: UYVA.
@ kY_VU
Plane 0: Y, Plane 1: VU.
@ kY_UV
Plane 0: Y, Plane 1: UV.
@ kY_VU_A
Plane 0: Y, Plane 1: VU, Plane 2: A.
@ kY_V_U
Plane 0: Y, Plane 1: V, Plane 2: U.
@ kY_UV_A
Plane 0: Y, Plane 1: UV, Plane 2: A.
static constexpr int kMaxPlanes
Definition SkYUVAInfo.h:98
@ k420
1 set of UV values for each 2x2 block of Y values.
@ k444
No subsampling. UV values for each Y.
static constexpr int NumPlanes(PlaneConfig)
Definition SkYUVAInfo.h:253
static SkYUVAPixmaps FromExternalPixmaps(const SkYUVAInfo &, const SkPixmap[kMaxPlanes])
SkYUVAPixmaps makeYUVAPixmaps(SkISize dimensions, SkYUVColorSpace yuvColorSpace, const SkBitmap bitmaps[], int numBitmaps) const
YUVAPlanarConfig(YUVFormat format, bool opaque, SkEncodedOrigin origin)
SkISize getISize() override
void onDraw(SkCanvas *canvas) override
void onOnceBeforeDraw() override
SkString getName() const override
static std::unique_ptr< LazyYUVImage > Make(sk_sp< SkData > data, skgpu::Mipmapped=skgpu::Mipmapped::kNo, sk_sp< SkColorSpace >=nullptr)
Definition YUVUtils.cpp:200
T * get() const
Definition SkRefCnt.h:303
GM(SkColor backgroundColor=SK_ColorWHITE)
Definition gm.cpp:81
void setBGColor(SkColor)
Definition gm.cpp:159
bool createImages(GrDirectContext *dContext, Recorder *recorder)
DrawResult onGpuSetup(SkCanvas *canvas, SkString *errorMsg, GraphiteTestContext *) override
void onDraw(SkCanvas *canvas) override
WackyYUVFormatsGM(bool useTargetColorSpace, bool useSubset, bool useCubicSampling, Type type)
SkString getName() const override
DrawResult onDraw(SkCanvas *canvas, SkString *msg) override
SkString getName() const override
DrawResult onGpuSetup(SkCanvas *canvas, SkString *errorMsg, GraphiteTestContext *) override
bool createImages(GrDirectContext *context)
const Paint & paint
static Editor::Movement convert(skui::Key key)
float SkScalar
Definition extension.cpp:12
static bool b
struct MyStruct a[10]
GAsyncResult * result
uint32_t uint32_t * format
const char * name
Definition fuchsia.cc:50
#define DEF_GM(CODE)
Definition gm.h:40
T __attribute__((ext_vector_type(N))) V
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 > SubsetTextureFrom(GrDirectContext *context, const SkImage *img, const SkIRect &subset)
sk_sp< SkImage > GetResourceAsImage(const char *resource)
Definition DecodeUtils.h:25
sk_sp< SkTypeface > CreatePortableTypeface(const char *name, SkFontStyle style)
SkColor color_to_565(SkColor color)
DrawResult
Definition gm.h:104
#define V(name)
Definition raw_object.h:124
static constexpr int kLabelHeight
static constexpr SkCubicResampler Mitchell()
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition SkRect.h:56
static constexpr SkISize Make(int32_t w, int32_t h)
Definition SkSize.h:20
SkImageInfo makeWH(int newWidth, int newHeight) const
SkISize dimensions() const
int width() const
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
int height() const
static SkImageInfo MakeA8(int width, int height)
float fX
x-axis value
static constexpr SkPoint Make(float x, float y)
float cross(const SkVector &vec) const
float fY
y-axis value
bool normalize()
Definition SkPoint.cpp:22
static SkRect Make(const SkISize &size)
Definition SkRect.h:669
void offsetTo(float newX, float newY)
Definition SkRect.h:1043
SkScalar fBottom
larger y-axis bounds
Definition extension.cpp:17
void inset(float dx, float dy)
Definition SkRect.h:1060
SkScalar fLeft
smaller x-axis bounds
Definition extension.cpp:14
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition SkRect.h:659
SkScalar fRight
larger x-axis bounds
Definition extension.cpp:16
void offset(float dx, float dy)
Definition SkRect.h:1016
constexpr float height() const
Definition SkRect.h:769
constexpr float width() const
Definition SkRect.h:762
static constexpr SkRect MakeWH(float w, float h)
Definition SkRect.h:609
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition SkRect.h:646
SkScalar fTop
smaller y-axis bounds
Definition extension.cpp:15
static void convert_rgba_to_yuva(const float mtx[20], SkColor col, uint8_t yuv[4])
static const int kSubsetPadding
@ kAYUV_YUVFormat
@ kNV21_YUVFormat
@ kLast_YUVFormat
@ kNV12_YUVFormat
@ kP016F_YUVFormat
@ kP010_YUVFormat
@ kY410_YUVFormat
@ kI420_YUVFormat
@ kP016_YUVFormat
@ kYV12_YUVFormat
@ kY416_YUVFormat
static void draw_col_label(SkCanvas *canvas, int x, int yuvColorSpace, bool opaque)
const SkYUVColorSpace color_space_array[]
static void draw_diff(SkCanvas *canvas, SkScalar x, SkScalar y, const SkImage *a, const SkImage *b)
static const int kPad
static SkBitmap make_quarter_2_channel(const SkBitmap &fullY, const SkBitmap &quarterU, const SkBitmap &quarterV, bool uv)
static const int kLabelWidth
static const int kLabelHeight
static SkBitmap make_16(const SkBitmap &src, SkColorType dstCT, std::function< void(uint16_t *dstPixel, const float *srcPixel)> convert)
static uint16_t flt_2_uint16(float flt)
static void draw_row_label(SkCanvas *canvas, int y, int yuvFormat)
static const int kTileWidthHeight
static int create_YUV(const PlaneData &planes, YUVFormat yuvFormat, SkBitmap resultBMs[], bool opaque)
static void add_arc(SkPath *path, const SkPoint &o1, const SkVector &v1, const SkPoint &o2, const SkVector &v2, SkTDArray< SkRect > *circles, bool takeLongWayRound)
static void extract_planes(const SkBitmap &origBM, SkYUVColorSpace yuvColorSpace, SkEncodedOrigin origin, PlaneData *planes)
static sk_sp< SkColorFilter > yuv_to_rgb_colorfilter()
static SkPath create_splat(const SkPoint &o, SkScalar innerRadius, SkScalar outerRadius, SkScalar ratio, int numLobes, SkTDArray< SkRect > *circles)
static bool has_alpha_channel(YUVFormat format)