blob: 2d178022cbab642854fca357815dca7632a24270 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "gm/gm.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -04009#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"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050014#include "include/core/SkColorPriv.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040015#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"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050021#include "include/core/SkImageGenerator.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040022#include "include/core/SkImageInfo.h"
23#include "include/core/SkMatrix.h"
24#include "include/core/SkPaint.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "include/core/SkPath.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040026#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"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050035#include "include/core/SkYUVAIndex.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040036#include "include/core/SkYUVASizeInfo.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050037#include "include/gpu/GrBackendSurface.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040038#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"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050046#include "src/gpu/GrContextPriv.h"
47#include "src/gpu/GrGpu.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040048#include "tools/ToolUtils.h"
49
50#include <math.h>
51#include <string.h>
52#include <initializer_list>
53#include <memory>
54#include <utility>
55
56class GrRenderTargetContext;
Robert Phillips51c89e42018-10-05 13:30:43 -040057
Robert Phillipsbfa76f22018-10-03 12:12:26 -040058static const int kTileWidthHeight = 128;
59static const int kLabelWidth = 64;
60static const int kLabelHeight = 32;
Michael Ludwiga6a84002019-04-12 15:03:02 -040061static const int kDomainPadding = 8;
Robert Phillipsbfa76f22018-10-03 12:12:26 -040062static const int kPad = 1;
Robert Phillipsbfa76f22018-10-03 12:12:26 -040063
64enum YUVFormat {
Robert Phillipsbb749902019-06-10 17:20:12 -040065 // 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
Robert Phillips17a3a0b2019-09-18 13:56:54 -040073 // This is laid out the same as kP016 and kP010 but uses F16 unstead of U16. In this case
74 // the 10 bits/channel vs 16 bits/channel distinction isn't relevant.
75 kP016F_YUVFormat,
76
Robert Phillipsbb749902019-06-10 17:20:12 -040077 // 4:4:4 formats, 64 bpp
78 kY416_YUVFormat, // 16-bit AVYU values all interleaved (1 texture)
79
Robert Phillipsbfa76f22018-10-03 12:12:26 -040080 // 4:4:4 formats, 32 bpp
Robert Phillipsbb749902019-06-10 17:20:12 -040081 kAYUV_YUVFormat, // 8-bit YUVA values all interleaved (1 texture)
82 kY410_YUVFormat, // AVYU w/ 10bpp for YUV and 2 for A all interleaved (1 texture)
Robert Phillipsbfa76f22018-10-03 12:12:26 -040083
84 // 4:2:0 formats, 12 bpp
Robert Phillipsbb749902019-06-10 17:20:12 -040085 kNV12_YUVFormat, // 8-bit Y plane + 2x2 down sampled interleaved U/V planes (2 textures)
86 kNV21_YUVFormat, // same as kNV12 but w/ U/V reversed in the interleaved texture (2 textures)
Robert Phillipsbfa76f22018-10-03 12:12:26 -040087
Robert Phillipsbb749902019-06-10 17:20:12 -040088 kI420_YUVFormat, // 8-bit Y plane + separate 2x2 down sampled U and V planes (3 textures)
89 kYV12_YUVFormat, // 8-bit Y plane + separate 2x2 down sampled V and U planes (3 textures)
Robert Phillipsbfa76f22018-10-03 12:12:26 -040090
91 kLast_YUVFormat = kYV12_YUVFormat
92};
93
Robert Phillips17a3a0b2019-09-18 13:56:54 -040094#ifdef SK_DEBUG
Robert Phillipsbb749902019-06-10 17:20:12 -040095static bool format_uses_16_bpp(YUVFormat yuvFormat) {
Robert Phillips17a3a0b2019-09-18 13:56:54 -040096 return kP016_YUVFormat == yuvFormat ||
97 kP010_YUVFormat == yuvFormat ||
98 kP016F_YUVFormat == yuvFormat ||
Robert Phillipsbb749902019-06-10 17:20:12 -040099 kY416_YUVFormat == yuvFormat;
100}
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400101#endif
Robert Phillipsbb749902019-06-10 17:20:12 -0400102
103static bool format_has_builtin_alpha(YUVFormat yuvFormat) {
104 return kY416_YUVFormat == yuvFormat ||
105 kAYUV_YUVFormat == yuvFormat ||
106 kY410_YUVFormat == yuvFormat;
107}
108
Robert Phillipsdc62b642019-05-16 10:10:45 -0400109// Helper to setup the SkYUVAIndex array correctly
110// Skia allows the client to tack an additional alpha plane onto any of the standard opaque
111// formats (via the addExtraAlpha) flag. In this case it is assumed to be a stand-alone single-
112// channel plane.
113static void setup_yuv_indices(YUVFormat yuvFormat, bool addExtraAlpha, SkYUVAIndex yuvaIndices[4]) {
114 switch (yuvFormat) {
Robert Phillipsbb749902019-06-10 17:20:12 -0400115 case kP016_YUVFormat: // fall through
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400116 case kP010_YUVFormat: // fall through
117 case kP016F_YUVFormat:
Robert Phillipsbb749902019-06-10 17:20:12 -0400118 yuvaIndices[0].fIndex = 0;
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400119 yuvaIndices[0].fChannel = SkColorChannel::kA; // bc 16bit is stored in A16 or AF16
Robert Phillipsbb749902019-06-10 17:20:12 -0400120 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;
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400126 yuvaIndices[3].fChannel = SkColorChannel::kA; // bc 16bit is stored in A16 or AF16
Robert Phillipsbb749902019-06-10 17:20:12 -0400127 } 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;
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400136 yuvaIndices[1].fChannel = SkColorChannel::kR;
Robert Phillipsbb749902019-06-10 17:20:12 -0400137 yuvaIndices[2].fIndex = 0;
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400138 yuvaIndices[2].fChannel = SkColorChannel::kB;
Robert Phillipsbb749902019-06-10 17:20:12 -0400139 yuvaIndices[3].fIndex = 0;
140 yuvaIndices[3].fChannel = SkColorChannel::kA;
141 break;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400142 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;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400166 yuvaIndices[0].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400167 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;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400180 yuvaIndices[0].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400181 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;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400194 yuvaIndices[0].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400195 yuvaIndices[1].fIndex = 1;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400196 yuvaIndices[1].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400197 yuvaIndices[2].fIndex = 2;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400198 yuvaIndices[2].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400199 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;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400208 yuvaIndices[0].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400209 yuvaIndices[1].fIndex = 2;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400210 yuvaIndices[1].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400211 yuvaIndices[2].fIndex = 1;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400212 yuvaIndices[2].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400213 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
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400223// All the planes we need to construct the various YUV formats
224struct 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
Robert Phillips429f0d32019-09-11 17:03:28 -0400231
232 SkBitmap fFull;
233 SkBitmap fQuarter; // 2x2 downsampled YUVA
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400234};
235
236// Add a portion of a circle to 'path'. The points 'o1' and 'o2' are on the border of the circle
237// and have tangents 'v1' and 'v2'.
238static void add_arc(SkPath* path,
239 const SkPoint& o1, const SkVector& v1,
240 const SkPoint& o2, const SkVector& v2,
241 SkTDArray<SkRect>* circles, bool takeLongWayRound) {
242
243 SkVector v3 = { -v1.fY, v1.fX };
244 SkVector v4 = { v2.fY, -v2.fX };
245
246 SkScalar t = ((o2.fX - o1.fX) * v4.fY - (o2.fY - o1.fY) * v4.fX) / v3.cross(v4);
247 SkPoint center = { o1.fX + t * v3.fX, o1.fY + t * v3.fY };
248
249 SkRect r = { center.fX - t, center.fY - t, center.fX + t, center.fY + t };
250
251 if (circles) {
252 circles->push_back(r);
253 }
254
255 SkVector startV = o1 - center, endV = o2 - center;
256 startV.normalize();
257 endV.normalize();
258
259 SkScalar startDeg = SkRadiansToDegrees(SkScalarATan2(startV.fY, startV.fX));
260 SkScalar endDeg = SkRadiansToDegrees(SkScalarATan2(endV.fY, endV.fX));
261
262 startDeg += 360.0f;
263 startDeg = fmodf(startDeg, 360.0f);
264
265 endDeg += 360.0f;
266 endDeg = fmodf(endDeg, 360.0f);
267
268 if (endDeg < startDeg) {
269 endDeg += 360.0f;
270 }
271
272 SkScalar sweepDeg = SkTAbs(endDeg - startDeg);
273 if (!takeLongWayRound) {
274 sweepDeg = sweepDeg - 360;
275 }
276
277 path->arcTo(r, startDeg, sweepDeg, false);
278}
279
280static SkPath create_splat(const SkPoint& o, SkScalar innerRadius, SkScalar outerRadius,
281 SkScalar ratio, int numLobes, SkTDArray<SkRect>* circles) {
282 if (numLobes <= 1) {
283 return SkPath();
284 }
285
286 SkPath p;
287
288 int numDivisions = 2 * numLobes;
289 SkScalar fullLobeDegrees = 360.0f / numLobes;
290 SkScalar outDegrees = ratio * fullLobeDegrees / (ratio + 1.0f);
291 SkScalar innerDegrees = fullLobeDegrees / (ratio + 1.0f);
292 SkMatrix outerStep, innerStep;
293 outerStep.setRotate(outDegrees);
294 innerStep.setRotate(innerDegrees);
295 SkVector curV = SkVector::Make(0.0f, 1.0f);
296
297 if (circles) {
298 circles->push_back(SkRect::MakeLTRB(o.fX - innerRadius, o.fY - innerRadius,
299 o.fX + innerRadius, o.fY + innerRadius));
300 }
301
302 p.moveTo(o.fX + innerRadius * curV.fX, o.fY + innerRadius * curV.fY);
303
304 for (int i = 0; i < numDivisions; ++i) {
305
306 SkVector nextV;
307 if (0 == (i % 2)) {
308 nextV = outerStep.mapVector(curV.fX, curV.fY);
309
310 SkPoint top = SkPoint::Make(o.fX + outerRadius * curV.fX,
311 o.fY + outerRadius * curV.fY);
312 SkPoint nextTop = SkPoint::Make(o.fX + outerRadius * nextV.fX,
313 o.fY + outerRadius * nextV.fY);
314
315 p.lineTo(top);
316 add_arc(&p, top, curV, nextTop, nextV, circles, true);
317 } else {
318 nextV = innerStep.mapVector(curV.fX, curV.fY);
319
320 SkPoint bot = SkPoint::Make(o.fX + innerRadius * curV.fX,
321 o.fY + innerRadius * curV.fY);
322 SkPoint nextBot = SkPoint::Make(o.fX + innerRadius * nextV.fX,
323 o.fY + innerRadius * nextV.fY);
324
325 p.lineTo(bot);
326 add_arc(&p, bot, curV, nextBot, nextV, nullptr, false);
327 }
328
329 curV = nextV;
330 }
331
332 p.close();
333
334 return p;
335}
336
Robert Phillips2dd1b472019-03-21 09:00:20 -0400337static SkBitmap make_bitmap(SkColorType colorType, const SkPath& path,
Michael Ludwiga6a84002019-04-12 15:03:02 -0400338 const SkTDArray<SkRect>& circles, bool opaque, bool padWithRed) {
Mike Kleinea3f0142019-03-20 11:12:10 -0500339 const SkColor kGreen = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 178, 240, 104));
340 const SkColor kBlue = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 173, 167, 252));
341 const SkColor kYellow = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 255, 221, 117));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400342
Michael Ludwiga6a84002019-04-12 15:03:02 -0400343 int widthHeight = kTileWidthHeight + (padWithRed ? 2 * kDomainPadding : 0);
344
345 SkImageInfo ii = SkImageInfo::Make(widthHeight, widthHeight,
Robert Phillips2dd1b472019-03-21 09:00:20 -0400346 colorType, kPremul_SkAlphaType);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400347
348 SkBitmap bm;
349 bm.allocPixels(ii);
350
Robert Phillips2dd1b472019-03-21 09:00:20 -0400351 std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirect(ii,
352 bm.getPixels(),
353 bm.rowBytes());
Michael Ludwiga6a84002019-04-12 15:03:02 -0400354 if (padWithRed) {
355 canvas->clear(SK_ColorRED);
356 canvas->translate(kDomainPadding, kDomainPadding);
357 canvas->clipRect(SkRect::MakeWH(kTileWidthHeight, kTileWidthHeight));
358 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400359 canvas->clear(opaque ? kGreen : SK_ColorTRANSPARENT);
360
361 SkPaint paint;
362 paint.setAntiAlias(false); // serialize-8888 doesn't seem to work well w/ partial transparency
363 paint.setColor(kBlue);
364
365 canvas->drawPath(path, paint);
366
367 paint.setColor(opaque ? kYellow : SK_ColorTRANSPARENT);
368 paint.setBlendMode(SkBlendMode::kSrc);
369 for (int i = 0; i < circles.count(); ++i) {
370 SkRect r = circles[i];
371 r.inset(r.width()/4, r.height()/4);
372 canvas->drawOval(r, paint);
373 }
374
375 return bm;
376}
377
Robert Phillips1c7062d2018-10-04 10:44:53 -0400378static void convert_rgba_to_yuva_601_shared(SkColor col, uint8_t yuv[4],
379 uint8_t off, uint8_t range) {
380 static const float Kr = 0.299f;
381 static const float Kb = 0.114f;
382 static const float Kg = 1.0f - Kr - Kb;
383
384 float r = SkColorGetR(col) / 255.0f;
385 float g = SkColorGetG(col) / 255.0f;
386 float b = SkColorGetB(col) / 255.0f;
387
388 float Ey = Kr * r + Kg * g + Kb * b;
389 float Ecb = (b - Ey) / 1.402f;
390 float Ecr = (r - Ey) / 1.772;
Robert Phillipsbb749902019-06-10 17:20:12 -0400391 SkASSERT(Ey >= 0.0f && Ey <= 1.0f);
392 SkASSERT(Ecb >= -0.5f && Ecb <= 0.5f);
393 SkASSERT(Ecr >= -0.5f && Ecr <= 0.5f);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400394
395 yuv[0] = SkScalarRoundToInt( range * Ey + off );
396 yuv[1] = SkScalarRoundToInt( 224 * Ecb + 128 );
397 yuv[2] = SkScalarRoundToInt( 224 * Ecr + 128 );
398 yuv[3] = SkColorGetA(col);
399}
400
401static void convert_rgba_to_yuva_jpeg(SkColor col, uint8_t yuv[4]) {
402 // full swing from 0..255
403 convert_rgba_to_yuva_601_shared(col, yuv, 0, 255);
404}
405
406static void convert_rgba_to_yuva_601(SkColor col, uint8_t yuv[4]) {
407 // partial swing from 16..235
408 convert_rgba_to_yuva_601_shared(col, yuv, 16, 219);
409
410}
411
412static void convert_rgba_to_yuva_709(SkColor col, uint8_t yuv[4]) {
413 static const float Kr = 0.2126f;
414 static const float Kb = 0.0722f;
415 static const float Kg = 1.0f - Kr - Kb;
416
417 float r = SkColorGetR(col) / 255.0f;
418 float g = SkColorGetG(col) / 255.0f;
419 float b = SkColorGetB(col) / 255.0f;
420
421 float Ey = Kr * r + Kg * g + Kb * b;
422 float Ecb = (b - Ey) / 1.8556f;
423 float Ecr = (r - Ey) / 1.5748;
Robert Phillipsbb749902019-06-10 17:20:12 -0400424 SkASSERT(Ey >= 0.0f && Ey <= 1.0f);
425 SkASSERT(Ecb >= -0.5f && Ecb <= 0.5f);
426 SkASSERT(Ecr >= -0.5f && Ecr <= 0.5f);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400427
428 yuv[0] = SkScalarRoundToInt( 219 * Ey + 16 );
429 yuv[1] = SkScalarRoundToInt( 224 * Ecb + 128 );
430 yuv[2] = SkScalarRoundToInt( 224 * Ecr + 128 );
431
432 yuv[3] = SkColorGetA(col);
433}
434
435
436static SkPMColor convert_yuva_to_rgba_jpeg(uint8_t y, uint8_t u, uint8_t v, uint8_t a) {
Robert Phillips2dd1b472019-03-21 09:00:20 -0400437 uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.0f * y + 1.402f * v - 0.703749f * 255),
Robert Phillips1c7062d2018-10-04 10:44:53 -0400438 0, 255);
Robert Phillips2dd1b472019-03-21 09:00:20 -0400439 uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.0f * y - (0.344136f * u) - (0.714136f * v) + 0.531211f * 255),
Robert Phillips1c7062d2018-10-04 10:44:53 -0400440 0, 255);
Robert Phillips2dd1b472019-03-21 09:00:20 -0400441 uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.0f * y + 1.772f * u - 0.889475f * 255),
Robert Phillips1c7062d2018-10-04 10:44:53 -0400442 0, 255);
443
Robert Phillips2dd1b472019-03-21 09:00:20 -0400444 SkPMColor c = SkPremultiplyARGBInline(a, b, g, r);
445 return c;
Robert Phillips1c7062d2018-10-04 10:44:53 -0400446}
447
448static SkPMColor convert_yuva_to_rgba_601(uint8_t y, uint8_t u, uint8_t v, uint8_t a) {
Robert Phillips2dd1b472019-03-21 09:00:20 -0400449 uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.164f * y + 1.596f * v - 0.87075f * 255), 0, 255);
450 uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.164f * y - (0.391f * u) - (0.813f * v) + 0.52925f * 255), 0, 255);
451 uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.164f * y + 2.018f * u - 1.08175f * 255), 0, 255);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400452
Robert Phillips2dd1b472019-03-21 09:00:20 -0400453 SkPMColor c = SkPremultiplyARGBInline(a, b, g, r);
454 return c;
Robert Phillips1c7062d2018-10-04 10:44:53 -0400455}
456
457static SkPMColor convert_yuva_to_rgba_709(uint8_t y, uint8_t u, uint8_t v, uint8_t a) {
Robert Phillips2dd1b472019-03-21 09:00:20 -0400458 uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.164f * y + (1.793f * v) - 0.96925f * 255), 0, 255);
459 uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.164f * y - (0.213f * u) - (0.533f * v) + 0.30025f * 255), 0, 255);
460 uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.164f * y + (2.112f * u) - 1.12875f * 255), 0, 255);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400461
Robert Phillips2dd1b472019-03-21 09:00:20 -0400462 SkPMColor c = SkPremultiplyARGBInline(a, b, g, r);
463 return c;
Robert Phillips1c7062d2018-10-04 10:44:53 -0400464}
465
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400466static void extract_planes(const SkBitmap& bm, SkYUVColorSpace yuvColorSpace, PlaneData* planes) {
Robert Phillips0a22ba82019-03-06 12:36:47 -0500467 if (kIdentity_SkYUVColorSpace == yuvColorSpace) {
468 // To test the identity color space we use JPEG YUV planes
469 yuvColorSpace = kJPEG_SkYUVColorSpace;
470 }
471
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400472 SkASSERT(!(bm.width() % 2));
473 SkASSERT(!(bm.height() % 2));
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400474 planes->fYFull.allocPixels(SkImageInfo::Make(bm.width(), bm.height(), kGray_8_SkColorType,
475 kUnpremul_SkAlphaType));
476 planes->fUFull.allocPixels(SkImageInfo::Make(bm.width(), bm.height(), kGray_8_SkColorType,
477 kUnpremul_SkAlphaType));
478 planes->fVFull.allocPixels(SkImageInfo::Make(bm.width(), bm.height(), kGray_8_SkColorType,
479 kUnpremul_SkAlphaType));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400480 planes->fAFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400481 planes->fUQuarter.allocPixels(SkImageInfo::Make(bm.width()/2, bm.height()/2,
482 kGray_8_SkColorType, kUnpremul_SkAlphaType));
483 planes->fVQuarter.allocPixels(SkImageInfo::Make(bm.width()/2, bm.height()/2,
484 kGray_8_SkColorType, kUnpremul_SkAlphaType));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400485
Robert Phillips429f0d32019-09-11 17:03:28 -0400486 planes->fFull.allocPixels(SkImageInfo::Make(bm.width(), bm.height(),
487 kRGBA_F32_SkColorType, kUnpremul_SkAlphaType));
488 planes->fQuarter.allocPixels(SkImageInfo::Make(bm.width()/2, bm.height()/2,
489 kRGBA_F32_SkColorType, kUnpremul_SkAlphaType));
490
491 SkColor4f* dst = (SkColor4f *) planes->fFull.getAddr(0, 0);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400492 for (int y = 0; y < bm.height(); ++y) {
493 for (int x = 0; x < bm.width(); ++x) {
494 SkColor col = bm.getColor(x, y);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400495
Robert Phillips1c7062d2018-10-04 10:44:53 -0400496 uint8_t yuva[4];
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400497
Robert Phillips1c7062d2018-10-04 10:44:53 -0400498 if (kJPEG_SkYUVColorSpace == yuvColorSpace) {
499 convert_rgba_to_yuva_jpeg(col, yuva);
500 } else if (kRec601_SkYUVColorSpace == yuvColorSpace) {
501 convert_rgba_to_yuva_601(col, yuva);
502 } else {
503 SkASSERT(kRec709_SkYUVColorSpace == yuvColorSpace);
504 convert_rgba_to_yuva_709(col, yuva);
505 }
506
507 *planes->fYFull.getAddr8(x, y) = yuva[0];
508 *planes->fUFull.getAddr8(x, y) = yuva[1];
509 *planes->fVFull.getAddr8(x, y) = yuva[2];
510 *planes->fAFull.getAddr8(x, y) = yuva[3];
Robert Phillips429f0d32019-09-11 17:03:28 -0400511
512 // TODO: render in F32 rather than converting here
513 dst->fR = yuva[0] / 255.0f;
514 dst->fG = yuva[1] / 255.0f;
515 dst->fB = yuva[2] / 255.0f;
516 dst->fA = yuva[3] / 255.0f;
517 ++dst;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400518 }
519 }
520
Robert Phillips429f0d32019-09-11 17:03:28 -0400521 dst = (SkColor4f *) planes->fQuarter.getAddr(0, 0);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400522 for (int y = 0; y < bm.height()/2; ++y) {
523 for (int x = 0; x < bm.width()/2; ++x) {
Robert Phillips429f0d32019-09-11 17:03:28 -0400524 uint32_t yAccum = 0, uAccum = 0, vAccum = 0, aAccum = 0;
525
526 yAccum += *planes->fYFull.getAddr8(2*x, 2*y);
527 yAccum += *planes->fYFull.getAddr8(2*x+1, 2*y);
528 yAccum += *planes->fYFull.getAddr8(2*x, 2*y+1);
529 yAccum += *planes->fYFull.getAddr8(2*x+1, 2*y+1);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400530
531 uAccum += *planes->fUFull.getAddr8(2*x, 2*y);
532 uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y);
533 uAccum += *planes->fUFull.getAddr8(2*x, 2*y+1);
534 uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y+1);
535
536 *planes->fUQuarter.getAddr8(x, y) = uAccum / 4.0f;
537
538 vAccum += *planes->fVFull.getAddr8(2*x, 2*y);
539 vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y);
540 vAccum += *planes->fVFull.getAddr8(2*x, 2*y+1);
541 vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y+1);
542
Robert Phillips1c7062d2018-10-04 10:44:53 -0400543 *planes->fVQuarter.getAddr8(x, y) = vAccum / 4.0f;
Robert Phillips429f0d32019-09-11 17:03:28 -0400544
545 aAccum += *planes->fAFull.getAddr8(2*x, 2*y);
546 aAccum += *planes->fAFull.getAddr8(2*x+1, 2*y);
547 aAccum += *planes->fAFull.getAddr8(2*x, 2*y+1);
548 aAccum += *planes->fAFull.getAddr8(2*x+1, 2*y+1);
549
550 // TODO: render in F32 rather than converting here
551 dst->fR = yAccum / (4.0f * 255.0f);
552 dst->fG = uAccum / (4.0f * 255.0f);
553 dst->fB = vAccum / (4.0f * 255.0f);
554 dst->fA = aAccum / (4.0f * 255.0f);
555 ++dst;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400556 }
557 }
558}
559
Robert Phillipsd470e1b2019-09-04 15:05:35 -0400560// Create a 2x2 downsampled SkBitmap. It is stored in an RG texture. It can optionally be
Robert Phillips429f0d32019-09-11 17:03:28 -0400561// uv (i.e., NV12) or vu (i.e., NV21).
Robert Phillipsbb749902019-06-10 17:20:12 -0400562static SkBitmap make_quarter_2_channel(const SkBitmap& fullY,
563 const SkBitmap& quarterU,
564 const SkBitmap& quarterV,
565 bool uv) {
566 SkBitmap result;
567
Robert Phillipsbb749902019-06-10 17:20:12 -0400568 result.allocPixels(SkImageInfo::Make(fullY.width()/2,
569 fullY.height()/2,
Robert Phillipsd470e1b2019-09-04 15:05:35 -0400570 kRG_88_SkColorType,
Robert Phillipsbb749902019-06-10 17:20:12 -0400571 kUnpremul_SkAlphaType));
572
573 for (int y = 0; y < fullY.height()/2; ++y) {
574 for (int x = 0; x < fullY.width()/2; ++x) {
575 uint8_t u8 = *quarterU.getAddr8(x, y);
576 uint8_t v8 = *quarterV.getAddr8(x, y);
577
578 if (uv) {
Robert Phillipsd470e1b2019-09-04 15:05:35 -0400579 *result.getAddr16(x, y) = (v8 << 8) | u8;
Robert Phillipsbb749902019-06-10 17:20:12 -0400580 } else {
Robert Phillipsd470e1b2019-09-04 15:05:35 -0400581 *result.getAddr16(x, y) = (u8 << 8) | v8;
Robert Phillipsbb749902019-06-10 17:20:12 -0400582 }
583 }
584 }
585
586 return result;
587}
588
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400589// Create some flavor of a 16bits/channel bitmap from a RGBA_F32 source
590static SkBitmap make_16(const SkBitmap& src, SkColorType dstCT,
591 std::function<void(uint16_t* dstPixel, const float* srcPixel)> convert) {
Robert Phillips429f0d32019-09-11 17:03:28 -0400592 SkASSERT(src.colorType() == kRGBA_F32_SkColorType);
593
594 SkBitmap result;
595
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400596 result.allocPixels(SkImageInfo::Make(src.width(), src.height(), dstCT,
Robert Phillips429f0d32019-09-11 17:03:28 -0400597 kUnpremul_SkAlphaType));
598
Robert Phillips429f0d32019-09-11 17:03:28 -0400599 for (int y = 0; y < src.height(); ++y) {
600 for (int x = 0; x < src.width(); ++x) {
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400601 const float* srcPixel = (const float*) src.getAddr(x, y);
602 uint16_t* dstPixel = (uint16_t*) result.getAddr(x, y);
Robert Phillips429f0d32019-09-11 17:03:28 -0400603
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400604 convert(dstPixel, srcPixel);
Robert Phillips429f0d32019-09-11 17:03:28 -0400605 }
606 }
607
608 return result;
609}
610
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400611static uint16_t flt_2_uint16(float flt) { return SkScalarRoundToInt(flt * 65535.0f); }
Robert Phillips429f0d32019-09-11 17:03:28 -0400612
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400613// Recombine the separate planes into some YUV format
614static void create_YUV(const PlaneData& planes, YUVFormat yuvFormat,
615 SkBitmap resultBMs[], SkYUVAIndex yuvaIndices[4], bool opaque) {
616 int nextLayer = 0;
617
618 switch (yuvFormat) {
Robert Phillipsbb749902019-06-10 17:20:12 -0400619 case kY416_YUVFormat: {
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400620 resultBMs[nextLayer++] = make_16(planes.fFull, kRGBA_16161616_SkColorType,
621 [] (uint16_t* dstPixel, const float* srcPixel) {
622 dstPixel[0] = flt_2_uint16(srcPixel[1]); // U
623 dstPixel[1] = flt_2_uint16(srcPixel[0]); // Y
624 dstPixel[2] = flt_2_uint16(srcPixel[2]); // V
625 dstPixel[3] = flt_2_uint16(srcPixel[3]); // A
626 });
Robert Phillipsbb749902019-06-10 17:20:12 -0400627 setup_yuv_indices(yuvFormat, false, yuvaIndices);
628 break;
629 }
Jim Van Verth976a6b02018-10-17 15:27:19 -0400630 case kAYUV_YUVFormat: {
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400631 SkBitmap yuvaFull;
632
Jim Van Verth47133fd2018-10-19 22:09:28 -0400633 yuvaFull.allocPixels(SkImageInfo::Make(planes.fYFull.width(), planes.fYFull.height(),
634 kRGBA_8888_SkColorType, kUnpremul_SkAlphaType));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400635
636 for (int y = 0; y < planes.fYFull.height(); ++y) {
637 for (int x = 0; x < planes.fYFull.width(); ++x) {
638
639 uint8_t Y = *planes.fYFull.getAddr8(x, y);
640 uint8_t U = *planes.fUFull.getAddr8(x, y);
641 uint8_t V = *planes.fVFull.getAddr8(x, y);
642 uint8_t A = *planes.fAFull.getAddr8(x, y);
643
644 // NOT premul!
Jim Van Verth47133fd2018-10-19 22:09:28 -0400645 // V and Y swapped to match RGBA layout
Robert Phillips2dd1b472019-03-21 09:00:20 -0400646 SkColor c = SkColorSetARGB(A, V, U, Y);
647 *yuvaFull.getAddr32(x, y) = c;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400648 }
649 }
650
651 resultBMs[nextLayer++] = yuvaFull;
652
Robert Phillipsdc62b642019-05-16 10:10:45 -0400653 setup_yuv_indices(yuvFormat, false, yuvaIndices);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400654 break;
655 }
Robert Phillips2dd1b472019-03-21 09:00:20 -0400656 case kY410_YUVFormat: {
657 SkBitmap yuvaFull;
Robert Phillipsf0313ee2019-05-21 13:51:11 -0400658 uint32_t Y, U, V;
659 uint8_t A;
Robert Phillips2dd1b472019-03-21 09:00:20 -0400660
661 yuvaFull.allocPixels(SkImageInfo::Make(planes.fYFull.width(), planes.fYFull.height(),
662 kRGBA_1010102_SkColorType,
663 kUnpremul_SkAlphaType));
664
665 for (int y = 0; y < planes.fYFull.height(); ++y) {
666 for (int x = 0; x < planes.fYFull.width(); ++x) {
667
Robert Phillipsf0313ee2019-05-21 13:51:11 -0400668 Y = SkScalarRoundToInt((*planes.fYFull.getAddr8(x, y) / 255.0f) * 1023.0f);
669 U = SkScalarRoundToInt((*planes.fUFull.getAddr8(x, y) / 255.0f) * 1023.0f);
670 V = SkScalarRoundToInt((*planes.fVFull.getAddr8(x, y) / 255.0f) * 1023.0f);
671 A = SkScalarRoundToInt((*planes.fAFull.getAddr8(x, y) / 255.0f) * 3.0f);
Robert Phillips2dd1b472019-03-21 09:00:20 -0400672
673 // NOT premul!
674 // AVYU but w/ V and U swapped to match RGBA layout
675 *yuvaFull.getAddr32(x, y) = (A << 30) | (U << 20) | (Y << 10) | (V << 0);
676 }
677 }
678
679 resultBMs[nextLayer++] = yuvaFull;
680
Robert Phillipsdc62b642019-05-16 10:10:45 -0400681 setup_yuv_indices(yuvFormat, false, yuvaIndices);
Robert Phillips2dd1b472019-03-21 09:00:20 -0400682 break;
683 }
Robert Phillipsbb749902019-06-10 17:20:12 -0400684 case kP016_YUVFormat: // fall through
Robert Phillips429f0d32019-09-11 17:03:28 -0400685 case kP010_YUVFormat: {
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400686 resultBMs[nextLayer++] = make_16(planes.fFull, kAlpha_16_SkColorType,
687 [tenBitsPP = (yuvFormat == kP010_YUVFormat)]
688 (uint16_t* dstPixel, const float* srcPixel) {
689 uint16_t val16 = flt_2_uint16(srcPixel[0]);
690 dstPixel[0] = tenBitsPP ? (val16 & 0xFFC0)
691 : val16;
692 });
693 resultBMs[nextLayer++] = make_16(planes.fQuarter, kRG_1616_SkColorType,
694 [tenBitsPP = (yuvFormat == kP010_YUVFormat)]
695 (uint16_t* dstPixel, const float* srcPixel) {
696 uint16_t u16 = flt_2_uint16(srcPixel[1]);
697 uint16_t v16 = flt_2_uint16(srcPixel[2]);
698 dstPixel[0] = tenBitsPP ? (u16 & 0xFFC0) : u16;
699 dstPixel[1] = tenBitsPP ? (v16 & 0xFFC0) : v16;
700 });
Robert Phillips429f0d32019-09-11 17:03:28 -0400701 if (!opaque) {
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400702 resultBMs[nextLayer] = make_16(planes.fFull, kAlpha_16_SkColorType,
703 [tenBitsPP = (yuvFormat == kP010_YUVFormat)]
704 (uint16_t* dstPixel, const float* srcPixel) {
705 uint16_t val16 = flt_2_uint16(srcPixel[3]);
706 dstPixel[0] = tenBitsPP ? (val16 & 0xFFC0)
707 : val16;
708 });
709 }
710 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
711 return;
712 }
713 case kP016F_YUVFormat: {
714 resultBMs[nextLayer++] = make_16(planes.fFull, kAlpha_F16_SkColorType,
715 [] (uint16_t* dstPixel, const float* srcPixel) {
716 dstPixel[0] = SkFloatToHalf(srcPixel[0]);
717 });
718 resultBMs[nextLayer++] = make_16(planes.fQuarter, kRG_F16_SkColorType,
719 [] (uint16_t* dstPixel, const float* srcPixel) {
720 dstPixel[0] = SkFloatToHalf(srcPixel[1]);
721 dstPixel[1] = SkFloatToHalf(srcPixel[2]);
722 });
723 if (!opaque) {
724 resultBMs[nextLayer] = make_16(planes.fFull, kAlpha_F16_SkColorType,
725 [] (uint16_t* dstPixel, const float* srcPixel) {
726 dstPixel[0] = SkFloatToHalf(srcPixel[3]);
727 });
Robert Phillips429f0d32019-09-11 17:03:28 -0400728 }
729 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
730 return;
731 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400732 case kNV12_YUVFormat: {
Robert Phillipsbb749902019-06-10 17:20:12 -0400733 SkBitmap uvQuarter = make_quarter_2_channel(planes.fYFull,
734 planes.fUQuarter,
735 planes.fVQuarter, true);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400736 resultBMs[nextLayer++] = planes.fYFull;
737 resultBMs[nextLayer++] = uvQuarter;
738
Robert Phillipsdc62b642019-05-16 10:10:45 -0400739 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400740 break;
741 }
742 case kNV21_YUVFormat: {
Robert Phillipsbb749902019-06-10 17:20:12 -0400743 SkBitmap vuQuarter = make_quarter_2_channel(planes.fYFull,
744 planes.fUQuarter,
745 planes.fVQuarter, false);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400746 resultBMs[nextLayer++] = planes.fYFull;
747 resultBMs[nextLayer++] = vuQuarter;
748
Robert Phillipsdc62b642019-05-16 10:10:45 -0400749 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400750 break;
751 }
752 case kI420_YUVFormat:
753 resultBMs[nextLayer++] = planes.fYFull;
754 resultBMs[nextLayer++] = planes.fUQuarter;
755 resultBMs[nextLayer++] = planes.fVQuarter;
756
Robert Phillipsdc62b642019-05-16 10:10:45 -0400757 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400758 break;
759 case kYV12_YUVFormat:
760 resultBMs[nextLayer++] = planes.fYFull;
761 resultBMs[nextLayer++] = planes.fVQuarter;
762 resultBMs[nextLayer++] = planes.fUQuarter;
763
Robert Phillipsdc62b642019-05-16 10:10:45 -0400764 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400765 break;
766 }
767
Robert Phillipsbb749902019-06-10 17:20:12 -0400768 if (!format_has_builtin_alpha(yuvFormat) && !opaque) {
Robert Phillipsdc62b642019-05-16 10:10:45 -0400769 resultBMs[nextLayer] = planes.fAFull;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400770 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400771}
772
Robert Phillips2dd1b472019-03-21 09:00:20 -0400773static uint8_t look_up(float x1, float y1, const SkBitmap& bm, SkColorChannel channel) {
Robert Phillips1c7062d2018-10-04 10:44:53 -0400774 uint8_t result;
775
Robert Phillips2dd1b472019-03-21 09:00:20 -0400776 SkASSERT(x1 > 0 && x1 < 1.0f);
777 SkASSERT(y1 > 0 && y1 < 1.0f);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400778 int x = SkScalarFloorToInt(x1 * bm.width());
779 int y = SkScalarFloorToInt(y1 * bm.height());
780
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400781 if (kGray_8_SkColorType == bm.colorType()) {
Robert Phillipsbb749902019-06-10 17:20:12 -0400782 SkASSERT(SkColorChannel::kA == channel || SkColorChannel::kR == channel);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400783 result = *bm.getAddr8(x, y);
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400784 } else if (kAlpha_8_SkColorType == bm.colorType() ||
785 kAlpha_16_SkColorType == bm.colorType() ||
786 kAlpha_F16_SkColorType == bm.colorType()) {
Robert Phillips429f0d32019-09-11 17:03:28 -0400787 SkASSERT(SkColorChannel::kA == channel);
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400788 SkColor c = bm.getColor(x, y);
789 result = SkColorGetA(c);
790 } else if (kRG_88_SkColorType == bm.colorType() ||
791 kRG_1616_SkColorType == bm.colorType() ||
792 kRG_F16_SkColorType == bm.colorType()) {
Robert Phillipsd470e1b2019-09-04 15:05:35 -0400793 SkASSERT(SkColorChannel::kR == channel || SkColorChannel::kG == channel);
794 SkColor c = bm.getColor(x, y);
795
796 switch (channel) {
797 case SkColorChannel::kR:
798 result = SkColorGetR(c);
799 break;
800 case SkColorChannel::kG:
801 result = SkColorGetG(c);
802 break;
803 case SkColorChannel::kB:
804 result = 0;
805 break;
806 case SkColorChannel::kA:
807 result = 255;
808 break;
809 }
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400810 } else {
811 SkASSERT(kRGBA_1010102_SkColorType == bm.colorType() ||
812 kRGBA_8888_SkColorType == bm.colorType() ||
813 kRGBA_16161616_SkColorType == bm.colorType());
814
815 SkColor c = bm.getColor(x, y);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400816
817 switch (channel) {
818 case SkColorChannel::kR:
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400819 result = SkColorGetR(c);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400820 break;
821 case SkColorChannel::kG:
Robert Phillips2dd1b472019-03-21 09:00:20 -0400822 result = SkColorGetG(c);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400823 break;
824 case SkColorChannel::kB:
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400825 result = SkColorGetB(c);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400826 break;
827 case SkColorChannel::kA:
Robert Phillips2dd1b472019-03-21 09:00:20 -0400828 result = SkColorGetA(c);
829 break;
830 }
Robert Phillips1c7062d2018-10-04 10:44:53 -0400831 }
832
833 return result;
834}
835
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400836class YUVGenerator : public SkImageGenerator {
837public:
838 YUVGenerator(const SkImageInfo& ii,
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400839 SkYUVColorSpace yuvColorSpace,
Jim Van Verth8f11e432018-10-18 14:36:59 -0400840 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
Jim Van Verthe24b5872018-10-29 16:26:02 -0400841 SkBitmap bitmaps[SkYUVASizeInfo::kMaxCount])
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400842 : SkImageGenerator(ii)
Robert Phillips2dd1b472019-03-21 09:00:20 -0400843 , fYUVColorSpace(yuvColorSpace)
844 , fAllA8(true) {
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400845 memcpy(fYUVAIndices, yuvaIndices, sizeof(fYUVAIndices));
846
Jim Van Verth8f11e432018-10-18 14:36:59 -0400847 SkAssertResult(SkYUVAIndex::AreValidIndices(fYUVAIndices, &fNumBitmaps));
Jim Van Verthe24b5872018-10-29 16:26:02 -0400848 SkASSERT(fNumBitmaps > 0 && fNumBitmaps <= SkYUVASizeInfo::kMaxCount);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400849
Jim Van Verth8f11e432018-10-18 14:36:59 -0400850 for (int i = 0; i < fNumBitmaps; ++i) {
851 fYUVBitmaps[i] = bitmaps[i];
Robert Phillips2dd1b472019-03-21 09:00:20 -0400852 if (kAlpha_8_SkColorType != fYUVBitmaps[i].colorType()) {
853 fAllA8 = false;
854 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400855 }
856 }
857
858protected:
859 bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
860 const Options&) override {
861
862 if (kUnknown_SkColorType == fFlattened.colorType()) {
Robert Phillips2dd1b472019-03-21 09:00:20 -0400863 fFlattened.allocPixels(info);
864 SkASSERT(kPremul_SkAlphaType == info.alphaType());
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400865
866 for (int y = 0; y < info.height(); ++y) {
867 for (int x = 0; x < info.width(); ++x) {
868
Robert Phillips1c7062d2018-10-04 10:44:53 -0400869 float x1 = (x + 0.5f) / info.width();
870 float y1 = (y + 0.5f) / info.height();
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400871
Robert Phillips1c7062d2018-10-04 10:44:53 -0400872 uint8_t Y = look_up(x1, y1,
873 fYUVBitmaps[fYUVAIndices[0].fIndex],
874 fYUVAIndices[0].fChannel);
875
876 uint8_t U = look_up(x1, y1,
877 fYUVBitmaps[fYUVAIndices[1].fIndex],
878 fYUVAIndices[1].fChannel);
879
880
881 uint8_t V = look_up(x1, y1,
882 fYUVBitmaps[fYUVAIndices[2].fIndex],
883 fYUVAIndices[2].fChannel);
884
885 uint8_t A = 255;
886 if (fYUVAIndices[3].fIndex >= 0) {
887 A = look_up(x1, y1,
888 fYUVBitmaps[fYUVAIndices[3].fIndex],
889 fYUVAIndices[3].fChannel);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400890 }
891
892 // Making premul here.
Robert Phillips1c7062d2018-10-04 10:44:53 -0400893 switch (fYUVColorSpace) {
894 case kJPEG_SkYUVColorSpace:
895 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_jpeg(Y, U, V, A);
896 break;
897 case kRec601_SkYUVColorSpace:
898 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_601(Y, U, V, A);
899 break;
900 case kRec709_SkYUVColorSpace:
901 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_709(Y, U, V, A);
902 break;
Robert Phillips0a22ba82019-03-06 12:36:47 -0500903 case kIdentity_SkYUVColorSpace:
Robert Phillips2dd1b472019-03-21 09:00:20 -0400904 *fFlattened.getAddr32(x, y) = SkPremultiplyARGBInline(A, V, U, Y);
Robert Phillips0a22ba82019-03-06 12:36:47 -0500905 break;
Robert Phillips1c7062d2018-10-04 10:44:53 -0400906 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400907 }
908 }
909 }
910
911 return fFlattened.readPixels(info, pixels, rowBytes, 0, 0);
912 }
913
Jim Van Verthe24b5872018-10-29 16:26:02 -0400914 bool onQueryYUVA8(SkYUVASizeInfo* size,
Jim Van Verth8f11e432018-10-18 14:36:59 -0400915 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
916 SkYUVColorSpace* yuvColorSpace) const override {
917
Robert Phillips2dd1b472019-03-21 09:00:20 -0400918 // The onQueryYUVA8/onGetYUVA8Planes can only handle A8 planes
919 if (!fAllA8) {
920 return false;
921 }
922
Jim Van Verth8f11e432018-10-18 14:36:59 -0400923 memcpy(yuvaIndices, fYUVAIndices, sizeof(fYUVAIndices));
924 *yuvColorSpace = fYUVColorSpace;
925
926 int i = 0;
927 for ( ; i < fNumBitmaps; ++i) {
Jim Van Verth8f11e432018-10-18 14:36:59 -0400928 size->fSizes[i].fWidth = fYUVBitmaps[i].width();
929 size->fSizes[i].fHeight = fYUVBitmaps[i].height();
930 size->fWidthBytes[i] = fYUVBitmaps[i].rowBytes();
931 }
Jim Van Verthe24b5872018-10-29 16:26:02 -0400932 for ( ; i < SkYUVASizeInfo::kMaxCount; ++i) {
Jim Van Verth8f11e432018-10-18 14:36:59 -0400933 size->fSizes[i].fWidth = 0;
934 size->fSizes[i].fHeight = 0;
935 size->fWidthBytes[i] = 0;
Jim Van Verthf99a6742018-10-18 16:13:18 +0000936 }
Jim Van Verth0c583af2018-10-18 10:31:59 -0400937
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400938 return true;
939 }
940
Jim Van Verthe24b5872018-10-29 16:26:02 -0400941 bool onGetYUVA8Planes(const SkYUVASizeInfo&, const SkYUVAIndex[SkYUVAIndex::kIndexCount],
942 void* planes[SkYUVASizeInfo::kMaxCount]) override {
Robert Phillips2dd1b472019-03-21 09:00:20 -0400943 SkASSERT(fAllA8);
Jim Van Verth8f11e432018-10-18 14:36:59 -0400944 for (int i = 0; i < fNumBitmaps; ++i) {
945 planes[i] = fYUVBitmaps[i].getPixels();
946 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400947 return true;
948 }
949
950private:
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400951 SkYUVColorSpace fYUVColorSpace;
Jim Van Verth8f11e432018-10-18 14:36:59 -0400952 SkYUVAIndex fYUVAIndices[SkYUVAIndex::kIndexCount];
953 int fNumBitmaps;
Jim Van Verthe24b5872018-10-29 16:26:02 -0400954 SkBitmap fYUVBitmaps[SkYUVASizeInfo::kMaxCount];
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400955 SkBitmap fFlattened;
Robert Phillips2dd1b472019-03-21 09:00:20 -0400956 bool fAllA8; // are all the SkBitmaps in "fYUVBitmaps" A8?
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400957
958};
959
960static sk_sp<SkImage> make_yuv_gen_image(const SkImageInfo& ii,
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400961 SkYUVColorSpace yuvColorSpace,
Jim Van Verth8f11e432018-10-18 14:36:59 -0400962 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400963 SkBitmap bitmaps[]) {
Jim Van Verth8f11e432018-10-18 14:36:59 -0400964 std::unique_ptr<SkImageGenerator> gen(new YUVGenerator(ii, yuvColorSpace,
965 yuvaIndices, bitmaps));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400966
967 return SkImage::MakeFromGenerator(std::move(gen));
968}
969
970static void draw_col_label(SkCanvas* canvas, int x, int yuvColorSpace, bool opaque) {
Robert Phillips0a22ba82019-03-06 12:36:47 -0500971 static const char* kYUVColorSpaceNames[] = { "JPEG", "601", "709", "Identity" };
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400972 GR_STATIC_ASSERT(SK_ARRAY_COUNT(kYUVColorSpaceNames) == kLastEnum_SkYUVColorSpace+1);
973
Mike Reed91919132019-01-02 12:21:01 -0500974 SkPaint paint;
Mike Kleinea3f0142019-03-20 11:12:10 -0500975 SkFont font(ToolUtils::create_portable_typeface(nullptr, SkFontStyle::Bold()), 16);
Mike Reed91919132019-01-02 12:21:01 -0500976 font.setEdging(SkFont::Edging::kAlias);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400977
978 SkRect textRect;
979 SkString colLabel;
980
981 colLabel.printf("%s", kYUVColorSpaceNames[yuvColorSpace]);
Ben Wagner51e15a62019-05-07 15:38:46 -0400982 font.measureText(colLabel.c_str(), colLabel.size(), SkTextEncoding::kUTF8, &textRect);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400983 int y = textRect.height();
984
Mike Reed91919132019-01-02 12:21:01 -0500985 SkTextUtils::DrawString(canvas, colLabel.c_str(), x, y, font, paint, SkTextUtils::kCenter_Align);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400986
987 colLabel.printf("%s", opaque ? "Opaque" : "Transparent");
Mike Reed71f5a0b2018-10-25 16:12:39 -0400988
Ben Wagner51e15a62019-05-07 15:38:46 -0400989 font.measureText(colLabel.c_str(), colLabel.size(), SkTextEncoding::kUTF8, &textRect);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400990 y += textRect.height();
991
Mike Reed91919132019-01-02 12:21:01 -0500992 SkTextUtils::DrawString(canvas, colLabel.c_str(), x, y, font, paint, SkTextUtils::kCenter_Align);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400993}
994
995static void draw_row_label(SkCanvas* canvas, int y, int yuvFormat) {
Robert Phillipsbb749902019-06-10 17:20:12 -0400996 static const char* kYUVFormatNames[] = {
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400997 "P016", "P010", "P016F", "Y416", "AYUV", "Y410", "NV12", "NV21", "I420", "YV12"
Robert Phillipsbb749902019-06-10 17:20:12 -0400998 };
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400999 GR_STATIC_ASSERT(SK_ARRAY_COUNT(kYUVFormatNames) == kLast_YUVFormat+1);
1000
Mike Reed91919132019-01-02 12:21:01 -05001001 SkPaint paint;
Mike Kleinea3f0142019-03-20 11:12:10 -05001002 SkFont font(ToolUtils::create_portable_typeface(nullptr, SkFontStyle::Bold()), 16);
Mike Reed91919132019-01-02 12:21:01 -05001003 font.setEdging(SkFont::Edging::kAlias);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001004
1005 SkRect textRect;
1006 SkString rowLabel;
1007
1008 rowLabel.printf("%s", kYUVFormatNames[yuvFormat]);
Ben Wagner51e15a62019-05-07 15:38:46 -04001009 font.measureText(rowLabel.c_str(), rowLabel.size(), SkTextEncoding::kUTF8, &textRect);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001010 y += kTileWidthHeight/2 + textRect.height()/2;
1011
Hal Canary89a644b2019-01-07 09:36:09 -05001012 canvas->drawString(rowLabel, 0, y, font, paint);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001013}
1014
Robert Phillipscb1adb42019-06-10 15:09:34 -04001015static GrBackendTexture create_yuva_texture(GrContext* context, const SkBitmap& bm,
Robert Phillipsbb749902019-06-10 17:20:12 -04001016 SkYUVAIndex yuvaIndices[4], int texIndex,
1017 YUVFormat yuvFormat) {
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001018#ifdef SK_DEBUG
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001019 SkASSERT(texIndex >= 0 && texIndex <= 3);
1020 int channelCount = 0;
1021 for (int i = 0; i < SkYUVAIndex::kIndexCount; ++i) {
1022 if (yuvaIndices[i].fIndex == texIndex) {
1023 ++channelCount;
1024 }
1025 }
Robert Phillipsbb749902019-06-10 17:20:12 -04001026
Robert Phillipsbb749902019-06-10 17:20:12 -04001027 if (format_uses_16_bpp(yuvFormat) || 2 == channelCount) {
Robert Phillipsbb749902019-06-10 17:20:12 -04001028 if (2 == channelCount) {
1029 if (format_uses_16_bpp(yuvFormat)) {
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001030 if (yuvFormat == kP016F_YUVFormat) {
1031 SkASSERT(kRG_F16_SkColorType == bm.colorType());
1032 } else {
1033 SkASSERT(yuvFormat == kP016_YUVFormat || yuvFormat == kP010_YUVFormat);
1034 SkASSERT(kRG_1616_SkColorType == bm.colorType());
1035 }
Robert Phillipsbb749902019-06-10 17:20:12 -04001036 } else {
Robert Phillipsd470e1b2019-09-04 15:05:35 -04001037 SkASSERT(kRG_88_SkColorType == bm.colorType());
Robert Phillipsbb749902019-06-10 17:20:12 -04001038 }
1039 } else {
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001040 if (yuvFormat == kY416_YUVFormat) {
1041 SkASSERT(kRGBA_16161616_SkColorType == bm.colorType());
1042 } else if (yuvFormat == kP016F_YUVFormat) {
1043 SkASSERT(kAlpha_F16_SkColorType == bm.colorType());
Robert Phillipsbb749902019-06-10 17:20:12 -04001044 } else {
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001045 SkASSERT(yuvFormat == kP016_YUVFormat || yuvFormat == kP010_YUVFormat);
Robert Phillips429f0d32019-09-11 17:03:28 -04001046 SkASSERT(kAlpha_16_SkColorType == bm.colorType());
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001047 }
1048 }
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001049 }
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001050#endif
Robert Phillipsbb749902019-06-10 17:20:12 -04001051
Robert Phillipsd470e1b2019-09-04 15:05:35 -04001052 return context->priv().createBackendTexture(&bm.pixmap(), 1,
1053 GrRenderable::kNo, GrProtected::kNo);
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001054}
1055
Robert Phillips0a22ba82019-03-06 12:36:47 -05001056static sk_sp<SkColorFilter> yuv_to_rgb_colorfilter() {
1057 static const float kJPEGConversionMatrix[20] = {
Mike Reede869a1e2019-04-30 12:18:54 -04001058 1.0f, 0.0f, 1.402f, 0.0f, -180.0f/255,
1059 1.0f, -0.344136f, -0.714136f, 0.0f, 136.0f/255,
1060 1.0f, 1.772f, 0.0f, 0.0f, -227.6f/255,
Robert Phillips0a22ba82019-03-06 12:36:47 -05001061 0.0f, 0.0f, 0.0f, 1.0f, 0.0f
1062 };
1063
Mike Reede869a1e2019-04-30 12:18:54 -04001064 return SkColorFilters::Matrix(kJPEGConversionMatrix);
Robert Phillips0a22ba82019-03-06 12:36:47 -05001065}
1066
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001067namespace skiagm {
1068
1069// This GM creates an opaque and transparent bitmap, extracts the planes and then recombines
1070// them into various YUV formats. It then renders the results in the grid:
1071//
Robert Phillips2dd1b472019-03-21 09:00:20 -04001072// JPEG 601 709 Identity
1073// Transparent Opaque Transparent Opaque Transparent Opaque Transparent Opaque
Robert Phillipsbb749902019-06-10 17:20:12 -04001074// originals
1075// P016
1076// P010
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001077// P016F
Robert Phillipsbb749902019-06-10 17:20:12 -04001078// Y416
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001079// AYUV
Robert Phillips2dd1b472019-03-21 09:00:20 -04001080// Y410
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001081// NV12
1082// NV21
1083// I420
1084// YV12
1085class WackyYUVFormatsGM : public GM {
1086public:
Michael Ludwiga6a84002019-04-12 15:03:02 -04001087 WackyYUVFormatsGM(bool useTargetColorSpace, bool useDomain)
1088 : fUseTargetColorSpace(useTargetColorSpace)
1089 , fUseDomain(useDomain) {
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001090 this->setBGColor(0xFFCCCCCC);
1091 }
1092
1093protected:
1094
1095 SkString onShortName() override {
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001096 SkString name("wacky_yuv_formats");
1097 if (fUseTargetColorSpace) {
1098 name += "_cs";
1099 }
Michael Ludwiga6a84002019-04-12 15:03:02 -04001100 if (fUseDomain) {
1101 name += "_domain";
1102 }
Robert Phillips2dd1b472019-03-21 09:00:20 -04001103
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001104 return name;
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001105 }
1106
1107 SkISize onISize() override {
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001108 int numCols = 2 * (kLastEnum_SkYUVColorSpace + 1); // opacity x #-color-spaces
1109 int numRows = 1 + (kLast_YUVFormat + 1); // original + #-yuv-formats
Michael Ludwiga6a84002019-04-12 15:03:02 -04001110 int wh = SkScalarCeilToInt(kTileWidthHeight * (fUseDomain ? 1.5f : 1.f));
1111 return SkISize::Make(kLabelWidth + numCols * (wh + kPad),
1112 kLabelHeight + numRows * (wh + kPad));
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001113 }
1114
1115 void onOnceBeforeDraw() override {
1116 SkPoint origin = { kTileWidthHeight/2.0f, kTileWidthHeight/2.0f };
1117 float outerRadius = kTileWidthHeight/2.0f - 20.0f;
1118 float innerRadius = 20.0f;
1119
1120 {
1121 // transparent
1122 SkTDArray<SkRect> circles;
1123 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 5, &circles);
Michael Ludwiga6a84002019-04-12 15:03:02 -04001124 fOriginalBMs[0] = make_bitmap(kRGBA_8888_SkColorType, path, circles, false, fUseDomain);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001125 }
1126
1127 {
1128 // opaque
1129 SkTDArray<SkRect> circles;
1130 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 7, &circles);
Michael Ludwiga6a84002019-04-12 15:03:02 -04001131 fOriginalBMs[1] = make_bitmap(kRGBA_8888_SkColorType, path, circles, true, fUseDomain);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001132 }
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001133
1134 if (fUseTargetColorSpace) {
1135 fTargetColorSpace = SkColorSpace::MakeSRGB()->makeColorSpin();
1136 }
Robert Phillips51c89e42018-10-05 13:30:43 -04001137 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001138
Robert Phillips51c89e42018-10-05 13:30:43 -04001139 void createImages(GrContext* context) {
Jim Van Verth9bf81202018-10-30 15:53:36 -04001140 int counter = 0;
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001141 for (bool opaque : { false, true }) {
1142 for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
1143 PlaneData planes;
1144 extract_planes(fOriginalBMs[opaque], (SkYUVColorSpace) cs, &planes);
1145
Robert Phillipsbb749902019-06-10 17:20:12 -04001146 for (int format = kP016_YUVFormat; format <= kLast_YUVFormat; ++format) {
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001147 SkBitmap resultBMs[4];
1148 SkYUVAIndex yuvaIndices[4];
Robert Phillips2dd1b472019-03-21 09:00:20 -04001149
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001150 create_YUV(planes, (YUVFormat) format, resultBMs, yuvaIndices, opaque);
Robert Phillips2dd1b472019-03-21 09:00:20 -04001151
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001152 int numTextures;
1153 if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures)) {
1154 continue;
1155 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001156
Robert Phillips51c89e42018-10-05 13:30:43 -04001157 if (context) {
Chris Dalton382b1222019-02-07 10:05:55 +00001158 if (context->abandoned()) {
1159 return;
1160 }
1161
Robert Phillips51c89e42018-10-05 13:30:43 -04001162 GrBackendTexture yuvaTextures[4];
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001163 SkPixmap yuvaPixmaps[4];
Robert Phillips51c89e42018-10-05 13:30:43 -04001164
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001165 for (int i = 0; i < numTextures; ++i) {
Robert Phillipscb1adb42019-06-10 15:09:34 -04001166 yuvaTextures[i] = create_yuva_texture(context, resultBMs[i],
Robert Phillipsbb749902019-06-10 17:20:12 -04001167 yuvaIndices, i,
1168 (YUVFormat) format);
Brian Salomonf2580c02019-01-30 09:13:31 -05001169 if (yuvaTextures[i].isValid()) {
1170 fBackendTextures.push_back(yuvaTextures[i]);
1171 }
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001172 yuvaPixmaps[i] = resultBMs[i].pixmap();
Robert Phillips51c89e42018-10-05 13:30:43 -04001173 }
1174
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001175 int counterMod = counter % 3;
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001176 if (fUseDomain && counterMod == 0) {
Michael Ludwiga6a84002019-04-12 15:03:02 -04001177 // Copies flatten to RGB when they copy the YUVA data, which doesn't
1178 // know about the intended domain and the domain padding bleeds in
1179 counterMod = 1;
1180 }
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001181 switch (counterMod) {
1182 case 0:
Jim Van Verth9bf81202018-10-30 15:53:36 -04001183 fImages[opaque][cs][format] = SkImage::MakeFromYUVATexturesCopy(
1184 context,
1185 (SkYUVColorSpace)cs,
1186 yuvaTextures,
1187 yuvaIndices,
1188 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1189 kTopLeft_GrSurfaceOrigin);
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001190 break;
1191 case 1:
Jim Van Verth9bf81202018-10-30 15:53:36 -04001192 fImages[opaque][cs][format] = SkImage::MakeFromYUVATextures(
1193 context,
1194 (SkYUVColorSpace)cs,
1195 yuvaTextures,
1196 yuvaIndices,
1197 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1198 kTopLeft_GrSurfaceOrigin);
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001199 break;
1200 case 2:
1201 default:
1202 fImages[opaque][cs][format] = SkImage::MakeFromYUVAPixmaps(
1203 context,
1204 (SkYUVColorSpace)cs,
1205 yuvaPixmaps,
1206 yuvaIndices,
1207 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1208 kTopLeft_GrSurfaceOrigin, true);
1209 break;
Jim Van Verth9bf81202018-10-30 15:53:36 -04001210 }
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001211 ++counter;
Jim Van Verth9bf81202018-10-30 15:53:36 -04001212 } else {
Robert Phillips51c89e42018-10-05 13:30:43 -04001213 fImages[opaque][cs][format] = make_yuv_gen_image(
1214 fOriginalBMs[opaque].info(),
Robert Phillips51c89e42018-10-05 13:30:43 -04001215 (SkYUVColorSpace) cs,
1216 yuvaIndices,
1217 resultBMs);
1218 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001219 }
1220 }
1221 }
1222 }
1223
1224 void onDraw(SkCanvas* canvas) override {
Robert Phillips51c89e42018-10-05 13:30:43 -04001225 this->createImages(canvas->getGrContext());
1226
Michael Ludwiga6a84002019-04-12 15:03:02 -04001227 SkRect srcRect = SkRect::MakeWH(fOriginalBMs[0].width(), fOriginalBMs[0].height());
1228 SkRect dstRect = SkRect::MakeXYWH(kLabelWidth, 0.f, srcRect.width(), srcRect.height());
1229
1230 SkCanvas::SrcRectConstraint constraint = SkCanvas::kFast_SrcRectConstraint;
1231 if (fUseDomain) {
1232 srcRect.inset(kDomainPadding, kDomainPadding);
1233 // Draw a larger rectangle to ensure bilerp filtering would normally read outside the
1234 // srcRect and hit the red pixels, if strict constraint weren't used.
1235 dstRect.fRight = kLabelWidth + 1.5f * srcRect.width();
1236 dstRect.fBottom = 1.5f * srcRect.height();
1237 constraint = SkCanvas::kStrict_SrcRectConstraint;
1238 }
1239
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001240 for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
Robert Phillips0a22ba82019-03-06 12:36:47 -05001241 SkPaint paint;
Michael Ludwiga6a84002019-04-12 15:03:02 -04001242 paint.setFilterQuality(kLow_SkFilterQuality);
Robert Phillips0a22ba82019-03-06 12:36:47 -05001243 if (kIdentity_SkYUVColorSpace == cs) {
1244 // The identity color space needs post processing to appear correctly
1245 paint.setColorFilter(yuv_to_rgb_colorfilter());
1246 }
1247
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001248 for (int opaque : { 0, 1 }) {
Michael Ludwiga6a84002019-04-12 15:03:02 -04001249 dstRect.offsetTo(dstRect.fLeft, kLabelHeight);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001250
Michael Ludwiga6a84002019-04-12 15:03:02 -04001251 draw_col_label(canvas, dstRect.fLeft + dstRect.height() / 2, cs, opaque);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001252
Michael Ludwiga6a84002019-04-12 15:03:02 -04001253 canvas->drawBitmapRect(fOriginalBMs[opaque], srcRect, dstRect, nullptr, constraint);
1254 dstRect.offset(0.f, dstRect.height() + kPad);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001255
Robert Phillipsbb749902019-06-10 17:20:12 -04001256 for (int format = kP016_YUVFormat; format <= kLast_YUVFormat; ++format) {
Michael Ludwiga6a84002019-04-12 15:03:02 -04001257 draw_row_label(canvas, dstRect.fTop, format);
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001258 if (fUseTargetColorSpace && fImages[opaque][cs][format]) {
Robert Phillips0a22ba82019-03-06 12:36:47 -05001259 // Making a CS-specific version of a kIdentity_SkYUVColorSpace YUV image
1260 // doesn't make a whole lot of sense. The colorSpace conversion will
1261 // operate on the YUV components rather than the RGB components.
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001262 sk_sp<SkImage> csImage =
1263 fImages[opaque][cs][format]->makeColorSpace(fTargetColorSpace);
Michael Ludwiga6a84002019-04-12 15:03:02 -04001264 canvas->drawImageRect(csImage, srcRect, dstRect, &paint, constraint);
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001265 } else {
Michael Ludwiga6a84002019-04-12 15:03:02 -04001266 canvas->drawImageRect(fImages[opaque][cs][format], srcRect, dstRect, &paint,
1267 constraint);
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001268 }
Michael Ludwiga6a84002019-04-12 15:03:02 -04001269 dstRect.offset(0.f, dstRect.height() + kPad);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001270 }
1271
Michael Ludwiga6a84002019-04-12 15:03:02 -04001272 dstRect.offset(dstRect.width() + kPad, 0.f);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001273 }
1274 }
Brian Salomon8f388ce2019-01-29 14:50:53 -05001275 if (auto context = canvas->getGrContext()) {
Chris Dalton382b1222019-02-07 10:05:55 +00001276 if (!context->abandoned()) {
1277 context->flush();
1278 GrGpu* gpu = context->priv().getGpu();
1279 SkASSERT(gpu);
1280 gpu->testingOnly_flushGpuAndSync();
1281 for (const auto& tex : fBackendTextures) {
Robert Phillips5c7a25b2019-05-20 08:38:07 -04001282 context->deleteBackendTexture(tex);
Chris Dalton382b1222019-02-07 10:05:55 +00001283 }
1284 fBackendTextures.reset();
Brian Salomon8f388ce2019-01-29 14:50:53 -05001285 }
Brian Salomon8f388ce2019-01-29 14:50:53 -05001286 }
1287 SkASSERT(!fBackendTextures.count());
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001288 }
1289
1290private:
Robert Phillips2dd1b472019-03-21 09:00:20 -04001291 SkBitmap fOriginalBMs[2];
1292 sk_sp<SkImage> fImages[2][kLastEnum_SkYUVColorSpace + 1][kLast_YUVFormat + 1];
Brian Salomon8f388ce2019-01-29 14:50:53 -05001293 SkTArray<GrBackendTexture> fBackendTextures;
Robert Phillips2dd1b472019-03-21 09:00:20 -04001294 bool fUseTargetColorSpace;
Michael Ludwiga6a84002019-04-12 15:03:02 -04001295 bool fUseDomain;
Robert Phillips2dd1b472019-03-21 09:00:20 -04001296 sk_sp<SkColorSpace> fTargetColorSpace;
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001297
1298 typedef GM INHERITED;
1299};
1300
1301//////////////////////////////////////////////////////////////////////////////
1302
Michael Ludwiga6a84002019-04-12 15:03:02 -04001303DEF_GM(return new WackyYUVFormatsGM(/* cs */ false, /* domain */ false);)
1304DEF_GM(return new WackyYUVFormatsGM(/* cs */ true, /* domain */ false);)
1305DEF_GM(return new WackyYUVFormatsGM(/* cs */ false, /* domain */ true);)
Brian Osmane9560492019-02-05 17:00:03 -05001306
Chris Dalton3a778372019-02-07 15:23:36 -07001307class YUVMakeColorSpaceGM : public GpuGM {
Brian Osmane9560492019-02-05 17:00:03 -05001308public:
1309 YUVMakeColorSpaceGM() {
1310 this->setBGColor(0xFFCCCCCC);
1311 }
1312
1313protected:
1314 SkString onShortName() override {
1315 return SkString("yuv_make_color_space");
1316 }
1317
1318 SkISize onISize() override {
1319 int numCols = 4; // (transparent, opaque) x (untagged, tagged)
1320 int numRows = 5; // original, YUV, subset, readPixels, makeNonTextureImage
1321 return SkISize::Make(numCols * (kTileWidthHeight + kPad) + kPad,
1322 numRows * (kTileWidthHeight + kPad) + kPad);
1323 }
1324
1325 void onOnceBeforeDraw() override {
1326 SkPoint origin = { kTileWidthHeight/2.0f, kTileWidthHeight/2.0f };
1327 float outerRadius = kTileWidthHeight/2.0f - 20.0f;
1328 float innerRadius = 20.0f;
1329
1330 {
1331 // transparent
1332 SkTDArray<SkRect> circles;
1333 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 5, &circles);
Michael Ludwiga6a84002019-04-12 15:03:02 -04001334 fOriginalBMs[0] = make_bitmap(kN32_SkColorType, path, circles, false, false);
Brian Osmane9560492019-02-05 17:00:03 -05001335 }
1336
1337 {
1338 // opaque
1339 SkTDArray<SkRect> circles;
1340 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 7, &circles);
Michael Ludwiga6a84002019-04-12 15:03:02 -04001341 fOriginalBMs[1] = make_bitmap(kN32_SkColorType, path, circles, true, false);
Brian Osmane9560492019-02-05 17:00:03 -05001342 }
1343
1344 fTargetColorSpace = SkColorSpace::MakeSRGB()->makeColorSpin();
1345 }
1346
1347 void createImages(GrContext* context) {
1348 for (bool opaque : { false, true }) {
1349 PlaneData planes;
1350 extract_planes(fOriginalBMs[opaque], kJPEG_SkYUVColorSpace, &planes);
1351
1352 SkBitmap resultBMs[4];
1353 SkYUVAIndex yuvaIndices[4];
Robert Phillips2dd1b472019-03-21 09:00:20 -04001354
Brian Osmane9560492019-02-05 17:00:03 -05001355 create_YUV(planes, kAYUV_YUVFormat, resultBMs, yuvaIndices, opaque);
Robert Phillips2dd1b472019-03-21 09:00:20 -04001356
Brian Osmane9560492019-02-05 17:00:03 -05001357 int numTextures;
1358 if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures)) {
1359 continue;
1360 }
1361
Brian Osmane9560492019-02-05 17:00:03 -05001362 GrBackendTexture yuvaTextures[4];
1363 for (int i = 0; i < numTextures; ++i) {
Robert Phillipsbb749902019-06-10 17:20:12 -04001364 yuvaTextures[i] = create_yuva_texture(context, resultBMs[i], yuvaIndices, i,
1365 kAYUV_YUVFormat);
Brian Osmane9560492019-02-05 17:00:03 -05001366 if (yuvaTextures[i].isValid()) {
1367 fBackendTextures.push_back(yuvaTextures[i]);
1368 }
1369 }
1370
1371 fImages[opaque][0] = SkImage::MakeFromYUVATextures(
1372 context,
1373 kJPEG_SkYUVColorSpace,
1374 yuvaTextures,
1375 yuvaIndices,
1376 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1377 kTopLeft_GrSurfaceOrigin);
1378 fImages[opaque][1] = SkImage::MakeFromYUVATextures(
1379 context,
1380 kJPEG_SkYUVColorSpace,
1381 yuvaTextures,
1382 yuvaIndices,
1383 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1384 kTopLeft_GrSurfaceOrigin,
1385 SkColorSpace::MakeSRGB());
1386 }
1387 }
1388
Chris Dalton3a778372019-02-07 15:23:36 -07001389 void onDraw(GrContext* context, GrRenderTargetContext*, SkCanvas* canvas) override {
Brian Osmane9560492019-02-05 17:00:03 -05001390 this->createImages(context);
1391
1392 int x = kPad;
1393 for (int tagged : { 0, 1 }) {
1394 for (int opaque : { 0, 1 }) {
1395 int y = kPad;
1396
1397 auto raster = SkImage::MakeFromBitmap(fOriginalBMs[opaque])
1398 ->makeColorSpace(fTargetColorSpace);
1399 canvas->drawImage(raster, x, y);
1400 y += kTileWidthHeight + kPad;
1401
1402 auto yuv = fImages[opaque][tagged]->makeColorSpace(fTargetColorSpace);
1403 SkASSERT(SkColorSpace::Equals(yuv->colorSpace(), fTargetColorSpace.get()));
1404 canvas->drawImage(yuv, x, y);
1405 y += kTileWidthHeight + kPad;
1406
1407 auto subset = yuv->makeSubset(SkIRect::MakeWH(kTileWidthHeight / 2,
1408 kTileWidthHeight / 2));
1409 canvas->drawImage(subset, x, y);
1410 y += kTileWidthHeight + kPad;
1411
1412 auto nonTexture = yuv->makeNonTextureImage();
1413 canvas->drawImage(nonTexture, x, y);
1414 y += kTileWidthHeight + kPad;
1415
1416 SkBitmap readBack;
Brian Salomon5ad6fd32019-03-21 15:30:08 -04001417 readBack.allocPixels(yuv->imageInfo());
Brian Osmane9560492019-02-05 17:00:03 -05001418 yuv->readPixels(readBack.pixmap(), 0, 0);
1419 canvas->drawBitmap(readBack, x, y);
1420
1421 x += kTileWidthHeight + kPad;
1422 }
1423 }
1424
1425 context->flush();
1426 GrGpu* gpu = context->priv().getGpu();
1427 SkASSERT(gpu);
1428 gpu->testingOnly_flushGpuAndSync();
1429 for (const auto& tex : fBackendTextures) {
Robert Phillips5c7a25b2019-05-20 08:38:07 -04001430 context->deleteBackendTexture(tex);
Brian Osmane9560492019-02-05 17:00:03 -05001431 }
1432 fBackendTextures.reset();
1433 }
1434
1435private:
1436 SkBitmap fOriginalBMs[2];
1437 sk_sp<SkImage> fImages[2][2];
1438 SkTArray<GrBackendTexture> fBackendTextures;
1439 sk_sp<SkColorSpace> fTargetColorSpace;
1440
1441 typedef GM INHERITED;
1442};
1443
1444DEF_GM(return new YUVMakeColorSpaceGM();)
1445
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001446}
Mike Reed6a5f7e22019-05-23 15:30:07 -04001447
1448///////////////
1449
Mike Reed6a5f7e22019-05-23 15:30:07 -04001450#include "include/effects/SkColorMatrix.h"
Mike Klein4b432fa2019-06-06 11:44:05 -05001451#include "src/core/SkAutoPixmapStorage.h"
1452#include "src/core/SkYUVMath.h"
1453#include "tools/Resources.h"
Mike Reed6a5f7e22019-05-23 15:30:07 -04001454
1455static void draw_into_alpha(const SkImage* img, sk_sp<SkColorFilter> cf, const SkPixmap& dst) {
1456 auto canvas = SkCanvas::MakeRasterDirect(dst.info(), dst.writable_addr(), dst.rowBytes());
1457 canvas->scale(1.0f * dst.width() / img->width(), 1.0f * dst.height() / img->height());
1458 SkPaint paint;
1459 paint.setFilterQuality(kLow_SkFilterQuality);
1460 paint.setColorFilter(cf);
1461 paint.setBlendMode(SkBlendMode::kSrc);
1462 canvas->drawImage(img, 0, 0, &paint);
1463}
1464
1465static void split_into_yuv(const SkImage* img, SkYUVColorSpace cs, const SkPixmap dst[3]) {
1466 float m[20];
1467 SkColorMatrix_RGB2YUV(cs, m);
1468
1469 memcpy(m + 15, m + 0, 5 * sizeof(float)); // copy Y into A
1470 draw_into_alpha(img, SkColorFilters::Matrix(m), dst[0]);
1471
1472 memcpy(m + 15, m + 5, 5 * sizeof(float)); // copy U into A
1473 draw_into_alpha(img, SkColorFilters::Matrix(m), dst[1]);
1474
1475 memcpy(m + 15, m + 10, 5 * sizeof(float)); // copy V into A
1476 draw_into_alpha(img, SkColorFilters::Matrix(m), dst[2]);
1477}
1478
1479static void draw_diff(SkCanvas* canvas, SkScalar x, SkScalar y,
1480 const SkImage* a, const SkImage* b) {
1481 auto sh = SkShaders::Blend(SkBlendMode::kDifference, a->makeShader(), b->makeShader());
1482 SkPaint paint;
1483 paint.setShader(sh);
1484 canvas->save();
1485 canvas->translate(x, y);
1486 canvas->drawRect(SkRect::MakeWH(a->width(), a->height()), paint);
1487
1488 SkColorMatrix cm;
1489 cm.setScale(64, 64, 64);
1490 paint.setShader(sh->makeWithColorFilter(SkColorFilters::Matrix(cm)));
1491 canvas->translate(0, a->height());
1492 canvas->drawRect(SkRect::MakeWH(a->width(), a->height()), paint);
1493
1494 canvas->restore();
1495}
1496
1497// Exercises SkColorMatrix_RGB2YUV for yuv colorspaces, showing the planes, and the
1498// resulting (recombined) images (gpu only for now).
1499//
1500class YUVSplitterGM : public skiagm::GM {
1501 sk_sp<SkImage> fOrig;
1502 SkAutoPixmapStorage fStorage[3];
1503 SkPixmap fPM[3];
1504
1505public:
1506 YUVSplitterGM() {}
1507
1508protected:
1509
1510 SkString onShortName() override {
1511 return SkString("yuv_splitter");
1512 }
1513
1514 SkISize onISize() override {
1515 return SkISize::Make(1024, 768);
1516 }
1517
1518 void onOnceBeforeDraw() override {
1519 fOrig = GetResourceAsImage("images/mandrill_256.png");
1520
1521 SkImageInfo info = SkImageInfo::Make(fOrig->width(), fOrig->height(), kAlpha_8_SkColorType,
1522 kPremul_SkAlphaType);
1523 fStorage[0].alloc(info);
1524 if (0) {
1525 // if you want to scale U,V down by 1/2
1526 info = info.makeWH(info.width()/2, info.height()/2);
1527 }
1528 fStorage[1].alloc(info);
1529 fStorage[2].alloc(info);
1530 for (int i = 0; i < 3; ++i) {
1531 fPM[i] = fStorage[i];
1532 }
1533 }
1534
1535 void onDraw(SkCanvas* canvas) override {
1536 SkYUVAIndex indices[4];
1537 indices[SkYUVAIndex::kY_Index] = {0, SkColorChannel::kR};
1538 indices[SkYUVAIndex::kU_Index] = {1, SkColorChannel::kR};
1539 indices[SkYUVAIndex::kV_Index] = {2, SkColorChannel::kR};
1540 indices[SkYUVAIndex::kA_Index] = {-1, SkColorChannel::kR};
1541
1542 canvas->translate(fOrig->width(), 0);
1543 canvas->save();
1544 for (auto cs : {kRec709_SkYUVColorSpace, kRec601_SkYUVColorSpace, kJPEG_SkYUVColorSpace}) {
1545 split_into_yuv(fOrig.get(), cs, fPM);
1546 auto img = SkImage::MakeFromYUVAPixmaps(canvas->getGrContext(), cs, fPM, indices,
1547 fPM[0].info().dimensions(),
1548 kTopLeft_GrSurfaceOrigin,
1549 false, false, nullptr);
1550 if (img) {
1551 canvas->drawImage(img, 0, 0, nullptr);
1552 draw_diff(canvas, 0, fOrig->height(), fOrig.get(), img.get());
1553 }
1554 canvas->translate(fOrig->width(), 0);
1555 }
1556 canvas->restore();
1557 canvas->translate(-fOrig->width(), 0);
1558
1559 canvas->drawImage(SkImage::MakeRasterCopy(fPM[0]), 0, 0, nullptr);
1560 canvas->drawImage(SkImage::MakeRasterCopy(fPM[1]), 0, fPM[0].height(), nullptr);
1561 canvas->drawImage(SkImage::MakeRasterCopy(fPM[2]),
1562 0, fPM[0].height() + fPM[1].height(), nullptr);
1563 }
1564
1565private:
1566 typedef GM INHERITED;
1567};
1568DEF_GM( return new YUVSplitterGM; )