blob: 0cc0a21a2cac6f613c198e1d5de72cb98b8491be [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
Robert Phillips51c89e42018-10-05 13:30:43 -040016#if SK_SUPPORT_GPU
17#include "GrBackendSurface.h"
18#include "GrContextPriv.h"
19#include "GrGpu.h"
20#endif
21
Robert Phillipsbfa76f22018-10-03 12:12:26 -040022static const int kTileWidthHeight = 128;
23static const int kLabelWidth = 64;
24static const int kLabelHeight = 32;
25static const int kPad = 1;
Robert Phillipsbfa76f22018-10-03 12:12:26 -040026
27enum YUVFormat {
28 // 4:4:4 formats, 32 bpp
Jim Van Verth976a6b02018-10-17 15:27:19 -040029 kAYUV_YUVFormat, // 8-bit YUVA values all interleaved
Robert Phillipsbfa76f22018-10-03 12:12:26 -040030
31 // 4:2:0 formats, 12 bpp
32 kNV12_YUVFormat, // 8-bit Y plane + 2x2 down sampled interleaved U/V planes
33 kNV21_YUVFormat, // same as kNV12 but w/ U/V reversed in the interleaved plane
34
35 kI420_YUVFormat, // 8-bit Y plane + 2x2 down sampled U and V planes
36 kYV12_YUVFormat, // 8-bit Y plane + 2x2 down sampled V and U planes
37
38 kLast_YUVFormat = kYV12_YUVFormat
39};
40
41// All the planes we need to construct the various YUV formats
42struct PlaneData {
43 SkBitmap fYFull;
44 SkBitmap fUFull;
45 SkBitmap fVFull;
46 SkBitmap fAFull;
47 SkBitmap fUQuarter; // 2x2 downsampled U channel
48 SkBitmap fVQuarter; // 2x2 downsampled V channel
49};
50
51// Add a portion of a circle to 'path'. The points 'o1' and 'o2' are on the border of the circle
52// and have tangents 'v1' and 'v2'.
53static void add_arc(SkPath* path,
54 const SkPoint& o1, const SkVector& v1,
55 const SkPoint& o2, const SkVector& v2,
56 SkTDArray<SkRect>* circles, bool takeLongWayRound) {
57
58 SkVector v3 = { -v1.fY, v1.fX };
59 SkVector v4 = { v2.fY, -v2.fX };
60
61 SkScalar t = ((o2.fX - o1.fX) * v4.fY - (o2.fY - o1.fY) * v4.fX) / v3.cross(v4);
62 SkPoint center = { o1.fX + t * v3.fX, o1.fY + t * v3.fY };
63
64 SkRect r = { center.fX - t, center.fY - t, center.fX + t, center.fY + t };
65
66 if (circles) {
67 circles->push_back(r);
68 }
69
70 SkVector startV = o1 - center, endV = o2 - center;
71 startV.normalize();
72 endV.normalize();
73
74 SkScalar startDeg = SkRadiansToDegrees(SkScalarATan2(startV.fY, startV.fX));
75 SkScalar endDeg = SkRadiansToDegrees(SkScalarATan2(endV.fY, endV.fX));
76
77 startDeg += 360.0f;
78 startDeg = fmodf(startDeg, 360.0f);
79
80 endDeg += 360.0f;
81 endDeg = fmodf(endDeg, 360.0f);
82
83 if (endDeg < startDeg) {
84 endDeg += 360.0f;
85 }
86
87 SkScalar sweepDeg = SkTAbs(endDeg - startDeg);
88 if (!takeLongWayRound) {
89 sweepDeg = sweepDeg - 360;
90 }
91
92 path->arcTo(r, startDeg, sweepDeg, false);
93}
94
95static SkPath create_splat(const SkPoint& o, SkScalar innerRadius, SkScalar outerRadius,
96 SkScalar ratio, int numLobes, SkTDArray<SkRect>* circles) {
97 if (numLobes <= 1) {
98 return SkPath();
99 }
100
101 SkPath p;
102
103 int numDivisions = 2 * numLobes;
104 SkScalar fullLobeDegrees = 360.0f / numLobes;
105 SkScalar outDegrees = ratio * fullLobeDegrees / (ratio + 1.0f);
106 SkScalar innerDegrees = fullLobeDegrees / (ratio + 1.0f);
107 SkMatrix outerStep, innerStep;
108 outerStep.setRotate(outDegrees);
109 innerStep.setRotate(innerDegrees);
110 SkVector curV = SkVector::Make(0.0f, 1.0f);
111
112 if (circles) {
113 circles->push_back(SkRect::MakeLTRB(o.fX - innerRadius, o.fY - innerRadius,
114 o.fX + innerRadius, o.fY + innerRadius));
115 }
116
117 p.moveTo(o.fX + innerRadius * curV.fX, o.fY + innerRadius * curV.fY);
118
119 for (int i = 0; i < numDivisions; ++i) {
120
121 SkVector nextV;
122 if (0 == (i % 2)) {
123 nextV = outerStep.mapVector(curV.fX, curV.fY);
124
125 SkPoint top = SkPoint::Make(o.fX + outerRadius * curV.fX,
126 o.fY + outerRadius * curV.fY);
127 SkPoint nextTop = SkPoint::Make(o.fX + outerRadius * nextV.fX,
128 o.fY + outerRadius * nextV.fY);
129
130 p.lineTo(top);
131 add_arc(&p, top, curV, nextTop, nextV, circles, true);
132 } else {
133 nextV = innerStep.mapVector(curV.fX, curV.fY);
134
135 SkPoint bot = SkPoint::Make(o.fX + innerRadius * curV.fX,
136 o.fY + innerRadius * curV.fY);
137 SkPoint nextBot = SkPoint::Make(o.fX + innerRadius * nextV.fX,
138 o.fY + innerRadius * nextV.fY);
139
140 p.lineTo(bot);
141 add_arc(&p, bot, curV, nextBot, nextV, nullptr, false);
142 }
143
144 curV = nextV;
145 }
146
147 p.close();
148
149 return p;
150}
151
152static SkBitmap make_bitmap(const SkPath& path, const SkTDArray<SkRect>& circles, bool opaque) {
Robert Phillips94ade752018-10-09 12:32:31 -0400153 const SkColor kGreen = sk_tool_utils::color_to_565(SkColorSetARGB(0xFF, 178, 240, 104));
154 const SkColor kBlue = sk_tool_utils::color_to_565(SkColorSetARGB(0xFF, 173, 167, 252));
155 const SkColor kYellow = sk_tool_utils::color_to_565(SkColorSetARGB(0xFF, 255, 221, 117));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400156
Robert Phillips1c7062d2018-10-04 10:44:53 -0400157 SkImageInfo ii = SkImageInfo::MakeN32(kTileWidthHeight, kTileWidthHeight, kPremul_SkAlphaType);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400158
159 SkBitmap bm;
160 bm.allocPixels(ii);
161
162 std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirectN32(ii.width(), ii.height(),
163 (SkPMColor*)bm.getPixels(),
164 bm.rowBytes());
165
166 canvas->clear(opaque ? kGreen : SK_ColorTRANSPARENT);
167
168 SkPaint paint;
169 paint.setAntiAlias(false); // serialize-8888 doesn't seem to work well w/ partial transparency
170 paint.setColor(kBlue);
171
172 canvas->drawPath(path, paint);
173
174 paint.setColor(opaque ? kYellow : SK_ColorTRANSPARENT);
175 paint.setBlendMode(SkBlendMode::kSrc);
176 for (int i = 0; i < circles.count(); ++i) {
177 SkRect r = circles[i];
178 r.inset(r.width()/4, r.height()/4);
179 canvas->drawOval(r, paint);
180 }
181
182 return bm;
183}
184
Robert Phillips1c7062d2018-10-04 10:44:53 -0400185static void convert_rgba_to_yuva_601_shared(SkColor col, uint8_t yuv[4],
186 uint8_t off, uint8_t range) {
187 static const float Kr = 0.299f;
188 static const float Kb = 0.114f;
189 static const float Kg = 1.0f - Kr - Kb;
190
191 float r = SkColorGetR(col) / 255.0f;
192 float g = SkColorGetG(col) / 255.0f;
193 float b = SkColorGetB(col) / 255.0f;
194
195 float Ey = Kr * r + Kg * g + Kb * b;
196 float Ecb = (b - Ey) / 1.402f;
197 float Ecr = (r - Ey) / 1.772;
198
199 yuv[0] = SkScalarRoundToInt( range * Ey + off );
200 yuv[1] = SkScalarRoundToInt( 224 * Ecb + 128 );
201 yuv[2] = SkScalarRoundToInt( 224 * Ecr + 128 );
202 yuv[3] = SkColorGetA(col);
203}
204
205static void convert_rgba_to_yuva_jpeg(SkColor col, uint8_t yuv[4]) {
206 // full swing from 0..255
207 convert_rgba_to_yuva_601_shared(col, yuv, 0, 255);
208}
209
210static void convert_rgba_to_yuva_601(SkColor col, uint8_t yuv[4]) {
211 // partial swing from 16..235
212 convert_rgba_to_yuva_601_shared(col, yuv, 16, 219);
213
214}
215
216static void convert_rgba_to_yuva_709(SkColor col, uint8_t yuv[4]) {
217 static const float Kr = 0.2126f;
218 static const float Kb = 0.0722f;
219 static const float Kg = 1.0f - Kr - Kb;
220
221 float r = SkColorGetR(col) / 255.0f;
222 float g = SkColorGetG(col) / 255.0f;
223 float b = SkColorGetB(col) / 255.0f;
224
225 float Ey = Kr * r + Kg * g + Kb * b;
226 float Ecb = (b - Ey) / 1.8556f;
227 float Ecr = (r - Ey) / 1.5748;
228
229 yuv[0] = SkScalarRoundToInt( 219 * Ey + 16 );
230 yuv[1] = SkScalarRoundToInt( 224 * Ecb + 128 );
231 yuv[2] = SkScalarRoundToInt( 224 * Ecr + 128 );
232
233 yuv[3] = SkColorGetA(col);
234}
235
236
237static SkPMColor convert_yuva_to_rgba_jpeg(uint8_t y, uint8_t u, uint8_t v, uint8_t a) {
238 int c = y;
239 int d = u - 128;
240 int e = v - 128;
241
242 uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.0f * c + 1.402f * e ),
243 0, 255);
244 uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.0f * c - (0.344136f * d) - (0.714136f * e)),
245 0, 255);
246 uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.0f * c + 1.773f * d ),
247 0, 255);
248
249 return SkPremultiplyARGBInline(a, r, g, b);
250}
251
252static SkPMColor convert_yuva_to_rgba_601(uint8_t y, uint8_t u, uint8_t v, uint8_t a) {
253 int c = y - 16;
254 int d = u - 128;
255 int e = v - 128;
256
257 uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.164f * c + 1.596f * e ), 0, 255);
258 uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.164f * c - (0.391f * d) - (0.813f * e)), 0, 255);
259 uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.164f * c + 2.018f * d ), 0, 255);
260
261 return SkPremultiplyARGBInline(a, r, g, b);
262}
263
264static SkPMColor convert_yuva_to_rgba_709(uint8_t y, uint8_t u, uint8_t v, uint8_t a) {
265 int c = y - 16;
266 int d = u - 128;
267 int e = v - 128;
268
269 uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.164f * c + 1.793f * e ), 0, 255);
270 uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.164f * c - (0.213f * d) - (0.533f * e)), 0, 255);
271 uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.164f * c + 2.112f * d ), 0, 255);
272
273 return SkPremultiplyARGBInline(a, r, g, b);
274}
275
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400276static void extract_planes(const SkBitmap& bm, SkYUVColorSpace yuvColorSpace, PlaneData* planes) {
277 SkASSERT(!(bm.width() % 2));
278 SkASSERT(!(bm.height() % 2));
279
280 planes->fYFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
281 planes->fUFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
282 planes->fVFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
283 planes->fAFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
284 planes->fUQuarter.allocPixels(SkImageInfo::MakeA8(bm.width()/2, bm.height()/2));
285 planes->fVQuarter.allocPixels(SkImageInfo::MakeA8(bm.width()/2, bm.height()/2));
286
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400287 for (int y = 0; y < bm.height(); ++y) {
288 for (int x = 0; x < bm.width(); ++x) {
289 SkColor col = bm.getColor(x, y);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400290
Robert Phillips1c7062d2018-10-04 10:44:53 -0400291 uint8_t yuva[4];
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400292
Robert Phillips1c7062d2018-10-04 10:44:53 -0400293 if (kJPEG_SkYUVColorSpace == yuvColorSpace) {
294 convert_rgba_to_yuva_jpeg(col, yuva);
295 } else if (kRec601_SkYUVColorSpace == yuvColorSpace) {
296 convert_rgba_to_yuva_601(col, yuva);
297 } else {
298 SkASSERT(kRec709_SkYUVColorSpace == yuvColorSpace);
299 convert_rgba_to_yuva_709(col, yuva);
300 }
301
302 *planes->fYFull.getAddr8(x, y) = yuva[0];
303 *planes->fUFull.getAddr8(x, y) = yuva[1];
304 *planes->fVFull.getAddr8(x, y) = yuva[2];
305 *planes->fAFull.getAddr8(x, y) = yuva[3];
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400306 }
307 }
308
309 for (int y = 0; y < bm.height()/2; ++y) {
310 for (int x = 0; x < bm.width()/2; ++x) {
311 uint32_t uAccum = 0, vAccum = 0;
312
313 uAccum += *planes->fUFull.getAddr8(2*x, 2*y);
314 uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y);
315 uAccum += *planes->fUFull.getAddr8(2*x, 2*y+1);
316 uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y+1);
317
318 *planes->fUQuarter.getAddr8(x, y) = uAccum / 4.0f;
319
320 vAccum += *planes->fVFull.getAddr8(2*x, 2*y);
321 vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y);
322 vAccum += *planes->fVFull.getAddr8(2*x, 2*y+1);
323 vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y+1);
324
Robert Phillips1c7062d2018-10-04 10:44:53 -0400325 *planes->fVQuarter.getAddr8(x, y) = vAccum / 4.0f;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400326 }
327 }
328}
329
330// Recombine the separate planes into some YUV format
331static void create_YUV(const PlaneData& planes, YUVFormat yuvFormat,
332 SkBitmap resultBMs[], SkYUVAIndex yuvaIndices[4], bool opaque) {
333 int nextLayer = 0;
334
335 switch (yuvFormat) {
Jim Van Verth976a6b02018-10-17 15:27:19 -0400336 case kAYUV_YUVFormat: {
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400337 SkBitmap yuvaFull;
338
Robert Phillips1c7062d2018-10-04 10:44:53 -0400339 yuvaFull.allocPixels(SkImageInfo::MakeN32(planes.fYFull.width(), planes.fYFull.height(),
340 kUnpremul_SkAlphaType));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400341
342 for (int y = 0; y < planes.fYFull.height(); ++y) {
343 for (int x = 0; x < planes.fYFull.width(); ++x) {
344
345 uint8_t Y = *planes.fYFull.getAddr8(x, y);
346 uint8_t U = *planes.fUFull.getAddr8(x, y);
347 uint8_t V = *planes.fVFull.getAddr8(x, y);
348 uint8_t A = *planes.fAFull.getAddr8(x, y);
349
350 // NOT premul!
Robert Phillips1c7062d2018-10-04 10:44:53 -0400351 *yuvaFull.getAddr32(x, y) = SkColorSetARGB(A, Y, U, V);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400352 }
353 }
354
355 resultBMs[nextLayer++] = yuvaFull;
356
357 yuvaIndices[0].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400358 yuvaIndices[0].fChannel = SkColorChannel::kR;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400359 yuvaIndices[1].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400360 yuvaIndices[1].fChannel = SkColorChannel::kG;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400361 yuvaIndices[2].fIndex = 0;
Robert Phillips1c7062d2018-10-04 10:44:53 -0400362 yuvaIndices[2].fChannel = SkColorChannel::kB;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400363 yuvaIndices[3].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400364 yuvaIndices[3].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400365 break;
366 }
367 case kNV12_YUVFormat: {
368 SkBitmap uvQuarter;
369
Robert Phillips1c7062d2018-10-04 10:44:53 -0400370 // There isn't a RG color type. Approx w/ RGBA.
371 uvQuarter.allocPixels(SkImageInfo::MakeN32(planes.fYFull.width()/2,
372 planes.fYFull.height()/2,
373 kUnpremul_SkAlphaType));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400374
375 for (int y = 0; y < planes.fYFull.height()/2; ++y) {
376 for (int x = 0; x < planes.fYFull.width()/2; ++x) {
Robert Phillips1c7062d2018-10-04 10:44:53 -0400377 uint8_t U = *planes.fUQuarter.getAddr8(x, y);
378 uint8_t V = *planes.fVQuarter.getAddr8(x, y);
379
380 // NOT premul!
381 *uvQuarter.getAddr32(x, y) = SkColorSetARGB(0, U, V, 0);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400382 }
383 }
384
385 resultBMs[nextLayer++] = planes.fYFull;
386 resultBMs[nextLayer++] = uvQuarter;
387
388 yuvaIndices[0].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400389 yuvaIndices[0].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400390 yuvaIndices[1].fIndex = 1;
Robert Phillips66a97342018-10-04 09:10:29 -0400391 yuvaIndices[1].fChannel = SkColorChannel::kR;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400392 yuvaIndices[2].fIndex = 1;
Robert Phillips66a97342018-10-04 09:10:29 -0400393 yuvaIndices[2].fChannel = SkColorChannel::kG;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400394 break;
395 }
396 case kNV21_YUVFormat: {
397 SkBitmap vuQuarter;
398
Robert Phillips1c7062d2018-10-04 10:44:53 -0400399 // There isn't a RG color type. Approx w/ RGBA.
400 vuQuarter.allocPixels(SkImageInfo::MakeN32(planes.fYFull.width()/2,
401 planes.fYFull.height()/2,
402 kUnpremul_SkAlphaType));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400403
404 for (int y = 0; y < planes.fYFull.height()/2; ++y) {
405 for (int x = 0; x < planes.fYFull.width()/2; ++x) {
Robert Phillips1c7062d2018-10-04 10:44:53 -0400406 uint8_t U = *planes.fUQuarter.getAddr8(x, y);
407 uint8_t V = *planes.fVQuarter.getAddr8(x, y);
408
409 // NOT premul!
410 *vuQuarter.getAddr32(x, y) = SkColorSetARGB(0, V, U, 0);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400411 }
412 }
413
414 resultBMs[nextLayer++] = planes.fYFull;
415 resultBMs[nextLayer++] = vuQuarter;
416
417 yuvaIndices[0].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400418 yuvaIndices[0].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400419 yuvaIndices[1].fIndex = 1;
Robert Phillips66a97342018-10-04 09:10:29 -0400420 yuvaIndices[1].fChannel = SkColorChannel::kG;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400421 yuvaIndices[2].fIndex = 1;
Robert Phillips66a97342018-10-04 09:10:29 -0400422 yuvaIndices[2].fChannel = SkColorChannel::kR;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400423 break;
424 }
425 case kI420_YUVFormat:
426 resultBMs[nextLayer++] = planes.fYFull;
427 resultBMs[nextLayer++] = planes.fUQuarter;
428 resultBMs[nextLayer++] = planes.fVQuarter;
429
430 yuvaIndices[0].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400431 yuvaIndices[0].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400432 yuvaIndices[1].fIndex = 1;
Robert Phillips66a97342018-10-04 09:10:29 -0400433 yuvaIndices[1].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400434 yuvaIndices[2].fIndex = 2;
Robert Phillips66a97342018-10-04 09:10:29 -0400435 yuvaIndices[2].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400436 break;
437 case kYV12_YUVFormat:
438 resultBMs[nextLayer++] = planes.fYFull;
439 resultBMs[nextLayer++] = planes.fVQuarter;
440 resultBMs[nextLayer++] = planes.fUQuarter;
441
442 yuvaIndices[0].fIndex = 0;
Robert Phillips66a97342018-10-04 09:10:29 -0400443 yuvaIndices[0].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400444 yuvaIndices[1].fIndex = 2;
Robert Phillips66a97342018-10-04 09:10:29 -0400445 yuvaIndices[1].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400446 yuvaIndices[2].fIndex = 1;
Robert Phillips66a97342018-10-04 09:10:29 -0400447 yuvaIndices[2].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400448 break;
449 }
450
Jim Van Verth976a6b02018-10-17 15:27:19 -0400451 if (kAYUV_YUVFormat != yuvFormat) {
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400452 if (opaque) {
453 yuvaIndices[3].fIndex = -1;
454 } else {
455 resultBMs[nextLayer] = planes.fAFull;
456
457 yuvaIndices[3].fIndex = nextLayer;
Robert Phillips66a97342018-10-04 09:10:29 -0400458 yuvaIndices[3].fChannel = SkColorChannel::kA;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400459 }
460 }
461
462}
463
Robert Phillips1c7062d2018-10-04 10:44:53 -0400464static uint8_t look_up(float x1, float y1, const SkBitmap& bm, SkColorChannel channel) {
465 uint8_t result;
466
467 int x = SkScalarFloorToInt(x1 * bm.width());
468 int y = SkScalarFloorToInt(y1 * bm.height());
469
470 if (kAlpha_8_SkColorType == bm.colorType()) {
471 SkASSERT(SkColorChannel::kA == channel);
472 result = *bm.getAddr8(x, y);
473 } else {
474 SkASSERT(kN32_SkColorType == bm.colorType());
475
476 switch (channel) {
477 case SkColorChannel::kR:
478 result = SkColorGetR(bm.getColor(x, y));
479 break;
480 case SkColorChannel::kG:
481 result = SkColorGetG(bm.getColor(x, y));
482 break;
483 case SkColorChannel::kB:
484 result = SkColorGetB(bm.getColor(x, y));
485 break;
486 case SkColorChannel::kA:
487 result = SkColorGetA(bm.getColor(x, y));
488 break;
489 }
490 }
491
492 return result;
493}
494
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400495class YUVGenerator : public SkImageGenerator {
496public:
497 YUVGenerator(const SkImageInfo& ii,
498 YUVFormat yuvFormat,
499 SkYUVColorSpace yuvColorSpace,
500 SkYUVAIndex yuvaIndices[4],
501 SkBitmap bitmaps[4])
502 : SkImageGenerator(ii)
503 , fYUVFormat(yuvFormat)
504 , fYUVColorSpace(yuvColorSpace) {
505 memcpy(fYUVAIndices, yuvaIndices, sizeof(fYUVAIndices));
506
507 bool used[4] = { false, false, false, false };
508 for (int i = 0; i < 4; ++i) {
509 if (yuvaIndices[i].fIndex >= 0) {
510 SkASSERT(yuvaIndices[i].fIndex < 4);
511 used[yuvaIndices[i].fIndex] = true;
512 } else {
513 SkASSERT(3 == i); // only the 'A' channel can be unspecified
514 }
515 }
516
517 for (int i = 0; i < 4; ++i) {
518 if (used[i]) {
519 fYUVBitmaps[i] = bitmaps[i];
520 }
521 }
522 }
523
524protected:
525 bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
526 const Options&) override {
527
528 if (kUnknown_SkColorType == fFlattened.colorType()) {
529 fFlattened.allocPixels(info);
530
531 for (int y = 0; y < info.height(); ++y) {
532 for (int x = 0; x < info.width(); ++x) {
533
Robert Phillips1c7062d2018-10-04 10:44:53 -0400534 float x1 = (x + 0.5f) / info.width();
535 float y1 = (y + 0.5f) / info.height();
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400536
Robert Phillips1c7062d2018-10-04 10:44:53 -0400537 uint8_t Y = look_up(x1, y1,
538 fYUVBitmaps[fYUVAIndices[0].fIndex],
539 fYUVAIndices[0].fChannel);
540
541 uint8_t U = look_up(x1, y1,
542 fYUVBitmaps[fYUVAIndices[1].fIndex],
543 fYUVAIndices[1].fChannel);
544
545
546 uint8_t V = look_up(x1, y1,
547 fYUVBitmaps[fYUVAIndices[2].fIndex],
548 fYUVAIndices[2].fChannel);
549
550 uint8_t A = 255;
551 if (fYUVAIndices[3].fIndex >= 0) {
552 A = look_up(x1, y1,
553 fYUVBitmaps[fYUVAIndices[3].fIndex],
554 fYUVAIndices[3].fChannel);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400555 }
556
557 // Making premul here.
Robert Phillips1c7062d2018-10-04 10:44:53 -0400558 switch (fYUVColorSpace) {
559 case kJPEG_SkYUVColorSpace:
560 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_jpeg(Y, U, V, A);
561 break;
562 case kRec601_SkYUVColorSpace:
563 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_601(Y, U, V, A);
564 break;
565 case kRec709_SkYUVColorSpace:
566 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_709(Y, U, V, A);
567 break;
568 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400569 }
570 }
571 }
572
573 return fFlattened.readPixels(info, pixels, rowBytes, 0, 0);
574 }
575
576 bool onQueryYUV8(SkYUVSizeInfo* size, SkYUVColorSpace* yuvColorSpace) const override {
577 if (kI420_YUVFormat != fYUVFormat && kYV12_YUVFormat != fYUVFormat) {
578 return false; // currently this API only supports planar formats
579 }
580
581 *yuvColorSpace = fYUVColorSpace;
582 size->fSizes[0].fWidth = fYUVBitmaps[fYUVAIndices[0].fIndex].width();
583 size->fSizes[0].fHeight = fYUVBitmaps[fYUVAIndices[0].fIndex].height();
584 size->fWidthBytes[0] = fYUVBitmaps[fYUVAIndices[0].fIndex].rowBytes();
585
586 size->fSizes[1].fWidth = fYUVBitmaps[fYUVAIndices[1].fIndex].width();
587 size->fSizes[1].fHeight = fYUVBitmaps[fYUVAIndices[1].fIndex].height();
588 size->fWidthBytes[1] = fYUVBitmaps[fYUVAIndices[1].fIndex].rowBytes();
589
590 size->fSizes[2].fWidth = fYUVBitmaps[fYUVAIndices[2].fIndex].width();
591 size->fSizes[2].fHeight = fYUVBitmaps[fYUVAIndices[2].fIndex].height();
592 size->fWidthBytes[2] = fYUVBitmaps[fYUVAIndices[2].fIndex].rowBytes();
593 return true;
594 }
595
596 bool onGetYUV8Planes(const SkYUVSizeInfo&, void* planes[3]) override {
597 planes[0] = fYUVBitmaps[fYUVAIndices[0].fIndex].getAddr(0, 0);
598 planes[1] = fYUVBitmaps[fYUVAIndices[1].fIndex].getAddr(0, 0);
599 planes[2] = fYUVBitmaps[fYUVAIndices[2].fIndex].getAddr(0, 0);
600 return true;
601 }
602
603private:
604 YUVFormat fYUVFormat;
605 SkYUVColorSpace fYUVColorSpace;
606 SkYUVAIndex fYUVAIndices[4];
607 SkBitmap fYUVBitmaps[4];
608 SkBitmap fFlattened;
609
610};
611
612static sk_sp<SkImage> make_yuv_gen_image(const SkImageInfo& ii,
613 YUVFormat yuvFormat,
614 SkYUVColorSpace yuvColorSpace,
615 SkYUVAIndex yuvaIndices[4],
616 SkBitmap bitmaps[]) {
617 std::unique_ptr<SkImageGenerator> gen(new YUVGenerator(ii, yuvFormat, yuvColorSpace,
618 yuvaIndices, bitmaps));
619
620 return SkImage::MakeFromGenerator(std::move(gen));
621}
622
623static void draw_col_label(SkCanvas* canvas, int x, int yuvColorSpace, bool opaque) {
624 static const char* kYUVColorSpaceNames[] = { "JPEG", "601", "709" };
625 GR_STATIC_ASSERT(SK_ARRAY_COUNT(kYUVColorSpaceNames) == kLastEnum_SkYUVColorSpace+1);
626
627 SkPaint textPaint;
628 textPaint.setTextAlign(SkPaint::kCenter_Align);
629 sk_tool_utils::set_portable_typeface(&textPaint, nullptr, SkFontStyle::Bold());
630 textPaint.setTextSize(16);
631
632 SkRect textRect;
633 SkString colLabel;
634
635 colLabel.printf("%s", kYUVColorSpaceNames[yuvColorSpace]);
636 textPaint.measureText(colLabel.c_str(), colLabel.size(), &textRect);
637 int y = textRect.height();
638
639 canvas->drawText(colLabel.c_str(), colLabel.size(), x, y, textPaint);
640
641 colLabel.printf("%s", opaque ? "Opaque" : "Transparent");
642 textPaint.measureText(colLabel.c_str(), colLabel.size(), &textRect);
643 y += textRect.height();
644
645 canvas->drawText(colLabel.c_str(), colLabel.size(), x, y, textPaint);
646}
647
648static void draw_row_label(SkCanvas* canvas, int y, int yuvFormat) {
649 static const char* kYUVFormatNames[] = { "AYUV", "NV12", "NV21", "I420", "YV12" };
650 GR_STATIC_ASSERT(SK_ARRAY_COUNT(kYUVFormatNames) == kLast_YUVFormat+1);
651
652 SkPaint textPaint;
653 textPaint.setTextAlign(SkPaint::kLeft_Align);
654 sk_tool_utils::set_portable_typeface(&textPaint, nullptr, SkFontStyle::Bold());
655 textPaint.setTextSize(16);
656
657 SkRect textRect;
658 SkString rowLabel;
659
660 rowLabel.printf("%s", kYUVFormatNames[yuvFormat]);
661 textPaint.measureText(rowLabel.c_str(), rowLabel.size(), &textRect);
662 y += kTileWidthHeight/2 + textRect.height()/2;
663
664 canvas->drawText(rowLabel.c_str(), rowLabel.size(), 0, y, textPaint);
665}
666
667namespace skiagm {
668
669// This GM creates an opaque and transparent bitmap, extracts the planes and then recombines
670// them into various YUV formats. It then renders the results in the grid:
671//
672// JPEG 601 709
673// Transparent Opaque Transparent Opaque Transparent Opaque
674// AYUV
675// NV12
676// NV21
677// I420
678// YV12
679class WackyYUVFormatsGM : public GM {
680public:
681 WackyYUVFormatsGM() {
682 this->setBGColor(0xFFCCCCCC);
683 }
684
685protected:
686
687 SkString onShortName() override {
688 return SkString("wacky_yuv_formats");
689 }
690
691 SkISize onISize() override {
692 int numCols = 2 * (kLastEnum_SkYUVColorSpace + 1); // opacity x color-space
693 int numRows = 1 + (kLast_YUVFormat + 1); // origin + # yuv formats
694 return SkISize::Make(kLabelWidth + numCols * (kTileWidthHeight + kPad),
695 kLabelHeight + numRows * (kTileWidthHeight + kPad));
696 }
697
698 void onOnceBeforeDraw() override {
699 SkPoint origin = { kTileWidthHeight/2.0f, kTileWidthHeight/2.0f };
700 float outerRadius = kTileWidthHeight/2.0f - 20.0f;
701 float innerRadius = 20.0f;
702
703 {
704 // transparent
705 SkTDArray<SkRect> circles;
706 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 5, &circles);
707 fOriginalBMs[0] = make_bitmap(path, circles, false);
708 }
709
710 {
711 // opaque
712 SkTDArray<SkRect> circles;
713 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 7, &circles);
714 fOriginalBMs[1] = make_bitmap(path, circles, true);
715 }
Robert Phillips51c89e42018-10-05 13:30:43 -0400716 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400717
Robert Phillips51c89e42018-10-05 13:30:43 -0400718 void createImages(GrContext* context) {
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400719 for (bool opaque : { false, true }) {
720 for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
721 PlaneData planes;
722 extract_planes(fOriginalBMs[opaque], (SkYUVColorSpace) cs, &planes);
723
Jim Van Verth976a6b02018-10-17 15:27:19 -0400724 for (int format = kAYUV_YUVFormat; format <= kLast_YUVFormat; ++format) {
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400725 SkBitmap resultBMs[4];
726 SkYUVAIndex yuvaIndices[4];
727 create_YUV(planes, (YUVFormat) format, resultBMs, yuvaIndices, opaque);
728
Robert Phillips51c89e42018-10-05 13:30:43 -0400729#if SK_SUPPORT_GPU
730 if (context) {
731 if (context->abandoned()) {
732 return;
733 }
734
735 GrGpu* gpu = context->contextPriv().getGpu();
736 if (!gpu) {
737 return;
738 }
739
740 bool used[4] = { false, false, false, false };
741 for (int i = 0; i < 4; ++i) {
742 if (yuvaIndices[i].fIndex >= 0) {
743 SkASSERT(yuvaIndices[i].fIndex < 4);
744 used[yuvaIndices[i].fIndex] = true;
745 }
746 }
747
748 GrBackendTexture yuvaTextures[4];
749
750 for (int i = 0; i < 4; ++i) {
751 if (!used[i]) {
752 continue;
753 }
754
755 yuvaTextures[i] = gpu->createTestingOnlyBackendTexture(
756 resultBMs[i].getPixels(),
757 resultBMs[i].width(),
758 resultBMs[i].height(),
759 resultBMs[i].colorType(),
760 false,
761 GrMipMapped::kNo,
762 resultBMs[i].rowBytes());
763 }
764
765 fImages[opaque][cs][format] = SkImage::MakeFromYUVATexturesCopy(
766 context,
767 (SkYUVColorSpace) cs,
768 yuvaTextures,
769 yuvaIndices,
770 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
771 kTopLeft_GrSurfaceOrigin);
772 } else
773#endif
774 {
775 fImages[opaque][cs][format] = make_yuv_gen_image(
776 fOriginalBMs[opaque].info(),
777 (YUVFormat) format,
778 (SkYUVColorSpace) cs,
779 yuvaIndices,
780 resultBMs);
781 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400782 }
783 }
784 }
785 }
786
787 void onDraw(SkCanvas* canvas) override {
Robert Phillips51c89e42018-10-05 13:30:43 -0400788 this->createImages(canvas->getGrContext());
789
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400790 int x = kLabelWidth;
791 for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
792 for (int opaque : { 0, 1 }) {
793 int y = kLabelHeight;
794
795 draw_col_label(canvas, x+kTileWidthHeight/2, cs, opaque);
796
797 canvas->drawBitmap(fOriginalBMs[opaque], x, y);
798 y += kTileWidthHeight + kPad;
799
Jim Van Verth976a6b02018-10-17 15:27:19 -0400800 for (int format = kAYUV_YUVFormat; format <= kLast_YUVFormat; ++format) {
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400801 draw_row_label(canvas, y, format);
802 canvas->drawImage(fImages[opaque][cs][format], x, y);
803
804 y += kTileWidthHeight + kPad;
805 }
806
807 x += kTileWidthHeight + kPad;
808 }
809 }
810 }
811
812private:
813 SkBitmap fOriginalBMs[2];
814 sk_sp<SkImage> fImages[2][kLastEnum_SkYUVColorSpace+1][kLast_YUVFormat+1];
815
816 typedef GM INHERITED;
817};
818
819//////////////////////////////////////////////////////////////////////////////
820
821DEF_GM(return new WackyYUVFormatsGM;)
822}