blob: a8cd873a7f020b0d48e5d8bc3752bf4be49545ac [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
73 // 4:4:4 formats, 64 bpp
74 kY416_YUVFormat, // 16-bit AVYU values all interleaved (1 texture)
75
Robert Phillipsbfa76f22018-10-03 12:12:26 -040076 // 4:4:4 formats, 32 bpp
Robert Phillipsbb749902019-06-10 17:20:12 -040077 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)
Robert Phillipsbfa76f22018-10-03 12:12:26 -040079
80 // 4:2:0 formats, 12 bpp
Robert Phillipsbb749902019-06-10 17:20:12 -040081 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)
Robert Phillipsbfa76f22018-10-03 12:12:26 -040083
Robert Phillipsbb749902019-06-10 17:20:12 -040084 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)
Robert Phillipsbfa76f22018-10-03 12:12:26 -040086
87 kLast_YUVFormat = kYV12_YUVFormat
88};
89
Robert Phillipsbb749902019-06-10 17:20:12 -040090static bool format_uses_16_bpp(YUVFormat yuvFormat) {
91 return kP016_YUVFormat == yuvFormat ||
92 kP010_YUVFormat == yuvFormat ||
93 kY416_YUVFormat == yuvFormat;
94}
95
96static bool format_has_builtin_alpha(YUVFormat yuvFormat) {
97 return kY416_YUVFormat == yuvFormat ||
98 kAYUV_YUVFormat == yuvFormat ||
99 kY410_YUVFormat == yuvFormat;
100}
101
102static bool format_cant_be_represented_with_pixmaps(YUVFormat yuvFormat) {
103 return kP016_YUVFormat == yuvFormat ||
104 kP010_YUVFormat == yuvFormat ||
105 kY416_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
116 case kP010_YUVFormat:
117 yuvaIndices[0].fIndex = 0;
118 yuvaIndices[0].fChannel = SkColorChannel::kR; // bc 16bit is stored in R16 format
119 yuvaIndices[1].fIndex = 1;
120 yuvaIndices[1].fChannel = SkColorChannel::kR;
121 yuvaIndices[2].fIndex = 1;
122 yuvaIndices[2].fChannel = SkColorChannel::kG;
123 if (addExtraAlpha) {
124 yuvaIndices[3].fIndex = 2;
125 yuvaIndices[3].fChannel = SkColorChannel::kR; // bc 16bit is stored in R16 format
126 } else {
127 yuvaIndices[3].fIndex = -1; // No alpha channel
128 }
129 break;
130 case kY416_YUVFormat:
131 SkASSERT(!addExtraAlpha); // this format already has an alpha channel
132 yuvaIndices[0].fIndex = 0;
133 yuvaIndices[0].fChannel = SkColorChannel::kG;
134 yuvaIndices[1].fIndex = 0;
135 yuvaIndices[1].fChannel = SkColorChannel::kB;
136 yuvaIndices[2].fIndex = 0;
137 yuvaIndices[2].fChannel = SkColorChannel::kR;
138 yuvaIndices[3].fIndex = 0;
139 yuvaIndices[3].fChannel = SkColorChannel::kA;
140 break;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400141 case kAYUV_YUVFormat:
142 SkASSERT(!addExtraAlpha); // this format already has an alpha channel
143 yuvaIndices[0].fIndex = 0;
144 yuvaIndices[0].fChannel = SkColorChannel::kR;
145 yuvaIndices[1].fIndex = 0;
146 yuvaIndices[1].fChannel = SkColorChannel::kG;
147 yuvaIndices[2].fIndex = 0;
148 yuvaIndices[2].fChannel = SkColorChannel::kB;
149 yuvaIndices[3].fIndex = 0;
150 yuvaIndices[3].fChannel = SkColorChannel::kA;
151 break;
152 case kY410_YUVFormat:
153 SkASSERT(!addExtraAlpha); // this format already has an alpha channel
154 yuvaIndices[0].fIndex = 0;
155 yuvaIndices[0].fChannel = SkColorChannel::kG;
156 yuvaIndices[1].fIndex = 0;
157 yuvaIndices[1].fChannel = SkColorChannel::kB;
158 yuvaIndices[2].fIndex = 0;
159 yuvaIndices[2].fChannel = SkColorChannel::kR;
160 yuvaIndices[3].fIndex = 0;
161 yuvaIndices[3].fChannel = SkColorChannel::kA;
162 break;
163 case kNV12_YUVFormat:
164 yuvaIndices[0].fIndex = 0;
165 yuvaIndices[0].fChannel = SkColorChannel::kA;
166 yuvaIndices[1].fIndex = 1;
167 yuvaIndices[1].fChannel = SkColorChannel::kR;
168 yuvaIndices[2].fIndex = 1;
169 yuvaIndices[2].fChannel = SkColorChannel::kG;
170 if (addExtraAlpha) {
171 yuvaIndices[3].fIndex = 2;
172 yuvaIndices[3].fChannel = SkColorChannel::kA;
173 } else {
174 yuvaIndices[3].fIndex = -1; // No alpha channel
175 }
176 break;
177 case kNV21_YUVFormat:
178 yuvaIndices[0].fIndex = 0;
179 yuvaIndices[0].fChannel = SkColorChannel::kA;
180 yuvaIndices[1].fIndex = 1;
181 yuvaIndices[1].fChannel = SkColorChannel::kG;
182 yuvaIndices[2].fIndex = 1;
183 yuvaIndices[2].fChannel = SkColorChannel::kR;
184 if (addExtraAlpha) {
185 yuvaIndices[3].fIndex = 2;
186 yuvaIndices[3].fChannel = SkColorChannel::kA;
187 } else {
188 yuvaIndices[3].fIndex = -1; // No alpha channel
189 }
190 break;
191 case kI420_YUVFormat:
192 yuvaIndices[0].fIndex = 0;
193 yuvaIndices[0].fChannel = SkColorChannel::kA;
194 yuvaIndices[1].fIndex = 1;
195 yuvaIndices[1].fChannel = SkColorChannel::kA;
196 yuvaIndices[2].fIndex = 2;
197 yuvaIndices[2].fChannel = SkColorChannel::kA;
198 if (addExtraAlpha) {
199 yuvaIndices[3].fIndex = 3;
200 yuvaIndices[3].fChannel = SkColorChannel::kA;
201 } else {
202 yuvaIndices[3].fIndex = -1; // No alpha channel
203 }
204 break;
205 case kYV12_YUVFormat:
206 yuvaIndices[0].fIndex = 0;
207 yuvaIndices[0].fChannel = SkColorChannel::kA;
208 yuvaIndices[1].fIndex = 2;
209 yuvaIndices[1].fChannel = SkColorChannel::kA;
210 yuvaIndices[2].fIndex = 1;
211 yuvaIndices[2].fChannel = SkColorChannel::kA;
212 if (addExtraAlpha) {
213 yuvaIndices[3].fIndex = 3;
214 yuvaIndices[3].fChannel = SkColorChannel::kA;
215 } else {
216 yuvaIndices[3].fIndex = -1; // No alpha channel
217 }
218 break;
219 }
220}
221
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400222// All the planes we need to construct the various YUV formats
223struct PlaneData {
224 SkBitmap fYFull;
225 SkBitmap fUFull;
226 SkBitmap fVFull;
227 SkBitmap fAFull;
228 SkBitmap fUQuarter; // 2x2 downsampled U channel
229 SkBitmap fVQuarter; // 2x2 downsampled V channel
230};
231
232// Add a portion of a circle to 'path'. The points 'o1' and 'o2' are on the border of the circle
233// and have tangents 'v1' and 'v2'.
234static void add_arc(SkPath* path,
235 const SkPoint& o1, const SkVector& v1,
236 const SkPoint& o2, const SkVector& v2,
237 SkTDArray<SkRect>* circles, bool takeLongWayRound) {
238
239 SkVector v3 = { -v1.fY, v1.fX };
240 SkVector v4 = { v2.fY, -v2.fX };
241
242 SkScalar t = ((o2.fX - o1.fX) * v4.fY - (o2.fY - o1.fY) * v4.fX) / v3.cross(v4);
243 SkPoint center = { o1.fX + t * v3.fX, o1.fY + t * v3.fY };
244
245 SkRect r = { center.fX - t, center.fY - t, center.fX + t, center.fY + t };
246
247 if (circles) {
248 circles->push_back(r);
249 }
250
251 SkVector startV = o1 - center, endV = o2 - center;
252 startV.normalize();
253 endV.normalize();
254
255 SkScalar startDeg = SkRadiansToDegrees(SkScalarATan2(startV.fY, startV.fX));
256 SkScalar endDeg = SkRadiansToDegrees(SkScalarATan2(endV.fY, endV.fX));
257
258 startDeg += 360.0f;
259 startDeg = fmodf(startDeg, 360.0f);
260
261 endDeg += 360.0f;
262 endDeg = fmodf(endDeg, 360.0f);
263
264 if (endDeg < startDeg) {
265 endDeg += 360.0f;
266 }
267
268 SkScalar sweepDeg = SkTAbs(endDeg - startDeg);
269 if (!takeLongWayRound) {
270 sweepDeg = sweepDeg - 360;
271 }
272
273 path->arcTo(r, startDeg, sweepDeg, false);
274}
275
276static SkPath create_splat(const SkPoint& o, SkScalar innerRadius, SkScalar outerRadius,
277 SkScalar ratio, int numLobes, SkTDArray<SkRect>* circles) {
278 if (numLobes <= 1) {
279 return SkPath();
280 }
281
282 SkPath p;
283
284 int numDivisions = 2 * numLobes;
285 SkScalar fullLobeDegrees = 360.0f / numLobes;
286 SkScalar outDegrees = ratio * fullLobeDegrees / (ratio + 1.0f);
287 SkScalar innerDegrees = fullLobeDegrees / (ratio + 1.0f);
288 SkMatrix outerStep, innerStep;
289 outerStep.setRotate(outDegrees);
290 innerStep.setRotate(innerDegrees);
291 SkVector curV = SkVector::Make(0.0f, 1.0f);
292
293 if (circles) {
294 circles->push_back(SkRect::MakeLTRB(o.fX - innerRadius, o.fY - innerRadius,
295 o.fX + innerRadius, o.fY + innerRadius));
296 }
297
298 p.moveTo(o.fX + innerRadius * curV.fX, o.fY + innerRadius * curV.fY);
299
300 for (int i = 0; i < numDivisions; ++i) {
301
302 SkVector nextV;
303 if (0 == (i % 2)) {
304 nextV = outerStep.mapVector(curV.fX, curV.fY);
305
306 SkPoint top = SkPoint::Make(o.fX + outerRadius * curV.fX,
307 o.fY + outerRadius * curV.fY);
308 SkPoint nextTop = SkPoint::Make(o.fX + outerRadius * nextV.fX,
309 o.fY + outerRadius * nextV.fY);
310
311 p.lineTo(top);
312 add_arc(&p, top, curV, nextTop, nextV, circles, true);
313 } else {
314 nextV = innerStep.mapVector(curV.fX, curV.fY);
315
316 SkPoint bot = SkPoint::Make(o.fX + innerRadius * curV.fX,
317 o.fY + innerRadius * curV.fY);
318 SkPoint nextBot = SkPoint::Make(o.fX + innerRadius * nextV.fX,
319 o.fY + innerRadius * nextV.fY);
320
321 p.lineTo(bot);
322 add_arc(&p, bot, curV, nextBot, nextV, nullptr, false);
323 }
324
325 curV = nextV;
326 }
327
328 p.close();
329
330 return p;
331}
332
Robert Phillips2dd1b472019-03-21 09:00:20 -0400333static SkBitmap make_bitmap(SkColorType colorType, const SkPath& path,
Michael Ludwiga6a84002019-04-12 15:03:02 -0400334 const SkTDArray<SkRect>& circles, bool opaque, bool padWithRed) {
Mike Kleinea3f0142019-03-20 11:12:10 -0500335 const SkColor kGreen = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 178, 240, 104));
336 const SkColor kBlue = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 173, 167, 252));
337 const SkColor kYellow = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 255, 221, 117));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400338
Michael Ludwiga6a84002019-04-12 15:03:02 -0400339 int widthHeight = kTileWidthHeight + (padWithRed ? 2 * kDomainPadding : 0);
340
341 SkImageInfo ii = SkImageInfo::Make(widthHeight, widthHeight,
Robert Phillips2dd1b472019-03-21 09:00:20 -0400342 colorType, kPremul_SkAlphaType);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400343
344 SkBitmap bm;
345 bm.allocPixels(ii);
346
Robert Phillips2dd1b472019-03-21 09:00:20 -0400347 std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirect(ii,
348 bm.getPixels(),
349 bm.rowBytes());
Michael Ludwiga6a84002019-04-12 15:03:02 -0400350 if (padWithRed) {
351 canvas->clear(SK_ColorRED);
352 canvas->translate(kDomainPadding, kDomainPadding);
353 canvas->clipRect(SkRect::MakeWH(kTileWidthHeight, kTileWidthHeight));
354 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400355 canvas->clear(opaque ? kGreen : SK_ColorTRANSPARENT);
356
357 SkPaint paint;
358 paint.setAntiAlias(false); // serialize-8888 doesn't seem to work well w/ partial transparency
359 paint.setColor(kBlue);
360
361 canvas->drawPath(path, paint);
362
363 paint.setColor(opaque ? kYellow : SK_ColorTRANSPARENT);
364 paint.setBlendMode(SkBlendMode::kSrc);
365 for (int i = 0; i < circles.count(); ++i) {
366 SkRect r = circles[i];
367 r.inset(r.width()/4, r.height()/4);
368 canvas->drawOval(r, paint);
369 }
370
371 return bm;
372}
373
Robert Phillips1c7062d2018-10-04 10:44:53 -0400374static void convert_rgba_to_yuva_601_shared(SkColor col, uint8_t yuv[4],
375 uint8_t off, uint8_t range) {
376 static const float Kr = 0.299f;
377 static const float Kb = 0.114f;
378 static const float Kg = 1.0f - Kr - Kb;
379
380 float r = SkColorGetR(col) / 255.0f;
381 float g = SkColorGetG(col) / 255.0f;
382 float b = SkColorGetB(col) / 255.0f;
383
384 float Ey = Kr * r + Kg * g + Kb * b;
385 float Ecb = (b - Ey) / 1.402f;
386 float Ecr = (r - Ey) / 1.772;
Robert Phillipsbb749902019-06-10 17:20:12 -0400387 SkASSERT(Ey >= 0.0f && Ey <= 1.0f);
388 SkASSERT(Ecb >= -0.5f && Ecb <= 0.5f);
389 SkASSERT(Ecr >= -0.5f && Ecr <= 0.5f);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400390
391 yuv[0] = SkScalarRoundToInt( range * Ey + off );
392 yuv[1] = SkScalarRoundToInt( 224 * Ecb + 128 );
393 yuv[2] = SkScalarRoundToInt( 224 * Ecr + 128 );
394 yuv[3] = SkColorGetA(col);
395}
396
397static void convert_rgba_to_yuva_jpeg(SkColor col, uint8_t yuv[4]) {
398 // full swing from 0..255
399 convert_rgba_to_yuva_601_shared(col, yuv, 0, 255);
400}
401
402static void convert_rgba_to_yuva_601(SkColor col, uint8_t yuv[4]) {
403 // partial swing from 16..235
404 convert_rgba_to_yuva_601_shared(col, yuv, 16, 219);
405
406}
407
408static void convert_rgba_to_yuva_709(SkColor col, uint8_t yuv[4]) {
409 static const float Kr = 0.2126f;
410 static const float Kb = 0.0722f;
411 static const float Kg = 1.0f - Kr - Kb;
412
413 float r = SkColorGetR(col) / 255.0f;
414 float g = SkColorGetG(col) / 255.0f;
415 float b = SkColorGetB(col) / 255.0f;
416
417 float Ey = Kr * r + Kg * g + Kb * b;
418 float Ecb = (b - Ey) / 1.8556f;
419 float Ecr = (r - Ey) / 1.5748;
Robert Phillipsbb749902019-06-10 17:20:12 -0400420 SkASSERT(Ey >= 0.0f && Ey <= 1.0f);
421 SkASSERT(Ecb >= -0.5f && Ecb <= 0.5f);
422 SkASSERT(Ecr >= -0.5f && Ecr <= 0.5f);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400423
424 yuv[0] = SkScalarRoundToInt( 219 * Ey + 16 );
425 yuv[1] = SkScalarRoundToInt( 224 * Ecb + 128 );
426 yuv[2] = SkScalarRoundToInt( 224 * Ecr + 128 );
427
428 yuv[3] = SkColorGetA(col);
429}
430
431
432static 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 -0400433 uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.0f * y + 1.402f * v - 0.703749f * 255),
Robert Phillips1c7062d2018-10-04 10:44:53 -0400434 0, 255);
Robert Phillips2dd1b472019-03-21 09:00:20 -0400435 uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.0f * y - (0.344136f * u) - (0.714136f * v) + 0.531211f * 255),
Robert Phillips1c7062d2018-10-04 10:44:53 -0400436 0, 255);
Robert Phillips2dd1b472019-03-21 09:00:20 -0400437 uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.0f * y + 1.772f * u - 0.889475f * 255),
Robert Phillips1c7062d2018-10-04 10:44:53 -0400438 0, 255);
439
Robert Phillips2dd1b472019-03-21 09:00:20 -0400440 SkPMColor c = SkPremultiplyARGBInline(a, b, g, r);
441 return c;
Robert Phillips1c7062d2018-10-04 10:44:53 -0400442}
443
444static 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 -0400445 uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.164f * y + 1.596f * v - 0.87075f * 255), 0, 255);
446 uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.164f * y - (0.391f * u) - (0.813f * v) + 0.52925f * 255), 0, 255);
447 uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.164f * y + 2.018f * u - 1.08175f * 255), 0, 255);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400448
Robert Phillips2dd1b472019-03-21 09:00:20 -0400449 SkPMColor c = SkPremultiplyARGBInline(a, b, g, r);
450 return c;
Robert Phillips1c7062d2018-10-04 10:44:53 -0400451}
452
453static 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 -0400454 uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.164f * y + (1.793f * v) - 0.96925f * 255), 0, 255);
455 uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.164f * y - (0.213f * u) - (0.533f * v) + 0.30025f * 255), 0, 255);
456 uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.164f * y + (2.112f * u) - 1.12875f * 255), 0, 255);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400457
Robert Phillips2dd1b472019-03-21 09:00:20 -0400458 SkPMColor c = SkPremultiplyARGBInline(a, b, g, r);
459 return c;
Robert Phillips1c7062d2018-10-04 10:44:53 -0400460}
461
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400462static void extract_planes(const SkBitmap& bm, SkYUVColorSpace yuvColorSpace, PlaneData* planes) {
Robert Phillips0a22ba82019-03-06 12:36:47 -0500463 if (kIdentity_SkYUVColorSpace == yuvColorSpace) {
464 // To test the identity color space we use JPEG YUV planes
465 yuvColorSpace = kJPEG_SkYUVColorSpace;
466 }
467
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400468 SkASSERT(!(bm.width() % 2));
469 SkASSERT(!(bm.height() % 2));
470
471 planes->fYFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
472 planes->fUFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
473 planes->fVFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
474 planes->fAFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
475 planes->fUQuarter.allocPixels(SkImageInfo::MakeA8(bm.width()/2, bm.height()/2));
476 planes->fVQuarter.allocPixels(SkImageInfo::MakeA8(bm.width()/2, bm.height()/2));
477
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400478 for (int y = 0; y < bm.height(); ++y) {
479 for (int x = 0; x < bm.width(); ++x) {
480 SkColor col = bm.getColor(x, y);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400481
Robert Phillips1c7062d2018-10-04 10:44:53 -0400482 uint8_t yuva[4];
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400483
Robert Phillips1c7062d2018-10-04 10:44:53 -0400484 if (kJPEG_SkYUVColorSpace == yuvColorSpace) {
485 convert_rgba_to_yuva_jpeg(col, yuva);
486 } else if (kRec601_SkYUVColorSpace == yuvColorSpace) {
487 convert_rgba_to_yuva_601(col, yuva);
488 } else {
489 SkASSERT(kRec709_SkYUVColorSpace == yuvColorSpace);
490 convert_rgba_to_yuva_709(col, yuva);
491 }
492
493 *planes->fYFull.getAddr8(x, y) = yuva[0];
494 *planes->fUFull.getAddr8(x, y) = yuva[1];
495 *planes->fVFull.getAddr8(x, y) = yuva[2];
496 *planes->fAFull.getAddr8(x, y) = yuva[3];
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400497 }
498 }
499
500 for (int y = 0; y < bm.height()/2; ++y) {
501 for (int x = 0; x < bm.width()/2; ++x) {
502 uint32_t uAccum = 0, vAccum = 0;
503
504 uAccum += *planes->fUFull.getAddr8(2*x, 2*y);
505 uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y);
506 uAccum += *planes->fUFull.getAddr8(2*x, 2*y+1);
507 uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y+1);
508
509 *planes->fUQuarter.getAddr8(x, y) = uAccum / 4.0f;
510
511 vAccum += *planes->fVFull.getAddr8(2*x, 2*y);
512 vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y);
513 vAccum += *planes->fVFull.getAddr8(2*x, 2*y+1);
514 vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y+1);
515
Robert Phillips1c7062d2018-10-04 10:44:53 -0400516 *planes->fVQuarter.getAddr8(x, y) = vAccum / 4.0f;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400517 }
518 }
519}
520
Robert Phillipsbb749902019-06-10 17:20:12 -0400521// Create a 2x2 downsampled SkBitmap. It is stored in an RGBA texture. It can optionally be
522// uv (i.e., for P016, P010 and NV12) or vu (i.e., NV21).
523static SkBitmap make_quarter_2_channel(const SkBitmap& fullY,
524 const SkBitmap& quarterU,
525 const SkBitmap& quarterV,
526 bool uv) {
527 SkBitmap result;
528
529 // There isn't a RG color type. Approx w/ RGBA.
530 result.allocPixels(SkImageInfo::Make(fullY.width()/2,
531 fullY.height()/2,
532 kRGBA_8888_SkColorType,
533 kUnpremul_SkAlphaType));
534
535 for (int y = 0; y < fullY.height()/2; ++y) {
536 for (int x = 0; x < fullY.width()/2; ++x) {
537 uint8_t u8 = *quarterU.getAddr8(x, y);
538 uint8_t v8 = *quarterV.getAddr8(x, y);
539
540 if (uv) {
541 // NOT premul!
542 // U and 0 swapped to match RGBA layout
543 *result.getAddr32(x, y) = SkColorSetARGB(0xFF, 0, v8, u8);
544 } else {
545 // NOT premul!
546 // V and 0 swapped to match RGBA layout
547 *result.getAddr32(x, y) = SkColorSetARGB(0xFF, 0, u8, v8);
548 }
549 }
550 }
551
552 return result;
553}
554
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400555// Recombine the separate planes into some YUV format
556static void create_YUV(const PlaneData& planes, YUVFormat yuvFormat,
557 SkBitmap resultBMs[], SkYUVAIndex yuvaIndices[4], bool opaque) {
558 int nextLayer = 0;
559
560 switch (yuvFormat) {
Robert Phillipsbb749902019-06-10 17:20:12 -0400561 case kY416_YUVFormat: {
562 // Although this is 16 bpp, store the data in an 8 bpp SkBitmap
563 SkBitmap yuvaFull;
564
565 yuvaFull.allocPixels(SkImageInfo::Make(planes.fYFull.width(), planes.fYFull.height(),
566 kRGBA_8888_SkColorType, kUnpremul_SkAlphaType));
567
568 for (int y = 0; y < planes.fYFull.height(); ++y) {
569 for (int x = 0; x < planes.fYFull.width(); ++x) {
570
571 uint8_t Y = *planes.fYFull.getAddr8(x, y);
572 uint8_t U = *planes.fUFull.getAddr8(x, y);
573 uint8_t V = *planes.fVFull.getAddr8(x, y);
574 uint8_t A = *planes.fAFull.getAddr8(x, y);
575
576 // NOT premul!
577 // U and V swapped to match RGBA layout
578 SkColor c = SkColorSetARGB(A, U, Y, V);
579 *yuvaFull.getAddr32(x, y) = c;
580 }
581 }
582
583 resultBMs[nextLayer++] = yuvaFull;
584
585 setup_yuv_indices(yuvFormat, false, yuvaIndices);
586 break;
587 }
Jim Van Verth976a6b02018-10-17 15:27:19 -0400588 case kAYUV_YUVFormat: {
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400589 SkBitmap yuvaFull;
590
Jim Van Verth47133fd2018-10-19 22:09:28 -0400591 yuvaFull.allocPixels(SkImageInfo::Make(planes.fYFull.width(), planes.fYFull.height(),
592 kRGBA_8888_SkColorType, kUnpremul_SkAlphaType));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400593
594 for (int y = 0; y < planes.fYFull.height(); ++y) {
595 for (int x = 0; x < planes.fYFull.width(); ++x) {
596
597 uint8_t Y = *planes.fYFull.getAddr8(x, y);
598 uint8_t U = *planes.fUFull.getAddr8(x, y);
599 uint8_t V = *planes.fVFull.getAddr8(x, y);
600 uint8_t A = *planes.fAFull.getAddr8(x, y);
601
602 // NOT premul!
Jim Van Verth47133fd2018-10-19 22:09:28 -0400603 // V and Y swapped to match RGBA layout
Robert Phillips2dd1b472019-03-21 09:00:20 -0400604 SkColor c = SkColorSetARGB(A, V, U, Y);
605 *yuvaFull.getAddr32(x, y) = c;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400606 }
607 }
608
609 resultBMs[nextLayer++] = yuvaFull;
610
Robert Phillipsdc62b642019-05-16 10:10:45 -0400611 setup_yuv_indices(yuvFormat, false, yuvaIndices);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400612 break;
613 }
Robert Phillips2dd1b472019-03-21 09:00:20 -0400614 case kY410_YUVFormat: {
615 SkBitmap yuvaFull;
Robert Phillipsf0313ee2019-05-21 13:51:11 -0400616 uint32_t Y, U, V;
617 uint8_t A;
Robert Phillips2dd1b472019-03-21 09:00:20 -0400618
619 yuvaFull.allocPixels(SkImageInfo::Make(planes.fYFull.width(), planes.fYFull.height(),
620 kRGBA_1010102_SkColorType,
621 kUnpremul_SkAlphaType));
622
623 for (int y = 0; y < planes.fYFull.height(); ++y) {
624 for (int x = 0; x < planes.fYFull.width(); ++x) {
625
Robert Phillipsf0313ee2019-05-21 13:51:11 -0400626 Y = SkScalarRoundToInt((*planes.fYFull.getAddr8(x, y) / 255.0f) * 1023.0f);
627 U = SkScalarRoundToInt((*planes.fUFull.getAddr8(x, y) / 255.0f) * 1023.0f);
628 V = SkScalarRoundToInt((*planes.fVFull.getAddr8(x, y) / 255.0f) * 1023.0f);
629 A = SkScalarRoundToInt((*planes.fAFull.getAddr8(x, y) / 255.0f) * 3.0f);
Robert Phillips2dd1b472019-03-21 09:00:20 -0400630
631 // NOT premul!
632 // AVYU but w/ V and U swapped to match RGBA layout
633 *yuvaFull.getAddr32(x, y) = (A << 30) | (U << 20) | (Y << 10) | (V << 0);
634 }
635 }
636
637 resultBMs[nextLayer++] = yuvaFull;
638
Robert Phillipsdc62b642019-05-16 10:10:45 -0400639 setup_yuv_indices(yuvFormat, false, yuvaIndices);
Robert Phillips2dd1b472019-03-21 09:00:20 -0400640 break;
641 }
Robert Phillipsbb749902019-06-10 17:20:12 -0400642 case kP016_YUVFormat: // fall through
643 case kP010_YUVFormat: // fall through
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400644 case kNV12_YUVFormat: {
Robert Phillipsbb749902019-06-10 17:20:12 -0400645 SkBitmap uvQuarter = make_quarter_2_channel(planes.fYFull,
646 planes.fUQuarter,
647 planes.fVQuarter, true);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400648 resultBMs[nextLayer++] = planes.fYFull;
649 resultBMs[nextLayer++] = uvQuarter;
650
Robert Phillipsdc62b642019-05-16 10:10:45 -0400651 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400652 break;
653 }
654 case kNV21_YUVFormat: {
Robert Phillipsbb749902019-06-10 17:20:12 -0400655 SkBitmap vuQuarter = make_quarter_2_channel(planes.fYFull,
656 planes.fUQuarter,
657 planes.fVQuarter, false);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400658 resultBMs[nextLayer++] = planes.fYFull;
659 resultBMs[nextLayer++] = vuQuarter;
660
Robert Phillipsdc62b642019-05-16 10:10:45 -0400661 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400662 break;
663 }
664 case kI420_YUVFormat:
665 resultBMs[nextLayer++] = planes.fYFull;
666 resultBMs[nextLayer++] = planes.fUQuarter;
667 resultBMs[nextLayer++] = planes.fVQuarter;
668
Robert Phillipsdc62b642019-05-16 10:10:45 -0400669 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400670 break;
671 case kYV12_YUVFormat:
672 resultBMs[nextLayer++] = planes.fYFull;
673 resultBMs[nextLayer++] = planes.fVQuarter;
674 resultBMs[nextLayer++] = planes.fUQuarter;
675
Robert Phillipsdc62b642019-05-16 10:10:45 -0400676 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400677 break;
678 }
679
Robert Phillipsbb749902019-06-10 17:20:12 -0400680 if (!format_has_builtin_alpha(yuvFormat) && !opaque) {
Robert Phillipsdc62b642019-05-16 10:10:45 -0400681 resultBMs[nextLayer] = planes.fAFull;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400682 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400683}
684
Robert Phillips2dd1b472019-03-21 09:00:20 -0400685static uint8_t look_up(float x1, float y1, const SkBitmap& bm, SkColorChannel channel) {
Robert Phillips1c7062d2018-10-04 10:44:53 -0400686 uint8_t result;
687
Robert Phillips2dd1b472019-03-21 09:00:20 -0400688 SkASSERT(x1 > 0 && x1 < 1.0f);
689 SkASSERT(y1 > 0 && y1 < 1.0f);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400690 int x = SkScalarFloorToInt(x1 * bm.width());
691 int y = SkScalarFloorToInt(y1 * bm.height());
692
693 if (kAlpha_8_SkColorType == bm.colorType()) {
Robert Phillipsbb749902019-06-10 17:20:12 -0400694 SkASSERT(SkColorChannel::kA == channel || SkColorChannel::kR == channel);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400695 result = *bm.getAddr8(x, y);
Robert Phillips2dd1b472019-03-21 09:00:20 -0400696 } else if (kRGBA_8888_SkColorType == bm.colorType()) {
697 SkColor c = *bm.getAddr32(x, y);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400698
699 switch (channel) {
700 case SkColorChannel::kR:
Robert Phillips2dd1b472019-03-21 09:00:20 -0400701 result = SkColorGetB(c);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400702 break;
703 case SkColorChannel::kG:
Robert Phillips2dd1b472019-03-21 09:00:20 -0400704 result = SkColorGetG(c);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400705 break;
706 case SkColorChannel::kB:
Robert Phillips2dd1b472019-03-21 09:00:20 -0400707 result = SkColorGetR(c);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400708 break;
709 case SkColorChannel::kA:
Robert Phillips2dd1b472019-03-21 09:00:20 -0400710 result = SkColorGetA(c);
711 break;
712 }
713 } else {
714 SkASSERT(kRGBA_1010102_SkColorType == bm.colorType());
715
716 SkColor c = *bm.getAddr32(x, y);
717
718 switch (channel) {
719 case SkColorChannel::kR:
Robert Phillipsf0313ee2019-05-21 13:51:11 -0400720 result = SkScalarRoundToInt(((c >> 0) & 0x3ff) * (255.0f/1023.0f));
Robert Phillips2dd1b472019-03-21 09:00:20 -0400721 break;
722 case SkColorChannel::kG:
Robert Phillipsf0313ee2019-05-21 13:51:11 -0400723 result = SkScalarRoundToInt(((c >> 10) & 0x3ff) * (255.0f/1023.0f));
Robert Phillips2dd1b472019-03-21 09:00:20 -0400724 break;
725 case SkColorChannel::kB:
Robert Phillipsf0313ee2019-05-21 13:51:11 -0400726 result = SkScalarRoundToInt(((c >> 20) & 0x3ff) * (255.0f/1023.0f));
Robert Phillips2dd1b472019-03-21 09:00:20 -0400727 break;
728 case SkColorChannel::kA:
Robert Phillipsf0313ee2019-05-21 13:51:11 -0400729 result = SkScalarRoundToInt(((c >> 30) & 0x3) * (255.0f/3.0f));
Robert Phillips1c7062d2018-10-04 10:44:53 -0400730 break;
731 }
732 }
733
734 return result;
735}
736
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400737class YUVGenerator : public SkImageGenerator {
738public:
739 YUVGenerator(const SkImageInfo& ii,
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400740 SkYUVColorSpace yuvColorSpace,
Jim Van Verth8f11e432018-10-18 14:36:59 -0400741 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
Jim Van Verthe24b5872018-10-29 16:26:02 -0400742 SkBitmap bitmaps[SkYUVASizeInfo::kMaxCount])
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400743 : SkImageGenerator(ii)
Robert Phillips2dd1b472019-03-21 09:00:20 -0400744 , fYUVColorSpace(yuvColorSpace)
745 , fAllA8(true) {
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400746 memcpy(fYUVAIndices, yuvaIndices, sizeof(fYUVAIndices));
747
Jim Van Verth8f11e432018-10-18 14:36:59 -0400748 SkAssertResult(SkYUVAIndex::AreValidIndices(fYUVAIndices, &fNumBitmaps));
Jim Van Verthe24b5872018-10-29 16:26:02 -0400749 SkASSERT(fNumBitmaps > 0 && fNumBitmaps <= SkYUVASizeInfo::kMaxCount);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400750
Jim Van Verth8f11e432018-10-18 14:36:59 -0400751 for (int i = 0; i < fNumBitmaps; ++i) {
752 fYUVBitmaps[i] = bitmaps[i];
Robert Phillips2dd1b472019-03-21 09:00:20 -0400753 if (kAlpha_8_SkColorType != fYUVBitmaps[i].colorType()) {
754 fAllA8 = false;
755 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400756 }
757 }
758
759protected:
760 bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
761 const Options&) override {
762
763 if (kUnknown_SkColorType == fFlattened.colorType()) {
Robert Phillips2dd1b472019-03-21 09:00:20 -0400764 fFlattened.allocPixels(info);
765 SkASSERT(kPremul_SkAlphaType == info.alphaType());
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400766
767 for (int y = 0; y < info.height(); ++y) {
768 for (int x = 0; x < info.width(); ++x) {
769
Robert Phillips1c7062d2018-10-04 10:44:53 -0400770 float x1 = (x + 0.5f) / info.width();
771 float y1 = (y + 0.5f) / info.height();
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400772
Robert Phillips1c7062d2018-10-04 10:44:53 -0400773 uint8_t Y = look_up(x1, y1,
774 fYUVBitmaps[fYUVAIndices[0].fIndex],
775 fYUVAIndices[0].fChannel);
776
777 uint8_t U = look_up(x1, y1,
778 fYUVBitmaps[fYUVAIndices[1].fIndex],
779 fYUVAIndices[1].fChannel);
780
781
782 uint8_t V = look_up(x1, y1,
783 fYUVBitmaps[fYUVAIndices[2].fIndex],
784 fYUVAIndices[2].fChannel);
785
786 uint8_t A = 255;
787 if (fYUVAIndices[3].fIndex >= 0) {
788 A = look_up(x1, y1,
789 fYUVBitmaps[fYUVAIndices[3].fIndex],
790 fYUVAIndices[3].fChannel);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400791 }
792
793 // Making premul here.
Robert Phillips1c7062d2018-10-04 10:44:53 -0400794 switch (fYUVColorSpace) {
795 case kJPEG_SkYUVColorSpace:
796 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_jpeg(Y, U, V, A);
797 break;
798 case kRec601_SkYUVColorSpace:
799 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_601(Y, U, V, A);
800 break;
801 case kRec709_SkYUVColorSpace:
802 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_709(Y, U, V, A);
803 break;
Robert Phillips0a22ba82019-03-06 12:36:47 -0500804 case kIdentity_SkYUVColorSpace:
Robert Phillips2dd1b472019-03-21 09:00:20 -0400805 *fFlattened.getAddr32(x, y) = SkPremultiplyARGBInline(A, V, U, Y);
Robert Phillips0a22ba82019-03-06 12:36:47 -0500806 break;
Robert Phillips1c7062d2018-10-04 10:44:53 -0400807 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400808 }
809 }
810 }
811
812 return fFlattened.readPixels(info, pixels, rowBytes, 0, 0);
813 }
814
Jim Van Verthe24b5872018-10-29 16:26:02 -0400815 bool onQueryYUVA8(SkYUVASizeInfo* size,
Jim Van Verth8f11e432018-10-18 14:36:59 -0400816 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
817 SkYUVColorSpace* yuvColorSpace) const override {
818
Robert Phillips2dd1b472019-03-21 09:00:20 -0400819 // The onQueryYUVA8/onGetYUVA8Planes can only handle A8 planes
820 if (!fAllA8) {
821 return false;
822 }
823
Jim Van Verth8f11e432018-10-18 14:36:59 -0400824 memcpy(yuvaIndices, fYUVAIndices, sizeof(fYUVAIndices));
825 *yuvColorSpace = fYUVColorSpace;
826
827 int i = 0;
828 for ( ; i < fNumBitmaps; ++i) {
Jim Van Verth8f11e432018-10-18 14:36:59 -0400829 size->fSizes[i].fWidth = fYUVBitmaps[i].width();
830 size->fSizes[i].fHeight = fYUVBitmaps[i].height();
831 size->fWidthBytes[i] = fYUVBitmaps[i].rowBytes();
832 }
Jim Van Verthe24b5872018-10-29 16:26:02 -0400833 for ( ; i < SkYUVASizeInfo::kMaxCount; ++i) {
Jim Van Verth8f11e432018-10-18 14:36:59 -0400834 size->fSizes[i].fWidth = 0;
835 size->fSizes[i].fHeight = 0;
836 size->fWidthBytes[i] = 0;
Jim Van Verthf99a6742018-10-18 16:13:18 +0000837 }
Jim Van Verth0c583af2018-10-18 10:31:59 -0400838
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400839 return true;
840 }
841
Jim Van Verthe24b5872018-10-29 16:26:02 -0400842 bool onGetYUVA8Planes(const SkYUVASizeInfo&, const SkYUVAIndex[SkYUVAIndex::kIndexCount],
843 void* planes[SkYUVASizeInfo::kMaxCount]) override {
Robert Phillips2dd1b472019-03-21 09:00:20 -0400844 SkASSERT(fAllA8);
Jim Van Verth8f11e432018-10-18 14:36:59 -0400845 for (int i = 0; i < fNumBitmaps; ++i) {
846 planes[i] = fYUVBitmaps[i].getPixels();
847 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400848 return true;
849 }
850
851private:
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400852 SkYUVColorSpace fYUVColorSpace;
Jim Van Verth8f11e432018-10-18 14:36:59 -0400853 SkYUVAIndex fYUVAIndices[SkYUVAIndex::kIndexCount];
854 int fNumBitmaps;
Jim Van Verthe24b5872018-10-29 16:26:02 -0400855 SkBitmap fYUVBitmaps[SkYUVASizeInfo::kMaxCount];
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400856 SkBitmap fFlattened;
Robert Phillips2dd1b472019-03-21 09:00:20 -0400857 bool fAllA8; // are all the SkBitmaps in "fYUVBitmaps" A8?
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400858
859};
860
861static sk_sp<SkImage> make_yuv_gen_image(const SkImageInfo& ii,
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400862 SkYUVColorSpace yuvColorSpace,
Jim Van Verth8f11e432018-10-18 14:36:59 -0400863 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400864 SkBitmap bitmaps[]) {
Jim Van Verth8f11e432018-10-18 14:36:59 -0400865 std::unique_ptr<SkImageGenerator> gen(new YUVGenerator(ii, yuvColorSpace,
866 yuvaIndices, bitmaps));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400867
868 return SkImage::MakeFromGenerator(std::move(gen));
869}
870
871static void draw_col_label(SkCanvas* canvas, int x, int yuvColorSpace, bool opaque) {
Robert Phillips0a22ba82019-03-06 12:36:47 -0500872 static const char* kYUVColorSpaceNames[] = { "JPEG", "601", "709", "Identity" };
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400873 GR_STATIC_ASSERT(SK_ARRAY_COUNT(kYUVColorSpaceNames) == kLastEnum_SkYUVColorSpace+1);
874
Mike Reed91919132019-01-02 12:21:01 -0500875 SkPaint paint;
Mike Kleinea3f0142019-03-20 11:12:10 -0500876 SkFont font(ToolUtils::create_portable_typeface(nullptr, SkFontStyle::Bold()), 16);
Mike Reed91919132019-01-02 12:21:01 -0500877 font.setEdging(SkFont::Edging::kAlias);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400878
879 SkRect textRect;
880 SkString colLabel;
881
882 colLabel.printf("%s", kYUVColorSpaceNames[yuvColorSpace]);
Ben Wagner51e15a62019-05-07 15:38:46 -0400883 font.measureText(colLabel.c_str(), colLabel.size(), SkTextEncoding::kUTF8, &textRect);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400884 int y = textRect.height();
885
Mike Reed91919132019-01-02 12:21:01 -0500886 SkTextUtils::DrawString(canvas, colLabel.c_str(), x, y, font, paint, SkTextUtils::kCenter_Align);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400887
888 colLabel.printf("%s", opaque ? "Opaque" : "Transparent");
Mike Reed71f5a0b2018-10-25 16:12:39 -0400889
Ben Wagner51e15a62019-05-07 15:38:46 -0400890 font.measureText(colLabel.c_str(), colLabel.size(), SkTextEncoding::kUTF8, &textRect);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400891 y += textRect.height();
892
Mike Reed91919132019-01-02 12:21:01 -0500893 SkTextUtils::DrawString(canvas, colLabel.c_str(), x, y, font, paint, SkTextUtils::kCenter_Align);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400894}
895
896static void draw_row_label(SkCanvas* canvas, int y, int yuvFormat) {
Robert Phillipsbb749902019-06-10 17:20:12 -0400897 static const char* kYUVFormatNames[] = {
898 "P016", "P010", "Y416", "AYUV", "Y410", "NV12", "NV21", "I420", "YV12"
899 };
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400900 GR_STATIC_ASSERT(SK_ARRAY_COUNT(kYUVFormatNames) == kLast_YUVFormat+1);
901
Mike Reed91919132019-01-02 12:21:01 -0500902 SkPaint paint;
Mike Kleinea3f0142019-03-20 11:12:10 -0500903 SkFont font(ToolUtils::create_portable_typeface(nullptr, SkFontStyle::Bold()), 16);
Mike Reed91919132019-01-02 12:21:01 -0500904 font.setEdging(SkFont::Edging::kAlias);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400905
906 SkRect textRect;
907 SkString rowLabel;
908
909 rowLabel.printf("%s", kYUVFormatNames[yuvFormat]);
Ben Wagner51e15a62019-05-07 15:38:46 -0400910 font.measureText(rowLabel.c_str(), rowLabel.size(), SkTextEncoding::kUTF8, &textRect);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400911 y += kTileWidthHeight/2 + textRect.height()/2;
912
Hal Canary89a644b2019-01-07 09:36:09 -0500913 canvas->drawString(rowLabel, 0, y, font, paint);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400914}
915
Robert Phillipsbb749902019-06-10 17:20:12 -0400916static void make_RG_88(const GrCaps* caps,
917 const SkBitmap& bm, YUVFormat yuvFormat,
918 SkAutoTMalloc<uint8_t>* pixels,
919 GrBackendFormat* format) {
920 SkASSERT(kNV12_YUVFormat == yuvFormat || kNV21_YUVFormat == yuvFormat);
921 SkASSERT(kRGBA_8888_SkColorType == bm.colorType()); // uv stored in rg
922
923 pixels->reset(2 * sizeof(uint8_t) * bm.width() * bm.height());
924 uint8_t* currPixel = pixels->get();
925 for (int y = 0; y < bm.height(); ++y) {
926 for (int x = 0; x < bm.width(); ++x) {
927 SkColor color = bm.getColor(x, y);
928 uint8_t u8 = SkColorGetR(color);
929 uint8_t v8 = SkColorGetG(color);
930
931 currPixel[0] = u8;
932 currPixel[1] = v8;
933 currPixel += 2;
934 }
935 }
936 *format = caps->getBackendFormatFromGrColorType(GrColorType::kRG_88,
937 GrSRGBEncoded::kNo);
938}
939
940static void make_RG_1616(const GrCaps* caps,
941 const SkBitmap& bm, YUVFormat yuvFormat,
942 SkAutoTMalloc<uint8_t>* pixels,
943 GrBackendFormat* format) {
944 SkASSERT(kP016_YUVFormat == yuvFormat || kP010_YUVFormat == yuvFormat);
945 SkASSERT(kRGBA_8888_SkColorType == bm.colorType()); // uv stored in rg
946
947 uint16_t u16, v16;
948 pixels->reset(2 * sizeof(uint16_t) * bm.width() * bm.height());
949 uint16_t* currPixel = (uint16_t*) pixels->get();
950 for (int y = 0; y < bm.height(); ++y) {
951 for (int x = 0; x < bm.width(); ++x) {
952 SkColor color = bm.getColor(x, y);
953
954 if (kP016_YUVFormat == yuvFormat) {
955 u16 = SkScalarRoundToInt((SkColorGetR(color) / 255.0f) * 65535.0f);
956 v16 = SkScalarRoundToInt((SkColorGetG(color) / 255.0f) * 65535.0f);
957 } else {
958 u16 = SkScalarRoundToInt((SkColorGetR(color) / 255.0f) * 1023.0f);
959 v16 = SkScalarRoundToInt((SkColorGetG(color) / 255.0f) * 1023.0f);
960 u16 <<= 6;
961 v16 <<= 6;
962 }
963
964 currPixel[0] = u16;
965 currPixel[1] = v16;
966 currPixel += 2;
967 }
968 }
969
970 *format = caps->getBackendFormatFromGrColorType(GrColorType::kRG_1616,
971 GrSRGBEncoded::kNo);
972}
973
974static void make_RGBA_16(const GrCaps* caps,
975 const SkBitmap& bm,
976 YUVFormat yuvFormat,
977 SkAutoTMalloc<uint8_t>* pixels,
978 GrBackendFormat* format) {
979 SkASSERT(kY416_YUVFormat == yuvFormat);
980 SkASSERT(kRGBA_8888_SkColorType == bm.colorType());
981
982 uint16_t y16, u16, v16, a16;
983 pixels->reset(4 * sizeof(uint16_t) * bm.width() * bm.height());
984 uint16_t* currPixel = (uint16_t*) pixels->get();
985 for (int y = 0; y < bm.height(); ++y) {
986 for (int x = 0; x < bm.width(); ++x) {
987 SkColor color = bm.getColor(x, y);
988
989 y16 = SkScalarRoundToInt((SkColorGetR(color) / 255.0f) * 65535.0f);
990 u16 = SkScalarRoundToInt((SkColorGetG(color) / 255.0f) * 65535.0f);
991 v16 = SkScalarRoundToInt((SkColorGetB(color) / 255.0f) * 65535.0f);
992 a16 = SkScalarRoundToInt((SkColorGetA(color) / 255.0f) * 65535.0f);
993
994 currPixel[0] = y16;
995 currPixel[1] = u16;
996 currPixel[2] = v16;
997 currPixel[3] = a16;
998 currPixel += 4;
999 }
1000 }
1001
Robert Phillips6b973872019-06-20 11:30:05 -04001002 *format = caps->getBackendFormatFromGrColorType(GrColorType::kRGBA_16161616,
1003 GrSRGBEncoded::kNo);
Robert Phillipsbb749902019-06-10 17:20:12 -04001004 return;
1005}
1006
1007static void make_R_16(const GrCaps* caps,
1008 const SkBitmap& bm,
1009 YUVFormat yuvFormat,
1010 SkAutoTMalloc<uint8_t>* pixels,
1011 GrBackendFormat* format) {
1012 SkASSERT(kP016_YUVFormat == yuvFormat || kP010_YUVFormat == yuvFormat);
1013 SkASSERT(kAlpha_8_SkColorType == bm.colorType());
1014
1015 uint16_t y16;
1016 pixels->reset(sizeof(uint16_t) * bm.width() * bm.height());
1017 uint16_t* currPixel = (uint16_t*) pixels->get();
1018 for (int y = 0; y < bm.height(); ++y) {
1019 for (int x = 0; x < bm.width(); ++x) {
1020 uint8_t y8 = *bm.getAddr8(x, y);
1021
1022 if (kP016_YUVFormat == yuvFormat) {
1023 y16 = SkScalarRoundToInt((y8 / 255.0f) * 65535.0f);
1024 } else {
1025 y16 = SkScalarRoundToInt((y8 / 255.0f) * 1023.0f);
1026 y16 <<= 6;
1027 }
1028
1029 currPixel[0] = y16;
1030 currPixel += 1;
1031 }
1032 }
1033
1034 *format = caps->getBackendFormatFromGrColorType(GrColorType::kR_16, GrSRGBEncoded::kNo);
1035}
1036
Robert Phillipscb1adb42019-06-10 15:09:34 -04001037static GrBackendTexture create_yuva_texture(GrContext* context, const SkBitmap& bm,
Robert Phillipsbb749902019-06-10 17:20:12 -04001038 SkYUVAIndex yuvaIndices[4], int texIndex,
1039 YUVFormat yuvFormat) {
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001040 SkASSERT(texIndex >= 0 && texIndex <= 3);
1041 int channelCount = 0;
1042 for (int i = 0; i < SkYUVAIndex::kIndexCount; ++i) {
1043 if (yuvaIndices[i].fIndex == texIndex) {
1044 ++channelCount;
1045 }
1046 }
Robert Phillipsbb749902019-06-10 17:20:12 -04001047
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001048 GrBackendTexture tex;
Robert Phillipsbb749902019-06-10 17:20:12 -04001049
1050 if (format_uses_16_bpp(yuvFormat) || 2 == channelCount) {
1051 // Due to the limitations of SkPixmap these cases need to be handled separately
Robert Phillipscb1adb42019-06-10 15:09:34 -04001052 const GrCaps* caps = context->priv().caps();
1053 GrGpu* gpu = context->priv().getGpu();
1054
Robert Phillipsbb749902019-06-10 17:20:12 -04001055 SkAutoTMalloc<uint8_t> pixels;
1056 GrBackendFormat format;
1057
1058 if (2 == channelCount) {
1059 if (format_uses_16_bpp(yuvFormat)) {
1060 make_RG_1616(caps, bm, yuvFormat, &pixels, &format);
1061 } else {
1062 make_RG_88(caps, bm, yuvFormat, &pixels, &format);
1063 }
1064 } else {
1065 if (kRGBA_8888_SkColorType == bm.colorType()) {
1066 make_RGBA_16(caps, bm, yuvFormat, &pixels, &format);
1067 } else {
1068 make_R_16(caps, bm, yuvFormat, &pixels, &format);
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001069 }
1070 }
Robert Phillipsbb749902019-06-10 17:20:12 -04001071
Robert Phillipsf0313ee2019-05-21 13:51:11 -04001072 tex = gpu->createBackendTexture(bm.width(), bm.height(), format,
1073 GrMipMapped::kNo, GrRenderable::kNo,
Robert Phillipsbb749902019-06-10 17:20:12 -04001074 pixels, 0, nullptr);
1075 } else {
Robert Phillipscb1adb42019-06-10 15:09:34 -04001076 tex = context->priv().createBackendTexture(&bm.pixmap(), 1, GrRenderable::kNo);
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001077 }
Robert Phillipsbb749902019-06-10 17:20:12 -04001078
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001079 return tex;
1080}
1081
Robert Phillips0a22ba82019-03-06 12:36:47 -05001082static sk_sp<SkColorFilter> yuv_to_rgb_colorfilter() {
1083 static const float kJPEGConversionMatrix[20] = {
Mike Reede869a1e2019-04-30 12:18:54 -04001084 1.0f, 0.0f, 1.402f, 0.0f, -180.0f/255,
1085 1.0f, -0.344136f, -0.714136f, 0.0f, 136.0f/255,
1086 1.0f, 1.772f, 0.0f, 0.0f, -227.6f/255,
Robert Phillips0a22ba82019-03-06 12:36:47 -05001087 0.0f, 0.0f, 0.0f, 1.0f, 0.0f
1088 };
1089
Mike Reede869a1e2019-04-30 12:18:54 -04001090 return SkColorFilters::Matrix(kJPEGConversionMatrix);
Robert Phillips0a22ba82019-03-06 12:36:47 -05001091}
1092
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001093namespace skiagm {
1094
1095// This GM creates an opaque and transparent bitmap, extracts the planes and then recombines
1096// them into various YUV formats. It then renders the results in the grid:
1097//
Robert Phillips2dd1b472019-03-21 09:00:20 -04001098// JPEG 601 709 Identity
1099// Transparent Opaque Transparent Opaque Transparent Opaque Transparent Opaque
Robert Phillipsbb749902019-06-10 17:20:12 -04001100// originals
1101// P016
1102// P010
1103// Y416
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001104// AYUV
Robert Phillips2dd1b472019-03-21 09:00:20 -04001105// Y410
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001106// NV12
1107// NV21
1108// I420
1109// YV12
1110class WackyYUVFormatsGM : public GM {
1111public:
Michael Ludwiga6a84002019-04-12 15:03:02 -04001112 WackyYUVFormatsGM(bool useTargetColorSpace, bool useDomain)
1113 : fUseTargetColorSpace(useTargetColorSpace)
1114 , fUseDomain(useDomain) {
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001115 this->setBGColor(0xFFCCCCCC);
1116 }
1117
1118protected:
1119
1120 SkString onShortName() override {
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001121 SkString name("wacky_yuv_formats");
1122 if (fUseTargetColorSpace) {
1123 name += "_cs";
1124 }
Michael Ludwiga6a84002019-04-12 15:03:02 -04001125 if (fUseDomain) {
1126 name += "_domain";
1127 }
Robert Phillips2dd1b472019-03-21 09:00:20 -04001128
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001129 return name;
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001130 }
1131
1132 SkISize onISize() override {
1133 int numCols = 2 * (kLastEnum_SkYUVColorSpace + 1); // opacity x color-space
1134 int numRows = 1 + (kLast_YUVFormat + 1); // origin + # yuv formats
Michael Ludwiga6a84002019-04-12 15:03:02 -04001135 int wh = SkScalarCeilToInt(kTileWidthHeight * (fUseDomain ? 1.5f : 1.f));
1136 return SkISize::Make(kLabelWidth + numCols * (wh + kPad),
1137 kLabelHeight + numRows * (wh + kPad));
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001138 }
1139
1140 void onOnceBeforeDraw() override {
1141 SkPoint origin = { kTileWidthHeight/2.0f, kTileWidthHeight/2.0f };
1142 float outerRadius = kTileWidthHeight/2.0f - 20.0f;
1143 float innerRadius = 20.0f;
1144
1145 {
1146 // transparent
1147 SkTDArray<SkRect> circles;
1148 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 5, &circles);
Michael Ludwiga6a84002019-04-12 15:03:02 -04001149 fOriginalBMs[0] = make_bitmap(kRGBA_8888_SkColorType, path, circles, false, fUseDomain);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001150 }
1151
1152 {
1153 // opaque
1154 SkTDArray<SkRect> circles;
1155 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 7, &circles);
Michael Ludwiga6a84002019-04-12 15:03:02 -04001156 fOriginalBMs[1] = make_bitmap(kRGBA_8888_SkColorType, path, circles, true, fUseDomain);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001157 }
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001158
1159 if (fUseTargetColorSpace) {
1160 fTargetColorSpace = SkColorSpace::MakeSRGB()->makeColorSpin();
1161 }
Robert Phillips51c89e42018-10-05 13:30:43 -04001162 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001163
Robert Phillips51c89e42018-10-05 13:30:43 -04001164 void createImages(GrContext* context) {
Jim Van Verth9bf81202018-10-30 15:53:36 -04001165 int counter = 0;
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001166 for (bool opaque : { false, true }) {
1167 for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
1168 PlaneData planes;
1169 extract_planes(fOriginalBMs[opaque], (SkYUVColorSpace) cs, &planes);
1170
Robert Phillipsbb749902019-06-10 17:20:12 -04001171 for (int format = kP016_YUVFormat; format <= kLast_YUVFormat; ++format) {
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001172 SkBitmap resultBMs[4];
1173 SkYUVAIndex yuvaIndices[4];
Robert Phillips2dd1b472019-03-21 09:00:20 -04001174
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001175 create_YUV(planes, (YUVFormat) format, resultBMs, yuvaIndices, opaque);
Robert Phillips2dd1b472019-03-21 09:00:20 -04001176
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001177 int numTextures;
1178 if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures)) {
1179 continue;
1180 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001181
Robert Phillips51c89e42018-10-05 13:30:43 -04001182 if (context) {
Chris Dalton382b1222019-02-07 10:05:55 +00001183 if (context->abandoned()) {
1184 return;
1185 }
1186
Robert Phillips51c89e42018-10-05 13:30:43 -04001187 GrBackendTexture yuvaTextures[4];
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001188 SkPixmap yuvaPixmaps[4];
Robert Phillips51c89e42018-10-05 13:30:43 -04001189
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001190 for (int i = 0; i < numTextures; ++i) {
Robert Phillipscb1adb42019-06-10 15:09:34 -04001191 yuvaTextures[i] = create_yuva_texture(context, resultBMs[i],
Robert Phillipsbb749902019-06-10 17:20:12 -04001192 yuvaIndices, i,
1193 (YUVFormat) format);
Brian Salomonf2580c02019-01-30 09:13:31 -05001194 if (yuvaTextures[i].isValid()) {
1195 fBackendTextures.push_back(yuvaTextures[i]);
1196 }
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001197 yuvaPixmaps[i] = resultBMs[i].pixmap();
Robert Phillips51c89e42018-10-05 13:30:43 -04001198 }
1199
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001200 int counterMod = counter % 3;
Robert Phillipsbb749902019-06-10 17:20:12 -04001201 if (format_cant_be_represented_with_pixmaps((YUVFormat) format) &&
1202 counterMod == 2) {
1203 // These formats don't work as pixmaps
Michael Ludwiga6a84002019-04-12 15:03:02 -04001204 counterMod = 1;
1205 } else if (fUseDomain && counterMod == 0) {
1206 // Copies flatten to RGB when they copy the YUVA data, which doesn't
1207 // know about the intended domain and the domain padding bleeds in
1208 counterMod = 1;
1209 }
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001210 switch (counterMod) {
1211 case 0:
Jim Van Verth9bf81202018-10-30 15:53:36 -04001212 fImages[opaque][cs][format] = SkImage::MakeFromYUVATexturesCopy(
1213 context,
1214 (SkYUVColorSpace)cs,
1215 yuvaTextures,
1216 yuvaIndices,
1217 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1218 kTopLeft_GrSurfaceOrigin);
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001219 break;
1220 case 1:
Jim Van Verth9bf81202018-10-30 15:53:36 -04001221 fImages[opaque][cs][format] = SkImage::MakeFromYUVATextures(
1222 context,
1223 (SkYUVColorSpace)cs,
1224 yuvaTextures,
1225 yuvaIndices,
1226 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1227 kTopLeft_GrSurfaceOrigin);
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001228 break;
1229 case 2:
1230 default:
1231 fImages[opaque][cs][format] = SkImage::MakeFromYUVAPixmaps(
1232 context,
1233 (SkYUVColorSpace)cs,
1234 yuvaPixmaps,
1235 yuvaIndices,
1236 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1237 kTopLeft_GrSurfaceOrigin, true);
1238 break;
Jim Van Verth9bf81202018-10-30 15:53:36 -04001239 }
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001240 ++counter;
Jim Van Verth9bf81202018-10-30 15:53:36 -04001241 } else {
Robert Phillips51c89e42018-10-05 13:30:43 -04001242 fImages[opaque][cs][format] = make_yuv_gen_image(
1243 fOriginalBMs[opaque].info(),
Robert Phillips51c89e42018-10-05 13:30:43 -04001244 (SkYUVColorSpace) cs,
1245 yuvaIndices,
1246 resultBMs);
1247 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001248 }
1249 }
1250 }
1251 }
1252
1253 void onDraw(SkCanvas* canvas) override {
Robert Phillips51c89e42018-10-05 13:30:43 -04001254 this->createImages(canvas->getGrContext());
1255
Michael Ludwiga6a84002019-04-12 15:03:02 -04001256 SkRect srcRect = SkRect::MakeWH(fOriginalBMs[0].width(), fOriginalBMs[0].height());
1257 SkRect dstRect = SkRect::MakeXYWH(kLabelWidth, 0.f, srcRect.width(), srcRect.height());
1258
1259 SkCanvas::SrcRectConstraint constraint = SkCanvas::kFast_SrcRectConstraint;
1260 if (fUseDomain) {
1261 srcRect.inset(kDomainPadding, kDomainPadding);
1262 // Draw a larger rectangle to ensure bilerp filtering would normally read outside the
1263 // srcRect and hit the red pixels, if strict constraint weren't used.
1264 dstRect.fRight = kLabelWidth + 1.5f * srcRect.width();
1265 dstRect.fBottom = 1.5f * srcRect.height();
1266 constraint = SkCanvas::kStrict_SrcRectConstraint;
1267 }
1268
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001269 for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
Robert Phillips0a22ba82019-03-06 12:36:47 -05001270 SkPaint paint;
Michael Ludwiga6a84002019-04-12 15:03:02 -04001271 paint.setFilterQuality(kLow_SkFilterQuality);
Robert Phillips0a22ba82019-03-06 12:36:47 -05001272 if (kIdentity_SkYUVColorSpace == cs) {
1273 // The identity color space needs post processing to appear correctly
1274 paint.setColorFilter(yuv_to_rgb_colorfilter());
1275 }
1276
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001277 for (int opaque : { 0, 1 }) {
Michael Ludwiga6a84002019-04-12 15:03:02 -04001278 dstRect.offsetTo(dstRect.fLeft, kLabelHeight);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001279
Michael Ludwiga6a84002019-04-12 15:03:02 -04001280 draw_col_label(canvas, dstRect.fLeft + dstRect.height() / 2, cs, opaque);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001281
Michael Ludwiga6a84002019-04-12 15:03:02 -04001282 canvas->drawBitmapRect(fOriginalBMs[opaque], srcRect, dstRect, nullptr, constraint);
1283 dstRect.offset(0.f, dstRect.height() + kPad);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001284
Robert Phillipsbb749902019-06-10 17:20:12 -04001285 for (int format = kP016_YUVFormat; format <= kLast_YUVFormat; ++format) {
Michael Ludwiga6a84002019-04-12 15:03:02 -04001286 draw_row_label(canvas, dstRect.fTop, format);
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001287 if (fUseTargetColorSpace && fImages[opaque][cs][format]) {
Robert Phillips0a22ba82019-03-06 12:36:47 -05001288 // Making a CS-specific version of a kIdentity_SkYUVColorSpace YUV image
1289 // doesn't make a whole lot of sense. The colorSpace conversion will
1290 // operate on the YUV components rather than the RGB components.
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001291 sk_sp<SkImage> csImage =
1292 fImages[opaque][cs][format]->makeColorSpace(fTargetColorSpace);
Michael Ludwiga6a84002019-04-12 15:03:02 -04001293 canvas->drawImageRect(csImage, srcRect, dstRect, &paint, constraint);
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001294 } else {
Michael Ludwiga6a84002019-04-12 15:03:02 -04001295 canvas->drawImageRect(fImages[opaque][cs][format], srcRect, dstRect, &paint,
1296 constraint);
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001297 }
Michael Ludwiga6a84002019-04-12 15:03:02 -04001298 dstRect.offset(0.f, dstRect.height() + kPad);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001299 }
1300
Michael Ludwiga6a84002019-04-12 15:03:02 -04001301 dstRect.offset(dstRect.width() + kPad, 0.f);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001302 }
1303 }
Brian Salomon8f388ce2019-01-29 14:50:53 -05001304 if (auto context = canvas->getGrContext()) {
Chris Dalton382b1222019-02-07 10:05:55 +00001305 if (!context->abandoned()) {
1306 context->flush();
1307 GrGpu* gpu = context->priv().getGpu();
1308 SkASSERT(gpu);
1309 gpu->testingOnly_flushGpuAndSync();
1310 for (const auto& tex : fBackendTextures) {
Robert Phillips5c7a25b2019-05-20 08:38:07 -04001311 context->deleteBackendTexture(tex);
Chris Dalton382b1222019-02-07 10:05:55 +00001312 }
1313 fBackendTextures.reset();
Brian Salomon8f388ce2019-01-29 14:50:53 -05001314 }
Brian Salomon8f388ce2019-01-29 14:50:53 -05001315 }
1316 SkASSERT(!fBackendTextures.count());
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001317 }
1318
1319private:
Robert Phillips2dd1b472019-03-21 09:00:20 -04001320 SkBitmap fOriginalBMs[2];
1321 sk_sp<SkImage> fImages[2][kLastEnum_SkYUVColorSpace + 1][kLast_YUVFormat + 1];
Brian Salomon8f388ce2019-01-29 14:50:53 -05001322 SkTArray<GrBackendTexture> fBackendTextures;
Robert Phillips2dd1b472019-03-21 09:00:20 -04001323 bool fUseTargetColorSpace;
Michael Ludwiga6a84002019-04-12 15:03:02 -04001324 bool fUseDomain;
Robert Phillips2dd1b472019-03-21 09:00:20 -04001325 sk_sp<SkColorSpace> fTargetColorSpace;
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001326
1327 typedef GM INHERITED;
1328};
1329
1330//////////////////////////////////////////////////////////////////////////////
1331
Michael Ludwiga6a84002019-04-12 15:03:02 -04001332DEF_GM(return new WackyYUVFormatsGM(/* cs */ false, /* domain */ false);)
1333DEF_GM(return new WackyYUVFormatsGM(/* cs */ true, /* domain */ false);)
1334DEF_GM(return new WackyYUVFormatsGM(/* cs */ false, /* domain */ true);)
Brian Osmane9560492019-02-05 17:00:03 -05001335
Chris Dalton3a778372019-02-07 15:23:36 -07001336class YUVMakeColorSpaceGM : public GpuGM {
Brian Osmane9560492019-02-05 17:00:03 -05001337public:
1338 YUVMakeColorSpaceGM() {
1339 this->setBGColor(0xFFCCCCCC);
1340 }
1341
1342protected:
1343 SkString onShortName() override {
1344 return SkString("yuv_make_color_space");
1345 }
1346
1347 SkISize onISize() override {
1348 int numCols = 4; // (transparent, opaque) x (untagged, tagged)
1349 int numRows = 5; // original, YUV, subset, readPixels, makeNonTextureImage
1350 return SkISize::Make(numCols * (kTileWidthHeight + kPad) + kPad,
1351 numRows * (kTileWidthHeight + kPad) + kPad);
1352 }
1353
1354 void onOnceBeforeDraw() override {
1355 SkPoint origin = { kTileWidthHeight/2.0f, kTileWidthHeight/2.0f };
1356 float outerRadius = kTileWidthHeight/2.0f - 20.0f;
1357 float innerRadius = 20.0f;
1358
1359 {
1360 // transparent
1361 SkTDArray<SkRect> circles;
1362 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 5, &circles);
Michael Ludwiga6a84002019-04-12 15:03:02 -04001363 fOriginalBMs[0] = make_bitmap(kN32_SkColorType, path, circles, false, false);
Brian Osmane9560492019-02-05 17:00:03 -05001364 }
1365
1366 {
1367 // opaque
1368 SkTDArray<SkRect> circles;
1369 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 7, &circles);
Michael Ludwiga6a84002019-04-12 15:03:02 -04001370 fOriginalBMs[1] = make_bitmap(kN32_SkColorType, path, circles, true, false);
Brian Osmane9560492019-02-05 17:00:03 -05001371 }
1372
1373 fTargetColorSpace = SkColorSpace::MakeSRGB()->makeColorSpin();
1374 }
1375
1376 void createImages(GrContext* context) {
1377 for (bool opaque : { false, true }) {
1378 PlaneData planes;
1379 extract_planes(fOriginalBMs[opaque], kJPEG_SkYUVColorSpace, &planes);
1380
1381 SkBitmap resultBMs[4];
1382 SkYUVAIndex yuvaIndices[4];
Robert Phillips2dd1b472019-03-21 09:00:20 -04001383
Brian Osmane9560492019-02-05 17:00:03 -05001384 create_YUV(planes, kAYUV_YUVFormat, resultBMs, yuvaIndices, opaque);
Robert Phillips2dd1b472019-03-21 09:00:20 -04001385
Brian Osmane9560492019-02-05 17:00:03 -05001386 int numTextures;
1387 if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures)) {
1388 continue;
1389 }
1390
Brian Osmane9560492019-02-05 17:00:03 -05001391 GrBackendTexture yuvaTextures[4];
1392 for (int i = 0; i < numTextures; ++i) {
Robert Phillipsbb749902019-06-10 17:20:12 -04001393 yuvaTextures[i] = create_yuva_texture(context, resultBMs[i], yuvaIndices, i,
1394 kAYUV_YUVFormat);
Brian Osmane9560492019-02-05 17:00:03 -05001395 if (yuvaTextures[i].isValid()) {
1396 fBackendTextures.push_back(yuvaTextures[i]);
1397 }
1398 }
1399
1400 fImages[opaque][0] = SkImage::MakeFromYUVATextures(
1401 context,
1402 kJPEG_SkYUVColorSpace,
1403 yuvaTextures,
1404 yuvaIndices,
1405 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1406 kTopLeft_GrSurfaceOrigin);
1407 fImages[opaque][1] = SkImage::MakeFromYUVATextures(
1408 context,
1409 kJPEG_SkYUVColorSpace,
1410 yuvaTextures,
1411 yuvaIndices,
1412 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1413 kTopLeft_GrSurfaceOrigin,
1414 SkColorSpace::MakeSRGB());
1415 }
1416 }
1417
Chris Dalton3a778372019-02-07 15:23:36 -07001418 void onDraw(GrContext* context, GrRenderTargetContext*, SkCanvas* canvas) override {
Brian Osmane9560492019-02-05 17:00:03 -05001419 this->createImages(context);
1420
1421 int x = kPad;
1422 for (int tagged : { 0, 1 }) {
1423 for (int opaque : { 0, 1 }) {
1424 int y = kPad;
1425
1426 auto raster = SkImage::MakeFromBitmap(fOriginalBMs[opaque])
1427 ->makeColorSpace(fTargetColorSpace);
1428 canvas->drawImage(raster, x, y);
1429 y += kTileWidthHeight + kPad;
1430
1431 auto yuv = fImages[opaque][tagged]->makeColorSpace(fTargetColorSpace);
1432 SkASSERT(SkColorSpace::Equals(yuv->colorSpace(), fTargetColorSpace.get()));
1433 canvas->drawImage(yuv, x, y);
1434 y += kTileWidthHeight + kPad;
1435
1436 auto subset = yuv->makeSubset(SkIRect::MakeWH(kTileWidthHeight / 2,
1437 kTileWidthHeight / 2));
1438 canvas->drawImage(subset, x, y);
1439 y += kTileWidthHeight + kPad;
1440
1441 auto nonTexture = yuv->makeNonTextureImage();
1442 canvas->drawImage(nonTexture, x, y);
1443 y += kTileWidthHeight + kPad;
1444
1445 SkBitmap readBack;
Brian Salomon5ad6fd32019-03-21 15:30:08 -04001446 readBack.allocPixels(yuv->imageInfo());
Brian Osmane9560492019-02-05 17:00:03 -05001447 yuv->readPixels(readBack.pixmap(), 0, 0);
1448 canvas->drawBitmap(readBack, x, y);
1449
1450 x += kTileWidthHeight + kPad;
1451 }
1452 }
1453
1454 context->flush();
1455 GrGpu* gpu = context->priv().getGpu();
1456 SkASSERT(gpu);
1457 gpu->testingOnly_flushGpuAndSync();
1458 for (const auto& tex : fBackendTextures) {
Robert Phillips5c7a25b2019-05-20 08:38:07 -04001459 context->deleteBackendTexture(tex);
Brian Osmane9560492019-02-05 17:00:03 -05001460 }
1461 fBackendTextures.reset();
1462 }
1463
1464private:
1465 SkBitmap fOriginalBMs[2];
1466 sk_sp<SkImage> fImages[2][2];
1467 SkTArray<GrBackendTexture> fBackendTextures;
1468 sk_sp<SkColorSpace> fTargetColorSpace;
1469
1470 typedef GM INHERITED;
1471};
1472
1473DEF_GM(return new YUVMakeColorSpaceGM();)
1474
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001475}
Mike Reed6a5f7e22019-05-23 15:30:07 -04001476
1477///////////////
1478
Mike Reed6a5f7e22019-05-23 15:30:07 -04001479#include "include/effects/SkColorMatrix.h"
Mike Klein4b432fa2019-06-06 11:44:05 -05001480#include "src/core/SkAutoPixmapStorage.h"
1481#include "src/core/SkYUVMath.h"
1482#include "tools/Resources.h"
Mike Reed6a5f7e22019-05-23 15:30:07 -04001483
1484static void draw_into_alpha(const SkImage* img, sk_sp<SkColorFilter> cf, const SkPixmap& dst) {
1485 auto canvas = SkCanvas::MakeRasterDirect(dst.info(), dst.writable_addr(), dst.rowBytes());
1486 canvas->scale(1.0f * dst.width() / img->width(), 1.0f * dst.height() / img->height());
1487 SkPaint paint;
1488 paint.setFilterQuality(kLow_SkFilterQuality);
1489 paint.setColorFilter(cf);
1490 paint.setBlendMode(SkBlendMode::kSrc);
1491 canvas->drawImage(img, 0, 0, &paint);
1492}
1493
1494static void split_into_yuv(const SkImage* img, SkYUVColorSpace cs, const SkPixmap dst[3]) {
1495 float m[20];
1496 SkColorMatrix_RGB2YUV(cs, m);
1497
1498 memcpy(m + 15, m + 0, 5 * sizeof(float)); // copy Y into A
1499 draw_into_alpha(img, SkColorFilters::Matrix(m), dst[0]);
1500
1501 memcpy(m + 15, m + 5, 5 * sizeof(float)); // copy U into A
1502 draw_into_alpha(img, SkColorFilters::Matrix(m), dst[1]);
1503
1504 memcpy(m + 15, m + 10, 5 * sizeof(float)); // copy V into A
1505 draw_into_alpha(img, SkColorFilters::Matrix(m), dst[2]);
1506}
1507
1508static void draw_diff(SkCanvas* canvas, SkScalar x, SkScalar y,
1509 const SkImage* a, const SkImage* b) {
1510 auto sh = SkShaders::Blend(SkBlendMode::kDifference, a->makeShader(), b->makeShader());
1511 SkPaint paint;
1512 paint.setShader(sh);
1513 canvas->save();
1514 canvas->translate(x, y);
1515 canvas->drawRect(SkRect::MakeWH(a->width(), a->height()), paint);
1516
1517 SkColorMatrix cm;
1518 cm.setScale(64, 64, 64);
1519 paint.setShader(sh->makeWithColorFilter(SkColorFilters::Matrix(cm)));
1520 canvas->translate(0, a->height());
1521 canvas->drawRect(SkRect::MakeWH(a->width(), a->height()), paint);
1522
1523 canvas->restore();
1524}
1525
1526// Exercises SkColorMatrix_RGB2YUV for yuv colorspaces, showing the planes, and the
1527// resulting (recombined) images (gpu only for now).
1528//
1529class YUVSplitterGM : public skiagm::GM {
1530 sk_sp<SkImage> fOrig;
1531 SkAutoPixmapStorage fStorage[3];
1532 SkPixmap fPM[3];
1533
1534public:
1535 YUVSplitterGM() {}
1536
1537protected:
1538
1539 SkString onShortName() override {
1540 return SkString("yuv_splitter");
1541 }
1542
1543 SkISize onISize() override {
1544 return SkISize::Make(1024, 768);
1545 }
1546
1547 void onOnceBeforeDraw() override {
1548 fOrig = GetResourceAsImage("images/mandrill_256.png");
1549
1550 SkImageInfo info = SkImageInfo::Make(fOrig->width(), fOrig->height(), kAlpha_8_SkColorType,
1551 kPremul_SkAlphaType);
1552 fStorage[0].alloc(info);
1553 if (0) {
1554 // if you want to scale U,V down by 1/2
1555 info = info.makeWH(info.width()/2, info.height()/2);
1556 }
1557 fStorage[1].alloc(info);
1558 fStorage[2].alloc(info);
1559 for (int i = 0; i < 3; ++i) {
1560 fPM[i] = fStorage[i];
1561 }
1562 }
1563
1564 void onDraw(SkCanvas* canvas) override {
1565 SkYUVAIndex indices[4];
1566 indices[SkYUVAIndex::kY_Index] = {0, SkColorChannel::kR};
1567 indices[SkYUVAIndex::kU_Index] = {1, SkColorChannel::kR};
1568 indices[SkYUVAIndex::kV_Index] = {2, SkColorChannel::kR};
1569 indices[SkYUVAIndex::kA_Index] = {-1, SkColorChannel::kR};
1570
1571 canvas->translate(fOrig->width(), 0);
1572 canvas->save();
1573 for (auto cs : {kRec709_SkYUVColorSpace, kRec601_SkYUVColorSpace, kJPEG_SkYUVColorSpace}) {
1574 split_into_yuv(fOrig.get(), cs, fPM);
1575 auto img = SkImage::MakeFromYUVAPixmaps(canvas->getGrContext(), cs, fPM, indices,
1576 fPM[0].info().dimensions(),
1577 kTopLeft_GrSurfaceOrigin,
1578 false, false, nullptr);
1579 if (img) {
1580 canvas->drawImage(img, 0, 0, nullptr);
1581 draw_diff(canvas, 0, fOrig->height(), fOrig.get(), img.get());
1582 }
1583 canvas->translate(fOrig->width(), 0);
1584 }
1585 canvas->restore();
1586 canvas->translate(-fOrig->width(), 0);
1587
1588 canvas->drawImage(SkImage::MakeRasterCopy(fPM[0]), 0, 0, nullptr);
1589 canvas->drawImage(SkImage::MakeRasterCopy(fPM[1]), 0, fPM[0].height(), nullptr);
1590 canvas->drawImage(SkImage::MakeRasterCopy(fPM[2]),
1591 0, fPM[0].height() + fPM[1].height(), nullptr);
1592 }
1593
1594private:
1595 typedef GM INHERITED;
1596};
1597DEF_GM( return new YUVSplitterGM; )