blob: 23c868d193c8ae316d925f707429e1969ecbd761 [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 Phillipsf47717a2019-09-19 13:04:33 -0400109static bool is_colorType_texturable(const GrCaps* caps, GrColorType ct) {
110 GrBackendFormat format = caps->getDefaultBackendFormat(ct, GrRenderable::kNo);
111 if (!format.isValid()) {
112 return false;
113 }
114
115 return caps->isFormatTexturable(format);
116}
117
118static bool is_format_natively_supported(GrContext* context, YUVFormat yuvFormat) {
119
120 const GrCaps* caps = context->priv().caps();
121
122 switch (yuvFormat) {
123 case kP016_YUVFormat: // fall through
124 case kP010_YUVFormat: return is_colorType_texturable(caps, GrColorType::kAlpha_16) &&
125 is_colorType_texturable(caps, GrColorType::kRG_1616);
126 case kP016F_YUVFormat: return is_colorType_texturable(caps, GrColorType::kAlpha_F16) &&
127 is_colorType_texturable(caps, GrColorType::kRG_F16);
128 case kY416_YUVFormat: return is_colorType_texturable(caps, GrColorType::kRGBA_16161616);
129 case kAYUV_YUVFormat: return is_colorType_texturable(caps, GrColorType::kRGBA_8888);
130 case kY410_YUVFormat: return is_colorType_texturable(caps, GrColorType::kRGBA_1010102);
131 case kNV12_YUVFormat: // fall through
132 case kNV21_YUVFormat: return is_colorType_texturable(caps, GrColorType::kGray_8) &&
133 is_colorType_texturable(caps, GrColorType::kRG_88);
134 case kI420_YUVFormat: // fall through
135 case kYV12_YUVFormat: return is_colorType_texturable(caps, GrColorType::kGray_8);
136 }
137
138 SkUNREACHABLE;
139}
140
Robert Phillipsdc62b642019-05-16 10:10:45 -0400141// Helper to setup the SkYUVAIndex array correctly
142// Skia allows the client to tack an additional alpha plane onto any of the standard opaque
143// formats (via the addExtraAlpha) flag. In this case it is assumed to be a stand-alone single-
144// channel plane.
145static void setup_yuv_indices(YUVFormat yuvFormat, bool addExtraAlpha, SkYUVAIndex yuvaIndices[4]) {
146 switch (yuvFormat) {
Robert Phillipsbb749902019-06-10 17:20:12 -0400147 case kP016_YUVFormat: // fall through
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400148 case kP010_YUVFormat: // fall through
149 case kP016F_YUVFormat:
Robert Phillipsbb749902019-06-10 17:20:12 -0400150 yuvaIndices[0].fIndex = 0;
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400151 yuvaIndices[0].fChannel = SkColorChannel::kA; // bc 16bit is stored in A16 or AF16
Robert Phillipsbb749902019-06-10 17:20:12 -0400152 yuvaIndices[1].fIndex = 1;
153 yuvaIndices[1].fChannel = SkColorChannel::kR;
154 yuvaIndices[2].fIndex = 1;
155 yuvaIndices[2].fChannel = SkColorChannel::kG;
156 if (addExtraAlpha) {
157 yuvaIndices[3].fIndex = 2;
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400158 yuvaIndices[3].fChannel = SkColorChannel::kA; // bc 16bit is stored in A16 or AF16
Robert Phillipsbb749902019-06-10 17:20:12 -0400159 } else {
160 yuvaIndices[3].fIndex = -1; // No alpha channel
161 }
162 break;
163 case kY416_YUVFormat:
164 SkASSERT(!addExtraAlpha); // this format already has an alpha channel
165 yuvaIndices[0].fIndex = 0;
166 yuvaIndices[0].fChannel = SkColorChannel::kG;
167 yuvaIndices[1].fIndex = 0;
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400168 yuvaIndices[1].fChannel = SkColorChannel::kR;
Robert Phillipsbb749902019-06-10 17:20:12 -0400169 yuvaIndices[2].fIndex = 0;
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400170 yuvaIndices[2].fChannel = SkColorChannel::kB;
Robert Phillipsbb749902019-06-10 17:20:12 -0400171 yuvaIndices[3].fIndex = 0;
172 yuvaIndices[3].fChannel = SkColorChannel::kA;
173 break;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400174 case kAYUV_YUVFormat:
175 SkASSERT(!addExtraAlpha); // this format already has an alpha channel
176 yuvaIndices[0].fIndex = 0;
177 yuvaIndices[0].fChannel = SkColorChannel::kR;
178 yuvaIndices[1].fIndex = 0;
179 yuvaIndices[1].fChannel = SkColorChannel::kG;
180 yuvaIndices[2].fIndex = 0;
181 yuvaIndices[2].fChannel = SkColorChannel::kB;
182 yuvaIndices[3].fIndex = 0;
183 yuvaIndices[3].fChannel = SkColorChannel::kA;
184 break;
185 case kY410_YUVFormat:
186 SkASSERT(!addExtraAlpha); // this format already has an alpha channel
187 yuvaIndices[0].fIndex = 0;
188 yuvaIndices[0].fChannel = SkColorChannel::kG;
189 yuvaIndices[1].fIndex = 0;
190 yuvaIndices[1].fChannel = SkColorChannel::kB;
191 yuvaIndices[2].fIndex = 0;
192 yuvaIndices[2].fChannel = SkColorChannel::kR;
193 yuvaIndices[3].fIndex = 0;
194 yuvaIndices[3].fChannel = SkColorChannel::kA;
195 break;
196 case kNV12_YUVFormat:
197 yuvaIndices[0].fIndex = 0;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400198 yuvaIndices[0].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400199 yuvaIndices[1].fIndex = 1;
200 yuvaIndices[1].fChannel = SkColorChannel::kR;
201 yuvaIndices[2].fIndex = 1;
202 yuvaIndices[2].fChannel = SkColorChannel::kG;
203 if (addExtraAlpha) {
204 yuvaIndices[3].fIndex = 2;
205 yuvaIndices[3].fChannel = SkColorChannel::kA;
206 } else {
207 yuvaIndices[3].fIndex = -1; // No alpha channel
208 }
209 break;
210 case kNV21_YUVFormat:
211 yuvaIndices[0].fIndex = 0;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400212 yuvaIndices[0].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400213 yuvaIndices[1].fIndex = 1;
214 yuvaIndices[1].fChannel = SkColorChannel::kG;
215 yuvaIndices[2].fIndex = 1;
216 yuvaIndices[2].fChannel = SkColorChannel::kR;
217 if (addExtraAlpha) {
218 yuvaIndices[3].fIndex = 2;
219 yuvaIndices[3].fChannel = SkColorChannel::kA;
220 } else {
221 yuvaIndices[3].fIndex = -1; // No alpha channel
222 }
223 break;
224 case kI420_YUVFormat:
225 yuvaIndices[0].fIndex = 0;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400226 yuvaIndices[0].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400227 yuvaIndices[1].fIndex = 1;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400228 yuvaIndices[1].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400229 yuvaIndices[2].fIndex = 2;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400230 yuvaIndices[2].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400231 if (addExtraAlpha) {
232 yuvaIndices[3].fIndex = 3;
233 yuvaIndices[3].fChannel = SkColorChannel::kA;
234 } else {
235 yuvaIndices[3].fIndex = -1; // No alpha channel
236 }
237 break;
238 case kYV12_YUVFormat:
239 yuvaIndices[0].fIndex = 0;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400240 yuvaIndices[0].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400241 yuvaIndices[1].fIndex = 2;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400242 yuvaIndices[1].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400243 yuvaIndices[2].fIndex = 1;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400244 yuvaIndices[2].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400245 if (addExtraAlpha) {
246 yuvaIndices[3].fIndex = 3;
247 yuvaIndices[3].fChannel = SkColorChannel::kA;
248 } else {
249 yuvaIndices[3].fIndex = -1; // No alpha channel
250 }
251 break;
252 }
253}
254
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400255// All the planes we need to construct the various YUV formats
256struct PlaneData {
257 SkBitmap fYFull;
258 SkBitmap fUFull;
259 SkBitmap fVFull;
260 SkBitmap fAFull;
261 SkBitmap fUQuarter; // 2x2 downsampled U channel
262 SkBitmap fVQuarter; // 2x2 downsampled V channel
Robert Phillips429f0d32019-09-11 17:03:28 -0400263
264 SkBitmap fFull;
265 SkBitmap fQuarter; // 2x2 downsampled YUVA
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400266};
267
268// Add a portion of a circle to 'path'. The points 'o1' and 'o2' are on the border of the circle
269// and have tangents 'v1' and 'v2'.
270static void add_arc(SkPath* path,
271 const SkPoint& o1, const SkVector& v1,
272 const SkPoint& o2, const SkVector& v2,
273 SkTDArray<SkRect>* circles, bool takeLongWayRound) {
274
275 SkVector v3 = { -v1.fY, v1.fX };
276 SkVector v4 = { v2.fY, -v2.fX };
277
278 SkScalar t = ((o2.fX - o1.fX) * v4.fY - (o2.fY - o1.fY) * v4.fX) / v3.cross(v4);
279 SkPoint center = { o1.fX + t * v3.fX, o1.fY + t * v3.fY };
280
281 SkRect r = { center.fX - t, center.fY - t, center.fX + t, center.fY + t };
282
283 if (circles) {
284 circles->push_back(r);
285 }
286
287 SkVector startV = o1 - center, endV = o2 - center;
288 startV.normalize();
289 endV.normalize();
290
291 SkScalar startDeg = SkRadiansToDegrees(SkScalarATan2(startV.fY, startV.fX));
292 SkScalar endDeg = SkRadiansToDegrees(SkScalarATan2(endV.fY, endV.fX));
293
294 startDeg += 360.0f;
295 startDeg = fmodf(startDeg, 360.0f);
296
297 endDeg += 360.0f;
298 endDeg = fmodf(endDeg, 360.0f);
299
300 if (endDeg < startDeg) {
301 endDeg += 360.0f;
302 }
303
304 SkScalar sweepDeg = SkTAbs(endDeg - startDeg);
305 if (!takeLongWayRound) {
306 sweepDeg = sweepDeg - 360;
307 }
308
309 path->arcTo(r, startDeg, sweepDeg, false);
310}
311
312static SkPath create_splat(const SkPoint& o, SkScalar innerRadius, SkScalar outerRadius,
313 SkScalar ratio, int numLobes, SkTDArray<SkRect>* circles) {
314 if (numLobes <= 1) {
315 return SkPath();
316 }
317
318 SkPath p;
319
320 int numDivisions = 2 * numLobes;
321 SkScalar fullLobeDegrees = 360.0f / numLobes;
322 SkScalar outDegrees = ratio * fullLobeDegrees / (ratio + 1.0f);
323 SkScalar innerDegrees = fullLobeDegrees / (ratio + 1.0f);
324 SkMatrix outerStep, innerStep;
325 outerStep.setRotate(outDegrees);
326 innerStep.setRotate(innerDegrees);
327 SkVector curV = SkVector::Make(0.0f, 1.0f);
328
329 if (circles) {
330 circles->push_back(SkRect::MakeLTRB(o.fX - innerRadius, o.fY - innerRadius,
331 o.fX + innerRadius, o.fY + innerRadius));
332 }
333
334 p.moveTo(o.fX + innerRadius * curV.fX, o.fY + innerRadius * curV.fY);
335
336 for (int i = 0; i < numDivisions; ++i) {
337
338 SkVector nextV;
339 if (0 == (i % 2)) {
340 nextV = outerStep.mapVector(curV.fX, curV.fY);
341
342 SkPoint top = SkPoint::Make(o.fX + outerRadius * curV.fX,
343 o.fY + outerRadius * curV.fY);
344 SkPoint nextTop = SkPoint::Make(o.fX + outerRadius * nextV.fX,
345 o.fY + outerRadius * nextV.fY);
346
347 p.lineTo(top);
348 add_arc(&p, top, curV, nextTop, nextV, circles, true);
349 } else {
350 nextV = innerStep.mapVector(curV.fX, curV.fY);
351
352 SkPoint bot = SkPoint::Make(o.fX + innerRadius * curV.fX,
353 o.fY + innerRadius * curV.fY);
354 SkPoint nextBot = SkPoint::Make(o.fX + innerRadius * nextV.fX,
355 o.fY + innerRadius * nextV.fY);
356
357 p.lineTo(bot);
358 add_arc(&p, bot, curV, nextBot, nextV, nullptr, false);
359 }
360
361 curV = nextV;
362 }
363
364 p.close();
365
366 return p;
367}
368
Robert Phillips2dd1b472019-03-21 09:00:20 -0400369static SkBitmap make_bitmap(SkColorType colorType, const SkPath& path,
Michael Ludwiga6a84002019-04-12 15:03:02 -0400370 const SkTDArray<SkRect>& circles, bool opaque, bool padWithRed) {
Mike Kleinea3f0142019-03-20 11:12:10 -0500371 const SkColor kGreen = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 178, 240, 104));
372 const SkColor kBlue = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 173, 167, 252));
373 const SkColor kYellow = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 255, 221, 117));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400374
Michael Ludwiga6a84002019-04-12 15:03:02 -0400375 int widthHeight = kTileWidthHeight + (padWithRed ? 2 * kDomainPadding : 0);
376
377 SkImageInfo ii = SkImageInfo::Make(widthHeight, widthHeight,
Robert Phillips2dd1b472019-03-21 09:00:20 -0400378 colorType, kPremul_SkAlphaType);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400379
380 SkBitmap bm;
381 bm.allocPixels(ii);
382
Robert Phillips2dd1b472019-03-21 09:00:20 -0400383 std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirect(ii,
384 bm.getPixels(),
385 bm.rowBytes());
Michael Ludwiga6a84002019-04-12 15:03:02 -0400386 if (padWithRed) {
387 canvas->clear(SK_ColorRED);
388 canvas->translate(kDomainPadding, kDomainPadding);
389 canvas->clipRect(SkRect::MakeWH(kTileWidthHeight, kTileWidthHeight));
390 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400391 canvas->clear(opaque ? kGreen : SK_ColorTRANSPARENT);
392
393 SkPaint paint;
394 paint.setAntiAlias(false); // serialize-8888 doesn't seem to work well w/ partial transparency
395 paint.setColor(kBlue);
396
397 canvas->drawPath(path, paint);
398
399 paint.setColor(opaque ? kYellow : SK_ColorTRANSPARENT);
400 paint.setBlendMode(SkBlendMode::kSrc);
401 for (int i = 0; i < circles.count(); ++i) {
402 SkRect r = circles[i];
403 r.inset(r.width()/4, r.height()/4);
404 canvas->drawOval(r, paint);
405 }
406
407 return bm;
408}
409
Robert Phillips1c7062d2018-10-04 10:44:53 -0400410static void convert_rgba_to_yuva_601_shared(SkColor col, uint8_t yuv[4],
411 uint8_t off, uint8_t range) {
412 static const float Kr = 0.299f;
413 static const float Kb = 0.114f;
414 static const float Kg = 1.0f - Kr - Kb;
415
416 float r = SkColorGetR(col) / 255.0f;
417 float g = SkColorGetG(col) / 255.0f;
418 float b = SkColorGetB(col) / 255.0f;
419
420 float Ey = Kr * r + Kg * g + Kb * b;
421 float Ecb = (b - Ey) / 1.402f;
422 float Ecr = (r - Ey) / 1.772;
Robert Phillipsbb749902019-06-10 17:20:12 -0400423 SkASSERT(Ey >= 0.0f && Ey <= 1.0f);
424 SkASSERT(Ecb >= -0.5f && Ecb <= 0.5f);
425 SkASSERT(Ecr >= -0.5f && Ecr <= 0.5f);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400426
427 yuv[0] = SkScalarRoundToInt( range * Ey + off );
428 yuv[1] = SkScalarRoundToInt( 224 * Ecb + 128 );
429 yuv[2] = SkScalarRoundToInt( 224 * Ecr + 128 );
430 yuv[3] = SkColorGetA(col);
431}
432
433static void convert_rgba_to_yuva_jpeg(SkColor col, uint8_t yuv[4]) {
434 // full swing from 0..255
435 convert_rgba_to_yuva_601_shared(col, yuv, 0, 255);
436}
437
438static void convert_rgba_to_yuva_601(SkColor col, uint8_t yuv[4]) {
439 // partial swing from 16..235
440 convert_rgba_to_yuva_601_shared(col, yuv, 16, 219);
441
442}
443
444static void convert_rgba_to_yuva_709(SkColor col, uint8_t yuv[4]) {
445 static const float Kr = 0.2126f;
446 static const float Kb = 0.0722f;
447 static const float Kg = 1.0f - Kr - Kb;
448
449 float r = SkColorGetR(col) / 255.0f;
450 float g = SkColorGetG(col) / 255.0f;
451 float b = SkColorGetB(col) / 255.0f;
452
453 float Ey = Kr * r + Kg * g + Kb * b;
454 float Ecb = (b - Ey) / 1.8556f;
455 float Ecr = (r - Ey) / 1.5748;
Robert Phillipsbb749902019-06-10 17:20:12 -0400456 SkASSERT(Ey >= 0.0f && Ey <= 1.0f);
457 SkASSERT(Ecb >= -0.5f && Ecb <= 0.5f);
458 SkASSERT(Ecr >= -0.5f && Ecr <= 0.5f);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400459
460 yuv[0] = SkScalarRoundToInt( 219 * Ey + 16 );
461 yuv[1] = SkScalarRoundToInt( 224 * Ecb + 128 );
462 yuv[2] = SkScalarRoundToInt( 224 * Ecr + 128 );
463
464 yuv[3] = SkColorGetA(col);
465}
466
467
468static 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 -0400469 uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.0f * y + 1.402f * v - 0.703749f * 255),
Robert Phillips1c7062d2018-10-04 10:44:53 -0400470 0, 255);
Robert Phillips2dd1b472019-03-21 09:00:20 -0400471 uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.0f * y - (0.344136f * u) - (0.714136f * v) + 0.531211f * 255),
Robert Phillips1c7062d2018-10-04 10:44:53 -0400472 0, 255);
Robert Phillips2dd1b472019-03-21 09:00:20 -0400473 uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.0f * y + 1.772f * u - 0.889475f * 255),
Robert Phillips1c7062d2018-10-04 10:44:53 -0400474 0, 255);
475
Robert Phillips2dd1b472019-03-21 09:00:20 -0400476 SkPMColor c = SkPremultiplyARGBInline(a, b, g, r);
477 return c;
Robert Phillips1c7062d2018-10-04 10:44:53 -0400478}
479
480static 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 -0400481 uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.164f * y + 1.596f * v - 0.87075f * 255), 0, 255);
482 uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.164f * y - (0.391f * u) - (0.813f * v) + 0.52925f * 255), 0, 255);
483 uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.164f * y + 2.018f * u - 1.08175f * 255), 0, 255);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400484
Robert Phillips2dd1b472019-03-21 09:00:20 -0400485 SkPMColor c = SkPremultiplyARGBInline(a, b, g, r);
486 return c;
Robert Phillips1c7062d2018-10-04 10:44:53 -0400487}
488
489static 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 -0400490 uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.164f * y + (1.793f * v) - 0.96925f * 255), 0, 255);
491 uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.164f * y - (0.213f * u) - (0.533f * v) + 0.30025f * 255), 0, 255);
492 uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.164f * y + (2.112f * u) - 1.12875f * 255), 0, 255);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400493
Robert Phillips2dd1b472019-03-21 09:00:20 -0400494 SkPMColor c = SkPremultiplyARGBInline(a, b, g, r);
495 return c;
Robert Phillips1c7062d2018-10-04 10:44:53 -0400496}
497
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400498static void extract_planes(const SkBitmap& bm, SkYUVColorSpace yuvColorSpace, PlaneData* planes) {
Robert Phillips0a22ba82019-03-06 12:36:47 -0500499 if (kIdentity_SkYUVColorSpace == yuvColorSpace) {
500 // To test the identity color space we use JPEG YUV planes
501 yuvColorSpace = kJPEG_SkYUVColorSpace;
502 }
503
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400504 SkASSERT(!(bm.width() % 2));
505 SkASSERT(!(bm.height() % 2));
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400506 planes->fYFull.allocPixels(SkImageInfo::Make(bm.width(), bm.height(), kGray_8_SkColorType,
507 kUnpremul_SkAlphaType));
508 planes->fUFull.allocPixels(SkImageInfo::Make(bm.width(), bm.height(), kGray_8_SkColorType,
509 kUnpremul_SkAlphaType));
510 planes->fVFull.allocPixels(SkImageInfo::Make(bm.width(), bm.height(), kGray_8_SkColorType,
511 kUnpremul_SkAlphaType));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400512 planes->fAFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400513 planes->fUQuarter.allocPixels(SkImageInfo::Make(bm.width()/2, bm.height()/2,
514 kGray_8_SkColorType, kUnpremul_SkAlphaType));
515 planes->fVQuarter.allocPixels(SkImageInfo::Make(bm.width()/2, bm.height()/2,
516 kGray_8_SkColorType, kUnpremul_SkAlphaType));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400517
Robert Phillips429f0d32019-09-11 17:03:28 -0400518 planes->fFull.allocPixels(SkImageInfo::Make(bm.width(), bm.height(),
519 kRGBA_F32_SkColorType, kUnpremul_SkAlphaType));
520 planes->fQuarter.allocPixels(SkImageInfo::Make(bm.width()/2, bm.height()/2,
521 kRGBA_F32_SkColorType, kUnpremul_SkAlphaType));
522
523 SkColor4f* dst = (SkColor4f *) planes->fFull.getAddr(0, 0);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400524 for (int y = 0; y < bm.height(); ++y) {
525 for (int x = 0; x < bm.width(); ++x) {
526 SkColor col = bm.getColor(x, y);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400527
Robert Phillips1c7062d2018-10-04 10:44:53 -0400528 uint8_t yuva[4];
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400529
Robert Phillips1c7062d2018-10-04 10:44:53 -0400530 if (kJPEG_SkYUVColorSpace == yuvColorSpace) {
531 convert_rgba_to_yuva_jpeg(col, yuva);
532 } else if (kRec601_SkYUVColorSpace == yuvColorSpace) {
533 convert_rgba_to_yuva_601(col, yuva);
534 } else {
535 SkASSERT(kRec709_SkYUVColorSpace == yuvColorSpace);
536 convert_rgba_to_yuva_709(col, yuva);
537 }
538
539 *planes->fYFull.getAddr8(x, y) = yuva[0];
540 *planes->fUFull.getAddr8(x, y) = yuva[1];
541 *planes->fVFull.getAddr8(x, y) = yuva[2];
542 *planes->fAFull.getAddr8(x, y) = yuva[3];
Robert Phillips429f0d32019-09-11 17:03:28 -0400543
544 // TODO: render in F32 rather than converting here
545 dst->fR = yuva[0] / 255.0f;
546 dst->fG = yuva[1] / 255.0f;
547 dst->fB = yuva[2] / 255.0f;
548 dst->fA = yuva[3] / 255.0f;
549 ++dst;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400550 }
551 }
552
Robert Phillips429f0d32019-09-11 17:03:28 -0400553 dst = (SkColor4f *) planes->fQuarter.getAddr(0, 0);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400554 for (int y = 0; y < bm.height()/2; ++y) {
555 for (int x = 0; x < bm.width()/2; ++x) {
Robert Phillips429f0d32019-09-11 17:03:28 -0400556 uint32_t yAccum = 0, uAccum = 0, vAccum = 0, aAccum = 0;
557
558 yAccum += *planes->fYFull.getAddr8(2*x, 2*y);
559 yAccum += *planes->fYFull.getAddr8(2*x+1, 2*y);
560 yAccum += *planes->fYFull.getAddr8(2*x, 2*y+1);
561 yAccum += *planes->fYFull.getAddr8(2*x+1, 2*y+1);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400562
563 uAccum += *planes->fUFull.getAddr8(2*x, 2*y);
564 uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y);
565 uAccum += *planes->fUFull.getAddr8(2*x, 2*y+1);
566 uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y+1);
567
568 *planes->fUQuarter.getAddr8(x, y) = uAccum / 4.0f;
569
570 vAccum += *planes->fVFull.getAddr8(2*x, 2*y);
571 vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y);
572 vAccum += *planes->fVFull.getAddr8(2*x, 2*y+1);
573 vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y+1);
574
Robert Phillips1c7062d2018-10-04 10:44:53 -0400575 *planes->fVQuarter.getAddr8(x, y) = vAccum / 4.0f;
Robert Phillips429f0d32019-09-11 17:03:28 -0400576
577 aAccum += *planes->fAFull.getAddr8(2*x, 2*y);
578 aAccum += *planes->fAFull.getAddr8(2*x+1, 2*y);
579 aAccum += *planes->fAFull.getAddr8(2*x, 2*y+1);
580 aAccum += *planes->fAFull.getAddr8(2*x+1, 2*y+1);
581
582 // TODO: render in F32 rather than converting here
583 dst->fR = yAccum / (4.0f * 255.0f);
584 dst->fG = uAccum / (4.0f * 255.0f);
585 dst->fB = vAccum / (4.0f * 255.0f);
586 dst->fA = aAccum / (4.0f * 255.0f);
587 ++dst;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400588 }
589 }
590}
591
Robert Phillipsd470e1b2019-09-04 15:05:35 -0400592// Create a 2x2 downsampled SkBitmap. It is stored in an RG texture. It can optionally be
Robert Phillips429f0d32019-09-11 17:03:28 -0400593// uv (i.e., NV12) or vu (i.e., NV21).
Robert Phillipsbb749902019-06-10 17:20:12 -0400594static SkBitmap make_quarter_2_channel(const SkBitmap& fullY,
595 const SkBitmap& quarterU,
596 const SkBitmap& quarterV,
597 bool uv) {
598 SkBitmap result;
599
Robert Phillipsbb749902019-06-10 17:20:12 -0400600 result.allocPixels(SkImageInfo::Make(fullY.width()/2,
601 fullY.height()/2,
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400602 kR8G8_unorm_SkColorType,
Robert Phillipsbb749902019-06-10 17:20:12 -0400603 kUnpremul_SkAlphaType));
604
605 for (int y = 0; y < fullY.height()/2; ++y) {
606 for (int x = 0; x < fullY.width()/2; ++x) {
607 uint8_t u8 = *quarterU.getAddr8(x, y);
608 uint8_t v8 = *quarterV.getAddr8(x, y);
609
610 if (uv) {
Robert Phillipsd470e1b2019-09-04 15:05:35 -0400611 *result.getAddr16(x, y) = (v8 << 8) | u8;
Robert Phillipsbb749902019-06-10 17:20:12 -0400612 } else {
Robert Phillipsd470e1b2019-09-04 15:05:35 -0400613 *result.getAddr16(x, y) = (u8 << 8) | v8;
Robert Phillipsbb749902019-06-10 17:20:12 -0400614 }
615 }
616 }
617
618 return result;
619}
620
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400621// Create some flavor of a 16bits/channel bitmap from a RGBA_F32 source
622static SkBitmap make_16(const SkBitmap& src, SkColorType dstCT,
623 std::function<void(uint16_t* dstPixel, const float* srcPixel)> convert) {
Robert Phillips429f0d32019-09-11 17:03:28 -0400624 SkASSERT(src.colorType() == kRGBA_F32_SkColorType);
625
626 SkBitmap result;
627
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400628 result.allocPixels(SkImageInfo::Make(src.width(), src.height(), dstCT,
Robert Phillips429f0d32019-09-11 17:03:28 -0400629 kUnpremul_SkAlphaType));
630
Robert Phillips429f0d32019-09-11 17:03:28 -0400631 for (int y = 0; y < src.height(); ++y) {
632 for (int x = 0; x < src.width(); ++x) {
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400633 const float* srcPixel = (const float*) src.getAddr(x, y);
634 uint16_t* dstPixel = (uint16_t*) result.getAddr(x, y);
Robert Phillips429f0d32019-09-11 17:03:28 -0400635
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400636 convert(dstPixel, srcPixel);
Robert Phillips429f0d32019-09-11 17:03:28 -0400637 }
638 }
639
640 return result;
641}
642
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400643static uint16_t flt_2_uint16(float flt) { return SkScalarRoundToInt(flt * 65535.0f); }
Robert Phillips429f0d32019-09-11 17:03:28 -0400644
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400645// Recombine the separate planes into some YUV format
646static void create_YUV(const PlaneData& planes, YUVFormat yuvFormat,
647 SkBitmap resultBMs[], SkYUVAIndex yuvaIndices[4], bool opaque) {
648 int nextLayer = 0;
649
650 switch (yuvFormat) {
Robert Phillipsbb749902019-06-10 17:20:12 -0400651 case kY416_YUVFormat: {
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400652 resultBMs[nextLayer++] = make_16(planes.fFull, kR16G16B16A16_unorm_SkColorType,
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400653 [] (uint16_t* dstPixel, const float* srcPixel) {
654 dstPixel[0] = flt_2_uint16(srcPixel[1]); // U
655 dstPixel[1] = flt_2_uint16(srcPixel[0]); // Y
656 dstPixel[2] = flt_2_uint16(srcPixel[2]); // V
657 dstPixel[3] = flt_2_uint16(srcPixel[3]); // A
658 });
Robert Phillipsbb749902019-06-10 17:20:12 -0400659 setup_yuv_indices(yuvFormat, false, yuvaIndices);
660 break;
661 }
Jim Van Verth976a6b02018-10-17 15:27:19 -0400662 case kAYUV_YUVFormat: {
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400663 SkBitmap yuvaFull;
664
Jim Van Verth47133fd2018-10-19 22:09:28 -0400665 yuvaFull.allocPixels(SkImageInfo::Make(planes.fYFull.width(), planes.fYFull.height(),
666 kRGBA_8888_SkColorType, kUnpremul_SkAlphaType));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400667
668 for (int y = 0; y < planes.fYFull.height(); ++y) {
669 for (int x = 0; x < planes.fYFull.width(); ++x) {
670
671 uint8_t Y = *planes.fYFull.getAddr8(x, y);
672 uint8_t U = *planes.fUFull.getAddr8(x, y);
673 uint8_t V = *planes.fVFull.getAddr8(x, y);
674 uint8_t A = *planes.fAFull.getAddr8(x, y);
675
676 // NOT premul!
Jim Van Verth47133fd2018-10-19 22:09:28 -0400677 // V and Y swapped to match RGBA layout
Robert Phillips2dd1b472019-03-21 09:00:20 -0400678 SkColor c = SkColorSetARGB(A, V, U, Y);
679 *yuvaFull.getAddr32(x, y) = c;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400680 }
681 }
682
683 resultBMs[nextLayer++] = yuvaFull;
684
Robert Phillipsdc62b642019-05-16 10:10:45 -0400685 setup_yuv_indices(yuvFormat, false, yuvaIndices);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400686 break;
687 }
Robert Phillips2dd1b472019-03-21 09:00:20 -0400688 case kY410_YUVFormat: {
689 SkBitmap yuvaFull;
Robert Phillipsf0313ee2019-05-21 13:51:11 -0400690 uint32_t Y, U, V;
691 uint8_t A;
Robert Phillips2dd1b472019-03-21 09:00:20 -0400692
693 yuvaFull.allocPixels(SkImageInfo::Make(planes.fYFull.width(), planes.fYFull.height(),
694 kRGBA_1010102_SkColorType,
695 kUnpremul_SkAlphaType));
696
697 for (int y = 0; y < planes.fYFull.height(); ++y) {
698 for (int x = 0; x < planes.fYFull.width(); ++x) {
699
Robert Phillipsf0313ee2019-05-21 13:51:11 -0400700 Y = SkScalarRoundToInt((*planes.fYFull.getAddr8(x, y) / 255.0f) * 1023.0f);
701 U = SkScalarRoundToInt((*planes.fUFull.getAddr8(x, y) / 255.0f) * 1023.0f);
702 V = SkScalarRoundToInt((*planes.fVFull.getAddr8(x, y) / 255.0f) * 1023.0f);
703 A = SkScalarRoundToInt((*planes.fAFull.getAddr8(x, y) / 255.0f) * 3.0f);
Robert Phillips2dd1b472019-03-21 09:00:20 -0400704
705 // NOT premul!
706 // AVYU but w/ V and U swapped to match RGBA layout
707 *yuvaFull.getAddr32(x, y) = (A << 30) | (U << 20) | (Y << 10) | (V << 0);
708 }
709 }
710
711 resultBMs[nextLayer++] = yuvaFull;
712
Robert Phillipsdc62b642019-05-16 10:10:45 -0400713 setup_yuv_indices(yuvFormat, false, yuvaIndices);
Robert Phillips2dd1b472019-03-21 09:00:20 -0400714 break;
715 }
Robert Phillipsbb749902019-06-10 17:20:12 -0400716 case kP016_YUVFormat: // fall through
Robert Phillips429f0d32019-09-11 17:03:28 -0400717 case kP010_YUVFormat: {
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400718 resultBMs[nextLayer++] = make_16(planes.fFull, kA16_unorm_SkColorType,
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400719 [tenBitsPP = (yuvFormat == kP010_YUVFormat)]
720 (uint16_t* dstPixel, const float* srcPixel) {
721 uint16_t val16 = flt_2_uint16(srcPixel[0]);
722 dstPixel[0] = tenBitsPP ? (val16 & 0xFFC0)
723 : val16;
724 });
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400725 resultBMs[nextLayer++] = make_16(planes.fQuarter, kR16G16_unorm_SkColorType,
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400726 [tenBitsPP = (yuvFormat == kP010_YUVFormat)]
727 (uint16_t* dstPixel, const float* srcPixel) {
728 uint16_t u16 = flt_2_uint16(srcPixel[1]);
729 uint16_t v16 = flt_2_uint16(srcPixel[2]);
730 dstPixel[0] = tenBitsPP ? (u16 & 0xFFC0) : u16;
731 dstPixel[1] = tenBitsPP ? (v16 & 0xFFC0) : v16;
732 });
Robert Phillips429f0d32019-09-11 17:03:28 -0400733 if (!opaque) {
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400734 resultBMs[nextLayer] = make_16(planes.fFull, kA16_unorm_SkColorType,
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400735 [tenBitsPP = (yuvFormat == kP010_YUVFormat)]
736 (uint16_t* dstPixel, const float* srcPixel) {
737 uint16_t val16 = flt_2_uint16(srcPixel[3]);
738 dstPixel[0] = tenBitsPP ? (val16 & 0xFFC0)
739 : val16;
740 });
741 }
742 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
743 return;
744 }
745 case kP016F_YUVFormat: {
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400746 resultBMs[nextLayer++] = make_16(planes.fFull, kA16_float_SkColorType,
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400747 [] (uint16_t* dstPixel, const float* srcPixel) {
748 dstPixel[0] = SkFloatToHalf(srcPixel[0]);
749 });
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400750 resultBMs[nextLayer++] = make_16(planes.fQuarter, kR16G16_float_SkColorType,
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400751 [] (uint16_t* dstPixel, const float* srcPixel) {
752 dstPixel[0] = SkFloatToHalf(srcPixel[1]);
753 dstPixel[1] = SkFloatToHalf(srcPixel[2]);
754 });
755 if (!opaque) {
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400756 resultBMs[nextLayer] = make_16(planes.fFull, kA16_float_SkColorType,
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400757 [] (uint16_t* dstPixel, const float* srcPixel) {
758 dstPixel[0] = SkFloatToHalf(srcPixel[3]);
759 });
Robert Phillips429f0d32019-09-11 17:03:28 -0400760 }
761 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
762 return;
763 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400764 case kNV12_YUVFormat: {
Robert Phillipsbb749902019-06-10 17:20:12 -0400765 SkBitmap uvQuarter = make_quarter_2_channel(planes.fYFull,
766 planes.fUQuarter,
767 planes.fVQuarter, true);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400768 resultBMs[nextLayer++] = planes.fYFull;
769 resultBMs[nextLayer++] = uvQuarter;
770
Robert Phillipsdc62b642019-05-16 10:10:45 -0400771 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400772 break;
773 }
774 case kNV21_YUVFormat: {
Robert Phillipsbb749902019-06-10 17:20:12 -0400775 SkBitmap vuQuarter = make_quarter_2_channel(planes.fYFull,
776 planes.fUQuarter,
777 planes.fVQuarter, false);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400778 resultBMs[nextLayer++] = planes.fYFull;
779 resultBMs[nextLayer++] = vuQuarter;
780
Robert Phillipsdc62b642019-05-16 10:10:45 -0400781 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400782 break;
783 }
784 case kI420_YUVFormat:
785 resultBMs[nextLayer++] = planes.fYFull;
786 resultBMs[nextLayer++] = planes.fUQuarter;
787 resultBMs[nextLayer++] = planes.fVQuarter;
788
Robert Phillipsdc62b642019-05-16 10:10:45 -0400789 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400790 break;
791 case kYV12_YUVFormat:
792 resultBMs[nextLayer++] = planes.fYFull;
793 resultBMs[nextLayer++] = planes.fVQuarter;
794 resultBMs[nextLayer++] = planes.fUQuarter;
795
Robert Phillipsdc62b642019-05-16 10:10:45 -0400796 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400797 break;
798 }
799
Robert Phillipsbb749902019-06-10 17:20:12 -0400800 if (!format_has_builtin_alpha(yuvFormat) && !opaque) {
Robert Phillipsdc62b642019-05-16 10:10:45 -0400801 resultBMs[nextLayer] = planes.fAFull;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400802 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400803}
804
Robert Phillips2dd1b472019-03-21 09:00:20 -0400805static uint8_t look_up(float x1, float y1, const SkBitmap& bm, SkColorChannel channel) {
Robert Phillips1c7062d2018-10-04 10:44:53 -0400806 uint8_t result;
807
Robert Phillips2dd1b472019-03-21 09:00:20 -0400808 SkASSERT(x1 > 0 && x1 < 1.0f);
809 SkASSERT(y1 > 0 && y1 < 1.0f);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400810 int x = SkScalarFloorToInt(x1 * bm.width());
811 int y = SkScalarFloorToInt(y1 * bm.height());
812
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400813 if (kGray_8_SkColorType == bm.colorType()) {
Robert Phillipsbb749902019-06-10 17:20:12 -0400814 SkASSERT(SkColorChannel::kA == channel || SkColorChannel::kR == channel);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400815 result = *bm.getAddr8(x, y);
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400816 } else if (kAlpha_8_SkColorType == bm.colorType() ||
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400817 kA16_unorm_SkColorType == bm.colorType() ||
818 kA16_float_SkColorType == bm.colorType()) {
Robert Phillips429f0d32019-09-11 17:03:28 -0400819 SkASSERT(SkColorChannel::kA == channel);
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400820 SkColor c = bm.getColor(x, y);
821 result = SkColorGetA(c);
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400822 } else if (kR8G8_unorm_SkColorType == bm.colorType() ||
823 kR16G16_unorm_SkColorType == bm.colorType() ||
824 kR16G16_float_SkColorType == bm.colorType()) {
Robert Phillipsd470e1b2019-09-04 15:05:35 -0400825 SkASSERT(SkColorChannel::kR == channel || SkColorChannel::kG == channel);
826 SkColor c = bm.getColor(x, y);
827
828 switch (channel) {
829 case SkColorChannel::kR:
830 result = SkColorGetR(c);
831 break;
832 case SkColorChannel::kG:
833 result = SkColorGetG(c);
834 break;
835 case SkColorChannel::kB:
836 result = 0;
837 break;
838 case SkColorChannel::kA:
839 result = 255;
840 break;
841 }
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400842 } else {
843 SkASSERT(kRGBA_1010102_SkColorType == bm.colorType() ||
844 kRGBA_8888_SkColorType == bm.colorType() ||
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400845 kR16G16B16A16_unorm_SkColorType == bm.colorType());
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400846
847 SkColor c = bm.getColor(x, y);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400848
849 switch (channel) {
850 case SkColorChannel::kR:
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400851 result = SkColorGetR(c);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400852 break;
853 case SkColorChannel::kG:
Robert Phillips2dd1b472019-03-21 09:00:20 -0400854 result = SkColorGetG(c);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400855 break;
856 case SkColorChannel::kB:
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400857 result = SkColorGetB(c);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400858 break;
859 case SkColorChannel::kA:
Robert Phillips2dd1b472019-03-21 09:00:20 -0400860 result = SkColorGetA(c);
861 break;
862 }
Robert Phillips1c7062d2018-10-04 10:44:53 -0400863 }
864
865 return result;
866}
867
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400868class YUVGenerator : public SkImageGenerator {
869public:
870 YUVGenerator(const SkImageInfo& ii,
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400871 SkYUVColorSpace yuvColorSpace,
Jim Van Verth8f11e432018-10-18 14:36:59 -0400872 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
Jim Van Verthe24b5872018-10-29 16:26:02 -0400873 SkBitmap bitmaps[SkYUVASizeInfo::kMaxCount])
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400874 : SkImageGenerator(ii)
Robert Phillips2dd1b472019-03-21 09:00:20 -0400875 , fYUVColorSpace(yuvColorSpace)
876 , fAllA8(true) {
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400877 memcpy(fYUVAIndices, yuvaIndices, sizeof(fYUVAIndices));
878
Jim Van Verth8f11e432018-10-18 14:36:59 -0400879 SkAssertResult(SkYUVAIndex::AreValidIndices(fYUVAIndices, &fNumBitmaps));
Jim Van Verthe24b5872018-10-29 16:26:02 -0400880 SkASSERT(fNumBitmaps > 0 && fNumBitmaps <= SkYUVASizeInfo::kMaxCount);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400881
Jim Van Verth8f11e432018-10-18 14:36:59 -0400882 for (int i = 0; i < fNumBitmaps; ++i) {
883 fYUVBitmaps[i] = bitmaps[i];
Robert Phillips2dd1b472019-03-21 09:00:20 -0400884 if (kAlpha_8_SkColorType != fYUVBitmaps[i].colorType()) {
885 fAllA8 = false;
886 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400887 }
888 }
889
890protected:
891 bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
892 const Options&) override {
893
894 if (kUnknown_SkColorType == fFlattened.colorType()) {
Robert Phillips2dd1b472019-03-21 09:00:20 -0400895 fFlattened.allocPixels(info);
896 SkASSERT(kPremul_SkAlphaType == info.alphaType());
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400897
898 for (int y = 0; y < info.height(); ++y) {
899 for (int x = 0; x < info.width(); ++x) {
900
Robert Phillips1c7062d2018-10-04 10:44:53 -0400901 float x1 = (x + 0.5f) / info.width();
902 float y1 = (y + 0.5f) / info.height();
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400903
Robert Phillips1c7062d2018-10-04 10:44:53 -0400904 uint8_t Y = look_up(x1, y1,
905 fYUVBitmaps[fYUVAIndices[0].fIndex],
906 fYUVAIndices[0].fChannel);
907
908 uint8_t U = look_up(x1, y1,
909 fYUVBitmaps[fYUVAIndices[1].fIndex],
910 fYUVAIndices[1].fChannel);
911
912
913 uint8_t V = look_up(x1, y1,
914 fYUVBitmaps[fYUVAIndices[2].fIndex],
915 fYUVAIndices[2].fChannel);
916
917 uint8_t A = 255;
918 if (fYUVAIndices[3].fIndex >= 0) {
919 A = look_up(x1, y1,
920 fYUVBitmaps[fYUVAIndices[3].fIndex],
921 fYUVAIndices[3].fChannel);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400922 }
923
924 // Making premul here.
Robert Phillips1c7062d2018-10-04 10:44:53 -0400925 switch (fYUVColorSpace) {
926 case kJPEG_SkYUVColorSpace:
927 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_jpeg(Y, U, V, A);
928 break;
929 case kRec601_SkYUVColorSpace:
930 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_601(Y, U, V, A);
931 break;
932 case kRec709_SkYUVColorSpace:
933 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_709(Y, U, V, A);
934 break;
Robert Phillips0a22ba82019-03-06 12:36:47 -0500935 case kIdentity_SkYUVColorSpace:
Robert Phillips2dd1b472019-03-21 09:00:20 -0400936 *fFlattened.getAddr32(x, y) = SkPremultiplyARGBInline(A, V, U, Y);
Robert Phillips0a22ba82019-03-06 12:36:47 -0500937 break;
Robert Phillips1c7062d2018-10-04 10:44:53 -0400938 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400939 }
940 }
941 }
942
943 return fFlattened.readPixels(info, pixels, rowBytes, 0, 0);
944 }
945
Jim Van Verthe24b5872018-10-29 16:26:02 -0400946 bool onQueryYUVA8(SkYUVASizeInfo* size,
Jim Van Verth8f11e432018-10-18 14:36:59 -0400947 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
948 SkYUVColorSpace* yuvColorSpace) const override {
949
Robert Phillips2dd1b472019-03-21 09:00:20 -0400950 // The onQueryYUVA8/onGetYUVA8Planes can only handle A8 planes
951 if (!fAllA8) {
952 return false;
953 }
954
Jim Van Verth8f11e432018-10-18 14:36:59 -0400955 memcpy(yuvaIndices, fYUVAIndices, sizeof(fYUVAIndices));
956 *yuvColorSpace = fYUVColorSpace;
957
958 int i = 0;
959 for ( ; i < fNumBitmaps; ++i) {
Jim Van Verth8f11e432018-10-18 14:36:59 -0400960 size->fSizes[i].fWidth = fYUVBitmaps[i].width();
961 size->fSizes[i].fHeight = fYUVBitmaps[i].height();
962 size->fWidthBytes[i] = fYUVBitmaps[i].rowBytes();
963 }
Jim Van Verthe24b5872018-10-29 16:26:02 -0400964 for ( ; i < SkYUVASizeInfo::kMaxCount; ++i) {
Jim Van Verth8f11e432018-10-18 14:36:59 -0400965 size->fSizes[i].fWidth = 0;
966 size->fSizes[i].fHeight = 0;
967 size->fWidthBytes[i] = 0;
Jim Van Verthf99a6742018-10-18 16:13:18 +0000968 }
Jim Van Verth0c583af2018-10-18 10:31:59 -0400969
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400970 return true;
971 }
972
Jim Van Verthe24b5872018-10-29 16:26:02 -0400973 bool onGetYUVA8Planes(const SkYUVASizeInfo&, const SkYUVAIndex[SkYUVAIndex::kIndexCount],
974 void* planes[SkYUVASizeInfo::kMaxCount]) override {
Robert Phillips2dd1b472019-03-21 09:00:20 -0400975 SkASSERT(fAllA8);
Jim Van Verth8f11e432018-10-18 14:36:59 -0400976 for (int i = 0; i < fNumBitmaps; ++i) {
977 planes[i] = fYUVBitmaps[i].getPixels();
978 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400979 return true;
980 }
981
982private:
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400983 SkYUVColorSpace fYUVColorSpace;
Jim Van Verth8f11e432018-10-18 14:36:59 -0400984 SkYUVAIndex fYUVAIndices[SkYUVAIndex::kIndexCount];
985 int fNumBitmaps;
Jim Van Verthe24b5872018-10-29 16:26:02 -0400986 SkBitmap fYUVBitmaps[SkYUVASizeInfo::kMaxCount];
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400987 SkBitmap fFlattened;
Robert Phillips2dd1b472019-03-21 09:00:20 -0400988 bool fAllA8; // are all the SkBitmaps in "fYUVBitmaps" A8?
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400989
990};
991
992static sk_sp<SkImage> make_yuv_gen_image(const SkImageInfo& ii,
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400993 SkYUVColorSpace yuvColorSpace,
Jim Van Verth8f11e432018-10-18 14:36:59 -0400994 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400995 SkBitmap bitmaps[]) {
Jim Van Verth8f11e432018-10-18 14:36:59 -0400996 std::unique_ptr<SkImageGenerator> gen(new YUVGenerator(ii, yuvColorSpace,
997 yuvaIndices, bitmaps));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400998
999 return SkImage::MakeFromGenerator(std::move(gen));
1000}
1001
1002static void draw_col_label(SkCanvas* canvas, int x, int yuvColorSpace, bool opaque) {
Robert Phillips0a22ba82019-03-06 12:36:47 -05001003 static const char* kYUVColorSpaceNames[] = { "JPEG", "601", "709", "Identity" };
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001004 GR_STATIC_ASSERT(SK_ARRAY_COUNT(kYUVColorSpaceNames) == kLastEnum_SkYUVColorSpace+1);
1005
Mike Reed91919132019-01-02 12:21:01 -05001006 SkPaint paint;
Mike Kleinea3f0142019-03-20 11:12:10 -05001007 SkFont font(ToolUtils::create_portable_typeface(nullptr, SkFontStyle::Bold()), 16);
Mike Reed91919132019-01-02 12:21:01 -05001008 font.setEdging(SkFont::Edging::kAlias);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001009
1010 SkRect textRect;
1011 SkString colLabel;
1012
1013 colLabel.printf("%s", kYUVColorSpaceNames[yuvColorSpace]);
Ben Wagner51e15a62019-05-07 15:38:46 -04001014 font.measureText(colLabel.c_str(), colLabel.size(), SkTextEncoding::kUTF8, &textRect);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001015 int y = textRect.height();
1016
Mike Reed91919132019-01-02 12:21:01 -05001017 SkTextUtils::DrawString(canvas, colLabel.c_str(), x, y, font, paint, SkTextUtils::kCenter_Align);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001018
1019 colLabel.printf("%s", opaque ? "Opaque" : "Transparent");
Mike Reed71f5a0b2018-10-25 16:12:39 -04001020
Ben Wagner51e15a62019-05-07 15:38:46 -04001021 font.measureText(colLabel.c_str(), colLabel.size(), SkTextEncoding::kUTF8, &textRect);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001022 y += textRect.height();
1023
Mike Reed91919132019-01-02 12:21:01 -05001024 SkTextUtils::DrawString(canvas, colLabel.c_str(), x, y, font, paint, SkTextUtils::kCenter_Align);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001025}
1026
1027static void draw_row_label(SkCanvas* canvas, int y, int yuvFormat) {
Robert Phillipsbb749902019-06-10 17:20:12 -04001028 static const char* kYUVFormatNames[] = {
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001029 "P016", "P010", "P016F", "Y416", "AYUV", "Y410", "NV12", "NV21", "I420", "YV12"
Robert Phillipsbb749902019-06-10 17:20:12 -04001030 };
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001031 GR_STATIC_ASSERT(SK_ARRAY_COUNT(kYUVFormatNames) == kLast_YUVFormat+1);
1032
Mike Reed91919132019-01-02 12:21:01 -05001033 SkPaint paint;
Mike Kleinea3f0142019-03-20 11:12:10 -05001034 SkFont font(ToolUtils::create_portable_typeface(nullptr, SkFontStyle::Bold()), 16);
Mike Reed91919132019-01-02 12:21:01 -05001035 font.setEdging(SkFont::Edging::kAlias);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001036
1037 SkRect textRect;
1038 SkString rowLabel;
1039
1040 rowLabel.printf("%s", kYUVFormatNames[yuvFormat]);
Ben Wagner51e15a62019-05-07 15:38:46 -04001041 font.measureText(rowLabel.c_str(), rowLabel.size(), SkTextEncoding::kUTF8, &textRect);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001042 y += kTileWidthHeight/2 + textRect.height()/2;
1043
Hal Canary89a644b2019-01-07 09:36:09 -05001044 canvas->drawString(rowLabel, 0, y, font, paint);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001045}
1046
Robert Phillipscb1adb42019-06-10 15:09:34 -04001047static GrBackendTexture create_yuva_texture(GrContext* context, const SkBitmap& bm,
Robert Phillipsbb749902019-06-10 17:20:12 -04001048 SkYUVAIndex yuvaIndices[4], int texIndex,
1049 YUVFormat yuvFormat) {
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001050#ifdef SK_DEBUG
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001051 SkASSERT(texIndex >= 0 && texIndex <= 3);
1052 int channelCount = 0;
1053 for (int i = 0; i < SkYUVAIndex::kIndexCount; ++i) {
1054 if (yuvaIndices[i].fIndex == texIndex) {
1055 ++channelCount;
1056 }
1057 }
Robert Phillipsbb749902019-06-10 17:20:12 -04001058
Robert Phillipsbb749902019-06-10 17:20:12 -04001059 if (format_uses_16_bpp(yuvFormat) || 2 == channelCount) {
Robert Phillipsbb749902019-06-10 17:20:12 -04001060 if (2 == channelCount) {
1061 if (format_uses_16_bpp(yuvFormat)) {
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001062 if (yuvFormat == kP016F_YUVFormat) {
Robert Phillipsea1b30b2019-09-19 16:05:48 -04001063 SkASSERT(kR16G16_float_SkColorType == bm.colorType());
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001064 } else {
1065 SkASSERT(yuvFormat == kP016_YUVFormat || yuvFormat == kP010_YUVFormat);
Robert Phillipsea1b30b2019-09-19 16:05:48 -04001066 SkASSERT(kR16G16_unorm_SkColorType == bm.colorType());
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001067 }
Robert Phillipsbb749902019-06-10 17:20:12 -04001068 } else {
Robert Phillipsea1b30b2019-09-19 16:05:48 -04001069 SkASSERT(kR8G8_unorm_SkColorType == bm.colorType());
Robert Phillipsbb749902019-06-10 17:20:12 -04001070 }
1071 } else {
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001072 if (yuvFormat == kY416_YUVFormat) {
Robert Phillipsea1b30b2019-09-19 16:05:48 -04001073 SkASSERT(kR16G16B16A16_unorm_SkColorType == bm.colorType());
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001074 } else if (yuvFormat == kP016F_YUVFormat) {
Robert Phillipsea1b30b2019-09-19 16:05:48 -04001075 SkASSERT(kA16_float_SkColorType == bm.colorType());
Robert Phillipsbb749902019-06-10 17:20:12 -04001076 } else {
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001077 SkASSERT(yuvFormat == kP016_YUVFormat || yuvFormat == kP010_YUVFormat);
Robert Phillipsea1b30b2019-09-19 16:05:48 -04001078 SkASSERT(kA16_unorm_SkColorType == bm.colorType());
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001079 }
1080 }
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001081 }
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001082#endif
Robert Phillipsbb749902019-06-10 17:20:12 -04001083
Robert Phillips66944402019-09-30 13:21:25 -04001084 return context->createBackendTexture(&bm.pixmap(), 1, GrRenderable::kNo, GrProtected::kNo);
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001085}
1086
Robert Phillips0a22ba82019-03-06 12:36:47 -05001087static sk_sp<SkColorFilter> yuv_to_rgb_colorfilter() {
1088 static const float kJPEGConversionMatrix[20] = {
Mike Reede869a1e2019-04-30 12:18:54 -04001089 1.0f, 0.0f, 1.402f, 0.0f, -180.0f/255,
1090 1.0f, -0.344136f, -0.714136f, 0.0f, 136.0f/255,
1091 1.0f, 1.772f, 0.0f, 0.0f, -227.6f/255,
Robert Phillips0a22ba82019-03-06 12:36:47 -05001092 0.0f, 0.0f, 0.0f, 1.0f, 0.0f
1093 };
1094
Mike Reede869a1e2019-04-30 12:18:54 -04001095 return SkColorFilters::Matrix(kJPEGConversionMatrix);
Robert Phillips0a22ba82019-03-06 12:36:47 -05001096}
1097
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001098namespace skiagm {
1099
1100// This GM creates an opaque and transparent bitmap, extracts the planes and then recombines
1101// them into various YUV formats. It then renders the results in the grid:
1102//
Robert Phillips2dd1b472019-03-21 09:00:20 -04001103// JPEG 601 709 Identity
1104// Transparent Opaque Transparent Opaque Transparent Opaque Transparent Opaque
Robert Phillipsbb749902019-06-10 17:20:12 -04001105// originals
1106// P016
1107// P010
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001108// P016F
Robert Phillipsbb749902019-06-10 17:20:12 -04001109// Y416
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001110// AYUV
Robert Phillips2dd1b472019-03-21 09:00:20 -04001111// Y410
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001112// NV12
1113// NV21
1114// I420
1115// YV12
1116class WackyYUVFormatsGM : public GM {
1117public:
Michael Ludwiga6a84002019-04-12 15:03:02 -04001118 WackyYUVFormatsGM(bool useTargetColorSpace, bool useDomain)
1119 : fUseTargetColorSpace(useTargetColorSpace)
1120 , fUseDomain(useDomain) {
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001121 this->setBGColor(0xFFCCCCCC);
1122 }
1123
1124protected:
1125
1126 SkString onShortName() override {
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001127 SkString name("wacky_yuv_formats");
1128 if (fUseTargetColorSpace) {
1129 name += "_cs";
1130 }
Michael Ludwiga6a84002019-04-12 15:03:02 -04001131 if (fUseDomain) {
1132 name += "_domain";
1133 }
Robert Phillips2dd1b472019-03-21 09:00:20 -04001134
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001135 return name;
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001136 }
1137
1138 SkISize onISize() override {
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001139 int numCols = 2 * (kLastEnum_SkYUVColorSpace + 1); // opacity x #-color-spaces
1140 int numRows = 1 + (kLast_YUVFormat + 1); // original + #-yuv-formats
Michael Ludwiga6a84002019-04-12 15:03:02 -04001141 int wh = SkScalarCeilToInt(kTileWidthHeight * (fUseDomain ? 1.5f : 1.f));
1142 return SkISize::Make(kLabelWidth + numCols * (wh + kPad),
1143 kLabelHeight + numRows * (wh + kPad));
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001144 }
1145
1146 void onOnceBeforeDraw() override {
1147 SkPoint origin = { kTileWidthHeight/2.0f, kTileWidthHeight/2.0f };
1148 float outerRadius = kTileWidthHeight/2.0f - 20.0f;
1149 float innerRadius = 20.0f;
1150
1151 {
1152 // transparent
1153 SkTDArray<SkRect> circles;
1154 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 5, &circles);
Michael Ludwiga6a84002019-04-12 15:03:02 -04001155 fOriginalBMs[0] = make_bitmap(kRGBA_8888_SkColorType, path, circles, false, fUseDomain);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001156 }
1157
1158 {
1159 // opaque
1160 SkTDArray<SkRect> circles;
1161 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 7, &circles);
Michael Ludwiga6a84002019-04-12 15:03:02 -04001162 fOriginalBMs[1] = make_bitmap(kRGBA_8888_SkColorType, path, circles, true, fUseDomain);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001163 }
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001164
1165 if (fUseTargetColorSpace) {
1166 fTargetColorSpace = SkColorSpace::MakeSRGB()->makeColorSpin();
1167 }
Robert Phillips51c89e42018-10-05 13:30:43 -04001168 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001169
Robert Phillips51c89e42018-10-05 13:30:43 -04001170 void createImages(GrContext* context) {
Jim Van Verth9bf81202018-10-30 15:53:36 -04001171 int counter = 0;
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001172 for (bool opaque : { false, true }) {
1173 for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
1174 PlaneData planes;
1175 extract_planes(fOriginalBMs[opaque], (SkYUVColorSpace) cs, &planes);
1176
Robert Phillipsbb749902019-06-10 17:20:12 -04001177 for (int format = kP016_YUVFormat; format <= kLast_YUVFormat; ++format) {
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001178 SkBitmap resultBMs[4];
1179 SkYUVAIndex yuvaIndices[4];
Robert Phillips2dd1b472019-03-21 09:00:20 -04001180
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001181 create_YUV(planes, (YUVFormat) format, resultBMs, yuvaIndices, opaque);
Robert Phillips2dd1b472019-03-21 09:00:20 -04001182
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001183 int numTextures;
1184 if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures)) {
1185 continue;
1186 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001187
Robert Phillips51c89e42018-10-05 13:30:43 -04001188 if (context) {
Chris Dalton382b1222019-02-07 10:05:55 +00001189 if (context->abandoned()) {
1190 return;
1191 }
1192
Robert Phillipsf47717a2019-09-19 13:04:33 -04001193 if (!is_format_natively_supported(context, (YUVFormat) format)) {
1194 continue;
1195 }
1196
Robert Phillips51c89e42018-10-05 13:30:43 -04001197 GrBackendTexture yuvaTextures[4];
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001198 SkPixmap yuvaPixmaps[4];
Robert Phillips51c89e42018-10-05 13:30:43 -04001199
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001200 for (int i = 0; i < numTextures; ++i) {
Robert Phillipscb1adb42019-06-10 15:09:34 -04001201 yuvaTextures[i] = create_yuva_texture(context, resultBMs[i],
Robert Phillipsbb749902019-06-10 17:20:12 -04001202 yuvaIndices, i,
1203 (YUVFormat) format);
Brian Salomonf2580c02019-01-30 09:13:31 -05001204 if (yuvaTextures[i].isValid()) {
1205 fBackendTextures.push_back(yuvaTextures[i]);
1206 }
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001207 yuvaPixmaps[i] = resultBMs[i].pixmap();
Robert Phillips51c89e42018-10-05 13:30:43 -04001208 }
1209
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001210 int counterMod = counter % 3;
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001211 if (fUseDomain && counterMod == 0) {
Michael Ludwiga6a84002019-04-12 15:03:02 -04001212 // Copies flatten to RGB when they copy the YUVA data, which doesn't
1213 // know about the intended domain and the domain padding bleeds in
1214 counterMod = 1;
1215 }
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001216 switch (counterMod) {
1217 case 0:
Jim Van Verth9bf81202018-10-30 15:53:36 -04001218 fImages[opaque][cs][format] = SkImage::MakeFromYUVATexturesCopy(
1219 context,
1220 (SkYUVColorSpace)cs,
1221 yuvaTextures,
1222 yuvaIndices,
1223 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1224 kTopLeft_GrSurfaceOrigin);
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001225 break;
1226 case 1:
Jim Van Verth9bf81202018-10-30 15:53:36 -04001227 fImages[opaque][cs][format] = SkImage::MakeFromYUVATextures(
1228 context,
1229 (SkYUVColorSpace)cs,
1230 yuvaTextures,
1231 yuvaIndices,
1232 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1233 kTopLeft_GrSurfaceOrigin);
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001234 break;
1235 case 2:
1236 default:
1237 fImages[opaque][cs][format] = SkImage::MakeFromYUVAPixmaps(
1238 context,
1239 (SkYUVColorSpace)cs,
1240 yuvaPixmaps,
1241 yuvaIndices,
1242 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1243 kTopLeft_GrSurfaceOrigin, true);
1244 break;
Jim Van Verth9bf81202018-10-30 15:53:36 -04001245 }
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001246 ++counter;
Jim Van Verth9bf81202018-10-30 15:53:36 -04001247 } else {
Robert Phillips51c89e42018-10-05 13:30:43 -04001248 fImages[opaque][cs][format] = make_yuv_gen_image(
1249 fOriginalBMs[opaque].info(),
Robert Phillips51c89e42018-10-05 13:30:43 -04001250 (SkYUVColorSpace) cs,
1251 yuvaIndices,
1252 resultBMs);
1253 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001254 }
1255 }
1256 }
1257 }
1258
1259 void onDraw(SkCanvas* canvas) override {
Robert Phillips51c89e42018-10-05 13:30:43 -04001260 this->createImages(canvas->getGrContext());
1261
Michael Ludwiga6a84002019-04-12 15:03:02 -04001262 SkRect srcRect = SkRect::MakeWH(fOriginalBMs[0].width(), fOriginalBMs[0].height());
1263 SkRect dstRect = SkRect::MakeXYWH(kLabelWidth, 0.f, srcRect.width(), srcRect.height());
1264
1265 SkCanvas::SrcRectConstraint constraint = SkCanvas::kFast_SrcRectConstraint;
1266 if (fUseDomain) {
1267 srcRect.inset(kDomainPadding, kDomainPadding);
1268 // Draw a larger rectangle to ensure bilerp filtering would normally read outside the
1269 // srcRect and hit the red pixels, if strict constraint weren't used.
1270 dstRect.fRight = kLabelWidth + 1.5f * srcRect.width();
1271 dstRect.fBottom = 1.5f * srcRect.height();
1272 constraint = SkCanvas::kStrict_SrcRectConstraint;
1273 }
1274
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001275 for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
Robert Phillips0a22ba82019-03-06 12:36:47 -05001276 SkPaint paint;
Michael Ludwiga6a84002019-04-12 15:03:02 -04001277 paint.setFilterQuality(kLow_SkFilterQuality);
Robert Phillips0a22ba82019-03-06 12:36:47 -05001278 if (kIdentity_SkYUVColorSpace == cs) {
1279 // The identity color space needs post processing to appear correctly
1280 paint.setColorFilter(yuv_to_rgb_colorfilter());
1281 }
1282
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001283 for (int opaque : { 0, 1 }) {
Michael Ludwiga6a84002019-04-12 15:03:02 -04001284 dstRect.offsetTo(dstRect.fLeft, kLabelHeight);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001285
Michael Ludwiga6a84002019-04-12 15:03:02 -04001286 draw_col_label(canvas, dstRect.fLeft + dstRect.height() / 2, cs, opaque);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001287
Michael Ludwiga6a84002019-04-12 15:03:02 -04001288 canvas->drawBitmapRect(fOriginalBMs[opaque], srcRect, dstRect, nullptr, constraint);
1289 dstRect.offset(0.f, dstRect.height() + kPad);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001290
Robert Phillipsbb749902019-06-10 17:20:12 -04001291 for (int format = kP016_YUVFormat; format <= kLast_YUVFormat; ++format) {
Michael Ludwiga6a84002019-04-12 15:03:02 -04001292 draw_row_label(canvas, dstRect.fTop, format);
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001293 if (fUseTargetColorSpace && fImages[opaque][cs][format]) {
Robert Phillips0a22ba82019-03-06 12:36:47 -05001294 // Making a CS-specific version of a kIdentity_SkYUVColorSpace YUV image
1295 // doesn't make a whole lot of sense. The colorSpace conversion will
1296 // operate on the YUV components rather than the RGB components.
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001297 sk_sp<SkImage> csImage =
1298 fImages[opaque][cs][format]->makeColorSpace(fTargetColorSpace);
Michael Ludwiga6a84002019-04-12 15:03:02 -04001299 canvas->drawImageRect(csImage, srcRect, dstRect, &paint, constraint);
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001300 } else {
Michael Ludwiga6a84002019-04-12 15:03:02 -04001301 canvas->drawImageRect(fImages[opaque][cs][format], srcRect, dstRect, &paint,
1302 constraint);
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001303 }
Michael Ludwiga6a84002019-04-12 15:03:02 -04001304 dstRect.offset(0.f, dstRect.height() + kPad);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001305 }
1306
Michael Ludwiga6a84002019-04-12 15:03:02 -04001307 dstRect.offset(dstRect.width() + kPad, 0.f);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001308 }
1309 }
Brian Salomon8f388ce2019-01-29 14:50:53 -05001310 if (auto context = canvas->getGrContext()) {
Chris Dalton382b1222019-02-07 10:05:55 +00001311 if (!context->abandoned()) {
1312 context->flush();
1313 GrGpu* gpu = context->priv().getGpu();
1314 SkASSERT(gpu);
1315 gpu->testingOnly_flushGpuAndSync();
1316 for (const auto& tex : fBackendTextures) {
Robert Phillips5c7a25b2019-05-20 08:38:07 -04001317 context->deleteBackendTexture(tex);
Chris Dalton382b1222019-02-07 10:05:55 +00001318 }
1319 fBackendTextures.reset();
Brian Salomon8f388ce2019-01-29 14:50:53 -05001320 }
Brian Salomon8f388ce2019-01-29 14:50:53 -05001321 }
1322 SkASSERT(!fBackendTextures.count());
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001323 }
1324
1325private:
Robert Phillips2dd1b472019-03-21 09:00:20 -04001326 SkBitmap fOriginalBMs[2];
1327 sk_sp<SkImage> fImages[2][kLastEnum_SkYUVColorSpace + 1][kLast_YUVFormat + 1];
Brian Salomon8f388ce2019-01-29 14:50:53 -05001328 SkTArray<GrBackendTexture> fBackendTextures;
Robert Phillips2dd1b472019-03-21 09:00:20 -04001329 bool fUseTargetColorSpace;
Michael Ludwiga6a84002019-04-12 15:03:02 -04001330 bool fUseDomain;
Robert Phillips2dd1b472019-03-21 09:00:20 -04001331 sk_sp<SkColorSpace> fTargetColorSpace;
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001332
1333 typedef GM INHERITED;
1334};
1335
1336//////////////////////////////////////////////////////////////////////////////
1337
Michael Ludwiga6a84002019-04-12 15:03:02 -04001338DEF_GM(return new WackyYUVFormatsGM(/* cs */ false, /* domain */ false);)
1339DEF_GM(return new WackyYUVFormatsGM(/* cs */ true, /* domain */ false);)
1340DEF_GM(return new WackyYUVFormatsGM(/* cs */ false, /* domain */ true);)
Brian Osmane9560492019-02-05 17:00:03 -05001341
Chris Dalton3a778372019-02-07 15:23:36 -07001342class YUVMakeColorSpaceGM : public GpuGM {
Brian Osmane9560492019-02-05 17:00:03 -05001343public:
1344 YUVMakeColorSpaceGM() {
1345 this->setBGColor(0xFFCCCCCC);
1346 }
1347
1348protected:
1349 SkString onShortName() override {
1350 return SkString("yuv_make_color_space");
1351 }
1352
1353 SkISize onISize() override {
1354 int numCols = 4; // (transparent, opaque) x (untagged, tagged)
1355 int numRows = 5; // original, YUV, subset, readPixels, makeNonTextureImage
1356 return SkISize::Make(numCols * (kTileWidthHeight + kPad) + kPad,
1357 numRows * (kTileWidthHeight + kPad) + kPad);
1358 }
1359
1360 void onOnceBeforeDraw() override {
1361 SkPoint origin = { kTileWidthHeight/2.0f, kTileWidthHeight/2.0f };
1362 float outerRadius = kTileWidthHeight/2.0f - 20.0f;
1363 float innerRadius = 20.0f;
1364
1365 {
1366 // transparent
1367 SkTDArray<SkRect> circles;
1368 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 5, &circles);
Michael Ludwiga6a84002019-04-12 15:03:02 -04001369 fOriginalBMs[0] = make_bitmap(kN32_SkColorType, path, circles, false, false);
Brian Osmane9560492019-02-05 17:00:03 -05001370 }
1371
1372 {
1373 // opaque
1374 SkTDArray<SkRect> circles;
1375 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 7, &circles);
Michael Ludwiga6a84002019-04-12 15:03:02 -04001376 fOriginalBMs[1] = make_bitmap(kN32_SkColorType, path, circles, true, false);
Brian Osmane9560492019-02-05 17:00:03 -05001377 }
1378
1379 fTargetColorSpace = SkColorSpace::MakeSRGB()->makeColorSpin();
1380 }
1381
1382 void createImages(GrContext* context) {
1383 for (bool opaque : { false, true }) {
1384 PlaneData planes;
1385 extract_planes(fOriginalBMs[opaque], kJPEG_SkYUVColorSpace, &planes);
1386
1387 SkBitmap resultBMs[4];
1388 SkYUVAIndex yuvaIndices[4];
Robert Phillips2dd1b472019-03-21 09:00:20 -04001389
Brian Osmane9560492019-02-05 17:00:03 -05001390 create_YUV(planes, kAYUV_YUVFormat, resultBMs, yuvaIndices, opaque);
Robert Phillips2dd1b472019-03-21 09:00:20 -04001391
Brian Osmane9560492019-02-05 17:00:03 -05001392 int numTextures;
1393 if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures)) {
1394 continue;
1395 }
1396
Brian Osmane9560492019-02-05 17:00:03 -05001397 GrBackendTexture yuvaTextures[4];
1398 for (int i = 0; i < numTextures; ++i) {
Robert Phillipsbb749902019-06-10 17:20:12 -04001399 yuvaTextures[i] = create_yuva_texture(context, resultBMs[i], yuvaIndices, i,
1400 kAYUV_YUVFormat);
Brian Osmane9560492019-02-05 17:00:03 -05001401 if (yuvaTextures[i].isValid()) {
1402 fBackendTextures.push_back(yuvaTextures[i]);
1403 }
1404 }
1405
1406 fImages[opaque][0] = SkImage::MakeFromYUVATextures(
1407 context,
1408 kJPEG_SkYUVColorSpace,
1409 yuvaTextures,
1410 yuvaIndices,
1411 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1412 kTopLeft_GrSurfaceOrigin);
1413 fImages[opaque][1] = SkImage::MakeFromYUVATextures(
1414 context,
1415 kJPEG_SkYUVColorSpace,
1416 yuvaTextures,
1417 yuvaIndices,
1418 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1419 kTopLeft_GrSurfaceOrigin,
1420 SkColorSpace::MakeSRGB());
1421 }
1422 }
1423
Chris Dalton3a778372019-02-07 15:23:36 -07001424 void onDraw(GrContext* context, GrRenderTargetContext*, SkCanvas* canvas) override {
Brian Osmane9560492019-02-05 17:00:03 -05001425 this->createImages(context);
1426
1427 int x = kPad;
1428 for (int tagged : { 0, 1 }) {
1429 for (int opaque : { 0, 1 }) {
1430 int y = kPad;
1431
1432 auto raster = SkImage::MakeFromBitmap(fOriginalBMs[opaque])
1433 ->makeColorSpace(fTargetColorSpace);
1434 canvas->drawImage(raster, x, y);
1435 y += kTileWidthHeight + kPad;
1436
1437 auto yuv = fImages[opaque][tagged]->makeColorSpace(fTargetColorSpace);
1438 SkASSERT(SkColorSpace::Equals(yuv->colorSpace(), fTargetColorSpace.get()));
1439 canvas->drawImage(yuv, x, y);
1440 y += kTileWidthHeight + kPad;
1441
1442 auto subset = yuv->makeSubset(SkIRect::MakeWH(kTileWidthHeight / 2,
1443 kTileWidthHeight / 2));
1444 canvas->drawImage(subset, x, y);
1445 y += kTileWidthHeight + kPad;
1446
1447 auto nonTexture = yuv->makeNonTextureImage();
1448 canvas->drawImage(nonTexture, x, y);
1449 y += kTileWidthHeight + kPad;
1450
1451 SkBitmap readBack;
Brian Salomon5ad6fd32019-03-21 15:30:08 -04001452 readBack.allocPixels(yuv->imageInfo());
Brian Osmane9560492019-02-05 17:00:03 -05001453 yuv->readPixels(readBack.pixmap(), 0, 0);
1454 canvas->drawBitmap(readBack, x, y);
1455
1456 x += kTileWidthHeight + kPad;
1457 }
1458 }
1459
1460 context->flush();
1461 GrGpu* gpu = context->priv().getGpu();
1462 SkASSERT(gpu);
1463 gpu->testingOnly_flushGpuAndSync();
1464 for (const auto& tex : fBackendTextures) {
Robert Phillips5c7a25b2019-05-20 08:38:07 -04001465 context->deleteBackendTexture(tex);
Brian Osmane9560492019-02-05 17:00:03 -05001466 }
1467 fBackendTextures.reset();
1468 }
1469
1470private:
1471 SkBitmap fOriginalBMs[2];
1472 sk_sp<SkImage> fImages[2][2];
1473 SkTArray<GrBackendTexture> fBackendTextures;
1474 sk_sp<SkColorSpace> fTargetColorSpace;
1475
1476 typedef GM INHERITED;
1477};
1478
1479DEF_GM(return new YUVMakeColorSpaceGM();)
1480
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001481}
Mike Reed6a5f7e22019-05-23 15:30:07 -04001482
1483///////////////
1484
Mike Reed6a5f7e22019-05-23 15:30:07 -04001485#include "include/effects/SkColorMatrix.h"
Mike Klein4b432fa2019-06-06 11:44:05 -05001486#include "src/core/SkAutoPixmapStorage.h"
1487#include "src/core/SkYUVMath.h"
1488#include "tools/Resources.h"
Mike Reed6a5f7e22019-05-23 15:30:07 -04001489
1490static void draw_into_alpha(const SkImage* img, sk_sp<SkColorFilter> cf, const SkPixmap& dst) {
1491 auto canvas = SkCanvas::MakeRasterDirect(dst.info(), dst.writable_addr(), dst.rowBytes());
1492 canvas->scale(1.0f * dst.width() / img->width(), 1.0f * dst.height() / img->height());
1493 SkPaint paint;
1494 paint.setFilterQuality(kLow_SkFilterQuality);
1495 paint.setColorFilter(cf);
1496 paint.setBlendMode(SkBlendMode::kSrc);
1497 canvas->drawImage(img, 0, 0, &paint);
1498}
1499
1500static void split_into_yuv(const SkImage* img, SkYUVColorSpace cs, const SkPixmap dst[3]) {
1501 float m[20];
1502 SkColorMatrix_RGB2YUV(cs, m);
1503
1504 memcpy(m + 15, m + 0, 5 * sizeof(float)); // copy Y into A
1505 draw_into_alpha(img, SkColorFilters::Matrix(m), dst[0]);
1506
1507 memcpy(m + 15, m + 5, 5 * sizeof(float)); // copy U into A
1508 draw_into_alpha(img, SkColorFilters::Matrix(m), dst[1]);
1509
1510 memcpy(m + 15, m + 10, 5 * sizeof(float)); // copy V into A
1511 draw_into_alpha(img, SkColorFilters::Matrix(m), dst[2]);
1512}
1513
1514static void draw_diff(SkCanvas* canvas, SkScalar x, SkScalar y,
1515 const SkImage* a, const SkImage* b) {
1516 auto sh = SkShaders::Blend(SkBlendMode::kDifference, a->makeShader(), b->makeShader());
1517 SkPaint paint;
1518 paint.setShader(sh);
1519 canvas->save();
1520 canvas->translate(x, y);
1521 canvas->drawRect(SkRect::MakeWH(a->width(), a->height()), paint);
1522
1523 SkColorMatrix cm;
1524 cm.setScale(64, 64, 64);
1525 paint.setShader(sh->makeWithColorFilter(SkColorFilters::Matrix(cm)));
1526 canvas->translate(0, a->height());
1527 canvas->drawRect(SkRect::MakeWH(a->width(), a->height()), paint);
1528
1529 canvas->restore();
1530}
1531
1532// Exercises SkColorMatrix_RGB2YUV for yuv colorspaces, showing the planes, and the
1533// resulting (recombined) images (gpu only for now).
1534//
1535class YUVSplitterGM : public skiagm::GM {
1536 sk_sp<SkImage> fOrig;
1537 SkAutoPixmapStorage fStorage[3];
1538 SkPixmap fPM[3];
1539
1540public:
1541 YUVSplitterGM() {}
1542
1543protected:
1544
1545 SkString onShortName() override {
1546 return SkString("yuv_splitter");
1547 }
1548
1549 SkISize onISize() override {
1550 return SkISize::Make(1024, 768);
1551 }
1552
1553 void onOnceBeforeDraw() override {
1554 fOrig = GetResourceAsImage("images/mandrill_256.png");
1555
1556 SkImageInfo info = SkImageInfo::Make(fOrig->width(), fOrig->height(), kAlpha_8_SkColorType,
1557 kPremul_SkAlphaType);
1558 fStorage[0].alloc(info);
1559 if (0) {
1560 // if you want to scale U,V down by 1/2
1561 info = info.makeWH(info.width()/2, info.height()/2);
1562 }
1563 fStorage[1].alloc(info);
1564 fStorage[2].alloc(info);
1565 for (int i = 0; i < 3; ++i) {
1566 fPM[i] = fStorage[i];
1567 }
1568 }
1569
1570 void onDraw(SkCanvas* canvas) override {
1571 SkYUVAIndex indices[4];
1572 indices[SkYUVAIndex::kY_Index] = {0, SkColorChannel::kR};
1573 indices[SkYUVAIndex::kU_Index] = {1, SkColorChannel::kR};
1574 indices[SkYUVAIndex::kV_Index] = {2, SkColorChannel::kR};
1575 indices[SkYUVAIndex::kA_Index] = {-1, SkColorChannel::kR};
1576
1577 canvas->translate(fOrig->width(), 0);
1578 canvas->save();
1579 for (auto cs : {kRec709_SkYUVColorSpace, kRec601_SkYUVColorSpace, kJPEG_SkYUVColorSpace}) {
1580 split_into_yuv(fOrig.get(), cs, fPM);
1581 auto img = SkImage::MakeFromYUVAPixmaps(canvas->getGrContext(), cs, fPM, indices,
1582 fPM[0].info().dimensions(),
1583 kTopLeft_GrSurfaceOrigin,
1584 false, false, nullptr);
1585 if (img) {
1586 canvas->drawImage(img, 0, 0, nullptr);
1587 draw_diff(canvas, 0, fOrig->height(), fOrig.get(), img.get());
1588 }
1589 canvas->translate(fOrig->width(), 0);
1590 }
1591 canvas->restore();
1592 canvas->translate(-fOrig->width(), 0);
1593
1594 canvas->drawImage(SkImage::MakeRasterCopy(fPM[0]), 0, 0, nullptr);
1595 canvas->drawImage(SkImage::MakeRasterCopy(fPM[1]), 0, fPM[0].height(), nullptr);
1596 canvas->drawImage(SkImage::MakeRasterCopy(fPM[2]),
1597 0, fPM[0].height() + fPM[1].height(), nullptr);
1598 }
1599
1600private:
1601 typedef GM INHERITED;
1602};
1603DEF_GM( return new YUVSplitterGM; )