blob: 717c98ea86177424035da2b2b1fa588b4c9fccf3 [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;
Robert Phillips1c7062d2018-10-04 10:44:53 -040020constexpr SkColor kGreen = SkColorSetARGB(0xFF, 178, 240, 104);
21constexpr SkColor kBlue = SkColorSetARGB(0xFF, 173, 167, 252);
22constexpr SkColor kYellow = SkColorSetARGB(0xFF, 255, 221, 117);
Robert Phillipsbfa76f22018-10-03 12:12:26 -040023
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
Robert Phillips1c7062d2018-10-04 10:44:53 -0400151 SkImageInfo ii = SkImageInfo::MakeN32(kTileWidthHeight, kTileWidthHeight, kPremul_SkAlphaType);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400152
153 SkBitmap bm;
154 bm.allocPixels(ii);
155
156 std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirectN32(ii.width(), ii.height(),
157 (SkPMColor*)bm.getPixels(),
158 bm.rowBytes());
159
160 canvas->clear(opaque ? kGreen : SK_ColorTRANSPARENT);
161
162 SkPaint paint;
163 paint.setAntiAlias(false); // serialize-8888 doesn't seem to work well w/ partial transparency
164 paint.setColor(kBlue);
165
166 canvas->drawPath(path, paint);
167
168 paint.setColor(opaque ? kYellow : SK_ColorTRANSPARENT);
169 paint.setBlendMode(SkBlendMode::kSrc);
170 for (int i = 0; i < circles.count(); ++i) {
171 SkRect r = circles[i];
172 r.inset(r.width()/4, r.height()/4);
173 canvas->drawOval(r, paint);
174 }
175
176 return bm;
177}
178
Robert Phillips1c7062d2018-10-04 10:44:53 -0400179static void convert_rgba_to_yuva_601_shared(SkColor col, uint8_t yuv[4],
180 uint8_t off, uint8_t range) {
181 static const float Kr = 0.299f;
182 static const float Kb = 0.114f;
183 static const float Kg = 1.0f - Kr - Kb;
184
185 float r = SkColorGetR(col) / 255.0f;
186 float g = SkColorGetG(col) / 255.0f;
187 float b = SkColorGetB(col) / 255.0f;
188
189 float Ey = Kr * r + Kg * g + Kb * b;
190 float Ecb = (b - Ey) / 1.402f;
191 float Ecr = (r - Ey) / 1.772;
192
193 yuv[0] = SkScalarRoundToInt( range * Ey + off );
194 yuv[1] = SkScalarRoundToInt( 224 * Ecb + 128 );
195 yuv[2] = SkScalarRoundToInt( 224 * Ecr + 128 );
196 yuv[3] = SkColorGetA(col);
197}
198
199static void convert_rgba_to_yuva_jpeg(SkColor col, uint8_t yuv[4]) {
200 // full swing from 0..255
201 convert_rgba_to_yuva_601_shared(col, yuv, 0, 255);
202}
203
204static void convert_rgba_to_yuva_601(SkColor col, uint8_t yuv[4]) {
205 // partial swing from 16..235
206 convert_rgba_to_yuva_601_shared(col, yuv, 16, 219);
207
208}
209
210static void convert_rgba_to_yuva_709(SkColor col, uint8_t yuv[4]) {
211 static const float Kr = 0.2126f;
212 static const float Kb = 0.0722f;
213 static const float Kg = 1.0f - Kr - Kb;
214
215 float r = SkColorGetR(col) / 255.0f;
216 float g = SkColorGetG(col) / 255.0f;
217 float b = SkColorGetB(col) / 255.0f;
218
219 float Ey = Kr * r + Kg * g + Kb * b;
220 float Ecb = (b - Ey) / 1.8556f;
221 float Ecr = (r - Ey) / 1.5748;
222
223 yuv[0] = SkScalarRoundToInt( 219 * Ey + 16 );
224 yuv[1] = SkScalarRoundToInt( 224 * Ecb + 128 );
225 yuv[2] = SkScalarRoundToInt( 224 * Ecr + 128 );
226
227 yuv[3] = SkColorGetA(col);
228}
229
230
231static SkPMColor convert_yuva_to_rgba_jpeg(uint8_t y, uint8_t u, uint8_t v, uint8_t a) {
232 int c = y;
233 int d = u - 128;
234 int e = v - 128;
235
236 uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.0f * c + 1.402f * e ),
237 0, 255);
238 uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.0f * c - (0.344136f * d) - (0.714136f * e)),
239 0, 255);
240 uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.0f * c + 1.773f * d ),
241 0, 255);
242
243 return SkPremultiplyARGBInline(a, r, g, b);
244}
245
246static SkPMColor convert_yuva_to_rgba_601(uint8_t y, uint8_t u, uint8_t v, uint8_t a) {
247 int c = y - 16;
248 int d = u - 128;
249 int e = v - 128;
250
251 uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.164f * c + 1.596f * e ), 0, 255);
252 uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.164f * c - (0.391f * d) - (0.813f * e)), 0, 255);
253 uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.164f * c + 2.018f * d ), 0, 255);
254
255 return SkPremultiplyARGBInline(a, r, g, b);
256}
257
258static SkPMColor convert_yuva_to_rgba_709(uint8_t y, uint8_t u, uint8_t v, uint8_t a) {
259 int c = y - 16;
260 int d = u - 128;
261 int e = v - 128;
262
263 uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.164f * c + 1.793f * e ), 0, 255);
264 uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.164f * c - (0.213f * d) - (0.533f * e)), 0, 255);
265 uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.164f * c + 2.112f * d ), 0, 255);
266
267 return SkPremultiplyARGBInline(a, r, g, b);
268}
269
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400270static void extract_planes(const SkBitmap& bm, SkYUVColorSpace yuvColorSpace, PlaneData* planes) {
271 SkASSERT(!(bm.width() % 2));
272 SkASSERT(!(bm.height() % 2));
273
274 planes->fYFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
275 planes->fUFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
276 planes->fVFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
277 planes->fAFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
278 planes->fUQuarter.allocPixels(SkImageInfo::MakeA8(bm.width()/2, bm.height()/2));
279 planes->fVQuarter.allocPixels(SkImageInfo::MakeA8(bm.width()/2, bm.height()/2));
280
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400281 for (int y = 0; y < bm.height(); ++y) {
282 for (int x = 0; x < bm.width(); ++x) {
283 SkColor col = bm.getColor(x, y);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400284
Robert Phillips1c7062d2018-10-04 10:44:53 -0400285 uint8_t yuva[4];
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400286
Robert Phillips1c7062d2018-10-04 10:44:53 -0400287 if (kJPEG_SkYUVColorSpace == yuvColorSpace) {
288 convert_rgba_to_yuva_jpeg(col, yuva);
289 } else if (kRec601_SkYUVColorSpace == yuvColorSpace) {
290 convert_rgba_to_yuva_601(col, yuva);
291 } else {
292 SkASSERT(kRec709_SkYUVColorSpace == yuvColorSpace);
293 convert_rgba_to_yuva_709(col, yuva);
294 }
295
296 *planes->fYFull.getAddr8(x, y) = yuva[0];
297 *planes->fUFull.getAddr8(x, y) = yuva[1];
298 *planes->fVFull.getAddr8(x, y) = yuva[2];
299 *planes->fAFull.getAddr8(x, y) = yuva[3];
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400300 }
301 }
302
303 for (int y = 0; y < bm.height()/2; ++y) {
304 for (int x = 0; x < bm.width()/2; ++x) {
305 uint32_t uAccum = 0, vAccum = 0;
306
307 uAccum += *planes->fUFull.getAddr8(2*x, 2*y);
308 uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y);
309 uAccum += *planes->fUFull.getAddr8(2*x, 2*y+1);
310 uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y+1);
311
312 *planes->fUQuarter.getAddr8(x, y) = uAccum / 4.0f;
313
314 vAccum += *planes->fVFull.getAddr8(2*x, 2*y);
315 vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y);
316 vAccum += *planes->fVFull.getAddr8(2*x, 2*y+1);
317 vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y+1);
318
Robert Phillips1c7062d2018-10-04 10:44:53 -0400319 *planes->fVQuarter.getAddr8(x, y) = vAccum / 4.0f;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400320 }
321 }
322}
323
324// Recombine the separate planes into some YUV format
325static void create_YUV(const PlaneData& planes, YUVFormat yuvFormat,
326 SkBitmap resultBMs[], SkYUVAIndex yuvaIndices[4], bool opaque) {
327 int nextLayer = 0;
328
329 switch (yuvFormat) {
330 case AYUV_YUVFormat: {
331 SkBitmap yuvaFull;
332
Robert Phillips1c7062d2018-10-04 10:44:53 -0400333 yuvaFull.allocPixels(SkImageInfo::MakeN32(planes.fYFull.width(), planes.fYFull.height(),
334 kUnpremul_SkAlphaType));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400335
336 for (int y = 0; y < planes.fYFull.height(); ++y) {
337 for (int x = 0; x < planes.fYFull.width(); ++x) {
338
339 uint8_t Y = *planes.fYFull.getAddr8(x, y);
340 uint8_t U = *planes.fUFull.getAddr8(x, y);
341 uint8_t V = *planes.fVFull.getAddr8(x, y);
342 uint8_t A = *planes.fAFull.getAddr8(x, y);
343
344 // NOT premul!
Robert Phillips1c7062d2018-10-04 10:44:53 -0400345 *yuvaFull.getAddr32(x, y) = SkColorSetARGB(A, Y, U, V);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400346 }
347 }
348
349 resultBMs[nextLayer++] = yuvaFull;
350
351 yuvaIndices[0].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400352 yuvaIndices[0].fChannel = SkColorChannel::kR;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400353 yuvaIndices[1].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400354 yuvaIndices[1].fChannel = SkColorChannel::kG;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400355 yuvaIndices[2].fIndex = 0;
Robert Phillips1c7062d2018-10-04 10:44:53 -0400356 yuvaIndices[2].fChannel = SkColorChannel::kB;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400357 yuvaIndices[3].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400358 yuvaIndices[3].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400359 break;
360 }
361 case kNV12_YUVFormat: {
362 SkBitmap uvQuarter;
363
Robert Phillips1c7062d2018-10-04 10:44:53 -0400364 // There isn't a RG color type. Approx w/ RGBA.
365 uvQuarter.allocPixels(SkImageInfo::MakeN32(planes.fYFull.width()/2,
366 planes.fYFull.height()/2,
367 kUnpremul_SkAlphaType));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400368
369 for (int y = 0; y < planes.fYFull.height()/2; ++y) {
370 for (int x = 0; x < planes.fYFull.width()/2; ++x) {
Robert Phillips1c7062d2018-10-04 10:44:53 -0400371 uint8_t U = *planes.fUQuarter.getAddr8(x, y);
372 uint8_t V = *planes.fVQuarter.getAddr8(x, y);
373
374 // NOT premul!
375 *uvQuarter.getAddr32(x, y) = SkColorSetARGB(0, U, V, 0);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400376 }
377 }
378
379 resultBMs[nextLayer++] = planes.fYFull;
380 resultBMs[nextLayer++] = uvQuarter;
381
382 yuvaIndices[0].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400383 yuvaIndices[0].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400384 yuvaIndices[1].fIndex = 1;
Robert Phillips66a97342018-10-04 09:10:29 -0400385 yuvaIndices[1].fChannel = SkColorChannel::kR;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400386 yuvaIndices[2].fIndex = 1;
Robert Phillips66a97342018-10-04 09:10:29 -0400387 yuvaIndices[2].fChannel = SkColorChannel::kG;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400388 break;
389 }
390 case kNV21_YUVFormat: {
391 SkBitmap vuQuarter;
392
Robert Phillips1c7062d2018-10-04 10:44:53 -0400393 // There isn't a RG color type. Approx w/ RGBA.
394 vuQuarter.allocPixels(SkImageInfo::MakeN32(planes.fYFull.width()/2,
395 planes.fYFull.height()/2,
396 kUnpremul_SkAlphaType));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400397
398 for (int y = 0; y < planes.fYFull.height()/2; ++y) {
399 for (int x = 0; x < planes.fYFull.width()/2; ++x) {
Robert Phillips1c7062d2018-10-04 10:44:53 -0400400 uint8_t U = *planes.fUQuarter.getAddr8(x, y);
401 uint8_t V = *planes.fVQuarter.getAddr8(x, y);
402
403 // NOT premul!
404 *vuQuarter.getAddr32(x, y) = SkColorSetARGB(0, V, U, 0);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400405 }
406 }
407
408 resultBMs[nextLayer++] = planes.fYFull;
409 resultBMs[nextLayer++] = vuQuarter;
410
411 yuvaIndices[0].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400412 yuvaIndices[0].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400413 yuvaIndices[1].fIndex = 1;
Robert Phillips66a97342018-10-04 09:10:29 -0400414 yuvaIndices[1].fChannel = SkColorChannel::kG;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400415 yuvaIndices[2].fIndex = 1;
Robert Phillips66a97342018-10-04 09:10:29 -0400416 yuvaIndices[2].fChannel = SkColorChannel::kR;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400417 break;
418 }
419 case kI420_YUVFormat:
420 resultBMs[nextLayer++] = planes.fYFull;
421 resultBMs[nextLayer++] = planes.fUQuarter;
422 resultBMs[nextLayer++] = planes.fVQuarter;
423
424 yuvaIndices[0].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400425 yuvaIndices[0].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400426 yuvaIndices[1].fIndex = 1;
Robert Phillips66a97342018-10-04 09:10:29 -0400427 yuvaIndices[1].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400428 yuvaIndices[2].fIndex = 2;
Robert Phillips66a97342018-10-04 09:10:29 -0400429 yuvaIndices[2].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400430 break;
431 case kYV12_YUVFormat:
432 resultBMs[nextLayer++] = planes.fYFull;
433 resultBMs[nextLayer++] = planes.fVQuarter;
434 resultBMs[nextLayer++] = planes.fUQuarter;
435
436 yuvaIndices[0].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400437 yuvaIndices[0].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400438 yuvaIndices[1].fIndex = 2;
Robert Phillips66a97342018-10-04 09:10:29 -0400439 yuvaIndices[1].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400440 yuvaIndices[2].fIndex = 1;
Robert Phillips66a97342018-10-04 09:10:29 -0400441 yuvaIndices[2].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400442 break;
443 }
444
445 if (AYUV_YUVFormat != yuvFormat) {
446 if (opaque) {
447 yuvaIndices[3].fIndex = -1;
448 } else {
449 resultBMs[nextLayer] = planes.fAFull;
450
451 yuvaIndices[3].fIndex = nextLayer;
Robert Phillips66a97342018-10-04 09:10:29 -0400452 yuvaIndices[3].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400453 }
454 }
455
456}
457
Robert Phillips1c7062d2018-10-04 10:44:53 -0400458static uint8_t look_up(float x1, float y1, const SkBitmap& bm, SkColorChannel channel) {
459 uint8_t result;
460
461 int x = SkScalarFloorToInt(x1 * bm.width());
462 int y = SkScalarFloorToInt(y1 * bm.height());
463
464 if (kAlpha_8_SkColorType == bm.colorType()) {
465 SkASSERT(SkColorChannel::kA == channel);
466 result = *bm.getAddr8(x, y);
467 } else {
468 SkASSERT(kN32_SkColorType == bm.colorType());
469
470 switch (channel) {
471 case SkColorChannel::kR:
472 result = SkColorGetR(bm.getColor(x, y));
473 break;
474 case SkColorChannel::kG:
475 result = SkColorGetG(bm.getColor(x, y));
476 break;
477 case SkColorChannel::kB:
478 result = SkColorGetB(bm.getColor(x, y));
479 break;
480 case SkColorChannel::kA:
481 result = SkColorGetA(bm.getColor(x, y));
482 break;
483 }
484 }
485
486 return result;
487}
488
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400489class YUVGenerator : public SkImageGenerator {
490public:
491 YUVGenerator(const SkImageInfo& ii,
492 YUVFormat yuvFormat,
493 SkYUVColorSpace yuvColorSpace,
494 SkYUVAIndex yuvaIndices[4],
495 SkBitmap bitmaps[4])
496 : SkImageGenerator(ii)
497 , fYUVFormat(yuvFormat)
498 , fYUVColorSpace(yuvColorSpace) {
499 memcpy(fYUVAIndices, yuvaIndices, sizeof(fYUVAIndices));
500
501 bool used[4] = { false, false, false, false };
502 for (int i = 0; i < 4; ++i) {
503 if (yuvaIndices[i].fIndex >= 0) {
504 SkASSERT(yuvaIndices[i].fIndex < 4);
505 used[yuvaIndices[i].fIndex] = true;
506 } else {
507 SkASSERT(3 == i); // only the 'A' channel can be unspecified
508 }
509 }
510
511 for (int i = 0; i < 4; ++i) {
512 if (used[i]) {
513 fYUVBitmaps[i] = bitmaps[i];
514 }
515 }
516 }
517
518protected:
519 bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
520 const Options&) override {
521
522 if (kUnknown_SkColorType == fFlattened.colorType()) {
523 fFlattened.allocPixels(info);
524
525 for (int y = 0; y < info.height(); ++y) {
526 for (int x = 0; x < info.width(); ++x) {
527
Robert Phillips1c7062d2018-10-04 10:44:53 -0400528 float x1 = (x + 0.5f) / info.width();
529 float y1 = (y + 0.5f) / info.height();
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400530
Robert Phillips1c7062d2018-10-04 10:44:53 -0400531 uint8_t Y = look_up(x1, y1,
532 fYUVBitmaps[fYUVAIndices[0].fIndex],
533 fYUVAIndices[0].fChannel);
534
535 uint8_t U = look_up(x1, y1,
536 fYUVBitmaps[fYUVAIndices[1].fIndex],
537 fYUVAIndices[1].fChannel);
538
539
540 uint8_t V = look_up(x1, y1,
541 fYUVBitmaps[fYUVAIndices[2].fIndex],
542 fYUVAIndices[2].fChannel);
543
544 uint8_t A = 255;
545 if (fYUVAIndices[3].fIndex >= 0) {
546 A = look_up(x1, y1,
547 fYUVBitmaps[fYUVAIndices[3].fIndex],
548 fYUVAIndices[3].fChannel);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400549 }
550
551 // Making premul here.
Robert Phillips1c7062d2018-10-04 10:44:53 -0400552 switch (fYUVColorSpace) {
553 case kJPEG_SkYUVColorSpace:
554 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_jpeg(Y, U, V, A);
555 break;
556 case kRec601_SkYUVColorSpace:
557 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_601(Y, U, V, A);
558 break;
559 case kRec709_SkYUVColorSpace:
560 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_709(Y, U, V, A);
561 break;
562 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400563 }
564 }
565 }
566
567 return fFlattened.readPixels(info, pixels, rowBytes, 0, 0);
568 }
569
570 bool onQueryYUV8(SkYUVSizeInfo* size, SkYUVColorSpace* yuvColorSpace) const override {
571 if (kI420_YUVFormat != fYUVFormat && kYV12_YUVFormat != fYUVFormat) {
572 return false; // currently this API only supports planar formats
573 }
574
575 *yuvColorSpace = fYUVColorSpace;
576 size->fSizes[0].fWidth = fYUVBitmaps[fYUVAIndices[0].fIndex].width();
577 size->fSizes[0].fHeight = fYUVBitmaps[fYUVAIndices[0].fIndex].height();
578 size->fWidthBytes[0] = fYUVBitmaps[fYUVAIndices[0].fIndex].rowBytes();
579
580 size->fSizes[1].fWidth = fYUVBitmaps[fYUVAIndices[1].fIndex].width();
581 size->fSizes[1].fHeight = fYUVBitmaps[fYUVAIndices[1].fIndex].height();
582 size->fWidthBytes[1] = fYUVBitmaps[fYUVAIndices[1].fIndex].rowBytes();
583
584 size->fSizes[2].fWidth = fYUVBitmaps[fYUVAIndices[2].fIndex].width();
585 size->fSizes[2].fHeight = fYUVBitmaps[fYUVAIndices[2].fIndex].height();
586 size->fWidthBytes[2] = fYUVBitmaps[fYUVAIndices[2].fIndex].rowBytes();
587 return true;
588 }
589
590 bool onGetYUV8Planes(const SkYUVSizeInfo&, void* planes[3]) override {
591 planes[0] = fYUVBitmaps[fYUVAIndices[0].fIndex].getAddr(0, 0);
592 planes[1] = fYUVBitmaps[fYUVAIndices[1].fIndex].getAddr(0, 0);
593 planes[2] = fYUVBitmaps[fYUVAIndices[2].fIndex].getAddr(0, 0);
594 return true;
595 }
596
597private:
598 YUVFormat fYUVFormat;
599 SkYUVColorSpace fYUVColorSpace;
600 SkYUVAIndex fYUVAIndices[4];
601 SkBitmap fYUVBitmaps[4];
602 SkBitmap fFlattened;
603
604};
605
606static sk_sp<SkImage> make_yuv_gen_image(const SkImageInfo& ii,
607 YUVFormat yuvFormat,
608 SkYUVColorSpace yuvColorSpace,
609 SkYUVAIndex yuvaIndices[4],
610 SkBitmap bitmaps[]) {
611 std::unique_ptr<SkImageGenerator> gen(new YUVGenerator(ii, yuvFormat, yuvColorSpace,
612 yuvaIndices, bitmaps));
613
614 return SkImage::MakeFromGenerator(std::move(gen));
615}
616
617static void draw_col_label(SkCanvas* canvas, int x, int yuvColorSpace, bool opaque) {
618 static const char* kYUVColorSpaceNames[] = { "JPEG", "601", "709" };
619 GR_STATIC_ASSERT(SK_ARRAY_COUNT(kYUVColorSpaceNames) == kLastEnum_SkYUVColorSpace+1);
620
621 SkPaint textPaint;
622 textPaint.setTextAlign(SkPaint::kCenter_Align);
623 sk_tool_utils::set_portable_typeface(&textPaint, nullptr, SkFontStyle::Bold());
624 textPaint.setTextSize(16);
625
626 SkRect textRect;
627 SkString colLabel;
628
629 colLabel.printf("%s", kYUVColorSpaceNames[yuvColorSpace]);
630 textPaint.measureText(colLabel.c_str(), colLabel.size(), &textRect);
631 int y = textRect.height();
632
633 canvas->drawText(colLabel.c_str(), colLabel.size(), x, y, textPaint);
634
635 colLabel.printf("%s", opaque ? "Opaque" : "Transparent");
636 textPaint.measureText(colLabel.c_str(), colLabel.size(), &textRect);
637 y += textRect.height();
638
639 canvas->drawText(colLabel.c_str(), colLabel.size(), x, y, textPaint);
640}
641
642static void draw_row_label(SkCanvas* canvas, int y, int yuvFormat) {
643 static const char* kYUVFormatNames[] = { "AYUV", "NV12", "NV21", "I420", "YV12" };
644 GR_STATIC_ASSERT(SK_ARRAY_COUNT(kYUVFormatNames) == kLast_YUVFormat+1);
645
646 SkPaint textPaint;
647 textPaint.setTextAlign(SkPaint::kLeft_Align);
648 sk_tool_utils::set_portable_typeface(&textPaint, nullptr, SkFontStyle::Bold());
649 textPaint.setTextSize(16);
650
651 SkRect textRect;
652 SkString rowLabel;
653
654 rowLabel.printf("%s", kYUVFormatNames[yuvFormat]);
655 textPaint.measureText(rowLabel.c_str(), rowLabel.size(), &textRect);
656 y += kTileWidthHeight/2 + textRect.height()/2;
657
658 canvas->drawText(rowLabel.c_str(), rowLabel.size(), 0, y, textPaint);
659}
660
661namespace skiagm {
662
663// This GM creates an opaque and transparent bitmap, extracts the planes and then recombines
664// them into various YUV formats. It then renders the results in the grid:
665//
666// JPEG 601 709
667// Transparent Opaque Transparent Opaque Transparent Opaque
668// AYUV
669// NV12
670// NV21
671// I420
672// YV12
673class WackyYUVFormatsGM : public GM {
674public:
675 WackyYUVFormatsGM() {
676 this->setBGColor(0xFFCCCCCC);
677 }
678
679protected:
680
681 SkString onShortName() override {
682 return SkString("wacky_yuv_formats");
683 }
684
685 SkISize onISize() override {
686 int numCols = 2 * (kLastEnum_SkYUVColorSpace + 1); // opacity x color-space
687 int numRows = 1 + (kLast_YUVFormat + 1); // origin + # yuv formats
688 return SkISize::Make(kLabelWidth + numCols * (kTileWidthHeight + kPad),
689 kLabelHeight + numRows * (kTileWidthHeight + kPad));
690 }
691
692 void onOnceBeforeDraw() override {
693 SkPoint origin = { kTileWidthHeight/2.0f, kTileWidthHeight/2.0f };
694 float outerRadius = kTileWidthHeight/2.0f - 20.0f;
695 float innerRadius = 20.0f;
696
697 {
698 // transparent
699 SkTDArray<SkRect> circles;
700 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 5, &circles);
701 fOriginalBMs[0] = make_bitmap(path, circles, false);
702 }
703
704 {
705 // opaque
706 SkTDArray<SkRect> circles;
707 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 7, &circles);
708 fOriginalBMs[1] = make_bitmap(path, circles, true);
709 }
710
711 for (bool opaque : { false, true }) {
712 for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
713 PlaneData planes;
714 extract_planes(fOriginalBMs[opaque], (SkYUVColorSpace) cs, &planes);
715
716 for (int format = AYUV_YUVFormat; format <= kLast_YUVFormat; ++format) {
717 SkBitmap resultBMs[4];
718 SkYUVAIndex yuvaIndices[4];
719 create_YUV(planes, (YUVFormat) format, resultBMs, yuvaIndices, opaque);
720
721 fImages[opaque][cs][format] = make_yuv_gen_image(fOriginalBMs[opaque].info(),
722 (YUVFormat) format,
723 (SkYUVColorSpace) cs,
724 yuvaIndices,
725 resultBMs);
726 }
727 }
728 }
729 }
730
731 void onDraw(SkCanvas* canvas) override {
732 int x = kLabelWidth;
733 for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
734 for (int opaque : { 0, 1 }) {
735 int y = kLabelHeight;
736
737 draw_col_label(canvas, x+kTileWidthHeight/2, cs, opaque);
738
739 canvas->drawBitmap(fOriginalBMs[opaque], x, y);
740 y += kTileWidthHeight + kPad;
741
742 for (int format = AYUV_YUVFormat; format <= kLast_YUVFormat; ++format) {
743 draw_row_label(canvas, y, format);
744 canvas->drawImage(fImages[opaque][cs][format], x, y);
745
746 y += kTileWidthHeight + kPad;
747 }
748
749 x += kTileWidthHeight + kPad;
750 }
751 }
752 }
753
754private:
755 SkBitmap fOriginalBMs[2];
756 sk_sp<SkImage> fImages[2][kLastEnum_SkYUVColorSpace+1][kLast_YUVFormat+1];
757
758 typedef GM INHERITED;
759};
760
761//////////////////////////////////////////////////////////////////////////////
762
763DEF_GM(return new WackyYUVFormatsGM;)
764}