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"
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkBlendMode.h"
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkColor.h"
13 #include "include/core/SkColorFilter.h"
14 #include "include/core/SkColorPriv.h"
15 #include "include/core/SkColorSpace.h"
16 #include "include/core/SkFilterQuality.h"
17 #include "include/core/SkFont.h"
18 #include "include/core/SkFontStyle.h"
19 #include "include/core/SkFontTypes.h"
20 #include "include/core/SkImage.h"
21 #include "include/core/SkImageGenerator.h"
22 #include "include/core/SkImageInfo.h"
23 #include "include/core/SkMatrix.h"
24 #include "include/core/SkPaint.h"
25 #include "include/core/SkPath.h"
26 #include "include/core/SkPixmap.h"
27 #include "include/core/SkPoint.h"
28 #include "include/core/SkRect.h"
29 #include "include/core/SkRefCnt.h"
30 #include "include/core/SkScalar.h"
31 #include "include/core/SkSize.h"
32 #include "include/core/SkString.h"
33 #include "include/core/SkTypeface.h"
34 #include "include/core/SkTypes.h"
35 #include "include/core/SkYUVAIndex.h"
36 #include "include/core/SkYUVASizeInfo.h"
37 #include "include/gpu/GrBackendSurface.h"
38 #include "include/gpu/GrConfig.h"
39 #include "include/gpu/GrContext.h"
40 #include "include/gpu/GrTypes.h"
41 #include "include/private/GrTypesPriv.h"
42 #include "include/private/SkTArray.h"
43 #include "include/private/SkTDArray.h"
44 #include "include/private/SkTemplates.h"
45 #include "include/utils/SkTextUtils.h"
46 #include "src/gpu/GrContextPriv.h"
47 #include "src/gpu/GrGpu.h"
48 #include "tools/ToolUtils.h"
49
50 #include <math.h>
51 #include <string.h>
52 #include <initializer_list>
53 #include <memory>
54 #include <utility>
55
56 class GrRenderTargetContext;
57
58 static const int kTileWidthHeight = 128;
59 static const int kLabelWidth = 64;
60 static const int kLabelHeight = 32;
61 static const int kDomainPadding = 8;
62 static const int kPad = 1;
63
64 enum YUVFormat {
65 // 4:2:0 formats, 24 bpp
66 kP016_YUVFormat, // 16-bit Y plane + 2x2 down sampled interleaved U/V plane (2 textures)
67 // 4:2:0 formats, "15 bpp" (but really 24 bpp)
68 kP010_YUVFormat, // same as kP016 except "10 bpp". Note that it is the same memory layout
69 // except that the bottom 6 bits are zeroed out (2 textures)
70 // TODO: we're cheating a bit w/ P010 and just treating it as unorm 16. This means its
71 // fully saturated values are 65504 rather than 65535 (that is just .9995 out of 1.0 though).
72
73 // 4:4:4 formats, 64 bpp
74 kY416_YUVFormat, // 16-bit AVYU values all interleaved (1 texture)
75
76 // 4:4:4 formats, 32 bpp
77 kAYUV_YUVFormat, // 8-bit YUVA values all interleaved (1 texture)
78 kY410_YUVFormat, // AVYU w/ 10bpp for YUV and 2 for A all interleaved (1 texture)
79
80 // 4:2:0 formats, 12 bpp
81 kNV12_YUVFormat, // 8-bit Y plane + 2x2 down sampled interleaved U/V planes (2 textures)
82 kNV21_YUVFormat, // same as kNV12 but w/ U/V reversed in the interleaved texture (2 textures)
83
84 kI420_YUVFormat, // 8-bit Y plane + separate 2x2 down sampled U and V planes (3 textures)
85 kYV12_YUVFormat, // 8-bit Y plane + separate 2x2 down sampled V and U planes (3 textures)
86
87 kLast_YUVFormat = kYV12_YUVFormat
88 };
89
format_uses_16_bpp(YUVFormat yuvFormat)90 static bool format_uses_16_bpp(YUVFormat yuvFormat) {
91 return kP016_YUVFormat == yuvFormat ||
92 kP010_YUVFormat == yuvFormat ||
93 kY416_YUVFormat == yuvFormat;
94 }
95
format_has_builtin_alpha(YUVFormat yuvFormat)96 static bool format_has_builtin_alpha(YUVFormat yuvFormat) {
97 return kY416_YUVFormat == yuvFormat ||
98 kAYUV_YUVFormat == yuvFormat ||
99 kY410_YUVFormat == yuvFormat;
100 }
101
format_cant_be_represented_with_pixmaps(YUVFormat yuvFormat)102 static bool format_cant_be_represented_with_pixmaps(YUVFormat yuvFormat) {
103 return kP016_YUVFormat == yuvFormat || // bc missing SkColorType::kRG_1616 and kR_16
104 kP010_YUVFormat == yuvFormat || // bc missing SkColorType::kRG_1616 and kR_16
105 kY416_YUVFormat == yuvFormat || // bc missing SkColorType::kRGBA_16161616
106 kNV12_YUVFormat == yuvFormat || // bc missing SkColorType::kRG_88
107 kNV21_YUVFormat == yuvFormat; // bc missing SkColorType::kRG_88
108 }
109
110 // Helper to setup the SkYUVAIndex array correctly
111 // Skia allows the client to tack an additional alpha plane onto any of the standard opaque
112 // formats (via the addExtraAlpha) flag. In this case it is assumed to be a stand-alone single-
113 // channel plane.
setup_yuv_indices(YUVFormat yuvFormat,bool addExtraAlpha,SkYUVAIndex yuvaIndices[4])114 static void setup_yuv_indices(YUVFormat yuvFormat, bool addExtraAlpha, SkYUVAIndex yuvaIndices[4]) {
115 switch (yuvFormat) {
116 case kP016_YUVFormat: // fall through
117 case kP010_YUVFormat:
118 yuvaIndices[0].fIndex = 0;
119 yuvaIndices[0].fChannel = SkColorChannel::kR; // bc 16bit is stored in R16 format
120 yuvaIndices[1].fIndex = 1;
121 yuvaIndices[1].fChannel = SkColorChannel::kR;
122 yuvaIndices[2].fIndex = 1;
123 yuvaIndices[2].fChannel = SkColorChannel::kG;
124 if (addExtraAlpha) {
125 yuvaIndices[3].fIndex = 2;
126 yuvaIndices[3].fChannel = SkColorChannel::kR; // bc 16bit is stored in R16 format
127 } else {
128 yuvaIndices[3].fIndex = -1; // No alpha channel
129 }
130 break;
131 case kY416_YUVFormat:
132 SkASSERT(!addExtraAlpha); // this format already has an alpha channel
133 yuvaIndices[0].fIndex = 0;
134 yuvaIndices[0].fChannel = SkColorChannel::kG;
135 yuvaIndices[1].fIndex = 0;
136 yuvaIndices[1].fChannel = SkColorChannel::kB;
137 yuvaIndices[2].fIndex = 0;
138 yuvaIndices[2].fChannel = SkColorChannel::kR;
139 yuvaIndices[3].fIndex = 0;
140 yuvaIndices[3].fChannel = SkColorChannel::kA;
141 break;
142 case kAYUV_YUVFormat:
143 SkASSERT(!addExtraAlpha); // this format already has an alpha channel
144 yuvaIndices[0].fIndex = 0;
145 yuvaIndices[0].fChannel = SkColorChannel::kR;
146 yuvaIndices[1].fIndex = 0;
147 yuvaIndices[1].fChannel = SkColorChannel::kG;
148 yuvaIndices[2].fIndex = 0;
149 yuvaIndices[2].fChannel = SkColorChannel::kB;
150 yuvaIndices[3].fIndex = 0;
151 yuvaIndices[3].fChannel = SkColorChannel::kA;
152 break;
153 case kY410_YUVFormat:
154 SkASSERT(!addExtraAlpha); // this format already has an alpha channel
155 yuvaIndices[0].fIndex = 0;
156 yuvaIndices[0].fChannel = SkColorChannel::kG;
157 yuvaIndices[1].fIndex = 0;
158 yuvaIndices[1].fChannel = SkColorChannel::kB;
159 yuvaIndices[2].fIndex = 0;
160 yuvaIndices[2].fChannel = SkColorChannel::kR;
161 yuvaIndices[3].fIndex = 0;
162 yuvaIndices[3].fChannel = SkColorChannel::kA;
163 break;
164 case kNV12_YUVFormat:
165 yuvaIndices[0].fIndex = 0;
166 yuvaIndices[0].fChannel = SkColorChannel::kR;
167 yuvaIndices[1].fIndex = 1;
168 yuvaIndices[1].fChannel = SkColorChannel::kR;
169 yuvaIndices[2].fIndex = 1;
170 yuvaIndices[2].fChannel = SkColorChannel::kG;
171 if (addExtraAlpha) {
172 yuvaIndices[3].fIndex = 2;
173 yuvaIndices[3].fChannel = SkColorChannel::kA;
174 } else {
175 yuvaIndices[3].fIndex = -1; // No alpha channel
176 }
177 break;
178 case kNV21_YUVFormat:
179 yuvaIndices[0].fIndex = 0;
180 yuvaIndices[0].fChannel = SkColorChannel::kR;
181 yuvaIndices[1].fIndex = 1;
182 yuvaIndices[1].fChannel = SkColorChannel::kG;
183 yuvaIndices[2].fIndex = 1;
184 yuvaIndices[2].fChannel = SkColorChannel::kR;
185 if (addExtraAlpha) {
186 yuvaIndices[3].fIndex = 2;
187 yuvaIndices[3].fChannel = SkColorChannel::kA;
188 } else {
189 yuvaIndices[3].fIndex = -1; // No alpha channel
190 }
191 break;
192 case kI420_YUVFormat:
193 yuvaIndices[0].fIndex = 0;
194 yuvaIndices[0].fChannel = SkColorChannel::kR;
195 yuvaIndices[1].fIndex = 1;
196 yuvaIndices[1].fChannel = SkColorChannel::kR;
197 yuvaIndices[2].fIndex = 2;
198 yuvaIndices[2].fChannel = SkColorChannel::kR;
199 if (addExtraAlpha) {
200 yuvaIndices[3].fIndex = 3;
201 yuvaIndices[3].fChannel = SkColorChannel::kA;
202 } else {
203 yuvaIndices[3].fIndex = -1; // No alpha channel
204 }
205 break;
206 case kYV12_YUVFormat:
207 yuvaIndices[0].fIndex = 0;
208 yuvaIndices[0].fChannel = SkColorChannel::kR;
209 yuvaIndices[1].fIndex = 2;
210 yuvaIndices[1].fChannel = SkColorChannel::kR;
211 yuvaIndices[2].fIndex = 1;
212 yuvaIndices[2].fChannel = SkColorChannel::kR;
213 if (addExtraAlpha) {
214 yuvaIndices[3].fIndex = 3;
215 yuvaIndices[3].fChannel = SkColorChannel::kA;
216 } else {
217 yuvaIndices[3].fIndex = -1; // No alpha channel
218 }
219 break;
220 }
221 }
222
223 // All the planes we need to construct the various YUV formats
224 struct PlaneData {
225 SkBitmap fYFull;
226 SkBitmap fUFull;
227 SkBitmap fVFull;
228 SkBitmap fAFull;
229 SkBitmap fUQuarter; // 2x2 downsampled U channel
230 SkBitmap fVQuarter; // 2x2 downsampled V channel
231 };
232
233 // Add a portion of a circle to 'path'. The points 'o1' and 'o2' are on the border of the circle
234 // and have tangents 'v1' and 'v2'.
add_arc(SkPath * path,const SkPoint & o1,const SkVector & v1,const SkPoint & o2,const SkVector & v2,SkTDArray<SkRect> * circles,bool takeLongWayRound)235 static void add_arc(SkPath* path,
236 const SkPoint& o1, const SkVector& v1,
237 const SkPoint& o2, const SkVector& v2,
238 SkTDArray<SkRect>* circles, bool takeLongWayRound) {
239
240 SkVector v3 = { -v1.fY, v1.fX };
241 SkVector v4 = { v2.fY, -v2.fX };
242
243 SkScalar t = ((o2.fX - o1.fX) * v4.fY - (o2.fY - o1.fY) * v4.fX) / v3.cross(v4);
244 SkPoint center = { o1.fX + t * v3.fX, o1.fY + t * v3.fY };
245
246 SkRect r = { center.fX - t, center.fY - t, center.fX + t, center.fY + t };
247
248 if (circles) {
249 circles->push_back(r);
250 }
251
252 SkVector startV = o1 - center, endV = o2 - center;
253 startV.normalize();
254 endV.normalize();
255
256 SkScalar startDeg = SkRadiansToDegrees(SkScalarATan2(startV.fY, startV.fX));
257 SkScalar endDeg = SkRadiansToDegrees(SkScalarATan2(endV.fY, endV.fX));
258
259 startDeg += 360.0f;
260 startDeg = fmodf(startDeg, 360.0f);
261
262 endDeg += 360.0f;
263 endDeg = fmodf(endDeg, 360.0f);
264
265 if (endDeg < startDeg) {
266 endDeg += 360.0f;
267 }
268
269 SkScalar sweepDeg = SkTAbs(endDeg - startDeg);
270 if (!takeLongWayRound) {
271 sweepDeg = sweepDeg - 360;
272 }
273
274 path->arcTo(r, startDeg, sweepDeg, false);
275 }
276
create_splat(const SkPoint & o,SkScalar innerRadius,SkScalar outerRadius,SkScalar ratio,int numLobes,SkTDArray<SkRect> * circles)277 static SkPath create_splat(const SkPoint& o, SkScalar innerRadius, SkScalar outerRadius,
278 SkScalar ratio, int numLobes, SkTDArray<SkRect>* circles) {
279 if (numLobes <= 1) {
280 return SkPath();
281 }
282
283 SkPath p;
284
285 int numDivisions = 2 * numLobes;
286 SkScalar fullLobeDegrees = 360.0f / numLobes;
287 SkScalar outDegrees = ratio * fullLobeDegrees / (ratio + 1.0f);
288 SkScalar innerDegrees = fullLobeDegrees / (ratio + 1.0f);
289 SkMatrix outerStep, innerStep;
290 outerStep.setRotate(outDegrees);
291 innerStep.setRotate(innerDegrees);
292 SkVector curV = SkVector::Make(0.0f, 1.0f);
293
294 if (circles) {
295 circles->push_back(SkRect::MakeLTRB(o.fX - innerRadius, o.fY - innerRadius,
296 o.fX + innerRadius, o.fY + innerRadius));
297 }
298
299 p.moveTo(o.fX + innerRadius * curV.fX, o.fY + innerRadius * curV.fY);
300
301 for (int i = 0; i < numDivisions; ++i) {
302
303 SkVector nextV;
304 if (0 == (i % 2)) {
305 nextV = outerStep.mapVector(curV.fX, curV.fY);
306
307 SkPoint top = SkPoint::Make(o.fX + outerRadius * curV.fX,
308 o.fY + outerRadius * curV.fY);
309 SkPoint nextTop = SkPoint::Make(o.fX + outerRadius * nextV.fX,
310 o.fY + outerRadius * nextV.fY);
311
312 p.lineTo(top);
313 add_arc(&p, top, curV, nextTop, nextV, circles, true);
314 } else {
315 nextV = innerStep.mapVector(curV.fX, curV.fY);
316
317 SkPoint bot = SkPoint::Make(o.fX + innerRadius * curV.fX,
318 o.fY + innerRadius * curV.fY);
319 SkPoint nextBot = SkPoint::Make(o.fX + innerRadius * nextV.fX,
320 o.fY + innerRadius * nextV.fY);
321
322 p.lineTo(bot);
323 add_arc(&p, bot, curV, nextBot, nextV, nullptr, false);
324 }
325
326 curV = nextV;
327 }
328
329 p.close();
330
331 return p;
332 }
333
make_bitmap(SkColorType colorType,const SkPath & path,const SkTDArray<SkRect> & circles,bool opaque,bool padWithRed)334 static SkBitmap make_bitmap(SkColorType colorType, const SkPath& path,
335 const SkTDArray<SkRect>& circles, bool opaque, bool padWithRed) {
336 const SkColor kGreen = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 178, 240, 104));
337 const SkColor kBlue = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 173, 167, 252));
338 const SkColor kYellow = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 255, 221, 117));
339
340 int widthHeight = kTileWidthHeight + (padWithRed ? 2 * kDomainPadding : 0);
341
342 SkImageInfo ii = SkImageInfo::Make(widthHeight, widthHeight,
343 colorType, kPremul_SkAlphaType);
344
345 SkBitmap bm;
346 bm.allocPixels(ii);
347
348 std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirect(ii,
349 bm.getPixels(),
350 bm.rowBytes());
351 if (padWithRed) {
352 canvas->clear(SK_ColorRED);
353 canvas->translate(kDomainPadding, kDomainPadding);
354 canvas->clipRect(SkRect::MakeWH(kTileWidthHeight, kTileWidthHeight));
355 }
356 canvas->clear(opaque ? kGreen : SK_ColorTRANSPARENT);
357
358 SkPaint paint;
359 paint.setAntiAlias(false); // serialize-8888 doesn't seem to work well w/ partial transparency
360 paint.setColor(kBlue);
361
362 canvas->drawPath(path, paint);
363
364 paint.setColor(opaque ? kYellow : SK_ColorTRANSPARENT);
365 paint.setBlendMode(SkBlendMode::kSrc);
366 for (int i = 0; i < circles.count(); ++i) {
367 SkRect r = circles[i];
368 r.inset(r.width()/4, r.height()/4);
369 canvas->drawOval(r, paint);
370 }
371
372 return bm;
373 }
374
convert_rgba_to_yuva_601_shared(SkColor col,uint8_t yuv[4],uint8_t off,uint8_t range)375 static void convert_rgba_to_yuva_601_shared(SkColor col, uint8_t yuv[4],
376 uint8_t off, uint8_t range) {
377 static const float Kr = 0.299f;
378 static const float Kb = 0.114f;
379 static const float Kg = 1.0f - Kr - Kb;
380
381 float r = SkColorGetR(col) / 255.0f;
382 float g = SkColorGetG(col) / 255.0f;
383 float b = SkColorGetB(col) / 255.0f;
384
385 float Ey = Kr * r + Kg * g + Kb * b;
386 float Ecb = (b - Ey) / 1.402f;
387 float Ecr = (r - Ey) / 1.772;
388 SkASSERT(Ey >= 0.0f && Ey <= 1.0f);
389 SkASSERT(Ecb >= -0.5f && Ecb <= 0.5f);
390 SkASSERT(Ecr >= -0.5f && Ecr <= 0.5f);
391
392 yuv[0] = SkScalarRoundToInt( range * Ey + off );
393 yuv[1] = SkScalarRoundToInt( 224 * Ecb + 128 );
394 yuv[2] = SkScalarRoundToInt( 224 * Ecr + 128 );
395 yuv[3] = SkColorGetA(col);
396 }
397
convert_rgba_to_yuva_jpeg(SkColor col,uint8_t yuv[4])398 static void convert_rgba_to_yuva_jpeg(SkColor col, uint8_t yuv[4]) {
399 // full swing from 0..255
400 convert_rgba_to_yuva_601_shared(col, yuv, 0, 255);
401 }
402
convert_rgba_to_yuva_601(SkColor col,uint8_t yuv[4])403 static void convert_rgba_to_yuva_601(SkColor col, uint8_t yuv[4]) {
404 // partial swing from 16..235
405 convert_rgba_to_yuva_601_shared(col, yuv, 16, 219);
406
407 }
408
convert_rgba_to_yuva_709(SkColor col,uint8_t yuv[4])409 static void convert_rgba_to_yuva_709(SkColor col, uint8_t yuv[4]) {
410 static const float Kr = 0.2126f;
411 static const float Kb = 0.0722f;
412 static const float Kg = 1.0f - Kr - Kb;
413
414 float r = SkColorGetR(col) / 255.0f;
415 float g = SkColorGetG(col) / 255.0f;
416 float b = SkColorGetB(col) / 255.0f;
417
418 float Ey = Kr * r + Kg * g + Kb * b;
419 float Ecb = (b - Ey) / 1.8556f;
420 float Ecr = (r - Ey) / 1.5748;
421 SkASSERT(Ey >= 0.0f && Ey <= 1.0f);
422 SkASSERT(Ecb >= -0.5f && Ecb <= 0.5f);
423 SkASSERT(Ecr >= -0.5f && Ecr <= 0.5f);
424
425 yuv[0] = SkScalarRoundToInt( 219 * Ey + 16 );
426 yuv[1] = SkScalarRoundToInt( 224 * Ecb + 128 );
427 yuv[2] = SkScalarRoundToInt( 224 * Ecr + 128 );
428
429 yuv[3] = SkColorGetA(col);
430 }
431
432
convert_yuva_to_rgba_jpeg(uint8_t y,uint8_t u,uint8_t v,uint8_t a)433 static SkPMColor convert_yuva_to_rgba_jpeg(uint8_t y, uint8_t u, uint8_t v, uint8_t a) {
434 uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.0f * y + 1.402f * v - 0.703749f * 255),
435 0, 255);
436 uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.0f * y - (0.344136f * u) - (0.714136f * v) + 0.531211f * 255),
437 0, 255);
438 uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.0f * y + 1.772f * u - 0.889475f * 255),
439 0, 255);
440
441 SkPMColor c = SkPremultiplyARGBInline(a, b, g, r);
442 return c;
443 }
444
convert_yuva_to_rgba_601(uint8_t y,uint8_t u,uint8_t v,uint8_t a)445 static SkPMColor convert_yuva_to_rgba_601(uint8_t y, uint8_t u, uint8_t v, uint8_t a) {
446 uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.164f * y + 1.596f * v - 0.87075f * 255), 0, 255);
447 uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.164f * y - (0.391f * u) - (0.813f * v) + 0.52925f * 255), 0, 255);
448 uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.164f * y + 2.018f * u - 1.08175f * 255), 0, 255);
449
450 SkPMColor c = SkPremultiplyARGBInline(a, b, g, r);
451 return c;
452 }
453
convert_yuva_to_rgba_709(uint8_t y,uint8_t u,uint8_t v,uint8_t a)454 static SkPMColor convert_yuva_to_rgba_709(uint8_t y, uint8_t u, uint8_t v, uint8_t a) {
455 uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.164f * y + (1.793f * v) - 0.96925f * 255), 0, 255);
456 uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.164f * y - (0.213f * u) - (0.533f * v) + 0.30025f * 255), 0, 255);
457 uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.164f * y + (2.112f * u) - 1.12875f * 255), 0, 255);
458
459 SkPMColor c = SkPremultiplyARGBInline(a, b, g, r);
460 return c;
461 }
462
extract_planes(const SkBitmap & bm,SkYUVColorSpace yuvColorSpace,PlaneData * planes)463 static void extract_planes(const SkBitmap& bm, SkYUVColorSpace yuvColorSpace, PlaneData* planes) {
464 if (kIdentity_SkYUVColorSpace == yuvColorSpace) {
465 // To test the identity color space we use JPEG YUV planes
466 yuvColorSpace = kJPEG_SkYUVColorSpace;
467 }
468
469 SkASSERT(!(bm.width() % 2));
470 SkASSERT(!(bm.height() % 2));
471 planes->fYFull.allocPixels(SkImageInfo::Make(bm.width(), bm.height(), kGray_8_SkColorType,
472 kUnpremul_SkAlphaType));
473 planes->fUFull.allocPixels(SkImageInfo::Make(bm.width(), bm.height(), kGray_8_SkColorType,
474 kUnpremul_SkAlphaType));
475 planes->fVFull.allocPixels(SkImageInfo::Make(bm.width(), bm.height(), kGray_8_SkColorType,
476 kUnpremul_SkAlphaType));
477 planes->fAFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
478 planes->fUQuarter.allocPixels(SkImageInfo::Make(bm.width()/2, bm.height()/2,
479 kGray_8_SkColorType, kUnpremul_SkAlphaType));
480 planes->fVQuarter.allocPixels(SkImageInfo::Make(bm.width()/2, bm.height()/2,
481 kGray_8_SkColorType, kUnpremul_SkAlphaType));
482
483 for (int y = 0; y < bm.height(); ++y) {
484 for (int x = 0; x < bm.width(); ++x) {
485 SkColor col = bm.getColor(x, y);
486
487 uint8_t yuva[4];
488
489 if (kJPEG_SkYUVColorSpace == yuvColorSpace) {
490 convert_rgba_to_yuva_jpeg(col, yuva);
491 } else if (kRec601_SkYUVColorSpace == yuvColorSpace) {
492 convert_rgba_to_yuva_601(col, yuva);
493 } else {
494 SkASSERT(kRec709_SkYUVColorSpace == yuvColorSpace);
495 convert_rgba_to_yuva_709(col, yuva);
496 }
497
498 *planes->fYFull.getAddr8(x, y) = yuva[0];
499 *planes->fUFull.getAddr8(x, y) = yuva[1];
500 *planes->fVFull.getAddr8(x, y) = yuva[2];
501 *planes->fAFull.getAddr8(x, y) = yuva[3];
502 }
503 }
504
505 for (int y = 0; y < bm.height()/2; ++y) {
506 for (int x = 0; x < bm.width()/2; ++x) {
507 uint32_t uAccum = 0, vAccum = 0;
508
509 uAccum += *planes->fUFull.getAddr8(2*x, 2*y);
510 uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y);
511 uAccum += *planes->fUFull.getAddr8(2*x, 2*y+1);
512 uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y+1);
513
514 *planes->fUQuarter.getAddr8(x, y) = uAccum / 4.0f;
515
516 vAccum += *planes->fVFull.getAddr8(2*x, 2*y);
517 vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y);
518 vAccum += *planes->fVFull.getAddr8(2*x, 2*y+1);
519 vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y+1);
520
521 *planes->fVQuarter.getAddr8(x, y) = vAccum / 4.0f;
522 }
523 }
524 }
525
526 // Create a 2x2 downsampled SkBitmap. It is stored in an RGBA texture. It can optionally be
527 // uv (i.e., for P016, P010 and NV12) or vu (i.e., NV21).
make_quarter_2_channel(const SkBitmap & fullY,const SkBitmap & quarterU,const SkBitmap & quarterV,bool uv)528 static SkBitmap make_quarter_2_channel(const SkBitmap& fullY,
529 const SkBitmap& quarterU,
530 const SkBitmap& quarterV,
531 bool uv) {
532 SkBitmap result;
533
534 // There isn't a RG color type. Approx w/ RGBA.
535 result.allocPixels(SkImageInfo::Make(fullY.width()/2,
536 fullY.height()/2,
537 kRGBA_8888_SkColorType,
538 kUnpremul_SkAlphaType));
539
540 for (int y = 0; y < fullY.height()/2; ++y) {
541 for (int x = 0; x < fullY.width()/2; ++x) {
542 uint8_t u8 = *quarterU.getAddr8(x, y);
543 uint8_t v8 = *quarterV.getAddr8(x, y);
544
545 if (uv) {
546 // NOT premul!
547 // U and 0 swapped to match RGBA layout
548 *result.getAddr32(x, y) = SkColorSetARGB(0xFF, 0, v8, u8);
549 } else {
550 // NOT premul!
551 // V and 0 swapped to match RGBA layout
552 *result.getAddr32(x, y) = SkColorSetARGB(0xFF, 0, u8, v8);
553 }
554 }
555 }
556
557 return result;
558 }
559
560 // Recombine the separate planes into some YUV format
create_YUV(const PlaneData & planes,YUVFormat yuvFormat,SkBitmap resultBMs[],SkYUVAIndex yuvaIndices[4],bool opaque)561 static void create_YUV(const PlaneData& planes, YUVFormat yuvFormat,
562 SkBitmap resultBMs[], SkYUVAIndex yuvaIndices[4], bool opaque) {
563 int nextLayer = 0;
564
565 switch (yuvFormat) {
566 case kY416_YUVFormat: {
567 // Although this is 16 bpp, store the data in an 8 bpp SkBitmap
568 SkBitmap yuvaFull;
569
570 yuvaFull.allocPixels(SkImageInfo::Make(planes.fYFull.width(), planes.fYFull.height(),
571 kRGBA_8888_SkColorType, kUnpremul_SkAlphaType));
572
573 for (int y = 0; y < planes.fYFull.height(); ++y) {
574 for (int x = 0; x < planes.fYFull.width(); ++x) {
575
576 uint8_t Y = *planes.fYFull.getAddr8(x, y);
577 uint8_t U = *planes.fUFull.getAddr8(x, y);
578 uint8_t V = *planes.fVFull.getAddr8(x, y);
579 uint8_t A = *planes.fAFull.getAddr8(x, y);
580
581 // NOT premul!
582 // U and V swapped to match RGBA layout
583 SkColor c = SkColorSetARGB(A, U, Y, V);
584 *yuvaFull.getAddr32(x, y) = c;
585 }
586 }
587
588 resultBMs[nextLayer++] = yuvaFull;
589
590 setup_yuv_indices(yuvFormat, false, yuvaIndices);
591 break;
592 }
593 case kAYUV_YUVFormat: {
594 SkBitmap yuvaFull;
595
596 yuvaFull.allocPixels(SkImageInfo::Make(planes.fYFull.width(), planes.fYFull.height(),
597 kRGBA_8888_SkColorType, kUnpremul_SkAlphaType));
598
599 for (int y = 0; y < planes.fYFull.height(); ++y) {
600 for (int x = 0; x < planes.fYFull.width(); ++x) {
601
602 uint8_t Y = *planes.fYFull.getAddr8(x, y);
603 uint8_t U = *planes.fUFull.getAddr8(x, y);
604 uint8_t V = *planes.fVFull.getAddr8(x, y);
605 uint8_t A = *planes.fAFull.getAddr8(x, y);
606
607 // NOT premul!
608 // V and Y swapped to match RGBA layout
609 SkColor c = SkColorSetARGB(A, V, U, Y);
610 *yuvaFull.getAddr32(x, y) = c;
611 }
612 }
613
614 resultBMs[nextLayer++] = yuvaFull;
615
616 setup_yuv_indices(yuvFormat, false, yuvaIndices);
617 break;
618 }
619 case kY410_YUVFormat: {
620 SkBitmap yuvaFull;
621 uint32_t Y, U, V;
622 uint8_t A;
623
624 yuvaFull.allocPixels(SkImageInfo::Make(planes.fYFull.width(), planes.fYFull.height(),
625 kRGBA_1010102_SkColorType,
626 kUnpremul_SkAlphaType));
627
628 for (int y = 0; y < planes.fYFull.height(); ++y) {
629 for (int x = 0; x < planes.fYFull.width(); ++x) {
630
631 Y = SkScalarRoundToInt((*planes.fYFull.getAddr8(x, y) / 255.0f) * 1023.0f);
632 U = SkScalarRoundToInt((*planes.fUFull.getAddr8(x, y) / 255.0f) * 1023.0f);
633 V = SkScalarRoundToInt((*planes.fVFull.getAddr8(x, y) / 255.0f) * 1023.0f);
634 A = SkScalarRoundToInt((*planes.fAFull.getAddr8(x, y) / 255.0f) * 3.0f);
635
636 // NOT premul!
637 // AVYU but w/ V and U swapped to match RGBA layout
638 *yuvaFull.getAddr32(x, y) = (A << 30) | (U << 20) | (Y << 10) | (V << 0);
639 }
640 }
641
642 resultBMs[nextLayer++] = yuvaFull;
643
644 setup_yuv_indices(yuvFormat, false, yuvaIndices);
645 break;
646 }
647 case kP016_YUVFormat: // fall through
648 case kP010_YUVFormat: // fall through
649 case kNV12_YUVFormat: {
650 SkBitmap uvQuarter = make_quarter_2_channel(planes.fYFull,
651 planes.fUQuarter,
652 planes.fVQuarter, true);
653 resultBMs[nextLayer++] = planes.fYFull;
654 resultBMs[nextLayer++] = uvQuarter;
655
656 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
657 break;
658 }
659 case kNV21_YUVFormat: {
660 SkBitmap vuQuarter = make_quarter_2_channel(planes.fYFull,
661 planes.fUQuarter,
662 planes.fVQuarter, false);
663 resultBMs[nextLayer++] = planes.fYFull;
664 resultBMs[nextLayer++] = vuQuarter;
665
666 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
667 break;
668 }
669 case kI420_YUVFormat:
670 resultBMs[nextLayer++] = planes.fYFull;
671 resultBMs[nextLayer++] = planes.fUQuarter;
672 resultBMs[nextLayer++] = planes.fVQuarter;
673
674 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
675 break;
676 case kYV12_YUVFormat:
677 resultBMs[nextLayer++] = planes.fYFull;
678 resultBMs[nextLayer++] = planes.fVQuarter;
679 resultBMs[nextLayer++] = planes.fUQuarter;
680
681 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
682 break;
683 }
684
685 if (!format_has_builtin_alpha(yuvFormat) && !opaque) {
686 resultBMs[nextLayer] = planes.fAFull;
687 }
688 }
689
look_up(float x1,float y1,const SkBitmap & bm,SkColorChannel channel)690 static uint8_t look_up(float x1, float y1, const SkBitmap& bm, SkColorChannel channel) {
691 uint8_t result;
692
693 SkASSERT(x1 > 0 && x1 < 1.0f);
694 SkASSERT(y1 > 0 && y1 < 1.0f);
695 int x = SkScalarFloorToInt(x1 * bm.width());
696 int y = SkScalarFloorToInt(y1 * bm.height());
697
698 if (kAlpha_8_SkColorType == bm.colorType() || kGray_8_SkColorType == bm.colorType()) {
699 SkASSERT(SkColorChannel::kA == channel || SkColorChannel::kR == channel);
700 result = *bm.getAddr8(x, y);
701 } else if (kRGBA_8888_SkColorType == bm.colorType()) {
702 SkColor c = *bm.getAddr32(x, y);
703
704 switch (channel) {
705 case SkColorChannel::kR:
706 result = SkColorGetB(c);
707 break;
708 case SkColorChannel::kG:
709 result = SkColorGetG(c);
710 break;
711 case SkColorChannel::kB:
712 result = SkColorGetR(c);
713 break;
714 case SkColorChannel::kA:
715 result = SkColorGetA(c);
716 break;
717 }
718 } else {
719 SkASSERT(kRGBA_1010102_SkColorType == bm.colorType());
720
721 SkColor c = *bm.getAddr32(x, y);
722
723 switch (channel) {
724 case SkColorChannel::kR:
725 result = SkScalarRoundToInt(((c >> 0) & 0x3ff) * (255.0f/1023.0f));
726 break;
727 case SkColorChannel::kG:
728 result = SkScalarRoundToInt(((c >> 10) & 0x3ff) * (255.0f/1023.0f));
729 break;
730 case SkColorChannel::kB:
731 result = SkScalarRoundToInt(((c >> 20) & 0x3ff) * (255.0f/1023.0f));
732 break;
733 case SkColorChannel::kA:
734 result = SkScalarRoundToInt(((c >> 30) & 0x3) * (255.0f/3.0f));
735 break;
736 }
737 }
738
739 return result;
740 }
741
742 class YUVGenerator : public SkImageGenerator {
743 public:
YUVGenerator(const SkImageInfo & ii,SkYUVColorSpace yuvColorSpace,SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],SkBitmap bitmaps[SkYUVASizeInfo::kMaxCount])744 YUVGenerator(const SkImageInfo& ii,
745 SkYUVColorSpace yuvColorSpace,
746 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
747 SkBitmap bitmaps[SkYUVASizeInfo::kMaxCount])
748 : SkImageGenerator(ii)
749 , fYUVColorSpace(yuvColorSpace)
750 , fAllA8(true) {
751 memcpy(fYUVAIndices, yuvaIndices, sizeof(fYUVAIndices));
752
753 SkAssertResult(SkYUVAIndex::AreValidIndices(fYUVAIndices, &fNumBitmaps));
754 SkASSERT(fNumBitmaps > 0 && fNumBitmaps <= SkYUVASizeInfo::kMaxCount);
755
756 for (int i = 0; i < fNumBitmaps; ++i) {
757 fYUVBitmaps[i] = bitmaps[i];
758 if (kAlpha_8_SkColorType != fYUVBitmaps[i].colorType()) {
759 fAllA8 = false;
760 }
761 }
762 }
763
764 protected:
onGetPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,const Options &)765 bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
766 const Options&) override {
767
768 if (kUnknown_SkColorType == fFlattened.colorType()) {
769 fFlattened.allocPixels(info);
770 SkASSERT(kPremul_SkAlphaType == info.alphaType());
771
772 for (int y = 0; y < info.height(); ++y) {
773 for (int x = 0; x < info.width(); ++x) {
774
775 float x1 = (x + 0.5f) / info.width();
776 float y1 = (y + 0.5f) / info.height();
777
778 uint8_t Y = look_up(x1, y1,
779 fYUVBitmaps[fYUVAIndices[0].fIndex],
780 fYUVAIndices[0].fChannel);
781
782 uint8_t U = look_up(x1, y1,
783 fYUVBitmaps[fYUVAIndices[1].fIndex],
784 fYUVAIndices[1].fChannel);
785
786
787 uint8_t V = look_up(x1, y1,
788 fYUVBitmaps[fYUVAIndices[2].fIndex],
789 fYUVAIndices[2].fChannel);
790
791 uint8_t A = 255;
792 if (fYUVAIndices[3].fIndex >= 0) {
793 A = look_up(x1, y1,
794 fYUVBitmaps[fYUVAIndices[3].fIndex],
795 fYUVAIndices[3].fChannel);
796 }
797
798 // Making premul here.
799 switch (fYUVColorSpace) {
800 case kJPEG_SkYUVColorSpace:
801 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_jpeg(Y, U, V, A);
802 break;
803 case kRec601_SkYUVColorSpace:
804 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_601(Y, U, V, A);
805 break;
806 case kRec709_SkYUVColorSpace:
807 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_709(Y, U, V, A);
808 break;
809 case kIdentity_SkYUVColorSpace:
810 *fFlattened.getAddr32(x, y) = SkPremultiplyARGBInline(A, V, U, Y);
811 break;
812 }
813 }
814 }
815 }
816
817 return fFlattened.readPixels(info, pixels, rowBytes, 0, 0);
818 }
819
onQueryYUVA8(SkYUVASizeInfo * size,SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],SkYUVColorSpace * yuvColorSpace) const820 bool onQueryYUVA8(SkYUVASizeInfo* size,
821 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
822 SkYUVColorSpace* yuvColorSpace) const override {
823
824 // The onQueryYUVA8/onGetYUVA8Planes can only handle A8 planes
825 if (!fAllA8) {
826 return false;
827 }
828
829 memcpy(yuvaIndices, fYUVAIndices, sizeof(fYUVAIndices));
830 *yuvColorSpace = fYUVColorSpace;
831
832 int i = 0;
833 for ( ; i < fNumBitmaps; ++i) {
834 size->fSizes[i].fWidth = fYUVBitmaps[i].width();
835 size->fSizes[i].fHeight = fYUVBitmaps[i].height();
836 size->fWidthBytes[i] = fYUVBitmaps[i].rowBytes();
837 }
838 for ( ; i < SkYUVASizeInfo::kMaxCount; ++i) {
839 size->fSizes[i].fWidth = 0;
840 size->fSizes[i].fHeight = 0;
841 size->fWidthBytes[i] = 0;
842 }
843
844 return true;
845 }
846
onGetYUVA8Planes(const SkYUVASizeInfo &,const SkYUVAIndex[SkYUVAIndex::kIndexCount],void * planes[SkYUVASizeInfo::kMaxCount])847 bool onGetYUVA8Planes(const SkYUVASizeInfo&, const SkYUVAIndex[SkYUVAIndex::kIndexCount],
848 void* planes[SkYUVASizeInfo::kMaxCount]) override {
849 SkASSERT(fAllA8);
850 for (int i = 0; i < fNumBitmaps; ++i) {
851 planes[i] = fYUVBitmaps[i].getPixels();
852 }
853 return true;
854 }
855
856 private:
857 SkYUVColorSpace fYUVColorSpace;
858 SkYUVAIndex fYUVAIndices[SkYUVAIndex::kIndexCount];
859 int fNumBitmaps;
860 SkBitmap fYUVBitmaps[SkYUVASizeInfo::kMaxCount];
861 SkBitmap fFlattened;
862 bool fAllA8; // are all the SkBitmaps in "fYUVBitmaps" A8?
863
864 };
865
make_yuv_gen_image(const SkImageInfo & ii,SkYUVColorSpace yuvColorSpace,SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],SkBitmap bitmaps[])866 static sk_sp<SkImage> make_yuv_gen_image(const SkImageInfo& ii,
867 SkYUVColorSpace yuvColorSpace,
868 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
869 SkBitmap bitmaps[]) {
870 std::unique_ptr<SkImageGenerator> gen(new YUVGenerator(ii, yuvColorSpace,
871 yuvaIndices, bitmaps));
872
873 return SkImage::MakeFromGenerator(std::move(gen));
874 }
875
draw_col_label(SkCanvas * canvas,int x,int yuvColorSpace,bool opaque)876 static void draw_col_label(SkCanvas* canvas, int x, int yuvColorSpace, bool opaque) {
877 static const char* kYUVColorSpaceNames[] = { "JPEG", "601", "709", "Identity" };
878 GR_STATIC_ASSERT(SK_ARRAY_COUNT(kYUVColorSpaceNames) == kLastEnum_SkYUVColorSpace+1);
879
880 SkPaint paint;
881 SkFont font(ToolUtils::create_portable_typeface(nullptr, SkFontStyle::Bold()), 16);
882 font.setEdging(SkFont::Edging::kAlias);
883
884 SkRect textRect;
885 SkString colLabel;
886
887 colLabel.printf("%s", kYUVColorSpaceNames[yuvColorSpace]);
888 font.measureText(colLabel.c_str(), colLabel.size(), SkTextEncoding::kUTF8, &textRect);
889 int y = textRect.height();
890
891 SkTextUtils::DrawString(canvas, colLabel.c_str(), x, y, font, paint, SkTextUtils::kCenter_Align);
892
893 colLabel.printf("%s", opaque ? "Opaque" : "Transparent");
894
895 font.measureText(colLabel.c_str(), colLabel.size(), SkTextEncoding::kUTF8, &textRect);
896 y += textRect.height();
897
898 SkTextUtils::DrawString(canvas, colLabel.c_str(), x, y, font, paint, SkTextUtils::kCenter_Align);
899 }
900
draw_row_label(SkCanvas * canvas,int y,int yuvFormat)901 static void draw_row_label(SkCanvas* canvas, int y, int yuvFormat) {
902 static const char* kYUVFormatNames[] = {
903 "P016", "P010", "Y416", "AYUV", "Y410", "NV12", "NV21", "I420", "YV12"
904 };
905 GR_STATIC_ASSERT(SK_ARRAY_COUNT(kYUVFormatNames) == kLast_YUVFormat+1);
906
907 SkPaint paint;
908 SkFont font(ToolUtils::create_portable_typeface(nullptr, SkFontStyle::Bold()), 16);
909 font.setEdging(SkFont::Edging::kAlias);
910
911 SkRect textRect;
912 SkString rowLabel;
913
914 rowLabel.printf("%s", kYUVFormatNames[yuvFormat]);
915 font.measureText(rowLabel.c_str(), rowLabel.size(), SkTextEncoding::kUTF8, &textRect);
916 y += kTileWidthHeight/2 + textRect.height()/2;
917
918 canvas->drawString(rowLabel, 0, y, font, paint);
919 }
920
make_RG_88(const GrCaps * caps,const SkBitmap & bm,YUVFormat yuvFormat,SkAutoTMalloc<uint8_t> * pixels,GrBackendFormat * format,size_t * rowBytes)921 static void make_RG_88(const GrCaps* caps,
922 const SkBitmap& bm, YUVFormat yuvFormat,
923 SkAutoTMalloc<uint8_t>* pixels,
924 GrBackendFormat* format, size_t* rowBytes) {
925 SkASSERT(kNV12_YUVFormat == yuvFormat || kNV21_YUVFormat == yuvFormat);
926 SkASSERT(kRGBA_8888_SkColorType == bm.colorType()); // uv stored in rg
927
928 *rowBytes = bm.width() * 2 * sizeof(uint8_t);
929 pixels->reset(*rowBytes * bm.height());
930 uint8_t* currPixel = pixels->get();
931 for (int y = 0; y < bm.height(); ++y) {
932 for (int x = 0; x < bm.width(); ++x) {
933 SkColor color = bm.getColor(x, y);
934 uint8_t u8 = SkColorGetR(color);
935 uint8_t v8 = SkColorGetG(color);
936
937 currPixel[0] = u8;
938 currPixel[1] = v8;
939 currPixel += 2;
940 }
941 }
942 *format = caps->getDefaultBackendFormat(GrColorType::kRG_88, GrRenderable::kNo);
943 }
944
make_RG_1616(const GrCaps * caps,const SkBitmap & bm,YUVFormat yuvFormat,SkAutoTMalloc<uint8_t> * pixels,GrBackendFormat * format,size_t * rowBytes)945 static void make_RG_1616(const GrCaps* caps,
946 const SkBitmap& bm, YUVFormat yuvFormat,
947 SkAutoTMalloc<uint8_t>* pixels,
948 GrBackendFormat* format, size_t* rowBytes) {
949 SkASSERT(kP016_YUVFormat == yuvFormat || kP010_YUVFormat == yuvFormat);
950 SkASSERT(kRGBA_8888_SkColorType == bm.colorType()); // uv stored in rg
951
952 uint16_t u16, v16;
953 *rowBytes = bm.width() * 2 * sizeof(uint16_t);
954 pixels->reset(*rowBytes * bm.height());
955 uint16_t* currPixel = (uint16_t*) pixels->get();
956 for (int y = 0; y < bm.height(); ++y) {
957 for (int x = 0; x < bm.width(); ++x) {
958 SkColor color = bm.getColor(x, y);
959
960 if (kP016_YUVFormat == yuvFormat) {
961 u16 = SkScalarRoundToInt((SkColorGetR(color) / 255.0f) * 65535.0f);
962 v16 = SkScalarRoundToInt((SkColorGetG(color) / 255.0f) * 65535.0f);
963 } else {
964 u16 = SkScalarRoundToInt((SkColorGetR(color) / 255.0f) * 1023.0f);
965 v16 = SkScalarRoundToInt((SkColorGetG(color) / 255.0f) * 1023.0f);
966 u16 <<= 6;
967 v16 <<= 6;
968 }
969
970 currPixel[0] = u16;
971 currPixel[1] = v16;
972 currPixel += 2;
973 }
974 }
975
976 *format = caps->getDefaultBackendFormat(GrColorType::kRG_1616, GrRenderable::kNo);
977 }
978
make_RGBA_16(const GrCaps * caps,const SkBitmap & bm,YUVFormat yuvFormat,SkAutoTMalloc<uint8_t> * pixels,GrBackendFormat * format,size_t * rowBytes)979 static void make_RGBA_16(const GrCaps* caps,
980 const SkBitmap& bm,
981 YUVFormat yuvFormat,
982 SkAutoTMalloc<uint8_t>* pixels,
983 GrBackendFormat* format,
984 size_t* rowBytes) {
985 SkASSERT(kY416_YUVFormat == yuvFormat);
986 SkASSERT(kRGBA_8888_SkColorType == bm.colorType());
987
988 uint16_t y16, u16, v16, a16;
989 *rowBytes = 4 * sizeof(uint16_t) * bm.width();
990 pixels->reset(*rowBytes * bm.height());
991 uint16_t* currPixel = (uint16_t*) pixels->get();
992 for (int y = 0; y < bm.height(); ++y) {
993 for (int x = 0; x < bm.width(); ++x) {
994 SkColor color = bm.getColor(x, y);
995
996 y16 = SkScalarRoundToInt((SkColorGetR(color) / 255.0f) * 65535.0f);
997 u16 = SkScalarRoundToInt((SkColorGetG(color) / 255.0f) * 65535.0f);
998 v16 = SkScalarRoundToInt((SkColorGetB(color) / 255.0f) * 65535.0f);
999 a16 = SkScalarRoundToInt((SkColorGetA(color) / 255.0f) * 65535.0f);
1000
1001 currPixel[0] = y16;
1002 currPixel[1] = u16;
1003 currPixel[2] = v16;
1004 currPixel[3] = a16;
1005 currPixel += 4;
1006 }
1007 }
1008
1009 *format = caps->getDefaultBackendFormat(GrColorType::kRGBA_16161616, GrRenderable::kNo);
1010 return;
1011 }
1012
make_R_16(const GrCaps * caps,const SkBitmap & bm,YUVFormat yuvFormat,SkAutoTMalloc<uint8_t> * pixels,GrBackendFormat * format,size_t * rowBytes)1013 static void make_R_16(const GrCaps* caps,
1014 const SkBitmap& bm,
1015 YUVFormat yuvFormat,
1016 SkAutoTMalloc<uint8_t>* pixels,
1017 GrBackendFormat* format,
1018 size_t* rowBytes) {
1019 SkASSERT(kP016_YUVFormat == yuvFormat || kP010_YUVFormat == yuvFormat);
1020 SkASSERT(kGray_8_SkColorType == bm.colorType() || kAlpha_8_SkColorType == bm.colorType());
1021
1022 uint16_t y16;
1023 *rowBytes = sizeof(uint16_t) * bm.width();
1024 pixels->reset(*rowBytes * bm.height());
1025 uint16_t* currPixel = (uint16_t*) pixels->get();
1026 for (int y = 0; y < bm.height(); ++y) {
1027 for (int x = 0; x < bm.width(); ++x) {
1028 uint8_t y8 = *bm.getAddr8(x, y);
1029
1030 if (kP016_YUVFormat == yuvFormat) {
1031 y16 = SkScalarRoundToInt((y8 / 255.0f) * 65535.0f);
1032 } else {
1033 y16 = SkScalarRoundToInt((y8 / 255.0f) * 1023.0f);
1034 y16 <<= 6;
1035 }
1036
1037 currPixel[0] = y16;
1038 currPixel += 1;
1039 }
1040 }
1041
1042 *format = caps->getDefaultBackendFormat(GrColorType::kR_16, GrRenderable::kNo);
1043 }
1044
create_yuva_texture(GrContext * context,const SkBitmap & bm,SkYUVAIndex yuvaIndices[4],int texIndex,YUVFormat yuvFormat)1045 static GrBackendTexture create_yuva_texture(GrContext* context, const SkBitmap& bm,
1046 SkYUVAIndex yuvaIndices[4], int texIndex,
1047 YUVFormat yuvFormat) {
1048 SkASSERT(texIndex >= 0 && texIndex <= 3);
1049 int channelCount = 0;
1050 for (int i = 0; i < SkYUVAIndex::kIndexCount; ++i) {
1051 if (yuvaIndices[i].fIndex == texIndex) {
1052 ++channelCount;
1053 }
1054 }
1055
1056 GrBackendTexture tex;
1057
1058 if (format_uses_16_bpp(yuvFormat) || 2 == channelCount) {
1059 // Due to the limitations of SkPixmap these cases need to be handled separately
1060 const GrCaps* caps = context->priv().caps();
1061 GrGpu* gpu = context->priv().getGpu();
1062
1063 SkAutoTMalloc<uint8_t> pixels;
1064 GrBackendFormat format;
1065 size_t rowBytes;
1066
1067 if (2 == channelCount) {
1068 if (format_uses_16_bpp(yuvFormat)) {
1069 make_RG_1616(caps, bm, yuvFormat, &pixels, &format, &rowBytes);
1070 } else {
1071 make_RG_88(caps, bm, yuvFormat, &pixels, &format, &rowBytes);
1072 }
1073 } else {
1074 if (kRGBA_8888_SkColorType == bm.colorType()) {
1075 make_RGBA_16(caps, bm, yuvFormat, &pixels, &format, &rowBytes);
1076 } else {
1077 make_R_16(caps, bm, yuvFormat, &pixels, &format, &rowBytes);
1078 }
1079 }
1080
1081 // TODO: SkColorType needs to be expanded to allow this to be done via the
1082 // GrContext::createBackendTexture API
1083 tex = gpu->createBackendTexture(bm.width(), bm.height(), format,
1084 GrMipMapped::kNo, GrRenderable::kNo,
1085 pixels, rowBytes, nullptr, GrProtected::kNo);
1086 } else {
1087 tex = context->priv().createBackendTexture(&bm.pixmap(), 1,
1088 GrRenderable::kNo, GrProtected::kNo);
1089 }
1090
1091 return tex;
1092 }
1093
yuv_to_rgb_colorfilter()1094 static sk_sp<SkColorFilter> yuv_to_rgb_colorfilter() {
1095 static const float kJPEGConversionMatrix[20] = {
1096 1.0f, 0.0f, 1.402f, 0.0f, -180.0f/255,
1097 1.0f, -0.344136f, -0.714136f, 0.0f, 136.0f/255,
1098 1.0f, 1.772f, 0.0f, 0.0f, -227.6f/255,
1099 0.0f, 0.0f, 0.0f, 1.0f, 0.0f
1100 };
1101
1102 return SkColorFilters::Matrix(kJPEGConversionMatrix);
1103 }
1104
1105 namespace skiagm {
1106
1107 // This GM creates an opaque and transparent bitmap, extracts the planes and then recombines
1108 // them into various YUV formats. It then renders the results in the grid:
1109 //
1110 // JPEG 601 709 Identity
1111 // Transparent Opaque Transparent Opaque Transparent Opaque Transparent Opaque
1112 // originals
1113 // P016
1114 // P010
1115 // Y416
1116 // AYUV
1117 // Y410
1118 // NV12
1119 // NV21
1120 // I420
1121 // YV12
1122 class WackyYUVFormatsGM : public GM {
1123 public:
WackyYUVFormatsGM(bool useTargetColorSpace,bool useDomain)1124 WackyYUVFormatsGM(bool useTargetColorSpace, bool useDomain)
1125 : fUseTargetColorSpace(useTargetColorSpace)
1126 , fUseDomain(useDomain) {
1127 this->setBGColor(0xFFCCCCCC);
1128 }
1129
1130 protected:
1131
onShortName()1132 SkString onShortName() override {
1133 SkString name("wacky_yuv_formats");
1134 if (fUseTargetColorSpace) {
1135 name += "_cs";
1136 }
1137 if (fUseDomain) {
1138 name += "_domain";
1139 }
1140
1141 return name;
1142 }
1143
onISize()1144 SkISize onISize() override {
1145 int numCols = 2 * (kLastEnum_SkYUVColorSpace + 1); // opacity x color-space
1146 int numRows = 1 + (kLast_YUVFormat + 1); // origin + # yuv formats
1147 int wh = SkScalarCeilToInt(kTileWidthHeight * (fUseDomain ? 1.5f : 1.f));
1148 return SkISize::Make(kLabelWidth + numCols * (wh + kPad),
1149 kLabelHeight + numRows * (wh + kPad));
1150 }
1151
onOnceBeforeDraw()1152 void onOnceBeforeDraw() override {
1153 SkPoint origin = { kTileWidthHeight/2.0f, kTileWidthHeight/2.0f };
1154 float outerRadius = kTileWidthHeight/2.0f - 20.0f;
1155 float innerRadius = 20.0f;
1156
1157 {
1158 // transparent
1159 SkTDArray<SkRect> circles;
1160 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 5, &circles);
1161 fOriginalBMs[0] = make_bitmap(kRGBA_8888_SkColorType, path, circles, false, fUseDomain);
1162 }
1163
1164 {
1165 // opaque
1166 SkTDArray<SkRect> circles;
1167 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 7, &circles);
1168 fOriginalBMs[1] = make_bitmap(kRGBA_8888_SkColorType, path, circles, true, fUseDomain);
1169 }
1170
1171 if (fUseTargetColorSpace) {
1172 fTargetColorSpace = SkColorSpace::MakeSRGB()->makeColorSpin();
1173 }
1174 }
1175
createImages(GrContext * context)1176 void createImages(GrContext* context) {
1177 int counter = 0;
1178 for (bool opaque : { false, true }) {
1179 for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
1180 PlaneData planes;
1181 extract_planes(fOriginalBMs[opaque], (SkYUVColorSpace) cs, &planes);
1182
1183 for (int format = kP016_YUVFormat; format <= kLast_YUVFormat; ++format) {
1184 SkBitmap resultBMs[4];
1185 SkYUVAIndex yuvaIndices[4];
1186
1187 create_YUV(planes, (YUVFormat) format, resultBMs, yuvaIndices, opaque);
1188
1189 int numTextures;
1190 if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures)) {
1191 continue;
1192 }
1193
1194 if (context) {
1195 if (context->abandoned()) {
1196 return;
1197 }
1198
1199 GrBackendTexture yuvaTextures[4];
1200 SkPixmap yuvaPixmaps[4];
1201
1202 for (int i = 0; i < numTextures; ++i) {
1203 yuvaTextures[i] = create_yuva_texture(context, resultBMs[i],
1204 yuvaIndices, i,
1205 (YUVFormat) format);
1206 if (yuvaTextures[i].isValid()) {
1207 fBackendTextures.push_back(yuvaTextures[i]);
1208 }
1209 yuvaPixmaps[i] = resultBMs[i].pixmap();
1210 }
1211
1212 int counterMod = counter % 3;
1213 if (format_cant_be_represented_with_pixmaps((YUVFormat) format) &&
1214 counterMod == 2) {
1215 // These formats don't work as pixmaps
1216 counterMod = 1;
1217 } else if (fUseDomain && counterMod == 0) {
1218 // Copies flatten to RGB when they copy the YUVA data, which doesn't
1219 // know about the intended domain and the domain padding bleeds in
1220 counterMod = 1;
1221 }
1222 switch (counterMod) {
1223 case 0:
1224 fImages[opaque][cs][format] = SkImage::MakeFromYUVATexturesCopy(
1225 context,
1226 (SkYUVColorSpace)cs,
1227 yuvaTextures,
1228 yuvaIndices,
1229 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1230 kTopLeft_GrSurfaceOrigin);
1231 break;
1232 case 1:
1233 fImages[opaque][cs][format] = SkImage::MakeFromYUVATextures(
1234 context,
1235 (SkYUVColorSpace)cs,
1236 yuvaTextures,
1237 yuvaIndices,
1238 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1239 kTopLeft_GrSurfaceOrigin);
1240 break;
1241 case 2:
1242 default:
1243 fImages[opaque][cs][format] = SkImage::MakeFromYUVAPixmaps(
1244 context,
1245 (SkYUVColorSpace)cs,
1246 yuvaPixmaps,
1247 yuvaIndices,
1248 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1249 kTopLeft_GrSurfaceOrigin, true);
1250 break;
1251 }
1252 ++counter;
1253 } else {
1254 fImages[opaque][cs][format] = make_yuv_gen_image(
1255 fOriginalBMs[opaque].info(),
1256 (SkYUVColorSpace) cs,
1257 yuvaIndices,
1258 resultBMs);
1259 }
1260 }
1261 }
1262 }
1263 }
1264
onDraw(SkCanvas * canvas)1265 void onDraw(SkCanvas* canvas) override {
1266 this->createImages(canvas->getGrContext());
1267
1268 SkRect srcRect = SkRect::MakeWH(fOriginalBMs[0].width(), fOriginalBMs[0].height());
1269 SkRect dstRect = SkRect::MakeXYWH(kLabelWidth, 0.f, srcRect.width(), srcRect.height());
1270
1271 SkCanvas::SrcRectConstraint constraint = SkCanvas::kFast_SrcRectConstraint;
1272 if (fUseDomain) {
1273 srcRect.inset(kDomainPadding, kDomainPadding);
1274 // Draw a larger rectangle to ensure bilerp filtering would normally read outside the
1275 // srcRect and hit the red pixels, if strict constraint weren't used.
1276 dstRect.fRight = kLabelWidth + 1.5f * srcRect.width();
1277 dstRect.fBottom = 1.5f * srcRect.height();
1278 constraint = SkCanvas::kStrict_SrcRectConstraint;
1279 }
1280
1281 for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
1282 SkPaint paint;
1283 paint.setFilterQuality(kLow_SkFilterQuality);
1284 if (kIdentity_SkYUVColorSpace == cs) {
1285 // The identity color space needs post processing to appear correctly
1286 paint.setColorFilter(yuv_to_rgb_colorfilter());
1287 }
1288
1289 for (int opaque : { 0, 1 }) {
1290 dstRect.offsetTo(dstRect.fLeft, kLabelHeight);
1291
1292 draw_col_label(canvas, dstRect.fLeft + dstRect.height() / 2, cs, opaque);
1293
1294 canvas->drawBitmapRect(fOriginalBMs[opaque], srcRect, dstRect, nullptr, constraint);
1295 dstRect.offset(0.f, dstRect.height() + kPad);
1296
1297 for (int format = kP016_YUVFormat; format <= kLast_YUVFormat; ++format) {
1298 draw_row_label(canvas, dstRect.fTop, format);
1299 if (fUseTargetColorSpace && fImages[opaque][cs][format]) {
1300 // Making a CS-specific version of a kIdentity_SkYUVColorSpace YUV image
1301 // doesn't make a whole lot of sense. The colorSpace conversion will
1302 // operate on the YUV components rather than the RGB components.
1303 sk_sp<SkImage> csImage =
1304 fImages[opaque][cs][format]->makeColorSpace(fTargetColorSpace);
1305 canvas->drawImageRect(csImage, srcRect, dstRect, &paint, constraint);
1306 } else {
1307 canvas->drawImageRect(fImages[opaque][cs][format], srcRect, dstRect, &paint,
1308 constraint);
1309 }
1310 dstRect.offset(0.f, dstRect.height() + kPad);
1311 }
1312
1313 dstRect.offset(dstRect.width() + kPad, 0.f);
1314 }
1315 }
1316 if (auto context = canvas->getGrContext()) {
1317 if (!context->abandoned()) {
1318 context->flush();
1319 GrGpu* gpu = context->priv().getGpu();
1320 SkASSERT(gpu);
1321 gpu->testingOnly_flushGpuAndSync();
1322 for (const auto& tex : fBackendTextures) {
1323 context->deleteBackendTexture(tex);
1324 }
1325 fBackendTextures.reset();
1326 }
1327 }
1328 SkASSERT(!fBackendTextures.count());
1329 }
1330
1331 private:
1332 SkBitmap fOriginalBMs[2];
1333 sk_sp<SkImage> fImages[2][kLastEnum_SkYUVColorSpace + 1][kLast_YUVFormat + 1];
1334 SkTArray<GrBackendTexture> fBackendTextures;
1335 bool fUseTargetColorSpace;
1336 bool fUseDomain;
1337 sk_sp<SkColorSpace> fTargetColorSpace;
1338
1339 typedef GM INHERITED;
1340 };
1341
1342 //////////////////////////////////////////////////////////////////////////////
1343
1344 DEF_GM(return new WackyYUVFormatsGM(/* cs */ false, /* domain */ false);)
1345 DEF_GM(return new WackyYUVFormatsGM(/* cs */ true, /* domain */ false);)
1346 DEF_GM(return new WackyYUVFormatsGM(/* cs */ false, /* domain */ true);)
1347
1348 class YUVMakeColorSpaceGM : public GpuGM {
1349 public:
YUVMakeColorSpaceGM()1350 YUVMakeColorSpaceGM() {
1351 this->setBGColor(0xFFCCCCCC);
1352 }
1353
1354 protected:
onShortName()1355 SkString onShortName() override {
1356 return SkString("yuv_make_color_space");
1357 }
1358
onISize()1359 SkISize onISize() override {
1360 int numCols = 4; // (transparent, opaque) x (untagged, tagged)
1361 int numRows = 5; // original, YUV, subset, readPixels, makeNonTextureImage
1362 return SkISize::Make(numCols * (kTileWidthHeight + kPad) + kPad,
1363 numRows * (kTileWidthHeight + kPad) + kPad);
1364 }
1365
onOnceBeforeDraw()1366 void onOnceBeforeDraw() override {
1367 SkPoint origin = { kTileWidthHeight/2.0f, kTileWidthHeight/2.0f };
1368 float outerRadius = kTileWidthHeight/2.0f - 20.0f;
1369 float innerRadius = 20.0f;
1370
1371 {
1372 // transparent
1373 SkTDArray<SkRect> circles;
1374 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 5, &circles);
1375 fOriginalBMs[0] = make_bitmap(kN32_SkColorType, path, circles, false, false);
1376 }
1377
1378 {
1379 // opaque
1380 SkTDArray<SkRect> circles;
1381 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 7, &circles);
1382 fOriginalBMs[1] = make_bitmap(kN32_SkColorType, path, circles, true, false);
1383 }
1384
1385 fTargetColorSpace = SkColorSpace::MakeSRGB()->makeColorSpin();
1386 }
1387
createImages(GrContext * context)1388 void createImages(GrContext* context) {
1389 for (bool opaque : { false, true }) {
1390 PlaneData planes;
1391 extract_planes(fOriginalBMs[opaque], kJPEG_SkYUVColorSpace, &planes);
1392
1393 SkBitmap resultBMs[4];
1394 SkYUVAIndex yuvaIndices[4];
1395
1396 create_YUV(planes, kAYUV_YUVFormat, resultBMs, yuvaIndices, opaque);
1397
1398 int numTextures;
1399 if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures)) {
1400 continue;
1401 }
1402
1403 GrBackendTexture yuvaTextures[4];
1404 for (int i = 0; i < numTextures; ++i) {
1405 yuvaTextures[i] = create_yuva_texture(context, resultBMs[i], yuvaIndices, i,
1406 kAYUV_YUVFormat);
1407 if (yuvaTextures[i].isValid()) {
1408 fBackendTextures.push_back(yuvaTextures[i]);
1409 }
1410 }
1411
1412 fImages[opaque][0] = SkImage::MakeFromYUVATextures(
1413 context,
1414 kJPEG_SkYUVColorSpace,
1415 yuvaTextures,
1416 yuvaIndices,
1417 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1418 kTopLeft_GrSurfaceOrigin);
1419 fImages[opaque][1] = SkImage::MakeFromYUVATextures(
1420 context,
1421 kJPEG_SkYUVColorSpace,
1422 yuvaTextures,
1423 yuvaIndices,
1424 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1425 kTopLeft_GrSurfaceOrigin,
1426 SkColorSpace::MakeSRGB());
1427 }
1428 }
1429
onDraw(GrContext * context,GrRenderTargetContext *,SkCanvas * canvas)1430 void onDraw(GrContext* context, GrRenderTargetContext*, SkCanvas* canvas) override {
1431 this->createImages(context);
1432
1433 int x = kPad;
1434 for (int tagged : { 0, 1 }) {
1435 for (int opaque : { 0, 1 }) {
1436 int y = kPad;
1437
1438 auto raster = SkImage::MakeFromBitmap(fOriginalBMs[opaque])
1439 ->makeColorSpace(fTargetColorSpace);
1440 canvas->drawImage(raster, x, y);
1441 y += kTileWidthHeight + kPad;
1442
1443 auto yuv = fImages[opaque][tagged]->makeColorSpace(fTargetColorSpace);
1444 SkASSERT(SkColorSpace::Equals(yuv->colorSpace(), fTargetColorSpace.get()));
1445 canvas->drawImage(yuv, x, y);
1446 y += kTileWidthHeight + kPad;
1447
1448 auto subset = yuv->makeSubset(SkIRect::MakeWH(kTileWidthHeight / 2,
1449 kTileWidthHeight / 2));
1450 canvas->drawImage(subset, x, y);
1451 y += kTileWidthHeight + kPad;
1452
1453 auto nonTexture = yuv->makeNonTextureImage();
1454 canvas->drawImage(nonTexture, x, y);
1455 y += kTileWidthHeight + kPad;
1456
1457 SkBitmap readBack;
1458 readBack.allocPixels(yuv->imageInfo());
1459 yuv->readPixels(readBack.pixmap(), 0, 0);
1460 canvas->drawBitmap(readBack, x, y);
1461
1462 x += kTileWidthHeight + kPad;
1463 }
1464 }
1465
1466 context->flush();
1467 GrGpu* gpu = context->priv().getGpu();
1468 SkASSERT(gpu);
1469 gpu->testingOnly_flushGpuAndSync();
1470 for (const auto& tex : fBackendTextures) {
1471 context->deleteBackendTexture(tex);
1472 }
1473 fBackendTextures.reset();
1474 }
1475
1476 private:
1477 SkBitmap fOriginalBMs[2];
1478 sk_sp<SkImage> fImages[2][2];
1479 SkTArray<GrBackendTexture> fBackendTextures;
1480 sk_sp<SkColorSpace> fTargetColorSpace;
1481
1482 typedef GM INHERITED;
1483 };
1484
1485 DEF_GM(return new YUVMakeColorSpaceGM();)
1486
1487 }
1488
1489 ///////////////
1490
1491 #include "include/effects/SkColorMatrix.h"
1492 #include "src/core/SkAutoPixmapStorage.h"
1493 #include "src/core/SkYUVMath.h"
1494 #include "tools/Resources.h"
1495
draw_into_alpha(const SkImage * img,sk_sp<SkColorFilter> cf,const SkPixmap & dst)1496 static void draw_into_alpha(const SkImage* img, sk_sp<SkColorFilter> cf, const SkPixmap& dst) {
1497 auto canvas = SkCanvas::MakeRasterDirect(dst.info(), dst.writable_addr(), dst.rowBytes());
1498 canvas->scale(1.0f * dst.width() / img->width(), 1.0f * dst.height() / img->height());
1499 SkPaint paint;
1500 paint.setFilterQuality(kLow_SkFilterQuality);
1501 paint.setColorFilter(cf);
1502 paint.setBlendMode(SkBlendMode::kSrc);
1503 canvas->drawImage(img, 0, 0, &paint);
1504 }
1505
split_into_yuv(const SkImage * img,SkYUVColorSpace cs,const SkPixmap dst[3])1506 static void split_into_yuv(const SkImage* img, SkYUVColorSpace cs, const SkPixmap dst[3]) {
1507 float m[20];
1508 SkColorMatrix_RGB2YUV(cs, m);
1509
1510 memcpy(m + 15, m + 0, 5 * sizeof(float)); // copy Y into A
1511 draw_into_alpha(img, SkColorFilters::Matrix(m), dst[0]);
1512
1513 memcpy(m + 15, m + 5, 5 * sizeof(float)); // copy U into A
1514 draw_into_alpha(img, SkColorFilters::Matrix(m), dst[1]);
1515
1516 memcpy(m + 15, m + 10, 5 * sizeof(float)); // copy V into A
1517 draw_into_alpha(img, SkColorFilters::Matrix(m), dst[2]);
1518 }
1519
draw_diff(SkCanvas * canvas,SkScalar x,SkScalar y,const SkImage * a,const SkImage * b)1520 static void draw_diff(SkCanvas* canvas, SkScalar x, SkScalar y,
1521 const SkImage* a, const SkImage* b) {
1522 auto sh = SkShaders::Blend(SkBlendMode::kDifference, a->makeShader(), b->makeShader());
1523 SkPaint paint;
1524 paint.setShader(sh);
1525 canvas->save();
1526 canvas->translate(x, y);
1527 canvas->drawRect(SkRect::MakeWH(a->width(), a->height()), paint);
1528
1529 SkColorMatrix cm;
1530 cm.setScale(64, 64, 64);
1531 paint.setShader(sh->makeWithColorFilter(SkColorFilters::Matrix(cm)));
1532 canvas->translate(0, a->height());
1533 canvas->drawRect(SkRect::MakeWH(a->width(), a->height()), paint);
1534
1535 canvas->restore();
1536 }
1537
1538 // Exercises SkColorMatrix_RGB2YUV for yuv colorspaces, showing the planes, and the
1539 // resulting (recombined) images (gpu only for now).
1540 //
1541 class YUVSplitterGM : public skiagm::GM {
1542 sk_sp<SkImage> fOrig;
1543 SkAutoPixmapStorage fStorage[3];
1544 SkPixmap fPM[3];
1545
1546 public:
YUVSplitterGM()1547 YUVSplitterGM() {}
1548
1549 protected:
1550
onShortName()1551 SkString onShortName() override {
1552 return SkString("yuv_splitter");
1553 }
1554
onISize()1555 SkISize onISize() override {
1556 return SkISize::Make(1024, 768);
1557 }
1558
onOnceBeforeDraw()1559 void onOnceBeforeDraw() override {
1560 fOrig = GetResourceAsImage("images/mandrill_256.png");
1561
1562 SkImageInfo info = SkImageInfo::Make(fOrig->width(), fOrig->height(), kAlpha_8_SkColorType,
1563 kPremul_SkAlphaType);
1564 fStorage[0].alloc(info);
1565 if (0) {
1566 // if you want to scale U,V down by 1/2
1567 info = info.makeWH(info.width()/2, info.height()/2);
1568 }
1569 fStorage[1].alloc(info);
1570 fStorage[2].alloc(info);
1571 for (int i = 0; i < 3; ++i) {
1572 fPM[i] = fStorage[i];
1573 }
1574 }
1575
onDraw(SkCanvas * canvas)1576 void onDraw(SkCanvas* canvas) override {
1577 SkYUVAIndex indices[4];
1578 indices[SkYUVAIndex::kY_Index] = {0, SkColorChannel::kR};
1579 indices[SkYUVAIndex::kU_Index] = {1, SkColorChannel::kR};
1580 indices[SkYUVAIndex::kV_Index] = {2, SkColorChannel::kR};
1581 indices[SkYUVAIndex::kA_Index] = {-1, SkColorChannel::kR};
1582
1583 canvas->translate(fOrig->width(), 0);
1584 canvas->save();
1585 for (auto cs : {kRec709_SkYUVColorSpace, kRec601_SkYUVColorSpace, kJPEG_SkYUVColorSpace}) {
1586 split_into_yuv(fOrig.get(), cs, fPM);
1587 auto img = SkImage::MakeFromYUVAPixmaps(canvas->getGrContext(), cs, fPM, indices,
1588 fPM[0].info().dimensions(),
1589 kTopLeft_GrSurfaceOrigin,
1590 false, false, nullptr);
1591 if (img) {
1592 canvas->drawImage(img, 0, 0, nullptr);
1593 draw_diff(canvas, 0, fOrig->height(), fOrig.get(), img.get());
1594 }
1595 canvas->translate(fOrig->width(), 0);
1596 }
1597 canvas->restore();
1598 canvas->translate(-fOrig->width(), 0);
1599
1600 canvas->drawImage(SkImage::MakeRasterCopy(fPM[0]), 0, 0, nullptr);
1601 canvas->drawImage(SkImage::MakeRasterCopy(fPM[1]), 0, fPM[0].height(), nullptr);
1602 canvas->drawImage(SkImage::MakeRasterCopy(fPM[2]),
1603 0, fPM[0].height() + fPM[1].height(), nullptr);
1604 }
1605
1606 private:
1607 typedef GM INHERITED;
1608 };
1609 DEF_GM( return new YUVSplitterGM; )
1610