blob: f421e2013e27816f0edc8c0bd77726f4fc2d24bf [file] [log] [blame]
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001/*
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.h"
9#include "sk_tool_utils.h"
10
11#include "SkColorPriv.h"
12#include "SkImageGenerator.h"
Robert Phillipsbfa76f22018-10-03 12:12:26 -040013#include "SkPath.h"
Robert Phillips66a97342018-10-04 09:10:29 -040014#include "SkYUVAIndex.h"
Robert Phillipsbfa76f22018-10-03 12:12:26 -040015
16static const int kTileWidthHeight = 128;
17static const int kLabelWidth = 64;
18static const int kLabelHeight = 32;
19static const int kPad = 1;
20constexpr SkColor kGreen = SkColorSetARGB(0xFF, 104, 240, 178);
21constexpr SkColor kBlue = SkColorSetARGB(0xFF, 252, 167, 173);
22constexpr SkColor kYellow = SkColorSetARGB(0xFF, 117, 221, 255);
23
24enum YUVFormat {
25 // 4:4:4 formats, 32 bpp
26 AYUV_YUVFormat, // 8-bit YUVA values all interleaved
27
28 // 4:2:0 formats, 12 bpp
29 kNV12_YUVFormat, // 8-bit Y plane + 2x2 down sampled interleaved U/V planes
30 kNV21_YUVFormat, // same as kNV12 but w/ U/V reversed in the interleaved plane
31
32 kI420_YUVFormat, // 8-bit Y plane + 2x2 down sampled U and V planes
33 kYV12_YUVFormat, // 8-bit Y plane + 2x2 down sampled V and U planes
34
35 kLast_YUVFormat = kYV12_YUVFormat
36};
37
38// All the planes we need to construct the various YUV formats
39struct PlaneData {
40 SkBitmap fYFull;
41 SkBitmap fUFull;
42 SkBitmap fVFull;
43 SkBitmap fAFull;
44 SkBitmap fUQuarter; // 2x2 downsampled U channel
45 SkBitmap fVQuarter; // 2x2 downsampled V channel
46};
47
48// Add a portion of a circle to 'path'. The points 'o1' and 'o2' are on the border of the circle
49// and have tangents 'v1' and 'v2'.
50static void add_arc(SkPath* path,
51 const SkPoint& o1, const SkVector& v1,
52 const SkPoint& o2, const SkVector& v2,
53 SkTDArray<SkRect>* circles, bool takeLongWayRound) {
54
55 SkVector v3 = { -v1.fY, v1.fX };
56 SkVector v4 = { v2.fY, -v2.fX };
57
58 SkScalar t = ((o2.fX - o1.fX) * v4.fY - (o2.fY - o1.fY) * v4.fX) / v3.cross(v4);
59 SkPoint center = { o1.fX + t * v3.fX, o1.fY + t * v3.fY };
60
61 SkRect r = { center.fX - t, center.fY - t, center.fX + t, center.fY + t };
62
63 if (circles) {
64 circles->push_back(r);
65 }
66
67 SkVector startV = o1 - center, endV = o2 - center;
68 startV.normalize();
69 endV.normalize();
70
71 SkScalar startDeg = SkRadiansToDegrees(SkScalarATan2(startV.fY, startV.fX));
72 SkScalar endDeg = SkRadiansToDegrees(SkScalarATan2(endV.fY, endV.fX));
73
74 startDeg += 360.0f;
75 startDeg = fmodf(startDeg, 360.0f);
76
77 endDeg += 360.0f;
78 endDeg = fmodf(endDeg, 360.0f);
79
80 if (endDeg < startDeg) {
81 endDeg += 360.0f;
82 }
83
84 SkScalar sweepDeg = SkTAbs(endDeg - startDeg);
85 if (!takeLongWayRound) {
86 sweepDeg = sweepDeg - 360;
87 }
88
89 path->arcTo(r, startDeg, sweepDeg, false);
90}
91
92static SkPath create_splat(const SkPoint& o, SkScalar innerRadius, SkScalar outerRadius,
93 SkScalar ratio, int numLobes, SkTDArray<SkRect>* circles) {
94 if (numLobes <= 1) {
95 return SkPath();
96 }
97
98 SkPath p;
99
100 int numDivisions = 2 * numLobes;
101 SkScalar fullLobeDegrees = 360.0f / numLobes;
102 SkScalar outDegrees = ratio * fullLobeDegrees / (ratio + 1.0f);
103 SkScalar innerDegrees = fullLobeDegrees / (ratio + 1.0f);
104 SkMatrix outerStep, innerStep;
105 outerStep.setRotate(outDegrees);
106 innerStep.setRotate(innerDegrees);
107 SkVector curV = SkVector::Make(0.0f, 1.0f);
108
109 if (circles) {
110 circles->push_back(SkRect::MakeLTRB(o.fX - innerRadius, o.fY - innerRadius,
111 o.fX + innerRadius, o.fY + innerRadius));
112 }
113
114 p.moveTo(o.fX + innerRadius * curV.fX, o.fY + innerRadius * curV.fY);
115
116 for (int i = 0; i < numDivisions; ++i) {
117
118 SkVector nextV;
119 if (0 == (i % 2)) {
120 nextV = outerStep.mapVector(curV.fX, curV.fY);
121
122 SkPoint top = SkPoint::Make(o.fX + outerRadius * curV.fX,
123 o.fY + outerRadius * curV.fY);
124 SkPoint nextTop = SkPoint::Make(o.fX + outerRadius * nextV.fX,
125 o.fY + outerRadius * nextV.fY);
126
127 p.lineTo(top);
128 add_arc(&p, top, curV, nextTop, nextV, circles, true);
129 } else {
130 nextV = innerStep.mapVector(curV.fX, curV.fY);
131
132 SkPoint bot = SkPoint::Make(o.fX + innerRadius * curV.fX,
133 o.fY + innerRadius * curV.fY);
134 SkPoint nextBot = SkPoint::Make(o.fX + innerRadius * nextV.fX,
135 o.fY + innerRadius * nextV.fY);
136
137 p.lineTo(bot);
138 add_arc(&p, bot, curV, nextBot, nextV, nullptr, false);
139 }
140
141 curV = nextV;
142 }
143
144 p.close();
145
146 return p;
147}
148
149static SkBitmap make_bitmap(const SkPath& path, const SkTDArray<SkRect>& circles, bool opaque) {
150
151 SkImageInfo ii = SkImageInfo::Make(kTileWidthHeight, kTileWidthHeight,
152 kRGBA_8888_SkColorType, kPremul_SkAlphaType);
153
154 SkBitmap bm;
155 bm.allocPixels(ii);
156
157 std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirectN32(ii.width(), ii.height(),
158 (SkPMColor*)bm.getPixels(),
159 bm.rowBytes());
160
161 canvas->clear(opaque ? kGreen : SK_ColorTRANSPARENT);
162
163 SkPaint paint;
164 paint.setAntiAlias(false); // serialize-8888 doesn't seem to work well w/ partial transparency
165 paint.setColor(kBlue);
166
167 canvas->drawPath(path, paint);
168
169 paint.setColor(opaque ? kYellow : SK_ColorTRANSPARENT);
170 paint.setBlendMode(SkBlendMode::kSrc);
171 for (int i = 0; i < circles.count(); ++i) {
172 SkRect r = circles[i];
173 r.inset(r.width()/4, r.height()/4);
174 canvas->drawOval(r, paint);
175 }
176
177 return bm;
178}
179
180static void extract_planes(const SkBitmap& bm, SkYUVColorSpace yuvColorSpace, PlaneData* planes) {
181 SkASSERT(!(bm.width() % 2));
182 SkASSERT(!(bm.height() % 2));
183
184 planes->fYFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
185 planes->fUFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
186 planes->fVFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
187 planes->fAFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
188 planes->fUQuarter.allocPixels(SkImageInfo::MakeA8(bm.width()/2, bm.height()/2));
189 planes->fVQuarter.allocPixels(SkImageInfo::MakeA8(bm.width()/2, bm.height()/2));
190
191 float Kr, Kb, Z, S;
192
193 switch (yuvColorSpace) {
194 case kJPEG_SkYUVColorSpace: // computer
195 Kr = 0.299f;
196 Kb = 0.114f;
197 Z = 0.0f;
198 S = 255.0f;
199 break;
200 case kRec601_SkYUVColorSpace: // SD
201 Kr = 0.299f;
202 Kb = 0.114f;
203 Z = 16.0f;
204 S = 219.f;
205 break;
206 case kRec709_SkYUVColorSpace: // HD
207 Kr = 0.2126f;
208 Kb = 0.0722f;
209 Z = 16.0f;
210 S = 219.0f;
211 break;
212 }
213
214 for (int y = 0; y < bm.height(); ++y) {
215 for (int x = 0; x < bm.width(); ++x) {
216 SkColor col = bm.getColor(x, y);
217 uint8_t r = SkColorGetR(col);
218 uint8_t b = SkColorGetB(col);
219 uint8_t g = SkColorGetG(col);
220
221 float L = Kr * r + Kb * g + (1.0f - Kr - Kb) * b;
222
223 uint8_t Y, U, V;
224 Y = SkScalarRoundToInt((219*(L-Z)/S + 16));
225 U = SkScalarPin(SkScalarRoundToInt((112*(b-L) / ((1-Kb)*S) + 128)), 0, 255);
226 V = SkScalarPin(SkScalarRoundToInt((112*(r-L) / ((1-Kr)*S) + 128)), 0, 255);
227 *planes->fYFull.getAddr8(x, y) = Y;
228 *planes->fUFull.getAddr8(x, y) = U;
229 *planes->fVFull.getAddr8(x, y) = V;
230 *planes->fAFull.getAddr8(x, y) = SkColorGetA(col);
231 }
232 }
233
234 for (int y = 0; y < bm.height()/2; ++y) {
235 for (int x = 0; x < bm.width()/2; ++x) {
236 uint32_t uAccum = 0, vAccum = 0;
237
238 uAccum += *planes->fUFull.getAddr8(2*x, 2*y);
239 uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y);
240 uAccum += *planes->fUFull.getAddr8(2*x, 2*y+1);
241 uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y+1);
242
243 *planes->fUQuarter.getAddr8(x, y) = uAccum / 4.0f;
244
245 vAccum += *planes->fVFull.getAddr8(2*x, 2*y);
246 vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y);
247 vAccum += *planes->fVFull.getAddr8(2*x, 2*y+1);
248 vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y+1);
249
250 *planes->fVQuarter.getAddr8(x, y) = uAccum / 4.0f;
251 }
252 }
253}
254
255// Recombine the separate planes into some YUV format
256static void create_YUV(const PlaneData& planes, YUVFormat yuvFormat,
257 SkBitmap resultBMs[], SkYUVAIndex yuvaIndices[4], bool opaque) {
258 int nextLayer = 0;
259
260 switch (yuvFormat) {
261 case AYUV_YUVFormat: {
262 SkBitmap yuvaFull;
263
264 yuvaFull.allocPixels(SkImageInfo::Make(planes.fYFull.width(), planes.fYFull.height(),
265 kRGBA_8888_SkColorType, kUnpremul_SkAlphaType));
266
267 for (int y = 0; y < planes.fYFull.height(); ++y) {
268 for (int x = 0; x < planes.fYFull.width(); ++x) {
269
270 uint8_t Y = *planes.fYFull.getAddr8(x, y);
271 uint8_t U = *planes.fUFull.getAddr8(x, y);
272 uint8_t V = *planes.fVFull.getAddr8(x, y);
273 uint8_t A = *planes.fAFull.getAddr8(x, y);
274
275 // NOT premul!
276 *yuvaFull.getAddr32(x, y) = SkPackARGB32NoCheck(A, Y, U, V);
277 }
278 }
279
280 resultBMs[nextLayer++] = yuvaFull;
281
282 yuvaIndices[0].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400283 yuvaIndices[0].fChannel = SkColorChannel::kR;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400284 yuvaIndices[1].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400285 yuvaIndices[1].fChannel = SkColorChannel::kG;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400286 yuvaIndices[2].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400287 yuvaIndices[2].fChannel = SkColorChannel::kG;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400288 yuvaIndices[3].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400289 yuvaIndices[3].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400290 break;
291 }
292 case kNV12_YUVFormat: {
293 SkBitmap uvQuarter;
294
295 // There isn't a RG color type. Approx w/ 2x wider A8.
296 uvQuarter.allocPixels(SkImageInfo::MakeA8(planes.fYFull.width(),
297 planes.fYFull.height()/2));
298
299 for (int y = 0; y < planes.fYFull.height()/2; ++y) {
300 for (int x = 0; x < planes.fYFull.width()/2; ++x) {
301 *uvQuarter.getAddr8(2*x, y) = *planes.fUQuarter.getAddr8(x, y);
302 *uvQuarter.getAddr8(2*x+1, y) = *planes.fVQuarter.getAddr8(x, y);
303 }
304 }
305
306 resultBMs[nextLayer++] = planes.fYFull;
307 resultBMs[nextLayer++] = uvQuarter;
308
309 yuvaIndices[0].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400310 yuvaIndices[0].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400311 yuvaIndices[1].fIndex = 1;
Robert Phillips66a97342018-10-04 09:10:29 -0400312 yuvaIndices[1].fChannel = SkColorChannel::kR;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400313 yuvaIndices[2].fIndex = 1;
Robert Phillips66a97342018-10-04 09:10:29 -0400314 yuvaIndices[2].fChannel = SkColorChannel::kG;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400315 break;
316 }
317 case kNV21_YUVFormat: {
318 SkBitmap vuQuarter;
319
320 // There isn't a RG color type. Approx w/ 2x wider A8.
321 vuQuarter.allocPixels(SkImageInfo::MakeA8(planes.fYFull.width(),
322 planes.fYFull.height()/2));
323
324 for (int y = 0; y < planes.fYFull.height()/2; ++y) {
325 for (int x = 0; x < planes.fYFull.width()/2; ++x) {
326 *vuQuarter.getAddr8(2*x, y) = *planes.fVQuarter.getAddr8(x, y);
327 *vuQuarter.getAddr8(2*x+1, y) = *planes.fUQuarter.getAddr8(x, y);
328 }
329 }
330
331 resultBMs[nextLayer++] = planes.fYFull;
332 resultBMs[nextLayer++] = vuQuarter;
333
334 yuvaIndices[0].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400335 yuvaIndices[0].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400336 yuvaIndices[1].fIndex = 1;
Robert Phillips66a97342018-10-04 09:10:29 -0400337 yuvaIndices[1].fChannel = SkColorChannel::kG;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400338 yuvaIndices[2].fIndex = 1;
Robert Phillips66a97342018-10-04 09:10:29 -0400339 yuvaIndices[2].fChannel = SkColorChannel::kR;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400340 break;
341 }
342 case kI420_YUVFormat:
343 resultBMs[nextLayer++] = planes.fYFull;
344 resultBMs[nextLayer++] = planes.fUQuarter;
345 resultBMs[nextLayer++] = planes.fVQuarter;
346
347 yuvaIndices[0].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400348 yuvaIndices[0].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400349 yuvaIndices[1].fIndex = 1;
Robert Phillips66a97342018-10-04 09:10:29 -0400350 yuvaIndices[1].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400351 yuvaIndices[2].fIndex = 2;
Robert Phillips66a97342018-10-04 09:10:29 -0400352 yuvaIndices[2].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400353 break;
354 case kYV12_YUVFormat:
355 resultBMs[nextLayer++] = planes.fYFull;
356 resultBMs[nextLayer++] = planes.fVQuarter;
357 resultBMs[nextLayer++] = planes.fUQuarter;
358
359 yuvaIndices[0].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400360 yuvaIndices[0].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400361 yuvaIndices[1].fIndex = 2;
Robert Phillips66a97342018-10-04 09:10:29 -0400362 yuvaIndices[1].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400363 yuvaIndices[2].fIndex = 1;
Robert Phillips66a97342018-10-04 09:10:29 -0400364 yuvaIndices[2].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400365 break;
366 }
367
368 if (AYUV_YUVFormat != yuvFormat) {
369 if (opaque) {
370 yuvaIndices[3].fIndex = -1;
371 } else {
372 resultBMs[nextLayer] = planes.fAFull;
373
374 yuvaIndices[3].fIndex = nextLayer;
Robert Phillips66a97342018-10-04 09:10:29 -0400375 yuvaIndices[3].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400376 }
377 }
378
379}
380
381class YUVGenerator : public SkImageGenerator {
382public:
383 YUVGenerator(const SkImageInfo& ii,
384 YUVFormat yuvFormat,
385 SkYUVColorSpace yuvColorSpace,
386 SkYUVAIndex yuvaIndices[4],
387 SkBitmap bitmaps[4])
388 : SkImageGenerator(ii)
389 , fYUVFormat(yuvFormat)
390 , fYUVColorSpace(yuvColorSpace) {
391 memcpy(fYUVAIndices, yuvaIndices, sizeof(fYUVAIndices));
392
393 bool used[4] = { false, false, false, false };
394 for (int i = 0; i < 4; ++i) {
395 if (yuvaIndices[i].fIndex >= 0) {
396 SkASSERT(yuvaIndices[i].fIndex < 4);
397 used[yuvaIndices[i].fIndex] = true;
398 } else {
399 SkASSERT(3 == i); // only the 'A' channel can be unspecified
400 }
401 }
402
403 for (int i = 0; i < 4; ++i) {
404 if (used[i]) {
405 fYUVBitmaps[i] = bitmaps[i];
406 }
407 }
408 }
409
410protected:
411 bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
412 const Options&) override {
413
414 if (kUnknown_SkColorType == fFlattened.colorType()) {
415 fFlattened.allocPixels(info);
416
417 for (int y = 0; y < info.height(); ++y) {
418 for (int x = 0; x < info.width(); ++x) {
419
420 uint8_t alpha = 255;
421 if (fYUVAIndices[3].fIndex >= 0) {
422 int alphaIndex = fYUVAIndices[3].fIndex;
423 if (kAlpha_8_SkColorType == fYUVBitmaps[alphaIndex].colorType()) {
424 alpha = *fYUVBitmaps[alphaIndex].getAddr8(x, y);
425 } else {
426 alpha = SkColorGetA(fYUVBitmaps[alphaIndex].getColor(x, y));
427 }
428 }
429
430 uint8_t g;
431 if (kAlpha_8_SkColorType == fYUVBitmaps[fYUVAIndices[0].fIndex].colorType()) {
432 g = *fYUVBitmaps[fYUVAIndices[0].fIndex].getAddr8(x, y);
433 } else {
434 g = SkColorGetR(fYUVBitmaps[fYUVAIndices[0].fIndex].getColor(x, y));
435 }
436
437 // Making premul here.
438 *fFlattened.getAddr32(x, y) = SkPreMultiplyARGB(alpha, g, g, g);
439 }
440 }
441 }
442
443 return fFlattened.readPixels(info, pixels, rowBytes, 0, 0);
444 }
445
446 bool onQueryYUV8(SkYUVSizeInfo* size, SkYUVColorSpace* yuvColorSpace) const override {
447 if (kI420_YUVFormat != fYUVFormat && kYV12_YUVFormat != fYUVFormat) {
448 return false; // currently this API only supports planar formats
449 }
450
451 *yuvColorSpace = fYUVColorSpace;
452 size->fSizes[0].fWidth = fYUVBitmaps[fYUVAIndices[0].fIndex].width();
453 size->fSizes[0].fHeight = fYUVBitmaps[fYUVAIndices[0].fIndex].height();
454 size->fWidthBytes[0] = fYUVBitmaps[fYUVAIndices[0].fIndex].rowBytes();
455
456 size->fSizes[1].fWidth = fYUVBitmaps[fYUVAIndices[1].fIndex].width();
457 size->fSizes[1].fHeight = fYUVBitmaps[fYUVAIndices[1].fIndex].height();
458 size->fWidthBytes[1] = fYUVBitmaps[fYUVAIndices[1].fIndex].rowBytes();
459
460 size->fSizes[2].fWidth = fYUVBitmaps[fYUVAIndices[2].fIndex].width();
461 size->fSizes[2].fHeight = fYUVBitmaps[fYUVAIndices[2].fIndex].height();
462 size->fWidthBytes[2] = fYUVBitmaps[fYUVAIndices[2].fIndex].rowBytes();
463 return true;
464 }
465
466 bool onGetYUV8Planes(const SkYUVSizeInfo&, void* planes[3]) override {
467 planes[0] = fYUVBitmaps[fYUVAIndices[0].fIndex].getAddr(0, 0);
468 planes[1] = fYUVBitmaps[fYUVAIndices[1].fIndex].getAddr(0, 0);
469 planes[2] = fYUVBitmaps[fYUVAIndices[2].fIndex].getAddr(0, 0);
470 return true;
471 }
472
473private:
474 YUVFormat fYUVFormat;
475 SkYUVColorSpace fYUVColorSpace;
476 SkYUVAIndex fYUVAIndices[4];
477 SkBitmap fYUVBitmaps[4];
478 SkBitmap fFlattened;
479
480};
481
482static sk_sp<SkImage> make_yuv_gen_image(const SkImageInfo& ii,
483 YUVFormat yuvFormat,
484 SkYUVColorSpace yuvColorSpace,
485 SkYUVAIndex yuvaIndices[4],
486 SkBitmap bitmaps[]) {
487 std::unique_ptr<SkImageGenerator> gen(new YUVGenerator(ii, yuvFormat, yuvColorSpace,
488 yuvaIndices, bitmaps));
489
490 return SkImage::MakeFromGenerator(std::move(gen));
491}
492
493static void draw_col_label(SkCanvas* canvas, int x, int yuvColorSpace, bool opaque) {
494 static const char* kYUVColorSpaceNames[] = { "JPEG", "601", "709" };
495 GR_STATIC_ASSERT(SK_ARRAY_COUNT(kYUVColorSpaceNames) == kLastEnum_SkYUVColorSpace+1);
496
497 SkPaint textPaint;
498 textPaint.setTextAlign(SkPaint::kCenter_Align);
499 sk_tool_utils::set_portable_typeface(&textPaint, nullptr, SkFontStyle::Bold());
500 textPaint.setTextSize(16);
501
502 SkRect textRect;
503 SkString colLabel;
504
505 colLabel.printf("%s", kYUVColorSpaceNames[yuvColorSpace]);
506 textPaint.measureText(colLabel.c_str(), colLabel.size(), &textRect);
507 int y = textRect.height();
508
509 canvas->drawText(colLabel.c_str(), colLabel.size(), x, y, textPaint);
510
511 colLabel.printf("%s", opaque ? "Opaque" : "Transparent");
512 textPaint.measureText(colLabel.c_str(), colLabel.size(), &textRect);
513 y += textRect.height();
514
515 canvas->drawText(colLabel.c_str(), colLabel.size(), x, y, textPaint);
516}
517
518static void draw_row_label(SkCanvas* canvas, int y, int yuvFormat) {
519 static const char* kYUVFormatNames[] = { "AYUV", "NV12", "NV21", "I420", "YV12" };
520 GR_STATIC_ASSERT(SK_ARRAY_COUNT(kYUVFormatNames) == kLast_YUVFormat+1);
521
522 SkPaint textPaint;
523 textPaint.setTextAlign(SkPaint::kLeft_Align);
524 sk_tool_utils::set_portable_typeface(&textPaint, nullptr, SkFontStyle::Bold());
525 textPaint.setTextSize(16);
526
527 SkRect textRect;
528 SkString rowLabel;
529
530 rowLabel.printf("%s", kYUVFormatNames[yuvFormat]);
531 textPaint.measureText(rowLabel.c_str(), rowLabel.size(), &textRect);
532 y += kTileWidthHeight/2 + textRect.height()/2;
533
534 canvas->drawText(rowLabel.c_str(), rowLabel.size(), 0, y, textPaint);
535}
536
537namespace skiagm {
538
539// This GM creates an opaque and transparent bitmap, extracts the planes and then recombines
540// them into various YUV formats. It then renders the results in the grid:
541//
542// JPEG 601 709
543// Transparent Opaque Transparent Opaque Transparent Opaque
544// AYUV
545// NV12
546// NV21
547// I420
548// YV12
549class WackyYUVFormatsGM : public GM {
550public:
551 WackyYUVFormatsGM() {
552 this->setBGColor(0xFFCCCCCC);
553 }
554
555protected:
556
557 SkString onShortName() override {
558 return SkString("wacky_yuv_formats");
559 }
560
561 SkISize onISize() override {
562 int numCols = 2 * (kLastEnum_SkYUVColorSpace + 1); // opacity x color-space
563 int numRows = 1 + (kLast_YUVFormat + 1); // origin + # yuv formats
564 return SkISize::Make(kLabelWidth + numCols * (kTileWidthHeight + kPad),
565 kLabelHeight + numRows * (kTileWidthHeight + kPad));
566 }
567
568 void onOnceBeforeDraw() override {
569 SkPoint origin = { kTileWidthHeight/2.0f, kTileWidthHeight/2.0f };
570 float outerRadius = kTileWidthHeight/2.0f - 20.0f;
571 float innerRadius = 20.0f;
572
573 {
574 // transparent
575 SkTDArray<SkRect> circles;
576 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 5, &circles);
577 fOriginalBMs[0] = make_bitmap(path, circles, false);
578 }
579
580 {
581 // opaque
582 SkTDArray<SkRect> circles;
583 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 7, &circles);
584 fOriginalBMs[1] = make_bitmap(path, circles, true);
585 }
586
587 for (bool opaque : { false, true }) {
588 for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
589 PlaneData planes;
590 extract_planes(fOriginalBMs[opaque], (SkYUVColorSpace) cs, &planes);
591
592 for (int format = AYUV_YUVFormat; format <= kLast_YUVFormat; ++format) {
593 SkBitmap resultBMs[4];
594 SkYUVAIndex yuvaIndices[4];
595 create_YUV(planes, (YUVFormat) format, resultBMs, yuvaIndices, opaque);
596
597 fImages[opaque][cs][format] = make_yuv_gen_image(fOriginalBMs[opaque].info(),
598 (YUVFormat) format,
599 (SkYUVColorSpace) cs,
600 yuvaIndices,
601 resultBMs);
602 }
603 }
604 }
605 }
606
607 void onDraw(SkCanvas* canvas) override {
608 int x = kLabelWidth;
609 for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
610 for (int opaque : { 0, 1 }) {
611 int y = kLabelHeight;
612
613 draw_col_label(canvas, x+kTileWidthHeight/2, cs, opaque);
614
615 canvas->drawBitmap(fOriginalBMs[opaque], x, y);
616 y += kTileWidthHeight + kPad;
617
618 for (int format = AYUV_YUVFormat; format <= kLast_YUVFormat; ++format) {
619 draw_row_label(canvas, y, format);
620 canvas->drawImage(fImages[opaque][cs][format], x, y);
621
622 y += kTileWidthHeight + kPad;
623 }
624
625 x += kTileWidthHeight + kPad;
626 }
627 }
628 }
629
630private:
631 SkBitmap fOriginalBMs[2];
632 sk_sp<SkImage> fImages[2][kLastEnum_SkYUVColorSpace+1][kLast_YUVFormat+1];
633
634 typedef GM INHERITED;
635};
636
637//////////////////////////////////////////////////////////////////////////////
638
639DEF_GM(return new WackyYUVFormatsGM;)
640}