1 /*
2  * Copyright 2018 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "gm.h"
9 #include "sk_tool_utils.h"
10 
11 #include "SkColorPriv.h"
12 #include "SkImageGenerator.h"
13 #include "SkPath.h"
14 #include "SkTextUtils.h"
15 #include "SkYUVAIndex.h"
16 
17 #if SK_SUPPORT_GPU
18 #include "GrBackendSurface.h"
19 #include "GrContextPriv.h"
20 #include "GrGpu.h"
21 #include "SkImage_GpuYUVA.h"
22 #endif
23 
24 static const int kTileWidthHeight = 128;
25 static const int kLabelWidth = 64;
26 static const int kLabelHeight = 32;
27 static const int kPad = 1;
28 
29 enum YUVFormat {
30     // 4:4:4 formats, 32 bpp
31     kAYUV_YUVFormat,  // 8-bit YUVA values all interleaved
32 
33     // 4:2:0 formats, 12 bpp
34     kNV12_YUVFormat, // 8-bit Y plane + 2x2 down sampled interleaved U/V planes
35     kNV21_YUVFormat, // same as kNV12 but w/ U/V reversed in the interleaved plane
36 
37     kI420_YUVFormat, // 8-bit Y plane + 2x2 down sampled U and V planes
38     kYV12_YUVFormat, // 8-bit Y plane + 2x2 down sampled V and U planes
39 
40     kLast_YUVFormat = kYV12_YUVFormat
41 };
42 
43 // All the planes we need to construct the various YUV formats
44 struct PlaneData {
45    SkBitmap fYFull;
46    SkBitmap fUFull;
47    SkBitmap fVFull;
48    SkBitmap fAFull;
49    SkBitmap fUQuarter; // 2x2 downsampled U channel
50    SkBitmap fVQuarter; // 2x2 downsampled V channel
51 };
52 
53 // Add a portion of a circle to 'path'. The points 'o1' and 'o2' are on the border of the circle
54 // and have tangents 'v1' and 'v2'.
add_arc(SkPath * path,const SkPoint & o1,const SkVector & v1,const SkPoint & o2,const SkVector & v2,SkTDArray<SkRect> * circles,bool takeLongWayRound)55 static void add_arc(SkPath* path,
56                     const SkPoint& o1, const SkVector& v1,
57                     const SkPoint& o2, const SkVector& v2,
58                     SkTDArray<SkRect>* circles, bool takeLongWayRound) {
59 
60     SkVector v3 = { -v1.fY, v1.fX };
61     SkVector v4 = { v2.fY, -v2.fX };
62 
63     SkScalar t = ((o2.fX - o1.fX) * v4.fY - (o2.fY - o1.fY) * v4.fX) / v3.cross(v4);
64     SkPoint center = { o1.fX + t * v3.fX, o1.fY + t * v3.fY };
65 
66     SkRect r = { center.fX - t, center.fY - t, center.fX + t, center.fY + t };
67 
68     if (circles) {
69         circles->push_back(r);
70     }
71 
72     SkVector startV = o1 - center, endV = o2 - center;
73     startV.normalize();
74     endV.normalize();
75 
76     SkScalar startDeg = SkRadiansToDegrees(SkScalarATan2(startV.fY, startV.fX));
77     SkScalar endDeg = SkRadiansToDegrees(SkScalarATan2(endV.fY, endV.fX));
78 
79     startDeg += 360.0f;
80     startDeg = fmodf(startDeg, 360.0f);
81 
82     endDeg += 360.0f;
83     endDeg = fmodf(endDeg, 360.0f);
84 
85     if (endDeg < startDeg) {
86         endDeg += 360.0f;
87     }
88 
89     SkScalar sweepDeg = SkTAbs(endDeg - startDeg);
90     if (!takeLongWayRound) {
91         sweepDeg = sweepDeg - 360;
92     }
93 
94     path->arcTo(r, startDeg, sweepDeg, false);
95 }
96 
create_splat(const SkPoint & o,SkScalar innerRadius,SkScalar outerRadius,SkScalar ratio,int numLobes,SkTDArray<SkRect> * circles)97 static SkPath create_splat(const SkPoint& o, SkScalar innerRadius, SkScalar outerRadius,
98                            SkScalar ratio, int numLobes, SkTDArray<SkRect>* circles) {
99     if (numLobes <= 1) {
100         return SkPath();
101     }
102 
103     SkPath p;
104 
105     int numDivisions = 2 * numLobes;
106     SkScalar fullLobeDegrees = 360.0f / numLobes;
107     SkScalar outDegrees = ratio * fullLobeDegrees / (ratio + 1.0f);
108     SkScalar innerDegrees = fullLobeDegrees / (ratio + 1.0f);
109     SkMatrix outerStep, innerStep;
110     outerStep.setRotate(outDegrees);
111     innerStep.setRotate(innerDegrees);
112     SkVector curV = SkVector::Make(0.0f, 1.0f);
113 
114     if (circles) {
115         circles->push_back(SkRect::MakeLTRB(o.fX - innerRadius, o.fY - innerRadius,
116                                             o.fX + innerRadius, o.fY + innerRadius));
117     }
118 
119     p.moveTo(o.fX + innerRadius * curV.fX, o.fY + innerRadius * curV.fY);
120 
121     for (int i = 0; i < numDivisions; ++i) {
122 
123         SkVector nextV;
124         if (0 == (i % 2)) {
125             nextV = outerStep.mapVector(curV.fX, curV.fY);
126 
127             SkPoint top = SkPoint::Make(o.fX + outerRadius * curV.fX,
128                                         o.fY + outerRadius * curV.fY);
129             SkPoint nextTop = SkPoint::Make(o.fX + outerRadius * nextV.fX,
130                                             o.fY + outerRadius * nextV.fY);
131 
132             p.lineTo(top);
133             add_arc(&p, top, curV, nextTop, nextV, circles, true);
134         } else {
135             nextV = innerStep.mapVector(curV.fX, curV.fY);
136 
137             SkPoint bot = SkPoint::Make(o.fX + innerRadius * curV.fX,
138                                         o.fY + innerRadius * curV.fY);
139             SkPoint nextBot = SkPoint::Make(o.fX + innerRadius * nextV.fX,
140                                             o.fY + innerRadius * nextV.fY);
141 
142             p.lineTo(bot);
143             add_arc(&p, bot, curV, nextBot, nextV, nullptr, false);
144         }
145 
146         curV = nextV;
147     }
148 
149     p.close();
150 
151     return p;
152 }
153 
make_bitmap(const SkPath & path,const SkTDArray<SkRect> & circles,bool opaque)154 static SkBitmap make_bitmap(const SkPath& path, const SkTDArray<SkRect>& circles, bool opaque) {
155     const SkColor kGreen  = sk_tool_utils::color_to_565(SkColorSetARGB(0xFF, 178, 240, 104));
156     const SkColor kBlue   = sk_tool_utils::color_to_565(SkColorSetARGB(0xFF, 173, 167, 252));
157     const SkColor kYellow = sk_tool_utils::color_to_565(SkColorSetARGB(0xFF, 255, 221, 117));
158 
159     SkImageInfo ii = SkImageInfo::MakeN32(kTileWidthHeight, kTileWidthHeight, kPremul_SkAlphaType);
160 
161     SkBitmap bm;
162     bm.allocPixels(ii);
163 
164     std::unique_ptr<SkCanvas> canvas = SkCanvas::MakeRasterDirectN32(ii.width(), ii.height(),
165                                                                      (SkPMColor*)bm.getPixels(),
166                                                                      bm.rowBytes());
167 
168     canvas->clear(opaque ? kGreen : SK_ColorTRANSPARENT);
169 
170     SkPaint paint;
171     paint.setAntiAlias(false); // serialize-8888 doesn't seem to work well w/ partial transparency
172     paint.setColor(kBlue);
173 
174     canvas->drawPath(path, paint);
175 
176     paint.setColor(opaque ? kYellow : SK_ColorTRANSPARENT);
177     paint.setBlendMode(SkBlendMode::kSrc);
178     for (int i = 0; i < circles.count(); ++i) {
179         SkRect r = circles[i];
180         r.inset(r.width()/4, r.height()/4);
181         canvas->drawOval(r, paint);
182     }
183 
184     return bm;
185 }
186 
convert_rgba_to_yuva_601_shared(SkColor col,uint8_t yuv[4],uint8_t off,uint8_t range)187 static void convert_rgba_to_yuva_601_shared(SkColor col, uint8_t yuv[4],
188                                             uint8_t off, uint8_t range) {
189     static const float Kr = 0.299f;
190     static const float Kb = 0.114f;
191     static const float Kg = 1.0f - Kr - Kb;
192 
193     float r = SkColorGetR(col) / 255.0f;
194     float g = SkColorGetG(col) / 255.0f;
195     float b = SkColorGetB(col) / 255.0f;
196 
197     float Ey = Kr * r + Kg * g + Kb * b;
198     float Ecb = (b - Ey) / 1.402f;
199     float Ecr = (r - Ey) / 1.772;
200 
201     yuv[0] = SkScalarRoundToInt( range * Ey + off );
202     yuv[1] = SkScalarRoundToInt( 224 * Ecb + 128 );
203     yuv[2] = SkScalarRoundToInt( 224 * Ecr + 128 );
204     yuv[3] = SkColorGetA(col);
205 }
206 
convert_rgba_to_yuva_jpeg(SkColor col,uint8_t yuv[4])207 static void convert_rgba_to_yuva_jpeg(SkColor col, uint8_t yuv[4]) {
208     // full swing from 0..255
209     convert_rgba_to_yuva_601_shared(col, yuv, 0, 255);
210 }
211 
convert_rgba_to_yuva_601(SkColor col,uint8_t yuv[4])212 static void convert_rgba_to_yuva_601(SkColor col, uint8_t yuv[4]) {
213     // partial swing from 16..235
214     convert_rgba_to_yuva_601_shared(col, yuv, 16, 219);
215 
216 }
217 
convert_rgba_to_yuva_709(SkColor col,uint8_t yuv[4])218 static void convert_rgba_to_yuva_709(SkColor col, uint8_t yuv[4]) {
219     static const float Kr = 0.2126f;
220     static const float Kb = 0.0722f;
221     static const float Kg = 1.0f - Kr - Kb;
222 
223     float r = SkColorGetR(col) / 255.0f;
224     float g = SkColorGetG(col) / 255.0f;
225     float b = SkColorGetB(col) / 255.0f;
226 
227     float Ey = Kr * r + Kg * g + Kb * b;
228     float Ecb = (b - Ey) / 1.8556f;
229     float Ecr = (r - Ey) / 1.5748;
230 
231     yuv[0] = SkScalarRoundToInt( 219 * Ey +  16 );
232     yuv[1] = SkScalarRoundToInt( 224 * Ecb + 128 );
233     yuv[2] = SkScalarRoundToInt( 224 * Ecr + 128 );
234 
235     yuv[3] = SkColorGetA(col);
236 }
237 
238 
convert_yuva_to_rgba_jpeg(uint8_t y,uint8_t u,uint8_t v,uint8_t a)239 static SkPMColor convert_yuva_to_rgba_jpeg(uint8_t y, uint8_t u, uint8_t v, uint8_t a) {
240     int c = y;
241     int d = u - 128;
242     int e = v - 128;
243 
244     uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.0f * c                   +  1.402f    * e ),
245                             0, 255);
246     uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.0f * c - (0.344136f * d) - (0.714136f * e)),
247                             0, 255);
248     uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.0f * c +  1.773f    * d                   ),
249                             0, 255);
250 
251     return SkPremultiplyARGBInline(a, r, g, b);
252 }
253 
convert_yuva_to_rgba_601(uint8_t y,uint8_t u,uint8_t v,uint8_t a)254 static SkPMColor convert_yuva_to_rgba_601(uint8_t y, uint8_t u, uint8_t v, uint8_t a) {
255     int c = y - 16;
256     int d = u - 128;
257     int e = v - 128;
258 
259     uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.164f * c                +  1.596f * e ), 0, 255);
260     uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.164f * c - (0.391f * d) - (0.813f * e)), 0, 255);
261     uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.164f * c +  2.018f * d                ), 0, 255);
262 
263     return SkPremultiplyARGBInline(a, r, g, b);
264 }
265 
convert_yuva_to_rgba_709(uint8_t y,uint8_t u,uint8_t v,uint8_t a)266 static SkPMColor convert_yuva_to_rgba_709(uint8_t y, uint8_t u, uint8_t v, uint8_t a) {
267     int c = y - 16;
268     int d = u - 128;
269     int e = v - 128;
270 
271     uint8_t r = SkScalarPin(SkScalarRoundToInt( 1.164f * c                +  1.793f * e ), 0, 255);
272     uint8_t g = SkScalarPin(SkScalarRoundToInt( 1.164f * c - (0.213f * d) - (0.533f * e)), 0, 255);
273     uint8_t b = SkScalarPin(SkScalarRoundToInt( 1.164f * c +  2.112f * d                ), 0, 255);
274 
275     return SkPremultiplyARGBInline(a, r, g, b);
276 }
277 
extract_planes(const SkBitmap & bm,SkYUVColorSpace yuvColorSpace,PlaneData * planes)278 static void extract_planes(const SkBitmap& bm, SkYUVColorSpace yuvColorSpace, PlaneData* planes) {
279     if (kIdentity_SkYUVColorSpace == yuvColorSpace) {
280         // To test the identity color space we use JPEG YUV planes
281         yuvColorSpace = kJPEG_SkYUVColorSpace;
282     }
283 
284     SkASSERT(!(bm.width() % 2));
285     SkASSERT(!(bm.height() % 2));
286 
287     planes->fYFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
288     planes->fUFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
289     planes->fVFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
290     planes->fAFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
291     planes->fUQuarter.allocPixels(SkImageInfo::MakeA8(bm.width()/2, bm.height()/2));
292     planes->fVQuarter.allocPixels(SkImageInfo::MakeA8(bm.width()/2, bm.height()/2));
293 
294     for (int y = 0; y < bm.height(); ++y) {
295         for (int x = 0; x < bm.width(); ++x) {
296             SkColor col = bm.getColor(x, y);
297 
298             uint8_t yuva[4];
299 
300             if (kJPEG_SkYUVColorSpace == yuvColorSpace) {
301                 convert_rgba_to_yuva_jpeg(col, yuva);
302             } else if (kRec601_SkYUVColorSpace == yuvColorSpace) {
303                 convert_rgba_to_yuva_601(col, yuva);
304             } else {
305                 SkASSERT(kRec709_SkYUVColorSpace == yuvColorSpace);
306                 convert_rgba_to_yuva_709(col, yuva);
307             }
308 
309             *planes->fYFull.getAddr8(x, y) = yuva[0];
310             *planes->fUFull.getAddr8(x, y) = yuva[1];
311             *planes->fVFull.getAddr8(x, y) = yuva[2];
312             *planes->fAFull.getAddr8(x, y) = yuva[3];
313         }
314     }
315 
316     for (int y = 0; y < bm.height()/2; ++y) {
317         for (int x = 0; x < bm.width()/2; ++x) {
318             uint32_t uAccum = 0, vAccum = 0;
319 
320             uAccum += *planes->fUFull.getAddr8(2*x, 2*y);
321             uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y);
322             uAccum += *planes->fUFull.getAddr8(2*x, 2*y+1);
323             uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y+1);
324 
325             *planes->fUQuarter.getAddr8(x, y) = uAccum / 4.0f;
326 
327             vAccum += *planes->fVFull.getAddr8(2*x, 2*y);
328             vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y);
329             vAccum += *planes->fVFull.getAddr8(2*x, 2*y+1);
330             vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y+1);
331 
332             *planes->fVQuarter.getAddr8(x, y) = vAccum / 4.0f;
333         }
334     }
335 }
336 
337 // Recombine the separate planes into some YUV format
create_YUV(const PlaneData & planes,YUVFormat yuvFormat,SkBitmap resultBMs[],SkYUVAIndex yuvaIndices[4],bool opaque)338 static void create_YUV(const PlaneData& planes, YUVFormat yuvFormat,
339                        SkBitmap resultBMs[], SkYUVAIndex yuvaIndices[4], bool opaque) {
340     int nextLayer = 0;
341 
342     switch (yuvFormat) {
343         case kAYUV_YUVFormat: {
344             SkBitmap yuvaFull;
345 
346             yuvaFull.allocPixels(SkImageInfo::Make(planes.fYFull.width(), planes.fYFull.height(),
347                                                    kRGBA_8888_SkColorType, kUnpremul_SkAlphaType));
348 
349             for (int y = 0; y < planes.fYFull.height(); ++y) {
350                 for (int x = 0; x < planes.fYFull.width(); ++x) {
351 
352                     uint8_t Y = *planes.fYFull.getAddr8(x, y);
353                     uint8_t U = *planes.fUFull.getAddr8(x, y);
354                     uint8_t V = *planes.fVFull.getAddr8(x, y);
355                     uint8_t A = *planes.fAFull.getAddr8(x, y);
356 
357                     // NOT premul!
358                     // V and Y swapped to match RGBA layout
359                     *yuvaFull.getAddr32(x, y) = SkColorSetARGB(A, V, U, Y);
360                 }
361             }
362 
363             resultBMs[nextLayer++] = yuvaFull;
364 
365             yuvaIndices[0].fIndex = 0;
366             yuvaIndices[0].fChannel = SkColorChannel::kR;
367             yuvaIndices[1].fIndex = 0;
368             yuvaIndices[1].fChannel = SkColorChannel::kG;
369             yuvaIndices[2].fIndex = 0;
370             yuvaIndices[2].fChannel = SkColorChannel::kB;
371             yuvaIndices[3].fIndex = 0;
372             yuvaIndices[3].fChannel = SkColorChannel::kA;
373             break;
374         }
375         case kNV12_YUVFormat: {
376             SkBitmap uvQuarter;
377 
378             // There isn't a RG color type. Approx w/ RGBA.
379             uvQuarter.allocPixels(SkImageInfo::Make(planes.fYFull.width()/2,
380                                                     planes.fYFull.height()/2,
381                                                     kRGBA_8888_SkColorType,
382                                                     kUnpremul_SkAlphaType));
383 
384             for (int y = 0; y < planes.fYFull.height()/2; ++y) {
385                 for (int x = 0; x < planes.fYFull.width()/2; ++x) {
386                     uint8_t U = *planes.fUQuarter.getAddr8(x, y);
387                     uint8_t V = *planes.fVQuarter.getAddr8(x, y);
388 
389                     // NOT premul!
390                     // U and 0 swapped to match RGBA layout
391                     *uvQuarter.getAddr32(x, y) = SkColorSetARGB(0, 0, V, U);
392                 }
393             }
394 
395             resultBMs[nextLayer++] = planes.fYFull;
396             resultBMs[nextLayer++] = uvQuarter;
397 
398             yuvaIndices[0].fIndex = 0;
399             yuvaIndices[0].fChannel = SkColorChannel::kA;
400             yuvaIndices[1].fIndex = 1;
401             yuvaIndices[1].fChannel = SkColorChannel::kR;
402             yuvaIndices[2].fIndex = 1;
403             yuvaIndices[2].fChannel = SkColorChannel::kG;
404             break;
405         }
406         case kNV21_YUVFormat: {
407             SkBitmap vuQuarter;
408 
409             // There isn't a RG color type. Approx w/ RGBA.
410             vuQuarter.allocPixels(SkImageInfo::Make(planes.fYFull.width()/2,
411                                                     planes.fYFull.height()/2,
412                                                     kRGBA_8888_SkColorType,
413                                                     kUnpremul_SkAlphaType));
414 
415             for (int y = 0; y < planes.fYFull.height()/2; ++y) {
416                 for (int x = 0; x < planes.fYFull.width()/2; ++x) {
417                     uint8_t U = *planes.fUQuarter.getAddr8(x, y);
418                     uint8_t V = *planes.fVQuarter.getAddr8(x, y);
419 
420                     // NOT premul!
421                     // V and 0 swapped to match RGBA layout
422                     *vuQuarter.getAddr32(x, y) = SkColorSetARGB(0, 0, U, V);
423                 }
424             }
425 
426             resultBMs[nextLayer++] = planes.fYFull;
427             resultBMs[nextLayer++] = vuQuarter;
428 
429             yuvaIndices[0].fIndex = 0;
430             yuvaIndices[0].fChannel = SkColorChannel::kA;
431             yuvaIndices[1].fIndex = 1;
432             yuvaIndices[1].fChannel = SkColorChannel::kG;
433             yuvaIndices[2].fIndex = 1;
434             yuvaIndices[2].fChannel = SkColorChannel::kR;
435             break;
436         }
437         case kI420_YUVFormat:
438             resultBMs[nextLayer++] = planes.fYFull;
439             resultBMs[nextLayer++] = planes.fUQuarter;
440             resultBMs[nextLayer++] = planes.fVQuarter;
441 
442             yuvaIndices[0].fIndex = 0;
443             yuvaIndices[0].fChannel = SkColorChannel::kA;
444             yuvaIndices[1].fIndex = 1;
445             yuvaIndices[1].fChannel = SkColorChannel::kA;
446             yuvaIndices[2].fIndex = 2;
447             yuvaIndices[2].fChannel = SkColorChannel::kA;
448             break;
449         case kYV12_YUVFormat:
450             resultBMs[nextLayer++] = planes.fYFull;
451             resultBMs[nextLayer++] = planes.fVQuarter;
452             resultBMs[nextLayer++] = planes.fUQuarter;
453 
454             yuvaIndices[0].fIndex = 0;
455             yuvaIndices[0].fChannel = SkColorChannel::kA;
456             yuvaIndices[1].fIndex = 2;
457             yuvaIndices[1].fChannel = SkColorChannel::kA;
458             yuvaIndices[2].fIndex = 1;
459             yuvaIndices[2].fChannel = SkColorChannel::kA;
460             break;
461     }
462 
463     if (kAYUV_YUVFormat != yuvFormat) {
464         if (opaque) {
465             yuvaIndices[3].fIndex = -1;
466         } else {
467             resultBMs[nextLayer] = planes.fAFull;
468 
469             yuvaIndices[3].fIndex = nextLayer;
470             yuvaIndices[3].fChannel = SkColorChannel::kA;
471         }
472     }
473 
474 }
475 
look_up(float x1,float y1,const SkBitmap & bm,SkColorChannel channel)476 static uint8_t look_up(float x1, float y1, const SkBitmap& bm, SkColorChannel  channel) {
477     uint8_t result;
478 
479     int x = SkScalarFloorToInt(x1 * bm.width());
480     int y = SkScalarFloorToInt(y1 * bm.height());
481 
482     if (kAlpha_8_SkColorType == bm.colorType()) {
483         SkASSERT(SkColorChannel::kA == channel);
484         result = *bm.getAddr8(x, y);
485     } else {
486         SkASSERT(kRGBA_8888_SkColorType == bm.colorType());
487 
488         switch (channel) {
489             case SkColorChannel::kR:
490                 result = SkColorGetR(bm.getColor(x, y));
491                 break;
492             case SkColorChannel::kG:
493                 result = SkColorGetG(bm.getColor(x, y));
494                 break;
495             case SkColorChannel::kB:
496                 result = SkColorGetB(bm.getColor(x, y));
497                 break;
498             case SkColorChannel::kA:
499                 result = SkColorGetA(bm.getColor(x, y));
500                 break;
501         }
502     }
503 
504     return result;
505 }
506 
507 class YUVGenerator : public SkImageGenerator {
508 public:
YUVGenerator(const SkImageInfo & ii,SkYUVColorSpace yuvColorSpace,SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],SkBitmap bitmaps[SkYUVASizeInfo::kMaxCount])509     YUVGenerator(const SkImageInfo& ii,
510                  SkYUVColorSpace yuvColorSpace,
511                  SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
512                  SkBitmap bitmaps[SkYUVASizeInfo::kMaxCount])
513             : SkImageGenerator(ii)
514             , fYUVColorSpace(yuvColorSpace) {
515         memcpy(fYUVAIndices, yuvaIndices, sizeof(fYUVAIndices));
516 
517         SkAssertResult(SkYUVAIndex::AreValidIndices(fYUVAIndices, &fNumBitmaps));
518         SkASSERT(fNumBitmaps > 0 && fNumBitmaps <= SkYUVASizeInfo::kMaxCount);
519 
520         for (int i = 0; i < fNumBitmaps; ++i) {
521             fYUVBitmaps[i] = bitmaps[i];
522         }
523     }
524 
525 protected:
onGetPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,const Options &)526     bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
527                      const Options&) override {
528 
529         if (kUnknown_SkColorType == fFlattened.colorType()) {
530             fFlattened.allocPixels(this->getInfo());
531 
532             for (int y = 0; y < info.height(); ++y) {
533                 for (int x = 0; x < info.width(); ++x) {
534 
535                     float x1 = (x + 0.5f) / info.width();
536                     float y1 = (y + 0.5f) / info.height();
537 
538                     uint8_t Y = look_up(x1, y1,
539                                         fYUVBitmaps[fYUVAIndices[0].fIndex],
540                                         fYUVAIndices[0].fChannel);
541 
542                     uint8_t U = look_up(x1, y1,
543                                         fYUVBitmaps[fYUVAIndices[1].fIndex],
544                                         fYUVAIndices[1].fChannel);
545 
546 
547                     uint8_t V = look_up(x1, y1,
548                                         fYUVBitmaps[fYUVAIndices[2].fIndex],
549                                         fYUVAIndices[2].fChannel);
550 
551                     uint8_t A = 255;
552                     if (fYUVAIndices[3].fIndex >= 0) {
553                         A = look_up(x1, y1,
554                                     fYUVBitmaps[fYUVAIndices[3].fIndex],
555                                     fYUVAIndices[3].fChannel);
556                     }
557 
558                     // Making premul here.
559                     switch (fYUVColorSpace) {
560                         case kJPEG_SkYUVColorSpace:
561                             *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_jpeg(Y, U, V, A);
562                             break;
563                         case kRec601_SkYUVColorSpace:
564                             *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_601(Y, U, V, A);
565                             break;
566                         case kRec709_SkYUVColorSpace:
567                             *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_709(Y, U, V, A);
568                             break;
569                         case kIdentity_SkYUVColorSpace:
570                             *fFlattened.getAddr32(x, y) = SkPremultiplyARGBInline(A, Y, U, V);
571                             break;
572                     }
573                 }
574             }
575         }
576 
577         return fFlattened.readPixels(info, pixels, rowBytes, 0, 0);
578     }
579 
onQueryYUVA8(SkYUVASizeInfo * size,SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],SkYUVColorSpace * yuvColorSpace) const580     bool onQueryYUVA8(SkYUVASizeInfo* size,
581                       SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
582                       SkYUVColorSpace* yuvColorSpace) const override {
583 
584         memcpy(yuvaIndices, fYUVAIndices, sizeof(fYUVAIndices));
585         *yuvColorSpace = fYUVColorSpace;
586 
587         int i = 0;
588         for ( ; i < fNumBitmaps; ++i) {
589             size->fSizes[i].fWidth = fYUVBitmaps[i].width();
590             size->fSizes[i].fHeight = fYUVBitmaps[i].height();
591             size->fWidthBytes[i] = fYUVBitmaps[i].rowBytes();
592         }
593         for ( ; i < SkYUVASizeInfo::kMaxCount; ++i) {
594             size->fSizes[i].fWidth = 0;
595             size->fSizes[i].fHeight = 0;
596             size->fWidthBytes[i] = 0;
597         }
598 
599         return true;
600     }
601 
onGetYUVA8Planes(const SkYUVASizeInfo &,const SkYUVAIndex[SkYUVAIndex::kIndexCount],void * planes[SkYUVASizeInfo::kMaxCount])602     bool onGetYUVA8Planes(const SkYUVASizeInfo&, const SkYUVAIndex[SkYUVAIndex::kIndexCount],
603                           void* planes[SkYUVASizeInfo::kMaxCount]) override {
604         for (int i = 0; i < fNumBitmaps; ++i) {
605             planes[i] = fYUVBitmaps[i].getPixels();
606         }
607         return true;
608     }
609 
610 private:
611     SkYUVColorSpace fYUVColorSpace;
612     SkYUVAIndex     fYUVAIndices[SkYUVAIndex::kIndexCount];
613     int             fNumBitmaps;
614     SkBitmap        fYUVBitmaps[SkYUVASizeInfo::kMaxCount];
615     SkBitmap        fFlattened;
616 
617 };
618 
make_yuv_gen_image(const SkImageInfo & ii,SkYUVColorSpace yuvColorSpace,SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],SkBitmap bitmaps[])619 static sk_sp<SkImage> make_yuv_gen_image(const SkImageInfo& ii,
620                                          SkYUVColorSpace yuvColorSpace,
621                                          SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
622                                          SkBitmap bitmaps[]) {
623     std::unique_ptr<SkImageGenerator> gen(new YUVGenerator(ii, yuvColorSpace,
624                                                            yuvaIndices, bitmaps));
625 
626     return SkImage::MakeFromGenerator(std::move(gen));
627 }
628 
draw_col_label(SkCanvas * canvas,int x,int yuvColorSpace,bool opaque)629 static void draw_col_label(SkCanvas* canvas, int x, int yuvColorSpace, bool opaque) {
630     static const char* kYUVColorSpaceNames[] = { "JPEG", "601", "709", "Identity" };
631     GR_STATIC_ASSERT(SK_ARRAY_COUNT(kYUVColorSpaceNames) == kLastEnum_SkYUVColorSpace+1);
632 
633     SkPaint paint;
634     SkFont font(sk_tool_utils::create_portable_typeface(nullptr, SkFontStyle::Bold()), 16);
635     font.setEdging(SkFont::Edging::kAlias);
636 
637     SkRect textRect;
638     SkString colLabel;
639 
640     colLabel.printf("%s", kYUVColorSpaceNames[yuvColorSpace]);
641     font.measureText(colLabel.c_str(), colLabel.size(), kUTF8_SkTextEncoding, &textRect);
642     int y = textRect.height();
643 
644     SkTextUtils::DrawString(canvas, colLabel.c_str(), x, y, font, paint, SkTextUtils::kCenter_Align);
645 
646     colLabel.printf("%s", opaque ? "Opaque" : "Transparent");
647 
648     font.measureText(colLabel.c_str(), colLabel.size(), kUTF8_SkTextEncoding, &textRect);
649     y += textRect.height();
650 
651     SkTextUtils::DrawString(canvas, colLabel.c_str(), x, y, font, paint, SkTextUtils::kCenter_Align);
652 }
653 
draw_row_label(SkCanvas * canvas,int y,int yuvFormat)654 static void draw_row_label(SkCanvas* canvas, int y, int yuvFormat) {
655     static const char* kYUVFormatNames[] = { "AYUV", "NV12", "NV21", "I420", "YV12" };
656     GR_STATIC_ASSERT(SK_ARRAY_COUNT(kYUVFormatNames) == kLast_YUVFormat+1);
657 
658     SkPaint paint;
659     SkFont font(sk_tool_utils::create_portable_typeface(nullptr, SkFontStyle::Bold()), 16);
660     font.setEdging(SkFont::Edging::kAlias);
661 
662     SkRect textRect;
663     SkString rowLabel;
664 
665     rowLabel.printf("%s", kYUVFormatNames[yuvFormat]);
666     font.measureText(rowLabel.c_str(), rowLabel.size(), kUTF8_SkTextEncoding, &textRect);
667     y += kTileWidthHeight/2 + textRect.height()/2;
668 
669     canvas->drawString(rowLabel, 0, y, font, paint);
670 }
671 
create_yuva_texture(GrGpu * gpu,const SkBitmap & bm,SkYUVAIndex yuvaIndices[4],int texIndex)672 static GrBackendTexture create_yuva_texture(GrGpu* gpu, const SkBitmap& bm,
673                                             SkYUVAIndex yuvaIndices[4], int texIndex) {
674     SkASSERT(texIndex >= 0 && texIndex <= 3);
675     int channelCount = 0;
676     for (int i = 0; i < SkYUVAIndex::kIndexCount; ++i) {
677         if (yuvaIndices[i].fIndex == texIndex) {
678             ++channelCount;
679         }
680     }
681     // Need to create an RG texture for two-channel planes
682     GrBackendTexture tex;
683     if (2 == channelCount) {
684         SkASSERT(kRGBA_8888_SkColorType == bm.colorType());
685         SkAutoTMalloc<char> pixels(2 * bm.width()*bm.height());
686         char* currPixel = pixels;
687         for (int y = 0; y < bm.height(); ++y) {
688             for (int x = 0; x < bm.width(); ++x) {
689                 SkColor color = bm.getColor(x, y);
690                 currPixel[0] = SkColorGetR(color);
691                 currPixel[1] = SkColorGetG(color);
692                 currPixel += 2;
693             }
694         }
695         tex = gpu->createTestingOnlyBackendTexture(
696             pixels,
697             bm.width(),
698             bm.height(),
699             GrColorType::kRG_88,
700             false,
701             GrMipMapped::kNo,
702             2*bm.width());
703     }
704     if (!tex.isValid()) {
705         tex = gpu->createTestingOnlyBackendTexture(
706             bm.getPixels(),
707             bm.width(),
708             bm.height(),
709             bm.colorType(),
710             false,
711             GrMipMapped::kNo,
712             bm.rowBytes());
713     }
714     return tex;
715 }
716 
yuv_to_rgb_colorfilter()717 static sk_sp<SkColorFilter> yuv_to_rgb_colorfilter() {
718     static const float kJPEGConversionMatrix[20] = {
719         1.0f,  0.0f,       1.402f,    0.0f, -180.0f,
720         1.0f, -0.344136f, -0.714136f, 0.0f,  136.0f,
721         1.0f,  1.772f,     0.0f,      0.0f, -227.6f,
722         0.0f,  0.0f,       0.0f,      1.0f,    0.0f
723     };
724 
725     return SkColorFilter::MakeMatrixFilterRowMajor255(kJPEGConversionMatrix);
726 }
727 
728 namespace skiagm {
729 
730 // This GM creates an opaque and transparent bitmap, extracts the planes and then recombines
731 // them into various YUV formats. It then renders the results in the grid:
732 //
733 //                   JPEG                   601                     709
734 //        Transparent  Opaque       Transparent  Opaque        Transparent  Opaque
735 // AYUV
736 // NV12
737 // NV21
738 // I420
739 // YV12
740 class WackyYUVFormatsGM : public GM {
741 public:
WackyYUVFormatsGM(bool useTargetColorSpace)742     WackyYUVFormatsGM(bool useTargetColorSpace) : fUseTargetColorSpace(useTargetColorSpace) {
743         this->setBGColor(0xFFCCCCCC);
744     }
745 
746 protected:
747 
onShortName()748     SkString onShortName() override {
749         SkString name("wacky_yuv_formats");
750         if (fUseTargetColorSpace) {
751             name += "_cs";
752         }
753         return name;
754     }
755 
onISize()756     SkISize onISize() override {
757         int numCols = 2 * (kLastEnum_SkYUVColorSpace + 1); // opacity x color-space
758         int numRows = 1 + (kLast_YUVFormat + 1);  // origin + # yuv formats
759         return SkISize::Make(kLabelWidth  + numCols * (kTileWidthHeight + kPad),
760                              kLabelHeight + numRows * (kTileWidthHeight + kPad));
761     }
762 
onOnceBeforeDraw()763     void onOnceBeforeDraw() override {
764         SkPoint origin = { kTileWidthHeight/2.0f, kTileWidthHeight/2.0f };
765         float outerRadius = kTileWidthHeight/2.0f - 20.0f;
766         float innerRadius = 20.0f;
767 
768         {
769             // transparent
770             SkTDArray<SkRect> circles;
771             SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 5, &circles);
772             fOriginalBMs[0] = make_bitmap(path, circles, false);
773         }
774 
775         {
776             // opaque
777             SkTDArray<SkRect> circles;
778             SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 7, &circles);
779             fOriginalBMs[1] = make_bitmap(path, circles, true);
780         }
781 
782         if (fUseTargetColorSpace) {
783             fTargetColorSpace = SkColorSpace::MakeSRGB()->makeColorSpin();
784         }
785     }
786 
createImages(GrContext * context)787     void createImages(GrContext* context) {
788         int counter = 0;
789         for (bool opaque : { false, true }) {
790             for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
791                 PlaneData planes;
792                 extract_planes(fOriginalBMs[opaque], (SkYUVColorSpace) cs, &planes);
793 
794                 for (int format = kAYUV_YUVFormat; format <= kLast_YUVFormat; ++format) {
795                     SkBitmap resultBMs[4];
796                     SkYUVAIndex yuvaIndices[4];
797                     create_YUV(planes, (YUVFormat) format, resultBMs, yuvaIndices, opaque);
798                     int numTextures;
799                     if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures)) {
800                         continue;
801                     }
802 
803                     if (context) {
804                         if (context->abandoned()) {
805                             return;
806                         }
807 
808                         GrGpu* gpu = context->priv().getGpu();
809                         if (!gpu) {
810                             return;
811                         }
812 
813                         GrBackendTexture yuvaTextures[4];
814                         SkPixmap yuvaPixmaps[4];
815 
816                         for (int i = 0; i < numTextures; ++i) {
817                             yuvaTextures[i] = create_yuva_texture(gpu, resultBMs[i],
818                                                                   yuvaIndices, i);
819                             if (yuvaTextures[i].isValid()) {
820                                 fBackendTextures.push_back(yuvaTextures[i]);
821                             }
822                             yuvaPixmaps[i] = resultBMs[i].pixmap();
823                         }
824 
825                         int counterMod = counter % 3;
826                         switch (counterMod) {
827                         case 0:
828                             fImages[opaque][cs][format] = SkImage::MakeFromYUVATexturesCopy(
829                                 context,
830                                 (SkYUVColorSpace)cs,
831                                 yuvaTextures,
832                                 yuvaIndices,
833                                 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
834                                 kTopLeft_GrSurfaceOrigin);
835                             break;
836                         case 1:
837                             fImages[opaque][cs][format] = SkImage::MakeFromYUVATextures(
838                                 context,
839                                 (SkYUVColorSpace)cs,
840                                 yuvaTextures,
841                                 yuvaIndices,
842                                 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
843                                 kTopLeft_GrSurfaceOrigin);
844                             break;
845                         case 2:
846                         default:
847                             fImages[opaque][cs][format] = SkImage::MakeFromYUVAPixmaps(
848                                 context,
849                                 (SkYUVColorSpace)cs,
850                                 yuvaPixmaps,
851                                 yuvaIndices,
852                                 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
853                                 kTopLeft_GrSurfaceOrigin, true);
854                             break;
855                         }
856                         ++counter;
857                     } else {
858                         fImages[opaque][cs][format] = make_yuv_gen_image(
859                                                                 fOriginalBMs[opaque].info(),
860                                                                 (SkYUVColorSpace) cs,
861                                                                 yuvaIndices,
862                                                                 resultBMs);
863                     }
864                 }
865             }
866         }
867     }
868 
onDraw(SkCanvas * canvas)869     void onDraw(SkCanvas* canvas) override {
870         this->createImages(canvas->getGrContext());
871 
872         int x = kLabelWidth;
873         for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
874             SkPaint paint;
875             if (kIdentity_SkYUVColorSpace == cs) {
876                 // The identity color space needs post processing to appear correctly
877                 paint.setColorFilter(yuv_to_rgb_colorfilter());
878             }
879 
880             for (int opaque : { 0, 1 }) {
881                 int y = kLabelHeight;
882 
883                 draw_col_label(canvas, x+kTileWidthHeight/2, cs, opaque);
884 
885                 canvas->drawBitmap(fOriginalBMs[opaque], x, y);
886                 y += kTileWidthHeight + kPad;
887 
888                 for (int format = kAYUV_YUVFormat; format <= kLast_YUVFormat; ++format) {
889                     draw_row_label(canvas, y, format);
890                     if (fUseTargetColorSpace && fImages[opaque][cs][format]) {
891                         // Making a CS-specific version of a kIdentity_SkYUVColorSpace YUV image
892                         // doesn't make a whole lot of sense. The colorSpace conversion will
893                         // operate on the YUV components rather than the RGB components.
894                         sk_sp<SkImage> csImage =
895                             fImages[opaque][cs][format]->makeColorSpace(fTargetColorSpace);
896                         canvas->drawImage(csImage, x, y, &paint);
897                     } else {
898                         canvas->drawImage(fImages[opaque][cs][format], x, y, &paint);
899                     }
900                     y += kTileWidthHeight + kPad;
901                 }
902 
903                 x += kTileWidthHeight + kPad;
904             }
905         }
906         if (auto context = canvas->getGrContext()) {
907             if (!context->abandoned()) {
908                 context->flush();
909                 GrGpu* gpu = context->priv().getGpu();
910                 SkASSERT(gpu);
911                 gpu->testingOnly_flushGpuAndSync();
912                 for (const auto& tex : fBackendTextures) {
913                     gpu->deleteTestingOnlyBackendTexture(tex);
914                 }
915                 fBackendTextures.reset();
916             }
917         }
918         SkASSERT(!fBackendTextures.count());
919     }
920 
921 private:
922     SkBitmap fOriginalBMs[2];
923     sk_sp<SkImage> fImages[2][kLastEnum_SkYUVColorSpace + 1][kLast_YUVFormat + 1];
924     SkTArray<GrBackendTexture> fBackendTextures;
925     bool fUseTargetColorSpace;
926     sk_sp<SkColorSpace> fTargetColorSpace;
927 
928     typedef GM INHERITED;
929 };
930 
931 //////////////////////////////////////////////////////////////////////////////
932 
933 DEF_GM(return new WackyYUVFormatsGM(false);)
934 DEF_GM(return new WackyYUVFormatsGM(true);)
935 
936 class YUVMakeColorSpaceGM : public GpuGM {
937 public:
YUVMakeColorSpaceGM()938     YUVMakeColorSpaceGM() {
939         this->setBGColor(0xFFCCCCCC);
940     }
941 
942 protected:
onShortName()943     SkString onShortName() override {
944         return SkString("yuv_make_color_space");
945     }
946 
onISize()947     SkISize onISize() override {
948         int numCols = 4; // (transparent, opaque) x (untagged, tagged)
949         int numRows = 5; // original, YUV, subset, readPixels, makeNonTextureImage
950         return SkISize::Make(numCols * (kTileWidthHeight + kPad) + kPad,
951                              numRows * (kTileWidthHeight + kPad) + kPad);
952     }
953 
onOnceBeforeDraw()954     void onOnceBeforeDraw() override {
955         SkPoint origin = { kTileWidthHeight/2.0f, kTileWidthHeight/2.0f };
956         float outerRadius = kTileWidthHeight/2.0f - 20.0f;
957         float innerRadius = 20.0f;
958 
959         {
960             // transparent
961             SkTDArray<SkRect> circles;
962             SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 5, &circles);
963             fOriginalBMs[0] = make_bitmap(path, circles, false);
964         }
965 
966         {
967             // opaque
968             SkTDArray<SkRect> circles;
969             SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 7, &circles);
970             fOriginalBMs[1] = make_bitmap(path, circles, true);
971         }
972 
973         fTargetColorSpace = SkColorSpace::MakeSRGB()->makeColorSpin();
974     }
975 
createImages(GrContext * context)976     void createImages(GrContext* context) {
977         for (bool opaque : { false, true }) {
978             PlaneData planes;
979             extract_planes(fOriginalBMs[opaque], kJPEG_SkYUVColorSpace, &planes);
980 
981             SkBitmap resultBMs[4];
982             SkYUVAIndex yuvaIndices[4];
983             create_YUV(planes, kAYUV_YUVFormat, resultBMs, yuvaIndices, opaque);
984             int numTextures;
985             if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures)) {
986                 continue;
987             }
988 
989             GrGpu* gpu = context->priv().getGpu();
990             if (!gpu) {
991                 return;
992             }
993 
994             GrBackendTexture yuvaTextures[4];
995             for (int i = 0; i < numTextures; ++i) {
996                 yuvaTextures[i] = create_yuva_texture(gpu, resultBMs[i], yuvaIndices, i);
997                 if (yuvaTextures[i].isValid()) {
998                     fBackendTextures.push_back(yuvaTextures[i]);
999                 }
1000             }
1001 
1002             fImages[opaque][0] = SkImage::MakeFromYUVATextures(
1003                     context,
1004                     kJPEG_SkYUVColorSpace,
1005                     yuvaTextures,
1006                     yuvaIndices,
1007                     { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1008                     kTopLeft_GrSurfaceOrigin);
1009             fImages[opaque][1] = SkImage::MakeFromYUVATextures(
1010                     context,
1011                     kJPEG_SkYUVColorSpace,
1012                     yuvaTextures,
1013                     yuvaIndices,
1014                     { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
1015                     kTopLeft_GrSurfaceOrigin,
1016                     SkColorSpace::MakeSRGB());
1017         }
1018     }
1019 
onDraw(GrContext * context,GrRenderTargetContext *,SkCanvas * canvas)1020     void onDraw(GrContext* context, GrRenderTargetContext*, SkCanvas* canvas) override {
1021         this->createImages(context);
1022 
1023         int x = kPad;
1024         for (int tagged : { 0, 1 }) {
1025             for (int opaque : { 0, 1 }) {
1026                 int y = kPad;
1027 
1028                 auto raster = SkImage::MakeFromBitmap(fOriginalBMs[opaque])
1029                     ->makeColorSpace(fTargetColorSpace);
1030                 canvas->drawImage(raster, x, y);
1031                 y += kTileWidthHeight + kPad;
1032 
1033                 auto yuv = fImages[opaque][tagged]->makeColorSpace(fTargetColorSpace);
1034                 SkASSERT(SkColorSpace::Equals(yuv->colorSpace(), fTargetColorSpace.get()));
1035                 canvas->drawImage(yuv, x, y);
1036                 y += kTileWidthHeight + kPad;
1037 
1038                 auto subset = yuv->makeSubset(SkIRect::MakeWH(kTileWidthHeight / 2,
1039                                                               kTileWidthHeight / 2));
1040                 canvas->drawImage(subset, x, y);
1041                 y += kTileWidthHeight + kPad;
1042 
1043                 auto nonTexture = yuv->makeNonTextureImage();
1044                 canvas->drawImage(nonTexture, x, y);
1045                 y += kTileWidthHeight + kPad;
1046 
1047                 SkBitmap readBack;
1048                 readBack.allocPixels(as_IB(yuv)->onImageInfo());
1049                 yuv->readPixels(readBack.pixmap(), 0, 0);
1050                 canvas->drawBitmap(readBack, x, y);
1051 
1052                 x += kTileWidthHeight + kPad;
1053             }
1054         }
1055 
1056         context->flush();
1057         GrGpu* gpu = context->priv().getGpu();
1058         SkASSERT(gpu);
1059         gpu->testingOnly_flushGpuAndSync();
1060         for (const auto& tex : fBackendTextures) {
1061             gpu->deleteTestingOnlyBackendTexture(tex);
1062         }
1063         fBackendTextures.reset();
1064     }
1065 
1066 private:
1067     SkBitmap fOriginalBMs[2];
1068     sk_sp<SkImage> fImages[2][2];
1069     SkTArray<GrBackendTexture> fBackendTextures;
1070     sk_sp<SkColorSpace> fTargetColorSpace;
1071 
1072     typedef GM INHERITED;
1073 };
1074 
1075 DEF_GM(return new YUVMakeColorSpaceGM();)
1076 
1077 }
1078