blob: 3fe6990ec04566ccc472082f6aa2181d1ed8611e [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"
Brian Osmanac8a16c2019-10-31 10:24:12 -040046#include "src/core/SkYUVMath.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050047#include "src/gpu/GrContextPriv.h"
48#include "src/gpu/GrGpu.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040049#include "tools/ToolUtils.h"
50
51#include <math.h>
52#include <string.h>
53#include <initializer_list>
54#include <memory>
55#include <utility>
56
57class GrRenderTargetContext;
Robert Phillips51c89e42018-10-05 13:30:43 -040058
Robert Phillipsbfa76f22018-10-03 12:12:26 -040059static const int kTileWidthHeight = 128;
60static const int kLabelWidth = 64;
61static const int kLabelHeight = 32;
Michael Ludwiga6a84002019-04-12 15:03:02 -040062static const int kDomainPadding = 8;
Robert Phillipsbfa76f22018-10-03 12:12:26 -040063static const int kPad = 1;
Robert Phillipsbfa76f22018-10-03 12:12:26 -040064
65enum YUVFormat {
Robert Phillipsbb749902019-06-10 17:20:12 -040066 // 4:2:0 formats, 24 bpp
67 kP016_YUVFormat, // 16-bit Y plane + 2x2 down sampled interleaved U/V plane (2 textures)
68 // 4:2:0 formats, "15 bpp" (but really 24 bpp)
69 kP010_YUVFormat, // same as kP016 except "10 bpp". Note that it is the same memory layout
70 // except that the bottom 6 bits are zeroed out (2 textures)
71 // TODO: we're cheating a bit w/ P010 and just treating it as unorm 16. This means its
72 // fully saturated values are 65504 rather than 65535 (that is just .9995 out of 1.0 though).
73
Robert Phillips17a3a0b2019-09-18 13:56:54 -040074 // This is laid out the same as kP016 and kP010 but uses F16 unstead of U16. In this case
75 // the 10 bits/channel vs 16 bits/channel distinction isn't relevant.
76 kP016F_YUVFormat,
77
Robert Phillipsbb749902019-06-10 17:20:12 -040078 // 4:4:4 formats, 64 bpp
79 kY416_YUVFormat, // 16-bit AVYU values all interleaved (1 texture)
80
Robert Phillipsbfa76f22018-10-03 12:12:26 -040081 // 4:4:4 formats, 32 bpp
Robert Phillipsbb749902019-06-10 17:20:12 -040082 kAYUV_YUVFormat, // 8-bit YUVA values all interleaved (1 texture)
83 kY410_YUVFormat, // AVYU w/ 10bpp for YUV and 2 for A all interleaved (1 texture)
Robert Phillipsbfa76f22018-10-03 12:12:26 -040084
85 // 4:2:0 formats, 12 bpp
Robert Phillipsbb749902019-06-10 17:20:12 -040086 kNV12_YUVFormat, // 8-bit Y plane + 2x2 down sampled interleaved U/V planes (2 textures)
87 kNV21_YUVFormat, // same as kNV12 but w/ U/V reversed in the interleaved texture (2 textures)
Robert Phillipsbfa76f22018-10-03 12:12:26 -040088
Robert Phillipsbb749902019-06-10 17:20:12 -040089 kI420_YUVFormat, // 8-bit Y plane + separate 2x2 down sampled U and V planes (3 textures)
90 kYV12_YUVFormat, // 8-bit Y plane + separate 2x2 down sampled V and U planes (3 textures)
Robert Phillipsbfa76f22018-10-03 12:12:26 -040091
92 kLast_YUVFormat = kYV12_YUVFormat
93};
94
Robert Phillips17a3a0b2019-09-18 13:56:54 -040095#ifdef SK_DEBUG
Robert Phillipsbb749902019-06-10 17:20:12 -040096static bool format_uses_16_bpp(YUVFormat yuvFormat) {
Robert Phillips17a3a0b2019-09-18 13:56:54 -040097 return kP016_YUVFormat == yuvFormat ||
98 kP010_YUVFormat == yuvFormat ||
99 kP016F_YUVFormat == yuvFormat ||
Robert Phillipsbb749902019-06-10 17:20:12 -0400100 kY416_YUVFormat == yuvFormat;
101}
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400102#endif
Robert Phillipsbb749902019-06-10 17:20:12 -0400103
104static bool format_has_builtin_alpha(YUVFormat yuvFormat) {
105 return kY416_YUVFormat == yuvFormat ||
106 kAYUV_YUVFormat == yuvFormat ||
107 kY410_YUVFormat == yuvFormat;
108}
109
Robert Phillipsf47717a2019-09-19 13:04:33 -0400110static bool is_colorType_texturable(const GrCaps* caps, GrColorType ct) {
111 GrBackendFormat format = caps->getDefaultBackendFormat(ct, GrRenderable::kNo);
112 if (!format.isValid()) {
113 return false;
114 }
115
116 return caps->isFormatTexturable(format);
117}
118
119static bool is_format_natively_supported(GrContext* context, YUVFormat yuvFormat) {
120
121 const GrCaps* caps = context->priv().caps();
122
123 switch (yuvFormat) {
124 case kP016_YUVFormat: // fall through
125 case kP010_YUVFormat: return is_colorType_texturable(caps, GrColorType::kAlpha_16) &&
126 is_colorType_texturable(caps, GrColorType::kRG_1616);
127 case kP016F_YUVFormat: return is_colorType_texturable(caps, GrColorType::kAlpha_F16) &&
128 is_colorType_texturable(caps, GrColorType::kRG_F16);
129 case kY416_YUVFormat: return is_colorType_texturable(caps, GrColorType::kRGBA_16161616);
130 case kAYUV_YUVFormat: return is_colorType_texturable(caps, GrColorType::kRGBA_8888);
131 case kY410_YUVFormat: return is_colorType_texturable(caps, GrColorType::kRGBA_1010102);
132 case kNV12_YUVFormat: // fall through
133 case kNV21_YUVFormat: return is_colorType_texturable(caps, GrColorType::kGray_8) &&
134 is_colorType_texturable(caps, GrColorType::kRG_88);
135 case kI420_YUVFormat: // fall through
136 case kYV12_YUVFormat: return is_colorType_texturable(caps, GrColorType::kGray_8);
137 }
138
139 SkUNREACHABLE;
140}
141
Robert Phillipsdc62b642019-05-16 10:10:45 -0400142// Helper to setup the SkYUVAIndex array correctly
143// Skia allows the client to tack an additional alpha plane onto any of the standard opaque
144// formats (via the addExtraAlpha) flag. In this case it is assumed to be a stand-alone single-
145// channel plane.
146static void setup_yuv_indices(YUVFormat yuvFormat, bool addExtraAlpha, SkYUVAIndex yuvaIndices[4]) {
147 switch (yuvFormat) {
Robert Phillipsbb749902019-06-10 17:20:12 -0400148 case kP016_YUVFormat: // fall through
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400149 case kP010_YUVFormat: // fall through
150 case kP016F_YUVFormat:
Robert Phillipsbb749902019-06-10 17:20:12 -0400151 yuvaIndices[0].fIndex = 0;
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400152 yuvaIndices[0].fChannel = SkColorChannel::kA; // bc 16bit is stored in A16 or AF16
Robert Phillipsbb749902019-06-10 17:20:12 -0400153 yuvaIndices[1].fIndex = 1;
154 yuvaIndices[1].fChannel = SkColorChannel::kR;
155 yuvaIndices[2].fIndex = 1;
156 yuvaIndices[2].fChannel = SkColorChannel::kG;
157 if (addExtraAlpha) {
158 yuvaIndices[3].fIndex = 2;
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400159 yuvaIndices[3].fChannel = SkColorChannel::kA; // bc 16bit is stored in A16 or AF16
Robert Phillipsbb749902019-06-10 17:20:12 -0400160 } else {
161 yuvaIndices[3].fIndex = -1; // No alpha channel
162 }
163 break;
164 case kY416_YUVFormat:
165 SkASSERT(!addExtraAlpha); // this format already has an alpha channel
166 yuvaIndices[0].fIndex = 0;
167 yuvaIndices[0].fChannel = SkColorChannel::kG;
168 yuvaIndices[1].fIndex = 0;
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400169 yuvaIndices[1].fChannel = SkColorChannel::kR;
Robert Phillipsbb749902019-06-10 17:20:12 -0400170 yuvaIndices[2].fIndex = 0;
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400171 yuvaIndices[2].fChannel = SkColorChannel::kB;
Robert Phillipsbb749902019-06-10 17:20:12 -0400172 yuvaIndices[3].fIndex = 0;
173 yuvaIndices[3].fChannel = SkColorChannel::kA;
174 break;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400175 case kAYUV_YUVFormat:
176 SkASSERT(!addExtraAlpha); // this format already has an alpha channel
177 yuvaIndices[0].fIndex = 0;
178 yuvaIndices[0].fChannel = SkColorChannel::kR;
179 yuvaIndices[1].fIndex = 0;
180 yuvaIndices[1].fChannel = SkColorChannel::kG;
181 yuvaIndices[2].fIndex = 0;
182 yuvaIndices[2].fChannel = SkColorChannel::kB;
183 yuvaIndices[3].fIndex = 0;
184 yuvaIndices[3].fChannel = SkColorChannel::kA;
185 break;
186 case kY410_YUVFormat:
187 SkASSERT(!addExtraAlpha); // this format already has an alpha channel
188 yuvaIndices[0].fIndex = 0;
189 yuvaIndices[0].fChannel = SkColorChannel::kG;
190 yuvaIndices[1].fIndex = 0;
191 yuvaIndices[1].fChannel = SkColorChannel::kB;
192 yuvaIndices[2].fIndex = 0;
193 yuvaIndices[2].fChannel = SkColorChannel::kR;
194 yuvaIndices[3].fIndex = 0;
195 yuvaIndices[3].fChannel = SkColorChannel::kA;
196 break;
197 case kNV12_YUVFormat:
198 yuvaIndices[0].fIndex = 0;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400199 yuvaIndices[0].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400200 yuvaIndices[1].fIndex = 1;
201 yuvaIndices[1].fChannel = SkColorChannel::kR;
202 yuvaIndices[2].fIndex = 1;
203 yuvaIndices[2].fChannel = SkColorChannel::kG;
204 if (addExtraAlpha) {
205 yuvaIndices[3].fIndex = 2;
206 yuvaIndices[3].fChannel = SkColorChannel::kA;
207 } else {
208 yuvaIndices[3].fIndex = -1; // No alpha channel
209 }
210 break;
211 case kNV21_YUVFormat:
212 yuvaIndices[0].fIndex = 0;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400213 yuvaIndices[0].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400214 yuvaIndices[1].fIndex = 1;
215 yuvaIndices[1].fChannel = SkColorChannel::kG;
216 yuvaIndices[2].fIndex = 1;
217 yuvaIndices[2].fChannel = SkColorChannel::kR;
218 if (addExtraAlpha) {
219 yuvaIndices[3].fIndex = 2;
220 yuvaIndices[3].fChannel = SkColorChannel::kA;
221 } else {
222 yuvaIndices[3].fIndex = -1; // No alpha channel
223 }
224 break;
225 case kI420_YUVFormat:
226 yuvaIndices[0].fIndex = 0;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400227 yuvaIndices[0].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400228 yuvaIndices[1].fIndex = 1;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400229 yuvaIndices[1].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400230 yuvaIndices[2].fIndex = 2;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400231 yuvaIndices[2].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400232 if (addExtraAlpha) {
233 yuvaIndices[3].fIndex = 3;
234 yuvaIndices[3].fChannel = SkColorChannel::kA;
235 } else {
236 yuvaIndices[3].fIndex = -1; // No alpha channel
237 }
238 break;
239 case kYV12_YUVFormat:
240 yuvaIndices[0].fIndex = 0;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400241 yuvaIndices[0].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400242 yuvaIndices[1].fIndex = 2;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400243 yuvaIndices[1].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400244 yuvaIndices[2].fIndex = 1;
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400245 yuvaIndices[2].fChannel = SkColorChannel::kR;
Robert Phillipsdc62b642019-05-16 10:10:45 -0400246 if (addExtraAlpha) {
247 yuvaIndices[3].fIndex = 3;
248 yuvaIndices[3].fChannel = SkColorChannel::kA;
249 } else {
250 yuvaIndices[3].fIndex = -1; // No alpha channel
251 }
252 break;
253 }
254}
255
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400256// All the planes we need to construct the various YUV formats
257struct PlaneData {
258 SkBitmap fYFull;
259 SkBitmap fUFull;
260 SkBitmap fVFull;
261 SkBitmap fAFull;
262 SkBitmap fUQuarter; // 2x2 downsampled U channel
263 SkBitmap fVQuarter; // 2x2 downsampled V channel
Robert Phillips429f0d32019-09-11 17:03:28 -0400264
265 SkBitmap fFull;
266 SkBitmap fQuarter; // 2x2 downsampled YUVA
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400267};
268
269// Add a portion of a circle to 'path'. The points 'o1' and 'o2' are on the border of the circle
270// and have tangents 'v1' and 'v2'.
271static void add_arc(SkPath* path,
272 const SkPoint& o1, const SkVector& v1,
273 const SkPoint& o2, const SkVector& v2,
274 SkTDArray<SkRect>* circles, bool takeLongWayRound) {
275
276 SkVector v3 = { -v1.fY, v1.fX };
277 SkVector v4 = { v2.fY, -v2.fX };
278
279 SkScalar t = ((o2.fX - o1.fX) * v4.fY - (o2.fY - o1.fY) * v4.fX) / v3.cross(v4);
280 SkPoint center = { o1.fX + t * v3.fX, o1.fY + t * v3.fY };
281
282 SkRect r = { center.fX - t, center.fY - t, center.fX + t, center.fY + t };
283
284 if (circles) {
285 circles->push_back(r);
286 }
287
288 SkVector startV = o1 - center, endV = o2 - center;
289 startV.normalize();
290 endV.normalize();
291
292 SkScalar startDeg = SkRadiansToDegrees(SkScalarATan2(startV.fY, startV.fX));
293 SkScalar endDeg = SkRadiansToDegrees(SkScalarATan2(endV.fY, endV.fX));
294
295 startDeg += 360.0f;
296 startDeg = fmodf(startDeg, 360.0f);
297
298 endDeg += 360.0f;
299 endDeg = fmodf(endDeg, 360.0f);
300
301 if (endDeg < startDeg) {
302 endDeg += 360.0f;
303 }
304
305 SkScalar sweepDeg = SkTAbs(endDeg - startDeg);
306 if (!takeLongWayRound) {
307 sweepDeg = sweepDeg - 360;
308 }
309
310 path->arcTo(r, startDeg, sweepDeg, false);
311}
312
313static SkPath create_splat(const SkPoint& o, SkScalar innerRadius, SkScalar outerRadius,
314 SkScalar ratio, int numLobes, SkTDArray<SkRect>* circles) {
315 if (numLobes <= 1) {
316 return SkPath();
317 }
318
319 SkPath p;
320
321 int numDivisions = 2 * numLobes;
322 SkScalar fullLobeDegrees = 360.0f / numLobes;
323 SkScalar outDegrees = ratio * fullLobeDegrees / (ratio + 1.0f);
324 SkScalar innerDegrees = fullLobeDegrees / (ratio + 1.0f);
325 SkMatrix outerStep, innerStep;
326 outerStep.setRotate(outDegrees);
327 innerStep.setRotate(innerDegrees);
328 SkVector curV = SkVector::Make(0.0f, 1.0f);
329
330 if (circles) {
331 circles->push_back(SkRect::MakeLTRB(o.fX - innerRadius, o.fY - innerRadius,
332 o.fX + innerRadius, o.fY + innerRadius));
333 }
334
335 p.moveTo(o.fX + innerRadius * curV.fX, o.fY + innerRadius * curV.fY);
336
337 for (int i = 0; i < numDivisions; ++i) {
338
339 SkVector nextV;
340 if (0 == (i % 2)) {
341 nextV = outerStep.mapVector(curV.fX, curV.fY);
342
343 SkPoint top = SkPoint::Make(o.fX + outerRadius * curV.fX,
344 o.fY + outerRadius * curV.fY);
345 SkPoint nextTop = SkPoint::Make(o.fX + outerRadius * nextV.fX,
346 o.fY + outerRadius * nextV.fY);
347
348 p.lineTo(top);
349 add_arc(&p, top, curV, nextTop, nextV, circles, true);
350 } else {
351 nextV = innerStep.mapVector(curV.fX, curV.fY);
352
353 SkPoint bot = SkPoint::Make(o.fX + innerRadius * curV.fX,
354 o.fY + innerRadius * curV.fY);
355 SkPoint nextBot = SkPoint::Make(o.fX + innerRadius * nextV.fX,
356 o.fY + innerRadius * nextV.fY);
357
358 p.lineTo(bot);
359 add_arc(&p, bot, curV, nextBot, nextV, nullptr, false);
360 }
361
362 curV = nextV;
363 }
364
365 p.close();
366
367 return p;
368}
369
Robert Phillips2dd1b472019-03-21 09:00:20 -0400370static SkBitmap make_bitmap(SkColorType colorType, const SkPath& path,
Michael Ludwiga6a84002019-04-12 15:03:02 -0400371 const SkTDArray<SkRect>& circles, bool opaque, bool padWithRed) {
Mike Kleinea3f0142019-03-20 11:12:10 -0500372 const SkColor kGreen = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 178, 240, 104));
373 const SkColor kBlue = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 173, 167, 252));
374 const SkColor kYellow = ToolUtils::color_to_565(SkColorSetARGB(0xFF, 255, 221, 117));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400375
Michael Ludwiga6a84002019-04-12 15:03:02 -0400376 int widthHeight = kTileWidthHeight + (padWithRed ? 2 * kDomainPadding : 0);
377
378 SkImageInfo ii = SkImageInfo::Make(widthHeight, widthHeight,
Robert Phillips2dd1b472019-03-21 09:00:20 -0400379 colorType, kPremul_SkAlphaType);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400380
381 SkBitmap bm;
382 bm.allocPixels(ii);
383
Robert Phillips2dd1b472019-03-21 09:00:20 -0400384 std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirect(ii,
385 bm.getPixels(),
386 bm.rowBytes());
Michael Ludwiga6a84002019-04-12 15:03:02 -0400387 if (padWithRed) {
388 canvas->clear(SK_ColorRED);
389 canvas->translate(kDomainPadding, kDomainPadding);
390 canvas->clipRect(SkRect::MakeWH(kTileWidthHeight, kTileWidthHeight));
391 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400392 canvas->clear(opaque ? kGreen : SK_ColorTRANSPARENT);
393
394 SkPaint paint;
395 paint.setAntiAlias(false); // serialize-8888 doesn't seem to work well w/ partial transparency
396 paint.setColor(kBlue);
397
398 canvas->drawPath(path, paint);
399
400 paint.setColor(opaque ? kYellow : SK_ColorTRANSPARENT);
401 paint.setBlendMode(SkBlendMode::kSrc);
402 for (int i = 0; i < circles.count(); ++i) {
403 SkRect r = circles[i];
404 r.inset(r.width()/4, r.height()/4);
405 canvas->drawOval(r, paint);
406 }
407
408 return bm;
409}
410
Brian Osmanac8a16c2019-10-31 10:24:12 -0400411static void convert_rgba_to_yuva(const float mtx[20], SkColor col, uint8_t yuv[4]) {
412 const uint8_t r = SkColorGetR(col);
413 const uint8_t g = SkColorGetG(col);
414 const uint8_t b = SkColorGetB(col);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400415
Brian Osmanaba642c2020-02-06 12:52:25 -0500416 yuv[0] = SkTPin(SkScalarRoundToInt(mtx[ 0]*r + mtx[ 1]*g + mtx[ 2]*b + mtx[ 4]*255), 0, 255);
417 yuv[1] = SkTPin(SkScalarRoundToInt(mtx[ 5]*r + mtx[ 6]*g + mtx[ 7]*b + mtx[ 9]*255), 0, 255);
418 yuv[2] = SkTPin(SkScalarRoundToInt(mtx[10]*r + mtx[11]*g + mtx[12]*b + mtx[14]*255), 0, 255);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400419 yuv[3] = SkColorGetA(col);
420}
421
Brian Osmanac8a16c2019-10-31 10:24:12 -0400422static SkPMColor convert_yuva_to_rgba(const float mtx[20],
423 uint8_t y, uint8_t u, uint8_t v, uint8_t a) {
Brian Osmanaba642c2020-02-06 12:52:25 -0500424 uint8_t r = SkTPin(SkScalarRoundToInt(mtx[ 0]*y + mtx[ 1]*u + mtx[ 2]*v + mtx[ 4]*255), 0, 255);
425 uint8_t g = SkTPin(SkScalarRoundToInt(mtx[ 5]*y + mtx[ 6]*u + mtx[ 7]*v + mtx[ 9]*255), 0, 255);
426 uint8_t b = SkTPin(SkScalarRoundToInt(mtx[10]*y + mtx[11]*u + mtx[12]*v + mtx[14]*255), 0, 255);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400427
Robert Phillipse0735522020-01-31 11:03:32 -0500428 return SkPremultiplyARGBInline(a, r, g, b);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400429}
430
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400431static void extract_planes(const SkBitmap& bm, SkYUVColorSpace yuvColorSpace, PlaneData* planes) {
Robert Phillips0a22ba82019-03-06 12:36:47 -0500432 if (kIdentity_SkYUVColorSpace == yuvColorSpace) {
433 // To test the identity color space we use JPEG YUV planes
434 yuvColorSpace = kJPEG_SkYUVColorSpace;
435 }
436
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400437 SkASSERT(!(bm.width() % 2));
438 SkASSERT(!(bm.height() % 2));
Brian Salomon4bc0c1f2019-09-30 15:12:27 -0400439 planes->fYFull.allocPixels(
440 SkImageInfo::Make(bm.dimensions(), kGray_8_SkColorType, kUnpremul_SkAlphaType));
441 planes->fUFull.allocPixels(
442 SkImageInfo::Make(bm.dimensions(), kGray_8_SkColorType, kUnpremul_SkAlphaType));
443 planes->fVFull.allocPixels(
444 SkImageInfo::Make(bm.dimensions(), kGray_8_SkColorType, kUnpremul_SkAlphaType));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400445 planes->fAFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
Robert Phillips00c9f0d2019-08-02 17:17:35 -0400446 planes->fUQuarter.allocPixels(SkImageInfo::Make(bm.width()/2, bm.height()/2,
447 kGray_8_SkColorType, kUnpremul_SkAlphaType));
448 planes->fVQuarter.allocPixels(SkImageInfo::Make(bm.width()/2, bm.height()/2,
449 kGray_8_SkColorType, kUnpremul_SkAlphaType));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400450
Brian Salomon4bc0c1f2019-09-30 15:12:27 -0400451 planes->fFull.allocPixels(
452 SkImageInfo::Make(bm.dimensions(), kRGBA_F32_SkColorType, kUnpremul_SkAlphaType));
Robert Phillips429f0d32019-09-11 17:03:28 -0400453 planes->fQuarter.allocPixels(SkImageInfo::Make(bm.width()/2, bm.height()/2,
454 kRGBA_F32_SkColorType, kUnpremul_SkAlphaType));
455
Brian Osmanac8a16c2019-10-31 10:24:12 -0400456 float mtx[20];
457 SkColorMatrix_RGB2YUV(yuvColorSpace, mtx);
458
Robert Phillips429f0d32019-09-11 17:03:28 -0400459 SkColor4f* dst = (SkColor4f *) planes->fFull.getAddr(0, 0);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400460 for (int y = 0; y < bm.height(); ++y) {
461 for (int x = 0; x < bm.width(); ++x) {
462 SkColor col = bm.getColor(x, y);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400463
Robert Phillips1c7062d2018-10-04 10:44:53 -0400464 uint8_t yuva[4];
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400465
Brian Osmanac8a16c2019-10-31 10:24:12 -0400466 convert_rgba_to_yuva(mtx, col, yuva);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400467
468 *planes->fYFull.getAddr8(x, y) = yuva[0];
469 *planes->fUFull.getAddr8(x, y) = yuva[1];
470 *planes->fVFull.getAddr8(x, y) = yuva[2];
471 *planes->fAFull.getAddr8(x, y) = yuva[3];
Robert Phillips429f0d32019-09-11 17:03:28 -0400472
473 // TODO: render in F32 rather than converting here
474 dst->fR = yuva[0] / 255.0f;
475 dst->fG = yuva[1] / 255.0f;
476 dst->fB = yuva[2] / 255.0f;
477 dst->fA = yuva[3] / 255.0f;
478 ++dst;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400479 }
480 }
481
Robert Phillips429f0d32019-09-11 17:03:28 -0400482 dst = (SkColor4f *) planes->fQuarter.getAddr(0, 0);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400483 for (int y = 0; y < bm.height()/2; ++y) {
484 for (int x = 0; x < bm.width()/2; ++x) {
Robert Phillips429f0d32019-09-11 17:03:28 -0400485 uint32_t yAccum = 0, uAccum = 0, vAccum = 0, aAccum = 0;
486
487 yAccum += *planes->fYFull.getAddr8(2*x, 2*y);
488 yAccum += *planes->fYFull.getAddr8(2*x+1, 2*y);
489 yAccum += *planes->fYFull.getAddr8(2*x, 2*y+1);
490 yAccum += *planes->fYFull.getAddr8(2*x+1, 2*y+1);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400491
492 uAccum += *planes->fUFull.getAddr8(2*x, 2*y);
493 uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y);
494 uAccum += *planes->fUFull.getAddr8(2*x, 2*y+1);
495 uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y+1);
496
497 *planes->fUQuarter.getAddr8(x, y) = uAccum / 4.0f;
498
499 vAccum += *planes->fVFull.getAddr8(2*x, 2*y);
500 vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y);
501 vAccum += *planes->fVFull.getAddr8(2*x, 2*y+1);
502 vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y+1);
503
Robert Phillips1c7062d2018-10-04 10:44:53 -0400504 *planes->fVQuarter.getAddr8(x, y) = vAccum / 4.0f;
Robert Phillips429f0d32019-09-11 17:03:28 -0400505
506 aAccum += *planes->fAFull.getAddr8(2*x, 2*y);
507 aAccum += *planes->fAFull.getAddr8(2*x+1, 2*y);
508 aAccum += *planes->fAFull.getAddr8(2*x, 2*y+1);
509 aAccum += *planes->fAFull.getAddr8(2*x+1, 2*y+1);
510
511 // TODO: render in F32 rather than converting here
512 dst->fR = yAccum / (4.0f * 255.0f);
513 dst->fG = uAccum / (4.0f * 255.0f);
514 dst->fB = vAccum / (4.0f * 255.0f);
515 dst->fA = aAccum / (4.0f * 255.0f);
516 ++dst;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400517 }
518 }
519}
520
Robert Phillipsd470e1b2019-09-04 15:05:35 -0400521// Create a 2x2 downsampled SkBitmap. It is stored in an RG texture. It can optionally be
Robert Phillips429f0d32019-09-11 17:03:28 -0400522// uv (i.e., NV12) or vu (i.e., NV21).
Robert Phillipsbb749902019-06-10 17:20:12 -0400523static SkBitmap make_quarter_2_channel(const SkBitmap& fullY,
524 const SkBitmap& quarterU,
525 const SkBitmap& quarterV,
526 bool uv) {
527 SkBitmap result;
528
Robert Phillipsbb749902019-06-10 17:20:12 -0400529 result.allocPixels(SkImageInfo::Make(fullY.width()/2,
530 fullY.height()/2,
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400531 kR8G8_unorm_SkColorType,
Robert Phillipsbb749902019-06-10 17:20:12 -0400532 kUnpremul_SkAlphaType));
533
534 for (int y = 0; y < fullY.height()/2; ++y) {
535 for (int x = 0; x < fullY.width()/2; ++x) {
536 uint8_t u8 = *quarterU.getAddr8(x, y);
537 uint8_t v8 = *quarterV.getAddr8(x, y);
538
539 if (uv) {
Robert Phillipsd470e1b2019-09-04 15:05:35 -0400540 *result.getAddr16(x, y) = (v8 << 8) | u8;
Robert Phillipsbb749902019-06-10 17:20:12 -0400541 } else {
Robert Phillipsd470e1b2019-09-04 15:05:35 -0400542 *result.getAddr16(x, y) = (u8 << 8) | v8;
Robert Phillipsbb749902019-06-10 17:20:12 -0400543 }
544 }
545 }
546
547 return result;
548}
549
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400550// Create some flavor of a 16bits/channel bitmap from a RGBA_F32 source
551static SkBitmap make_16(const SkBitmap& src, SkColorType dstCT,
552 std::function<void(uint16_t* dstPixel, const float* srcPixel)> convert) {
Robert Phillips429f0d32019-09-11 17:03:28 -0400553 SkASSERT(src.colorType() == kRGBA_F32_SkColorType);
554
555 SkBitmap result;
556
Brian Salomon4bc0c1f2019-09-30 15:12:27 -0400557 result.allocPixels(SkImageInfo::Make(src.dimensions(), dstCT, kUnpremul_SkAlphaType));
Robert Phillips429f0d32019-09-11 17:03:28 -0400558
Robert Phillips429f0d32019-09-11 17:03:28 -0400559 for (int y = 0; y < src.height(); ++y) {
560 for (int x = 0; x < src.width(); ++x) {
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400561 const float* srcPixel = (const float*) src.getAddr(x, y);
562 uint16_t* dstPixel = (uint16_t*) result.getAddr(x, y);
Robert Phillips429f0d32019-09-11 17:03:28 -0400563
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400564 convert(dstPixel, srcPixel);
Robert Phillips429f0d32019-09-11 17:03:28 -0400565 }
566 }
567
568 return result;
569}
570
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400571static uint16_t flt_2_uint16(float flt) { return SkScalarRoundToInt(flt * 65535.0f); }
Robert Phillips429f0d32019-09-11 17:03:28 -0400572
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400573// Recombine the separate planes into some YUV format
574static void create_YUV(const PlaneData& planes, YUVFormat yuvFormat,
575 SkBitmap resultBMs[], SkYUVAIndex yuvaIndices[4], bool opaque) {
576 int nextLayer = 0;
577
578 switch (yuvFormat) {
Robert Phillipsbb749902019-06-10 17:20:12 -0400579 case kY416_YUVFormat: {
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400580 resultBMs[nextLayer++] = make_16(planes.fFull, kR16G16B16A16_unorm_SkColorType,
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400581 [] (uint16_t* dstPixel, const float* srcPixel) {
582 dstPixel[0] = flt_2_uint16(srcPixel[1]); // U
583 dstPixel[1] = flt_2_uint16(srcPixel[0]); // Y
584 dstPixel[2] = flt_2_uint16(srcPixel[2]); // V
585 dstPixel[3] = flt_2_uint16(srcPixel[3]); // A
586 });
Robert Phillipsbb749902019-06-10 17:20:12 -0400587 setup_yuv_indices(yuvFormat, false, yuvaIndices);
588 break;
589 }
Jim Van Verth976a6b02018-10-17 15:27:19 -0400590 case kAYUV_YUVFormat: {
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400591 SkBitmap yuvaFull;
592
Jim Van Verth47133fd2018-10-19 22:09:28 -0400593 yuvaFull.allocPixels(SkImageInfo::Make(planes.fYFull.width(), planes.fYFull.height(),
594 kRGBA_8888_SkColorType, kUnpremul_SkAlphaType));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400595
596 for (int y = 0; y < planes.fYFull.height(); ++y) {
597 for (int x = 0; x < planes.fYFull.width(); ++x) {
598
599 uint8_t Y = *planes.fYFull.getAddr8(x, y);
600 uint8_t U = *planes.fUFull.getAddr8(x, y);
601 uint8_t V = *planes.fVFull.getAddr8(x, y);
602 uint8_t A = *planes.fAFull.getAddr8(x, y);
603
604 // NOT premul!
Jim Van Verth47133fd2018-10-19 22:09:28 -0400605 // V and Y swapped to match RGBA layout
Robert Phillips2dd1b472019-03-21 09:00:20 -0400606 SkColor c = SkColorSetARGB(A, V, U, Y);
607 *yuvaFull.getAddr32(x, y) = c;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400608 }
609 }
610
611 resultBMs[nextLayer++] = yuvaFull;
612
Robert Phillipsdc62b642019-05-16 10:10:45 -0400613 setup_yuv_indices(yuvFormat, false, yuvaIndices);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400614 break;
615 }
Robert Phillips2dd1b472019-03-21 09:00:20 -0400616 case kY410_YUVFormat: {
617 SkBitmap yuvaFull;
Robert Phillipsf0313ee2019-05-21 13:51:11 -0400618 uint32_t Y, U, V;
619 uint8_t A;
Robert Phillips2dd1b472019-03-21 09:00:20 -0400620
621 yuvaFull.allocPixels(SkImageInfo::Make(planes.fYFull.width(), planes.fYFull.height(),
622 kRGBA_1010102_SkColorType,
623 kUnpremul_SkAlphaType));
624
625 for (int y = 0; y < planes.fYFull.height(); ++y) {
626 for (int x = 0; x < planes.fYFull.width(); ++x) {
627
Robert Phillipsf0313ee2019-05-21 13:51:11 -0400628 Y = SkScalarRoundToInt((*planes.fYFull.getAddr8(x, y) / 255.0f) * 1023.0f);
629 U = SkScalarRoundToInt((*planes.fUFull.getAddr8(x, y) / 255.0f) * 1023.0f);
630 V = SkScalarRoundToInt((*planes.fVFull.getAddr8(x, y) / 255.0f) * 1023.0f);
631 A = SkScalarRoundToInt((*planes.fAFull.getAddr8(x, y) / 255.0f) * 3.0f);
Robert Phillips2dd1b472019-03-21 09:00:20 -0400632
633 // NOT premul!
634 // AVYU but w/ V and U swapped to match RGBA layout
635 *yuvaFull.getAddr32(x, y) = (A << 30) | (U << 20) | (Y << 10) | (V << 0);
636 }
637 }
638
639 resultBMs[nextLayer++] = yuvaFull;
640
Robert Phillipsdc62b642019-05-16 10:10:45 -0400641 setup_yuv_indices(yuvFormat, false, yuvaIndices);
Robert Phillips2dd1b472019-03-21 09:00:20 -0400642 break;
643 }
Robert Phillipsbb749902019-06-10 17:20:12 -0400644 case kP016_YUVFormat: // fall through
Robert Phillips429f0d32019-09-11 17:03:28 -0400645 case kP010_YUVFormat: {
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400646 resultBMs[nextLayer++] = make_16(planes.fFull, kA16_unorm_SkColorType,
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400647 [tenBitsPP = (yuvFormat == kP010_YUVFormat)]
648 (uint16_t* dstPixel, const float* srcPixel) {
649 uint16_t val16 = flt_2_uint16(srcPixel[0]);
650 dstPixel[0] = tenBitsPP ? (val16 & 0xFFC0)
651 : val16;
652 });
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400653 resultBMs[nextLayer++] = make_16(planes.fQuarter, kR16G16_unorm_SkColorType,
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400654 [tenBitsPP = (yuvFormat == kP010_YUVFormat)]
655 (uint16_t* dstPixel, const float* srcPixel) {
656 uint16_t u16 = flt_2_uint16(srcPixel[1]);
657 uint16_t v16 = flt_2_uint16(srcPixel[2]);
658 dstPixel[0] = tenBitsPP ? (u16 & 0xFFC0) : u16;
659 dstPixel[1] = tenBitsPP ? (v16 & 0xFFC0) : v16;
660 });
Robert Phillips429f0d32019-09-11 17:03:28 -0400661 if (!opaque) {
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400662 resultBMs[nextLayer] = make_16(planes.fFull, kA16_unorm_SkColorType,
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400663 [tenBitsPP = (yuvFormat == kP010_YUVFormat)]
664 (uint16_t* dstPixel, const float* srcPixel) {
665 uint16_t val16 = flt_2_uint16(srcPixel[3]);
666 dstPixel[0] = tenBitsPP ? (val16 & 0xFFC0)
667 : val16;
668 });
669 }
670 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
671 return;
672 }
673 case kP016F_YUVFormat: {
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400674 resultBMs[nextLayer++] = make_16(planes.fFull, kA16_float_SkColorType,
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400675 [] (uint16_t* dstPixel, const float* srcPixel) {
676 dstPixel[0] = SkFloatToHalf(srcPixel[0]);
677 });
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400678 resultBMs[nextLayer++] = make_16(planes.fQuarter, kR16G16_float_SkColorType,
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400679 [] (uint16_t* dstPixel, const float* srcPixel) {
680 dstPixel[0] = SkFloatToHalf(srcPixel[1]);
681 dstPixel[1] = SkFloatToHalf(srcPixel[2]);
682 });
683 if (!opaque) {
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400684 resultBMs[nextLayer] = make_16(planes.fFull, kA16_float_SkColorType,
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400685 [] (uint16_t* dstPixel, const float* srcPixel) {
686 dstPixel[0] = SkFloatToHalf(srcPixel[3]);
687 });
Robert Phillips429f0d32019-09-11 17:03:28 -0400688 }
689 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
690 return;
691 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400692 case kNV12_YUVFormat: {
Robert Phillipsbb749902019-06-10 17:20:12 -0400693 SkBitmap uvQuarter = make_quarter_2_channel(planes.fYFull,
694 planes.fUQuarter,
695 planes.fVQuarter, true);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400696 resultBMs[nextLayer++] = planes.fYFull;
697 resultBMs[nextLayer++] = uvQuarter;
698
Robert Phillipsdc62b642019-05-16 10:10:45 -0400699 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400700 break;
701 }
702 case kNV21_YUVFormat: {
Robert Phillipsbb749902019-06-10 17:20:12 -0400703 SkBitmap vuQuarter = make_quarter_2_channel(planes.fYFull,
704 planes.fUQuarter,
705 planes.fVQuarter, false);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400706 resultBMs[nextLayer++] = planes.fYFull;
707 resultBMs[nextLayer++] = vuQuarter;
708
Robert Phillipsdc62b642019-05-16 10:10:45 -0400709 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400710 break;
711 }
712 case kI420_YUVFormat:
713 resultBMs[nextLayer++] = planes.fYFull;
714 resultBMs[nextLayer++] = planes.fUQuarter;
715 resultBMs[nextLayer++] = planes.fVQuarter;
716
Robert Phillipsdc62b642019-05-16 10:10:45 -0400717 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400718 break;
719 case kYV12_YUVFormat:
720 resultBMs[nextLayer++] = planes.fYFull;
721 resultBMs[nextLayer++] = planes.fVQuarter;
722 resultBMs[nextLayer++] = planes.fUQuarter;
723
Robert Phillipsdc62b642019-05-16 10:10:45 -0400724 setup_yuv_indices(yuvFormat, !opaque, yuvaIndices);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400725 break;
726 }
727
Robert Phillipsbb749902019-06-10 17:20:12 -0400728 if (!format_has_builtin_alpha(yuvFormat) && !opaque) {
Robert Phillipsdc62b642019-05-16 10:10:45 -0400729 resultBMs[nextLayer] = planes.fAFull;
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400730 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400731}
732
Robert Phillips2dd1b472019-03-21 09:00:20 -0400733static uint8_t look_up(float x1, float y1, const SkBitmap& bm, SkColorChannel channel) {
Robert Phillips1c7062d2018-10-04 10:44:53 -0400734 uint8_t result;
735
Robert Phillips2dd1b472019-03-21 09:00:20 -0400736 SkASSERT(x1 > 0 && x1 < 1.0f);
737 SkASSERT(y1 > 0 && y1 < 1.0f);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400738 int x = SkScalarFloorToInt(x1 * bm.width());
739 int y = SkScalarFloorToInt(y1 * bm.height());
740
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400741 if (kGray_8_SkColorType == bm.colorType()) {
Robert Phillipsbb749902019-06-10 17:20:12 -0400742 SkASSERT(SkColorChannel::kA == channel || SkColorChannel::kR == channel);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400743 result = *bm.getAddr8(x, y);
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400744 } else if (kAlpha_8_SkColorType == bm.colorType() ||
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400745 kA16_unorm_SkColorType == bm.colorType() ||
746 kA16_float_SkColorType == bm.colorType()) {
Robert Phillips429f0d32019-09-11 17:03:28 -0400747 SkASSERT(SkColorChannel::kA == channel);
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400748 SkColor c = bm.getColor(x, y);
749 result = SkColorGetA(c);
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400750 } else if (kR8G8_unorm_SkColorType == bm.colorType() ||
751 kR16G16_unorm_SkColorType == bm.colorType() ||
752 kR16G16_float_SkColorType == bm.colorType()) {
Robert Phillipsd470e1b2019-09-04 15:05:35 -0400753 SkASSERT(SkColorChannel::kR == channel || SkColorChannel::kG == channel);
754 SkColor c = bm.getColor(x, y);
755
756 switch (channel) {
757 case SkColorChannel::kR:
758 result = SkColorGetR(c);
759 break;
760 case SkColorChannel::kG:
761 result = SkColorGetG(c);
762 break;
763 case SkColorChannel::kB:
764 result = 0;
765 break;
766 case SkColorChannel::kA:
767 result = 255;
768 break;
769 }
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400770 } else {
771 SkASSERT(kRGBA_1010102_SkColorType == bm.colorType() ||
772 kRGBA_8888_SkColorType == bm.colorType() ||
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400773 kR16G16B16A16_unorm_SkColorType == bm.colorType());
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400774
775 SkColor c = bm.getColor(x, y);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400776
777 switch (channel) {
778 case SkColorChannel::kR:
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400779 result = SkColorGetR(c);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400780 break;
781 case SkColorChannel::kG:
Robert Phillips2dd1b472019-03-21 09:00:20 -0400782 result = SkColorGetG(c);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400783 break;
784 case SkColorChannel::kB:
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400785 result = SkColorGetB(c);
Robert Phillips1c7062d2018-10-04 10:44:53 -0400786 break;
787 case SkColorChannel::kA:
Robert Phillips2dd1b472019-03-21 09:00:20 -0400788 result = SkColorGetA(c);
789 break;
790 }
Robert Phillips1c7062d2018-10-04 10:44:53 -0400791 }
792
793 return result;
794}
795
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400796class YUVGenerator : public SkImageGenerator {
797public:
798 YUVGenerator(const SkImageInfo& ii,
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400799 SkYUVColorSpace yuvColorSpace,
Jim Van Verth8f11e432018-10-18 14:36:59 -0400800 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
Jim Van Verthe24b5872018-10-29 16:26:02 -0400801 SkBitmap bitmaps[SkYUVASizeInfo::kMaxCount])
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400802 : SkImageGenerator(ii)
Robert Phillips2dd1b472019-03-21 09:00:20 -0400803 , fYUVColorSpace(yuvColorSpace)
804 , fAllA8(true) {
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400805 memcpy(fYUVAIndices, yuvaIndices, sizeof(fYUVAIndices));
806
Jim Van Verth8f11e432018-10-18 14:36:59 -0400807 SkAssertResult(SkYUVAIndex::AreValidIndices(fYUVAIndices, &fNumBitmaps));
Jim Van Verthe24b5872018-10-29 16:26:02 -0400808 SkASSERT(fNumBitmaps > 0 && fNumBitmaps <= SkYUVASizeInfo::kMaxCount);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400809
Jim Van Verth8f11e432018-10-18 14:36:59 -0400810 for (int i = 0; i < fNumBitmaps; ++i) {
811 fYUVBitmaps[i] = bitmaps[i];
Robert Phillips2dd1b472019-03-21 09:00:20 -0400812 if (kAlpha_8_SkColorType != fYUVBitmaps[i].colorType()) {
813 fAllA8 = false;
814 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400815 }
816 }
817
818protected:
819 bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
820 const Options&) override {
821
822 if (kUnknown_SkColorType == fFlattened.colorType()) {
Robert Phillips2dd1b472019-03-21 09:00:20 -0400823 fFlattened.allocPixels(info);
Robert Phillipse0735522020-01-31 11:03:32 -0500824 SkASSERT(kN32_SkColorType == info.colorType());
Robert Phillips2dd1b472019-03-21 09:00:20 -0400825 SkASSERT(kPremul_SkAlphaType == info.alphaType());
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400826
Brian Osmanac8a16c2019-10-31 10:24:12 -0400827 float mtx[20];
828 SkColorMatrix_YUV2RGB(fYUVColorSpace, mtx);
829
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400830 for (int y = 0; y < info.height(); ++y) {
831 for (int x = 0; x < info.width(); ++x) {
832
Robert Phillips1c7062d2018-10-04 10:44:53 -0400833 float x1 = (x + 0.5f) / info.width();
834 float y1 = (y + 0.5f) / info.height();
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400835
Robert Phillips1c7062d2018-10-04 10:44:53 -0400836 uint8_t Y = look_up(x1, y1,
837 fYUVBitmaps[fYUVAIndices[0].fIndex],
838 fYUVAIndices[0].fChannel);
839
840 uint8_t U = look_up(x1, y1,
841 fYUVBitmaps[fYUVAIndices[1].fIndex],
842 fYUVAIndices[1].fChannel);
843
844
845 uint8_t V = look_up(x1, y1,
846 fYUVBitmaps[fYUVAIndices[2].fIndex],
847 fYUVAIndices[2].fChannel);
848
849 uint8_t A = 255;
850 if (fYUVAIndices[3].fIndex >= 0) {
851 A = look_up(x1, y1,
852 fYUVBitmaps[fYUVAIndices[3].fIndex],
853 fYUVAIndices[3].fChannel);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400854 }
855
856 // Making premul here.
Brian Osmanac8a16c2019-10-31 10:24:12 -0400857 *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba(mtx, Y, U, V, A);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400858 }
859 }
860 }
861
862 return fFlattened.readPixels(info, pixels, rowBytes, 0, 0);
863 }
864
Jim Van Verthe24b5872018-10-29 16:26:02 -0400865 bool onQueryYUVA8(SkYUVASizeInfo* size,
Jim Van Verth8f11e432018-10-18 14:36:59 -0400866 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
867 SkYUVColorSpace* yuvColorSpace) const override {
868
Robert Phillips2dd1b472019-03-21 09:00:20 -0400869 // The onQueryYUVA8/onGetYUVA8Planes can only handle A8 planes
870 if (!fAllA8) {
871 return false;
872 }
873
Jim Van Verth8f11e432018-10-18 14:36:59 -0400874 memcpy(yuvaIndices, fYUVAIndices, sizeof(fYUVAIndices));
875 *yuvColorSpace = fYUVColorSpace;
876
877 int i = 0;
878 for ( ; i < fNumBitmaps; ++i) {
Jim Van Verth8f11e432018-10-18 14:36:59 -0400879 size->fSizes[i].fWidth = fYUVBitmaps[i].width();
880 size->fSizes[i].fHeight = fYUVBitmaps[i].height();
881 size->fWidthBytes[i] = fYUVBitmaps[i].rowBytes();
882 }
Jim Van Verthe24b5872018-10-29 16:26:02 -0400883 for ( ; i < SkYUVASizeInfo::kMaxCount; ++i) {
Jim Van Verth8f11e432018-10-18 14:36:59 -0400884 size->fSizes[i].fWidth = 0;
885 size->fSizes[i].fHeight = 0;
886 size->fWidthBytes[i] = 0;
Jim Van Verthf99a6742018-10-18 16:13:18 +0000887 }
Jim Van Verth0c583af2018-10-18 10:31:59 -0400888
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400889 return true;
890 }
891
Jim Van Verthe24b5872018-10-29 16:26:02 -0400892 bool onGetYUVA8Planes(const SkYUVASizeInfo&, const SkYUVAIndex[SkYUVAIndex::kIndexCount],
893 void* planes[SkYUVASizeInfo::kMaxCount]) override {
Robert Phillips2dd1b472019-03-21 09:00:20 -0400894 SkASSERT(fAllA8);
Jim Van Verth8f11e432018-10-18 14:36:59 -0400895 for (int i = 0; i < fNumBitmaps; ++i) {
896 planes[i] = fYUVBitmaps[i].getPixels();
897 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400898 return true;
899 }
900
901private:
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400902 SkYUVColorSpace fYUVColorSpace;
Jim Van Verth8f11e432018-10-18 14:36:59 -0400903 SkYUVAIndex fYUVAIndices[SkYUVAIndex::kIndexCount];
904 int fNumBitmaps;
Jim Van Verthe24b5872018-10-29 16:26:02 -0400905 SkBitmap fYUVBitmaps[SkYUVASizeInfo::kMaxCount];
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400906 SkBitmap fFlattened;
Robert Phillips2dd1b472019-03-21 09:00:20 -0400907 bool fAllA8; // are all the SkBitmaps in "fYUVBitmaps" A8?
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400908
909};
910
911static sk_sp<SkImage> make_yuv_gen_image(const SkImageInfo& ii,
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400912 SkYUVColorSpace yuvColorSpace,
Jim Van Verth8f11e432018-10-18 14:36:59 -0400913 SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400914 SkBitmap bitmaps[]) {
Jim Van Verth8f11e432018-10-18 14:36:59 -0400915 std::unique_ptr<SkImageGenerator> gen(new YUVGenerator(ii, yuvColorSpace,
916 yuvaIndices, bitmaps));
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400917
918 return SkImage::MakeFromGenerator(std::move(gen));
919}
920
921static void draw_col_label(SkCanvas* canvas, int x, int yuvColorSpace, bool opaque) {
Brian Osman2b73e662019-11-01 10:02:24 -0400922 static const char* kYUVColorSpaceNames[] = { "JPEG", "601", "709", "2020", "Identity" };
Brian Salomon4dea72a2019-12-18 10:43:10 -0500923 static_assert(SK_ARRAY_COUNT(kYUVColorSpaceNames) == kLastEnum_SkYUVColorSpace + 1);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400924
Mike Reed91919132019-01-02 12:21:01 -0500925 SkPaint paint;
Mike Kleinea3f0142019-03-20 11:12:10 -0500926 SkFont font(ToolUtils::create_portable_typeface(nullptr, SkFontStyle::Bold()), 16);
Mike Reed91919132019-01-02 12:21:01 -0500927 font.setEdging(SkFont::Edging::kAlias);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400928
929 SkRect textRect;
930 SkString colLabel;
931
932 colLabel.printf("%s", kYUVColorSpaceNames[yuvColorSpace]);
Ben Wagner51e15a62019-05-07 15:38:46 -0400933 font.measureText(colLabel.c_str(), colLabel.size(), SkTextEncoding::kUTF8, &textRect);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400934 int y = textRect.height();
935
Mike Reed91919132019-01-02 12:21:01 -0500936 SkTextUtils::DrawString(canvas, colLabel.c_str(), x, y, font, paint, SkTextUtils::kCenter_Align);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400937
938 colLabel.printf("%s", opaque ? "Opaque" : "Transparent");
Mike Reed71f5a0b2018-10-25 16:12:39 -0400939
Ben Wagner51e15a62019-05-07 15:38:46 -0400940 font.measureText(colLabel.c_str(), colLabel.size(), SkTextEncoding::kUTF8, &textRect);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400941 y += textRect.height();
942
Mike Reed91919132019-01-02 12:21:01 -0500943 SkTextUtils::DrawString(canvas, colLabel.c_str(), x, y, font, paint, SkTextUtils::kCenter_Align);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400944}
945
946static void draw_row_label(SkCanvas* canvas, int y, int yuvFormat) {
Robert Phillipsbb749902019-06-10 17:20:12 -0400947 static const char* kYUVFormatNames[] = {
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400948 "P016", "P010", "P016F", "Y416", "AYUV", "Y410", "NV12", "NV21", "I420", "YV12"
Robert Phillipsbb749902019-06-10 17:20:12 -0400949 };
Brian Salomon4dea72a2019-12-18 10:43:10 -0500950 static_assert(SK_ARRAY_COUNT(kYUVFormatNames) == kLast_YUVFormat + 1);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400951
Mike Reed91919132019-01-02 12:21:01 -0500952 SkPaint paint;
Mike Kleinea3f0142019-03-20 11:12:10 -0500953 SkFont font(ToolUtils::create_portable_typeface(nullptr, SkFontStyle::Bold()), 16);
Mike Reed91919132019-01-02 12:21:01 -0500954 font.setEdging(SkFont::Edging::kAlias);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400955
956 SkRect textRect;
957 SkString rowLabel;
958
959 rowLabel.printf("%s", kYUVFormatNames[yuvFormat]);
Ben Wagner51e15a62019-05-07 15:38:46 -0400960 font.measureText(rowLabel.c_str(), rowLabel.size(), SkTextEncoding::kUTF8, &textRect);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400961 y += kTileWidthHeight/2 + textRect.height()/2;
962
Hal Canary89a644b2019-01-07 09:36:09 -0500963 canvas->drawString(rowLabel, 0, y, font, paint);
Robert Phillipsbfa76f22018-10-03 12:12:26 -0400964}
965
Robert Phillipscb1adb42019-06-10 15:09:34 -0400966static GrBackendTexture create_yuva_texture(GrContext* context, const SkBitmap& bm,
Robert Phillipsbb749902019-06-10 17:20:12 -0400967 SkYUVAIndex yuvaIndices[4], int texIndex,
968 YUVFormat yuvFormat) {
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400969#ifdef SK_DEBUG
Jim Van Verth60ac5d02018-12-06 13:11:53 -0500970 SkASSERT(texIndex >= 0 && texIndex <= 3);
971 int channelCount = 0;
972 for (int i = 0; i < SkYUVAIndex::kIndexCount; ++i) {
973 if (yuvaIndices[i].fIndex == texIndex) {
974 ++channelCount;
975 }
976 }
Robert Phillipsbb749902019-06-10 17:20:12 -0400977
Robert Phillipsbb749902019-06-10 17:20:12 -0400978 if (format_uses_16_bpp(yuvFormat) || 2 == channelCount) {
Robert Phillipsbb749902019-06-10 17:20:12 -0400979 if (2 == channelCount) {
980 if (format_uses_16_bpp(yuvFormat)) {
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400981 if (yuvFormat == kP016F_YUVFormat) {
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400982 SkASSERT(kR16G16_float_SkColorType == bm.colorType());
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400983 } else {
984 SkASSERT(yuvFormat == kP016_YUVFormat || yuvFormat == kP010_YUVFormat);
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400985 SkASSERT(kR16G16_unorm_SkColorType == bm.colorType());
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400986 }
Robert Phillipsbb749902019-06-10 17:20:12 -0400987 } else {
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400988 SkASSERT(kR8G8_unorm_SkColorType == bm.colorType());
Robert Phillipsbb749902019-06-10 17:20:12 -0400989 }
990 } else {
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400991 if (yuvFormat == kY416_YUVFormat) {
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400992 SkASSERT(kR16G16B16A16_unorm_SkColorType == bm.colorType());
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400993 } else if (yuvFormat == kP016F_YUVFormat) {
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400994 SkASSERT(kA16_float_SkColorType == bm.colorType());
Robert Phillipsbb749902019-06-10 17:20:12 -0400995 } else {
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400996 SkASSERT(yuvFormat == kP016_YUVFormat || yuvFormat == kP010_YUVFormat);
Robert Phillipsea1b30b2019-09-19 16:05:48 -0400997 SkASSERT(kA16_unorm_SkColorType == bm.colorType());
Jim Van Verth60ac5d02018-12-06 13:11:53 -0500998 }
999 }
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001000 }
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001001#endif
Robert Phillipsbb749902019-06-10 17:20:12 -04001002
Robert Phillips66944402019-09-30 13:21:25 -04001003 return context->createBackendTexture(&bm.pixmap(), 1, GrRenderable::kNo, GrProtected::kNo);
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001004}
1005
Robert Phillips0a22ba82019-03-06 12:36:47 -05001006static sk_sp<SkColorFilter> yuv_to_rgb_colorfilter() {
1007 static const float kJPEGConversionMatrix[20] = {
Mike Reede869a1e2019-04-30 12:18:54 -04001008 1.0f, 0.0f, 1.402f, 0.0f, -180.0f/255,
1009 1.0f, -0.344136f, -0.714136f, 0.0f, 136.0f/255,
1010 1.0f, 1.772f, 0.0f, 0.0f, -227.6f/255,
Robert Phillips0a22ba82019-03-06 12:36:47 -05001011 0.0f, 0.0f, 0.0f, 1.0f, 0.0f
1012 };
1013
Mike Reede869a1e2019-04-30 12:18:54 -04001014 return SkColorFilters::Matrix(kJPEGConversionMatrix);
Robert Phillips0a22ba82019-03-06 12:36:47 -05001015}
1016
Robert Phillips99044e12020-01-29 08:37:01 -05001017// Get the SkColorType to use when creating an SkSurface wrapping 'format'.
1018static SkColorType get_color_type(const GrBackendFormat& format) {
1019
1020 GrGLFormat glFormat = format.asGLFormat();
1021 if (GrGLFormat::kUnknown != glFormat) {
1022 switch (glFormat) {
1023 case GrGLFormat::kLUMINANCE8: // fall through
1024 case GrGLFormat::kR8: // fall through
1025 case GrGLFormat::kALPHA8: return kAlpha_8_SkColorType;
1026 case GrGLFormat::kRG8: return kR8G8_unorm_SkColorType;
1027 case GrGLFormat::kRGB8: return kRGB_888x_SkColorType;
1028 case GrGLFormat::kRGBA8: return kRGBA_8888_SkColorType;
1029 case GrGLFormat::kBGRA8: return kBGRA_8888_SkColorType;
1030 case GrGLFormat::kRGB10_A2: return kRGBA_1010102_SkColorType;
1031 case GrGLFormat::kLUMINANCE16F: // fall through
1032 case GrGLFormat::kR16F: return kA16_float_SkColorType;
1033 case GrGLFormat::kRG16F: return kR16G16_float_SkColorType;
1034 case GrGLFormat::kR16: return kA16_unorm_SkColorType;
1035 case GrGLFormat::kRG16: return kR16G16_unorm_SkColorType;
1036 case GrGLFormat::kRGBA16: return kR16G16B16A16_unorm_SkColorType;
1037 default: return kUnknown_SkColorType;
1038 }
1039
1040 SkUNREACHABLE;
1041 }
1042
1043 VkFormat vkFormat;
1044 if (format.asVkFormat(&vkFormat)) {
1045 switch (vkFormat) {
1046 case VK_FORMAT_R8_UNORM: return kAlpha_8_SkColorType;
1047 case VK_FORMAT_R8G8_UNORM: return kR8G8_unorm_SkColorType;
1048 case VK_FORMAT_R8G8B8_UNORM: return kRGB_888x_SkColorType;
1049 case VK_FORMAT_R8G8B8A8_UNORM: return kRGBA_8888_SkColorType;
1050 case VK_FORMAT_B8G8R8A8_UNORM: return kBGRA_8888_SkColorType;
1051 case VK_FORMAT_A2B10G10R10_UNORM_PACK32: return kRGBA_1010102_SkColorType;
1052 case VK_FORMAT_R16_SFLOAT: return kA16_float_SkColorType;
1053 case VK_FORMAT_R16G16_SFLOAT: return kR16G16_float_SkColorType;
1054 case VK_FORMAT_R16_UNORM: return kA16_unorm_SkColorType;
1055 case VK_FORMAT_R16G16_UNORM: return kR16G16_unorm_SkColorType;
1056 case VK_FORMAT_R16G16B16A16_UNORM: return kR16G16B16A16_unorm_SkColorType;
1057 default: return kUnknown_SkColorType;
1058 }
1059
1060 SkUNREACHABLE;
1061 }
1062
1063 return kUnknown_SkColorType;
1064}
1065
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001066namespace skiagm {
1067
1068// This GM creates an opaque and transparent bitmap, extracts the planes and then recombines
1069// them into various YUV formats. It then renders the results in the grid:
1070//
Robert Phillips2dd1b472019-03-21 09:00:20 -04001071// JPEG 601 709 Identity
1072// Transparent Opaque Transparent Opaque Transparent Opaque Transparent Opaque
Robert Phillipsbb749902019-06-10 17:20:12 -04001073// originals
1074// P016
1075// P010
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001076// P016F
Robert Phillipsbb749902019-06-10 17:20:12 -04001077// Y416
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001078// AYUV
Robert Phillips2dd1b472019-03-21 09:00:20 -04001079// Y410
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001080// NV12
1081// NV21
1082// I420
1083// YV12
1084class WackyYUVFormatsGM : public GM {
1085public:
Robert Phillips99044e12020-01-29 08:37:01 -05001086 WackyYUVFormatsGM(bool useTargetColorSpace, bool useDomain, bool quarterSize)
Michael Ludwiga6a84002019-04-12 15:03:02 -04001087 : fUseTargetColorSpace(useTargetColorSpace)
Robert Phillips99044e12020-01-29 08:37:01 -05001088 , fUseDomain(useDomain)
1089 , fQuarterSize(quarterSize) {
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001090 this->setBGColor(0xFFCCCCCC);
1091 }
1092
1093protected:
1094
1095 SkString onShortName() override {
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001096 SkString name("wacky_yuv_formats");
1097 if (fUseTargetColorSpace) {
1098 name += "_cs";
1099 }
Michael Ludwiga6a84002019-04-12 15:03:02 -04001100 if (fUseDomain) {
1101 name += "_domain";
1102 }
Robert Phillips99044e12020-01-29 08:37:01 -05001103 if (fQuarterSize) {
1104 name += "_qtr";
1105 }
Robert Phillips2dd1b472019-03-21 09:00:20 -04001106
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001107 return name;
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001108 }
1109
1110 SkISize onISize() override {
Robert Phillips17a3a0b2019-09-18 13:56:54 -04001111 int numCols = 2 * (kLastEnum_SkYUVColorSpace + 1); // opacity x #-color-spaces
1112 int numRows = 1 + (kLast_YUVFormat + 1); // original + #-yuv-formats
Michael Ludwiga6a84002019-04-12 15:03:02 -04001113 int wh = SkScalarCeilToInt(kTileWidthHeight * (fUseDomain ? 1.5f : 1.f));
1114 return SkISize::Make(kLabelWidth + numCols * (wh + kPad),
1115 kLabelHeight + numRows * (wh + kPad));
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001116 }
1117
1118 void onOnceBeforeDraw() override {
1119 SkPoint origin = { kTileWidthHeight/2.0f, kTileWidthHeight/2.0f };
1120 float outerRadius = kTileWidthHeight/2.0f - 20.0f;
1121 float innerRadius = 20.0f;
1122
1123 {
1124 // transparent
1125 SkTDArray<SkRect> circles;
1126 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 5, &circles);
Michael Ludwiga6a84002019-04-12 15:03:02 -04001127 fOriginalBMs[0] = make_bitmap(kRGBA_8888_SkColorType, path, circles, false, fUseDomain);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001128 }
1129
1130 {
1131 // opaque
1132 SkTDArray<SkRect> circles;
1133 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 7, &circles);
Michael Ludwiga6a84002019-04-12 15:03:02 -04001134 fOriginalBMs[1] = make_bitmap(kRGBA_8888_SkColorType, path, circles, true, fUseDomain);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001135 }
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001136
1137 if (fUseTargetColorSpace) {
1138 fTargetColorSpace = SkColorSpace::MakeSRGB()->makeColorSpin();
1139 }
Robert Phillips51c89e42018-10-05 13:30:43 -04001140 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001141
Robert Phillips99044e12020-01-29 08:37:01 -05001142 // Resize all the backend textures in 'yuvaTextures' to a quarter their size.
1143 sk_sp<SkImage> resizeOnGpu(GrContext* context,
1144 YUVFormat yuvFormat,
1145 SkYUVColorSpace yuvColorSpace,
1146 bool opaque,
1147 const GrBackendTexture yuvaTextures[],
1148 const SkYUVAIndex yuvaIndices[4],
1149 int numTextures,
1150 SkISize imageSize) {
1151 GrBackendTexture shrunkTextures[4];
1152
1153 for (int i = 0; i < numTextures; ++i) {
1154 SkColorType ct = get_color_type(yuvaTextures[i].getBackendFormat());
1155 if (ct == kUnknown_SkColorType || !context->colorTypeSupportedAsSurface(ct)) {
1156 return nullptr;
1157 }
1158
Robert Phillipse0735522020-01-31 11:03:32 -05001159 if (ct == kRGBA_8888_SkColorType || ct == kRGBA_1010102_SkColorType) {
1160 // We disallow resizing AYUV and Y410 formats on the GPU bc resizing them w/ a
1161 // premul draw combines the YUV channels w/ the A channel in an inappropriate
1162 // manner.
1163 return nullptr;
1164 }
1165
Robert Phillips99044e12020-01-29 08:37:01 -05001166 SkISize shrunkPlaneSize = { yuvaTextures[i].width() / 2, yuvaTextures[i].height() / 2 };
1167
1168 sk_sp<SkImage> wrappedOrig = SkImage::MakeFromTexture(context, yuvaTextures[i],
1169 kTopLeft_GrSurfaceOrigin,
1170 ct,
1171 kPremul_SkAlphaType,
1172 nullptr);
1173
1174 shrunkTextures[i] = context->createBackendTexture(shrunkPlaneSize.width(),
1175 shrunkPlaneSize.height(),
1176 yuvaTextures[i].getBackendFormat(),
1177 GrMipMapped::kNo,
1178 GrRenderable::kYes);
1179 if (!shrunkTextures[i].isValid()) {
1180 return nullptr;
1181 }
1182
1183 // Store this away so it will be cleaned up at the end.
1184 fBackendTextures.push_back(shrunkTextures[i]);
1185
1186 sk_sp<SkSurface> s = SkSurface::MakeFromBackendTexture(context, shrunkTextures[i],
1187 kTopLeft_GrSurfaceOrigin, 0,
1188 ct, nullptr, nullptr);
1189 if (!s) {
1190 return nullptr;
1191 }
1192 SkCanvas* c = s->getCanvas();
1193
1194 SkPaint paint;
1195 paint.setBlendMode(SkBlendMode::kSrc);
1196
1197 c->drawImageRect(wrappedOrig,
1198 SkRect::MakeWH(shrunkPlaneSize.width(), shrunkPlaneSize.height()),
1199 &paint);
1200
1201 s->flush();
1202 }
1203
1204 SkISize shrunkImageSize = { imageSize.width() / 2, imageSize.height() / 2 };
1205
1206 return SkImage::MakeFromYUVATextures(context,
1207 yuvColorSpace,
1208 shrunkTextures,
1209 yuvaIndices,
1210 shrunkImageSize,
1211 kTopLeft_GrSurfaceOrigin);
1212 }
1213
Robert Phillips51c89e42018-10-05 13:30:43 -04001214 void createImages(GrContext* context) {
Jim Van Verth9bf81202018-10-30 15:53:36 -04001215 int counter = 0;
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001216 for (bool opaque : { false, true }) {
1217 for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
1218 PlaneData planes;
1219 extract_planes(fOriginalBMs[opaque], (SkYUVColorSpace) cs, &planes);
1220
Robert Phillipsbb749902019-06-10 17:20:12 -04001221 for (int format = kP016_YUVFormat; format <= kLast_YUVFormat; ++format) {
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001222 SkBitmap resultBMs[4];
1223 SkYUVAIndex yuvaIndices[4];
Robert Phillips2dd1b472019-03-21 09:00:20 -04001224
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001225 create_YUV(planes, (YUVFormat) format, resultBMs, yuvaIndices, opaque);
Robert Phillips2dd1b472019-03-21 09:00:20 -04001226
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001227 int numTextures;
1228 if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures)) {
1229 continue;
1230 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001231
Robert Phillips51c89e42018-10-05 13:30:43 -04001232 if (context) {
Chris Dalton382b1222019-02-07 10:05:55 +00001233 if (context->abandoned()) {
1234 return;
1235 }
1236
Robert Phillipsf47717a2019-09-19 13:04:33 -04001237 if (!is_format_natively_supported(context, (YUVFormat) format)) {
1238 continue;
1239 }
1240
Robert Phillips51c89e42018-10-05 13:30:43 -04001241 GrBackendTexture yuvaTextures[4];
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001242 SkPixmap yuvaPixmaps[4];
Robert Phillips51c89e42018-10-05 13:30:43 -04001243
Jim Van Verth60ac5d02018-12-06 13:11:53 -05001244 for (int i = 0; i < numTextures; ++i) {
Robert Phillipscb1adb42019-06-10 15:09:34 -04001245 yuvaTextures[i] = create_yuva_texture(context, resultBMs[i],
Robert Phillipsbb749902019-06-10 17:20:12 -04001246 yuvaIndices, i,
1247 (YUVFormat) format);
Brian Salomonf2580c02019-01-30 09:13:31 -05001248 if (yuvaTextures[i].isValid()) {
1249 fBackendTextures.push_back(yuvaTextures[i]);
1250 }
Jim Van Verthc8429ad2018-11-20 11:12:37 -05001251 yuvaPixmaps[i] = resultBMs[i].pixmap();
Robert Phillips51c89e42018-10-05 13:30:43 -04001252 }
1253
Robert Phillips99044e12020-01-29 08:37:01 -05001254 if (fQuarterSize) {
1255 fImages[opaque][cs][format] = this->resizeOnGpu(
Jim Van Verth9bf81202018-10-30 15:53:36 -04001256 context,
Robert Phillips99044e12020-01-29 08:37:01 -05001257 (YUVFormat) format,
1258 (SkYUVColorSpace) cs,
1259 opaque,
Jim Van Verth9bf81202018-10-30 15:53:36 -04001260 yuvaTextures,
1261 yuvaIndices,
Robert Phillips99044e12020-01-29 08:37:01 -05001262 numTextures,
1263 fOriginalBMs[opaque].dimensions());
1264 } else {
1265 int counterMod = counter % 3;
1266 if (fUseDomain && counterMod == 0) {
1267 // Copies flatten to RGB when they copy the YUVA data, which doesn't
1268 // know about the intended domain and the domain padding bleeds in
1269 counterMod = 1;
1270 }
1271
1272 switch (counterMod) {
1273 case 0:
1274 fImages[opaque][cs][format] = SkImage::MakeFromYUVATexturesCopy(
1275 context,
1276 (SkYUVColorSpace)cs,
1277 yuvaTextures,
1278 yuvaIndices,
1279 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1280 kTopLeft_GrSurfaceOrigin);
1281 break;
1282 case 1:
1283 fImages[opaque][cs][format] = SkImage::MakeFromYUVATextures(
1284 context,
1285 (SkYUVColorSpace)cs,
1286 yuvaTextures,
1287 yuvaIndices,
1288 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1289 kTopLeft_GrSurfaceOrigin);
1290 break;
1291 case 2:
1292 default:
1293 fImages[opaque][cs][format] = SkImage::MakeFromYUVAPixmaps(
1294 context,
1295 (SkYUVColorSpace)cs,
1296 yuvaPixmaps,
1297 yuvaIndices,
1298 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1299 kTopLeft_GrSurfaceOrigin, true);
1300 break;
1301 }
1302 ++counter;
Jim Van Verth9bf81202018-10-30 15:53:36 -04001303 }
1304 } else {
Robert Phillipse0735522020-01-31 11:03:32 -05001305 SkImageInfo ii = SkImageInfo::MakeN32(fOriginalBMs[opaque].width(),
1306 fOriginalBMs[opaque].height(),
1307 kPremul_SkAlphaType);
1308
1309 fImages[opaque][cs][format] = make_yuv_gen_image(ii,
1310 (SkYUVColorSpace) cs,
1311 yuvaIndices,
1312 resultBMs);
Robert Phillips51c89e42018-10-05 13:30:43 -04001313 }
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001314 }
1315 }
1316 }
1317 }
1318
1319 void onDraw(SkCanvas* canvas) override {
Robert Phillips51c89e42018-10-05 13:30:43 -04001320 this->createImages(canvas->getGrContext());
1321
Robert Phillips99044e12020-01-29 08:37:01 -05001322 float cellWidth = kTileWidthHeight, cellHeight = kTileWidthHeight;
1323 if (fUseDomain) {
1324 cellWidth *= 1.5f;
1325 cellHeight *= 1.5f;
1326 }
1327
1328 SkRect origSrcRect = SkRect::MakeWH(fOriginalBMs[0].width(), fOriginalBMs[0].height());
1329
Michael Ludwiga6a84002019-04-12 15:03:02 -04001330 SkRect srcRect = SkRect::MakeWH(fOriginalBMs[0].width(), fOriginalBMs[0].height());
1331 SkRect dstRect = SkRect::MakeXYWH(kLabelWidth, 0.f, srcRect.width(), srcRect.height());
Robert Phillips99044e12020-01-29 08:37:01 -05001332 if (fQuarterSize) {
1333 if (canvas->getGrContext()) {
1334 // The src is only shrunk on the GPU
1335 srcRect = SkRect::MakeWH(fOriginalBMs[0].width()/2.0f,
1336 fOriginalBMs[0].height()/2.0f);
1337 }
1338 // but the dest is always drawn smaller
1339 dstRect = SkRect::MakeXYWH(kLabelWidth, 0.f,
1340 fOriginalBMs[0].width()/2.0f,
1341 fOriginalBMs[0].height()/2.0f);
1342 }
Michael Ludwiga6a84002019-04-12 15:03:02 -04001343
1344 SkCanvas::SrcRectConstraint constraint = SkCanvas::kFast_SrcRectConstraint;
1345 if (fUseDomain) {
1346 srcRect.inset(kDomainPadding, kDomainPadding);
Robert Phillips99044e12020-01-29 08:37:01 -05001347 origSrcRect.inset(kDomainPadding, kDomainPadding);
Michael Ludwiga6a84002019-04-12 15:03:02 -04001348 // Draw a larger rectangle to ensure bilerp filtering would normally read outside the
1349 // srcRect and hit the red pixels, if strict constraint weren't used.
1350 dstRect.fRight = kLabelWidth + 1.5f * srcRect.width();
1351 dstRect.fBottom = 1.5f * srcRect.height();
1352 constraint = SkCanvas::kStrict_SrcRectConstraint;
1353 }
1354
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001355 for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
Robert Phillips0a22ba82019-03-06 12:36:47 -05001356 SkPaint paint;
Michael Ludwiga6a84002019-04-12 15:03:02 -04001357 paint.setFilterQuality(kLow_SkFilterQuality);
Robert Phillips0a22ba82019-03-06 12:36:47 -05001358 if (kIdentity_SkYUVColorSpace == cs) {
1359 // The identity color space needs post processing to appear correctly
1360 paint.setColorFilter(yuv_to_rgb_colorfilter());
1361 }
1362
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001363 for (int opaque : { 0, 1 }) {
Michael Ludwiga6a84002019-04-12 15:03:02 -04001364 dstRect.offsetTo(dstRect.fLeft, kLabelHeight);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001365
Robert Phillips99044e12020-01-29 08:37:01 -05001366 draw_col_label(canvas, dstRect.fLeft + cellWidth / 2, cs, opaque);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001367
Robert Phillips99044e12020-01-29 08:37:01 -05001368 canvas->drawBitmapRect(fOriginalBMs[opaque], origSrcRect, dstRect,
1369 nullptr, constraint);
1370 dstRect.offset(0.f, cellHeight + kPad);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001371
Robert Phillipsbb749902019-06-10 17:20:12 -04001372 for (int format = kP016_YUVFormat; format <= kLast_YUVFormat; ++format) {
Michael Ludwiga6a84002019-04-12 15:03:02 -04001373 draw_row_label(canvas, dstRect.fTop, format);
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001374 if (fUseTargetColorSpace && fImages[opaque][cs][format]) {
Robert Phillips0a22ba82019-03-06 12:36:47 -05001375 // Making a CS-specific version of a kIdentity_SkYUVColorSpace YUV image
1376 // doesn't make a whole lot of sense. The colorSpace conversion will
1377 // operate on the YUV components rather than the RGB components.
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001378 sk_sp<SkImage> csImage =
1379 fImages[opaque][cs][format]->makeColorSpace(fTargetColorSpace);
Michael Ludwiga6a84002019-04-12 15:03:02 -04001380 canvas->drawImageRect(csImage, srcRect, dstRect, &paint, constraint);
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001381 } else {
Robert Phillips99044e12020-01-29 08:37:01 -05001382 canvas->drawImageRect(fImages[opaque][cs][format], srcRect, dstRect,
1383 &paint, constraint);
Jim Van Verth3e4c2f32019-01-11 13:32:45 -05001384 }
Robert Phillips99044e12020-01-29 08:37:01 -05001385 dstRect.offset(0.f, cellHeight + kPad);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001386 }
1387
Robert Phillips99044e12020-01-29 08:37:01 -05001388 dstRect.offset(cellWidth + kPad, 0.f);
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001389 }
1390 }
Brian Salomon8f388ce2019-01-29 14:50:53 -05001391 if (auto context = canvas->getGrContext()) {
Chris Dalton382b1222019-02-07 10:05:55 +00001392 if (!context->abandoned()) {
1393 context->flush();
1394 GrGpu* gpu = context->priv().getGpu();
1395 SkASSERT(gpu);
1396 gpu->testingOnly_flushGpuAndSync();
1397 for (const auto& tex : fBackendTextures) {
Robert Phillips5c7a25b2019-05-20 08:38:07 -04001398 context->deleteBackendTexture(tex);
Chris Dalton382b1222019-02-07 10:05:55 +00001399 }
1400 fBackendTextures.reset();
Brian Salomon8f388ce2019-01-29 14:50:53 -05001401 }
Brian Salomon8f388ce2019-01-29 14:50:53 -05001402 }
1403 SkASSERT(!fBackendTextures.count());
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001404 }
1405
1406private:
Robert Phillips2dd1b472019-03-21 09:00:20 -04001407 SkBitmap fOriginalBMs[2];
1408 sk_sp<SkImage> fImages[2][kLastEnum_SkYUVColorSpace + 1][kLast_YUVFormat + 1];
Brian Salomon8f388ce2019-01-29 14:50:53 -05001409 SkTArray<GrBackendTexture> fBackendTextures;
Robert Phillips2dd1b472019-03-21 09:00:20 -04001410 bool fUseTargetColorSpace;
Michael Ludwiga6a84002019-04-12 15:03:02 -04001411 bool fUseDomain;
Robert Phillips99044e12020-01-29 08:37:01 -05001412 bool fQuarterSize;
Robert Phillips2dd1b472019-03-21 09:00:20 -04001413 sk_sp<SkColorSpace> fTargetColorSpace;
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001414
1415 typedef GM INHERITED;
1416};
1417
1418//////////////////////////////////////////////////////////////////////////////
1419
Robert Phillips99044e12020-01-29 08:37:01 -05001420DEF_GM(return new WackyYUVFormatsGM(/* cs */ false, /* domain */ false, /* quarterSize */ false);)
1421DEF_GM(return new WackyYUVFormatsGM(/* cs */ false, /* domain */ false, /* quarterSize */ true);)
1422DEF_GM(return new WackyYUVFormatsGM(/* cs */ true, /* domain */ false, /* quarterSize */ false);)
1423DEF_GM(return new WackyYUVFormatsGM(/* cs */ false, /* domain */ true, /* quarterSize */ false);)
Brian Osmane9560492019-02-05 17:00:03 -05001424
Chris Dalton3a778372019-02-07 15:23:36 -07001425class YUVMakeColorSpaceGM : public GpuGM {
Brian Osmane9560492019-02-05 17:00:03 -05001426public:
1427 YUVMakeColorSpaceGM() {
1428 this->setBGColor(0xFFCCCCCC);
1429 }
1430
1431protected:
1432 SkString onShortName() override {
1433 return SkString("yuv_make_color_space");
1434 }
1435
1436 SkISize onISize() override {
1437 int numCols = 4; // (transparent, opaque) x (untagged, tagged)
1438 int numRows = 5; // original, YUV, subset, readPixels, makeNonTextureImage
1439 return SkISize::Make(numCols * (kTileWidthHeight + kPad) + kPad,
1440 numRows * (kTileWidthHeight + kPad) + kPad);
1441 }
1442
1443 void onOnceBeforeDraw() override {
1444 SkPoint origin = { kTileWidthHeight/2.0f, kTileWidthHeight/2.0f };
1445 float outerRadius = kTileWidthHeight/2.0f - 20.0f;
1446 float innerRadius = 20.0f;
1447
1448 {
1449 // transparent
1450 SkTDArray<SkRect> circles;
1451 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 5, &circles);
Michael Ludwiga6a84002019-04-12 15:03:02 -04001452 fOriginalBMs[0] = make_bitmap(kN32_SkColorType, path, circles, false, false);
Brian Osmane9560492019-02-05 17:00:03 -05001453 }
1454
1455 {
1456 // opaque
1457 SkTDArray<SkRect> circles;
1458 SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 7, &circles);
Michael Ludwiga6a84002019-04-12 15:03:02 -04001459 fOriginalBMs[1] = make_bitmap(kN32_SkColorType, path, circles, true, false);
Brian Osmane9560492019-02-05 17:00:03 -05001460 }
1461
1462 fTargetColorSpace = SkColorSpace::MakeSRGB()->makeColorSpin();
1463 }
1464
1465 void createImages(GrContext* context) {
1466 for (bool opaque : { false, true }) {
1467 PlaneData planes;
1468 extract_planes(fOriginalBMs[opaque], kJPEG_SkYUVColorSpace, &planes);
1469
1470 SkBitmap resultBMs[4];
1471 SkYUVAIndex yuvaIndices[4];
Robert Phillips2dd1b472019-03-21 09:00:20 -04001472
Brian Osmane9560492019-02-05 17:00:03 -05001473 create_YUV(planes, kAYUV_YUVFormat, resultBMs, yuvaIndices, opaque);
Robert Phillips2dd1b472019-03-21 09:00:20 -04001474
Brian Osmane9560492019-02-05 17:00:03 -05001475 int numTextures;
1476 if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures)) {
1477 continue;
1478 }
1479
Brian Osmane9560492019-02-05 17:00:03 -05001480 GrBackendTexture yuvaTextures[4];
1481 for (int i = 0; i < numTextures; ++i) {
Robert Phillipsbb749902019-06-10 17:20:12 -04001482 yuvaTextures[i] = create_yuva_texture(context, resultBMs[i], yuvaIndices, i,
1483 kAYUV_YUVFormat);
Brian Osmane9560492019-02-05 17:00:03 -05001484 if (yuvaTextures[i].isValid()) {
1485 fBackendTextures.push_back(yuvaTextures[i]);
1486 }
1487 }
1488
1489 fImages[opaque][0] = SkImage::MakeFromYUVATextures(
1490 context,
1491 kJPEG_SkYUVColorSpace,
1492 yuvaTextures,
1493 yuvaIndices,
1494 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1495 kTopLeft_GrSurfaceOrigin);
1496 fImages[opaque][1] = SkImage::MakeFromYUVATextures(
1497 context,
1498 kJPEG_SkYUVColorSpace,
1499 yuvaTextures,
1500 yuvaIndices,
1501 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1502 kTopLeft_GrSurfaceOrigin,
1503 SkColorSpace::MakeSRGB());
1504 }
1505 }
1506
Chris Dalton3a778372019-02-07 15:23:36 -07001507 void onDraw(GrContext* context, GrRenderTargetContext*, SkCanvas* canvas) override {
Brian Osmane9560492019-02-05 17:00:03 -05001508 this->createImages(context);
1509
1510 int x = kPad;
1511 for (int tagged : { 0, 1 }) {
1512 for (int opaque : { 0, 1 }) {
1513 int y = kPad;
1514
1515 auto raster = SkImage::MakeFromBitmap(fOriginalBMs[opaque])
1516 ->makeColorSpace(fTargetColorSpace);
1517 canvas->drawImage(raster, x, y);
1518 y += kTileWidthHeight + kPad;
1519
1520 auto yuv = fImages[opaque][tagged]->makeColorSpace(fTargetColorSpace);
1521 SkASSERT(SkColorSpace::Equals(yuv->colorSpace(), fTargetColorSpace.get()));
1522 canvas->drawImage(yuv, x, y);
1523 y += kTileWidthHeight + kPad;
1524
1525 auto subset = yuv->makeSubset(SkIRect::MakeWH(kTileWidthHeight / 2,
1526 kTileWidthHeight / 2));
1527 canvas->drawImage(subset, x, y);
1528 y += kTileWidthHeight + kPad;
1529
1530 auto nonTexture = yuv->makeNonTextureImage();
1531 canvas->drawImage(nonTexture, x, y);
1532 y += kTileWidthHeight + kPad;
1533
1534 SkBitmap readBack;
Brian Salomon5ad6fd32019-03-21 15:30:08 -04001535 readBack.allocPixels(yuv->imageInfo());
Brian Osmane9560492019-02-05 17:00:03 -05001536 yuv->readPixels(readBack.pixmap(), 0, 0);
1537 canvas->drawBitmap(readBack, x, y);
1538
1539 x += kTileWidthHeight + kPad;
1540 }
1541 }
1542
1543 context->flush();
1544 GrGpu* gpu = context->priv().getGpu();
1545 SkASSERT(gpu);
1546 gpu->testingOnly_flushGpuAndSync();
1547 for (const auto& tex : fBackendTextures) {
Robert Phillips5c7a25b2019-05-20 08:38:07 -04001548 context->deleteBackendTexture(tex);
Brian Osmane9560492019-02-05 17:00:03 -05001549 }
1550 fBackendTextures.reset();
1551 }
1552
1553private:
1554 SkBitmap fOriginalBMs[2];
1555 sk_sp<SkImage> fImages[2][2];
1556 SkTArray<GrBackendTexture> fBackendTextures;
1557 sk_sp<SkColorSpace> fTargetColorSpace;
1558
1559 typedef GM INHERITED;
1560};
1561
1562DEF_GM(return new YUVMakeColorSpaceGM();)
1563
Robert Phillipsbfa76f22018-10-03 12:12:26 -04001564}
Mike Reed6a5f7e22019-05-23 15:30:07 -04001565
1566///////////////
1567
Mike Reed6a5f7e22019-05-23 15:30:07 -04001568#include "include/effects/SkColorMatrix.h"
Mike Klein4b432fa2019-06-06 11:44:05 -05001569#include "src/core/SkAutoPixmapStorage.h"
Mike Klein4b432fa2019-06-06 11:44:05 -05001570#include "tools/Resources.h"
Mike Reed6a5f7e22019-05-23 15:30:07 -04001571
1572static void draw_into_alpha(const SkImage* img, sk_sp<SkColorFilter> cf, const SkPixmap& dst) {
1573 auto canvas = SkCanvas::MakeRasterDirect(dst.info(), dst.writable_addr(), dst.rowBytes());
1574 canvas->scale(1.0f * dst.width() / img->width(), 1.0f * dst.height() / img->height());
1575 SkPaint paint;
1576 paint.setFilterQuality(kLow_SkFilterQuality);
1577 paint.setColorFilter(cf);
1578 paint.setBlendMode(SkBlendMode::kSrc);
1579 canvas->drawImage(img, 0, 0, &paint);
1580}
1581
1582static void split_into_yuv(const SkImage* img, SkYUVColorSpace cs, const SkPixmap dst[3]) {
1583 float m[20];
1584 SkColorMatrix_RGB2YUV(cs, m);
1585
1586 memcpy(m + 15, m + 0, 5 * sizeof(float)); // copy Y into A
1587 draw_into_alpha(img, SkColorFilters::Matrix(m), dst[0]);
1588
1589 memcpy(m + 15, m + 5, 5 * sizeof(float)); // copy U into A
1590 draw_into_alpha(img, SkColorFilters::Matrix(m), dst[1]);
1591
1592 memcpy(m + 15, m + 10, 5 * sizeof(float)); // copy V into A
1593 draw_into_alpha(img, SkColorFilters::Matrix(m), dst[2]);
1594}
1595
1596static void draw_diff(SkCanvas* canvas, SkScalar x, SkScalar y,
1597 const SkImage* a, const SkImage* b) {
1598 auto sh = SkShaders::Blend(SkBlendMode::kDifference, a->makeShader(), b->makeShader());
1599 SkPaint paint;
1600 paint.setShader(sh);
1601 canvas->save();
1602 canvas->translate(x, y);
1603 canvas->drawRect(SkRect::MakeWH(a->width(), a->height()), paint);
1604
1605 SkColorMatrix cm;
1606 cm.setScale(64, 64, 64);
1607 paint.setShader(sh->makeWithColorFilter(SkColorFilters::Matrix(cm)));
1608 canvas->translate(0, a->height());
1609 canvas->drawRect(SkRect::MakeWH(a->width(), a->height()), paint);
1610
1611 canvas->restore();
1612}
1613
1614// Exercises SkColorMatrix_RGB2YUV for yuv colorspaces, showing the planes, and the
1615// resulting (recombined) images (gpu only for now).
1616//
1617class YUVSplitterGM : public skiagm::GM {
1618 sk_sp<SkImage> fOrig;
1619 SkAutoPixmapStorage fStorage[3];
1620 SkPixmap fPM[3];
1621
1622public:
1623 YUVSplitterGM() {}
1624
1625protected:
1626
1627 SkString onShortName() override {
1628 return SkString("yuv_splitter");
1629 }
1630
1631 SkISize onISize() override {
Brian Osman2b73e662019-11-01 10:02:24 -04001632 return SkISize::Make(1280, 768);
Mike Reed6a5f7e22019-05-23 15:30:07 -04001633 }
1634
1635 void onOnceBeforeDraw() override {
1636 fOrig = GetResourceAsImage("images/mandrill_256.png");
1637
1638 SkImageInfo info = SkImageInfo::Make(fOrig->width(), fOrig->height(), kAlpha_8_SkColorType,
1639 kPremul_SkAlphaType);
1640 fStorage[0].alloc(info);
1641 if (0) {
1642 // if you want to scale U,V down by 1/2
1643 info = info.makeWH(info.width()/2, info.height()/2);
1644 }
1645 fStorage[1].alloc(info);
1646 fStorage[2].alloc(info);
1647 for (int i = 0; i < 3; ++i) {
1648 fPM[i] = fStorage[i];
1649 }
1650 }
1651
1652 void onDraw(SkCanvas* canvas) override {
1653 SkYUVAIndex indices[4];
Robert Phillipse0735522020-01-31 11:03:32 -05001654 indices[SkYUVAIndex::kY_Index] = {0, SkColorChannel::kR};
1655 indices[SkYUVAIndex::kU_Index] = {1, SkColorChannel::kR};
1656 indices[SkYUVAIndex::kV_Index] = {2, SkColorChannel::kR};
Mike Reed6a5f7e22019-05-23 15:30:07 -04001657 indices[SkYUVAIndex::kA_Index] = {-1, SkColorChannel::kR};
1658
1659 canvas->translate(fOrig->width(), 0);
1660 canvas->save();
Brian Osman2b73e662019-11-01 10:02:24 -04001661 for (auto cs : {kRec709_SkYUVColorSpace, kRec601_SkYUVColorSpace, kJPEG_SkYUVColorSpace,
1662 kBT2020_SkYUVColorSpace}) {
Mike Reed6a5f7e22019-05-23 15:30:07 -04001663 split_into_yuv(fOrig.get(), cs, fPM);
1664 auto img = SkImage::MakeFromYUVAPixmaps(canvas->getGrContext(), cs, fPM, indices,
1665 fPM[0].info().dimensions(),
1666 kTopLeft_GrSurfaceOrigin,
1667 false, false, nullptr);
1668 if (img) {
1669 canvas->drawImage(img, 0, 0, nullptr);
1670 draw_diff(canvas, 0, fOrig->height(), fOrig.get(), img.get());
1671 }
1672 canvas->translate(fOrig->width(), 0);
1673 }
1674 canvas->restore();
1675 canvas->translate(-fOrig->width(), 0);
1676
1677 canvas->drawImage(SkImage::MakeRasterCopy(fPM[0]), 0, 0, nullptr);
1678 canvas->drawImage(SkImage::MakeRasterCopy(fPM[1]), 0, fPM[0].height(), nullptr);
1679 canvas->drawImage(SkImage::MakeRasterCopy(fPM[2]),
1680 0, fPM[0].height() + fPM[1].height(), nullptr);
1681 }
1682
1683private:
1684 typedef GM INHERITED;
1685};
1686DEF_GM( return new YUVSplitterGM; )