• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     SkASSERT(!(bm.width() % 2));
280     SkASSERT(!(bm.height() % 2));
281 
282     planes->fYFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
283     planes->fUFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
284     planes->fVFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
285     planes->fAFull.allocPixels(SkImageInfo::MakeA8(bm.width(), bm.height()));
286     planes->fUQuarter.allocPixels(SkImageInfo::MakeA8(bm.width()/2, bm.height()/2));
287     planes->fVQuarter.allocPixels(SkImageInfo::MakeA8(bm.width()/2, bm.height()/2));
288 
289     for (int y = 0; y < bm.height(); ++y) {
290         for (int x = 0; x < bm.width(); ++x) {
291             SkColor col = bm.getColor(x, y);
292 
293             uint8_t yuva[4];
294 
295             if (kJPEG_SkYUVColorSpace == yuvColorSpace) {
296                 convert_rgba_to_yuva_jpeg(col, yuva);
297             } else if (kRec601_SkYUVColorSpace == yuvColorSpace) {
298                 convert_rgba_to_yuva_601(col, yuva);
299             } else {
300                 SkASSERT(kRec709_SkYUVColorSpace == yuvColorSpace);
301                 convert_rgba_to_yuva_709(col, yuva);
302             }
303 
304             *planes->fYFull.getAddr8(x, y) = yuva[0];
305             *planes->fUFull.getAddr8(x, y) = yuva[1];
306             *planes->fVFull.getAddr8(x, y) = yuva[2];
307             *planes->fAFull.getAddr8(x, y) = yuva[3];
308         }
309     }
310 
311     for (int y = 0; y < bm.height()/2; ++y) {
312         for (int x = 0; x < bm.width()/2; ++x) {
313             uint32_t uAccum = 0, vAccum = 0;
314 
315             uAccum += *planes->fUFull.getAddr8(2*x, 2*y);
316             uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y);
317             uAccum += *planes->fUFull.getAddr8(2*x, 2*y+1);
318             uAccum += *planes->fUFull.getAddr8(2*x+1, 2*y+1);
319 
320             *planes->fUQuarter.getAddr8(x, y) = uAccum / 4.0f;
321 
322             vAccum += *planes->fVFull.getAddr8(2*x, 2*y);
323             vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y);
324             vAccum += *planes->fVFull.getAddr8(2*x, 2*y+1);
325             vAccum += *planes->fVFull.getAddr8(2*x+1, 2*y+1);
326 
327             *planes->fVQuarter.getAddr8(x, y) = vAccum / 4.0f;
328         }
329     }
330 }
331 
332 // Recombine the separate planes into some YUV format
create_YUV(const PlaneData & planes,YUVFormat yuvFormat,SkBitmap resultBMs[],SkYUVAIndex yuvaIndices[4],bool opaque)333 static void create_YUV(const PlaneData& planes, YUVFormat yuvFormat,
334                        SkBitmap resultBMs[], SkYUVAIndex yuvaIndices[4], bool opaque) {
335     int nextLayer = 0;
336 
337     switch (yuvFormat) {
338         case kAYUV_YUVFormat: {
339             SkBitmap yuvaFull;
340 
341             yuvaFull.allocPixels(SkImageInfo::Make(planes.fYFull.width(), planes.fYFull.height(),
342                                                    kRGBA_8888_SkColorType, kUnpremul_SkAlphaType));
343 
344             for (int y = 0; y < planes.fYFull.height(); ++y) {
345                 for (int x = 0; x < planes.fYFull.width(); ++x) {
346 
347                     uint8_t Y = *planes.fYFull.getAddr8(x, y);
348                     uint8_t U = *planes.fUFull.getAddr8(x, y);
349                     uint8_t V = *planes.fVFull.getAddr8(x, y);
350                     uint8_t A = *planes.fAFull.getAddr8(x, y);
351 
352                     // NOT premul!
353                     // V and Y swapped to match RGBA layout
354                     *yuvaFull.getAddr32(x, y) = SkColorSetARGB(A, V, U, Y);
355                 }
356             }
357 
358             resultBMs[nextLayer++] = yuvaFull;
359 
360             yuvaIndices[0].fIndex = 0;
361             yuvaIndices[0].fChannel = SkColorChannel::kR;
362             yuvaIndices[1].fIndex = 0;
363             yuvaIndices[1].fChannel = SkColorChannel::kG;
364             yuvaIndices[2].fIndex = 0;
365             yuvaIndices[2].fChannel = SkColorChannel::kB;
366             yuvaIndices[3].fIndex = 0;
367             yuvaIndices[3].fChannel = SkColorChannel::kA;
368             break;
369         }
370         case kNV12_YUVFormat: {
371             SkBitmap uvQuarter;
372 
373             // There isn't a RG color type. Approx w/ RGBA.
374             uvQuarter.allocPixels(SkImageInfo::Make(planes.fYFull.width()/2,
375                                                     planes.fYFull.height()/2,
376                                                     kRGBA_8888_SkColorType,
377                                                     kUnpremul_SkAlphaType));
378 
379             for (int y = 0; y < planes.fYFull.height()/2; ++y) {
380                 for (int x = 0; x < planes.fYFull.width()/2; ++x) {
381                     uint8_t U = *planes.fUQuarter.getAddr8(x, y);
382                     uint8_t V = *planes.fVQuarter.getAddr8(x, y);
383 
384                     // NOT premul!
385                     // U and 0 swapped to match RGBA layout
386                     *uvQuarter.getAddr32(x, y) = SkColorSetARGB(0, 0, V, U);
387                 }
388             }
389 
390             resultBMs[nextLayer++] = planes.fYFull;
391             resultBMs[nextLayer++] = uvQuarter;
392 
393             yuvaIndices[0].fIndex = 0;
394             yuvaIndices[0].fChannel = SkColorChannel::kA;
395             yuvaIndices[1].fIndex = 1;
396             yuvaIndices[1].fChannel = SkColorChannel::kR;
397             yuvaIndices[2].fIndex = 1;
398             yuvaIndices[2].fChannel = SkColorChannel::kG;
399             break;
400         }
401         case kNV21_YUVFormat: {
402             SkBitmap vuQuarter;
403 
404             // There isn't a RG color type. Approx w/ RGBA.
405             vuQuarter.allocPixels(SkImageInfo::Make(planes.fYFull.width()/2,
406                                                     planes.fYFull.height()/2,
407                                                     kRGBA_8888_SkColorType,
408                                                     kUnpremul_SkAlphaType));
409 
410             for (int y = 0; y < planes.fYFull.height()/2; ++y) {
411                 for (int x = 0; x < planes.fYFull.width()/2; ++x) {
412                     uint8_t U = *planes.fUQuarter.getAddr8(x, y);
413                     uint8_t V = *planes.fVQuarter.getAddr8(x, y);
414 
415                     // NOT premul!
416                     // V and 0 swapped to match RGBA layout
417                     *vuQuarter.getAddr32(x, y) = SkColorSetARGB(0, 0, U, V);
418                 }
419             }
420 
421             resultBMs[nextLayer++] = planes.fYFull;
422             resultBMs[nextLayer++] = vuQuarter;
423 
424             yuvaIndices[0].fIndex = 0;
425             yuvaIndices[0].fChannel = SkColorChannel::kA;
426             yuvaIndices[1].fIndex = 1;
427             yuvaIndices[1].fChannel = SkColorChannel::kG;
428             yuvaIndices[2].fIndex = 1;
429             yuvaIndices[2].fChannel = SkColorChannel::kR;
430             break;
431         }
432         case kI420_YUVFormat:
433             resultBMs[nextLayer++] = planes.fYFull;
434             resultBMs[nextLayer++] = planes.fUQuarter;
435             resultBMs[nextLayer++] = planes.fVQuarter;
436 
437             yuvaIndices[0].fIndex = 0;
438             yuvaIndices[0].fChannel = SkColorChannel::kA;
439             yuvaIndices[1].fIndex = 1;
440             yuvaIndices[1].fChannel = SkColorChannel::kA;
441             yuvaIndices[2].fIndex = 2;
442             yuvaIndices[2].fChannel = SkColorChannel::kA;
443             break;
444         case kYV12_YUVFormat:
445             resultBMs[nextLayer++] = planes.fYFull;
446             resultBMs[nextLayer++] = planes.fVQuarter;
447             resultBMs[nextLayer++] = planes.fUQuarter;
448 
449             yuvaIndices[0].fIndex = 0;
450             yuvaIndices[0].fChannel = SkColorChannel::kA;
451             yuvaIndices[1].fIndex = 2;
452             yuvaIndices[1].fChannel = SkColorChannel::kA;
453             yuvaIndices[2].fIndex = 1;
454             yuvaIndices[2].fChannel = SkColorChannel::kA;
455             break;
456     }
457 
458     if (kAYUV_YUVFormat != yuvFormat) {
459         if (opaque) {
460             yuvaIndices[3].fIndex = -1;
461         } else {
462             resultBMs[nextLayer] = planes.fAFull;
463 
464             yuvaIndices[3].fIndex = nextLayer;
465             yuvaIndices[3].fChannel = SkColorChannel::kA;
466         }
467     }
468 
469 }
470 
look_up(float x1,float y1,const SkBitmap & bm,SkColorChannel channel)471 static uint8_t look_up(float x1, float y1, const SkBitmap& bm, SkColorChannel  channel) {
472     uint8_t result;
473 
474     int x = SkScalarFloorToInt(x1 * bm.width());
475     int y = SkScalarFloorToInt(y1 * bm.height());
476 
477     if (kAlpha_8_SkColorType == bm.colorType()) {
478         SkASSERT(SkColorChannel::kA == channel);
479         result = *bm.getAddr8(x, y);
480     } else {
481         SkASSERT(kRGBA_8888_SkColorType == bm.colorType());
482 
483         switch (channel) {
484             case SkColorChannel::kR:
485                 result = SkColorGetR(bm.getColor(x, y));
486                 break;
487             case SkColorChannel::kG:
488                 result = SkColorGetG(bm.getColor(x, y));
489                 break;
490             case SkColorChannel::kB:
491                 result = SkColorGetB(bm.getColor(x, y));
492                 break;
493             case SkColorChannel::kA:
494                 result = SkColorGetA(bm.getColor(x, y));
495                 break;
496         }
497     }
498 
499     return result;
500 }
501 
502 class YUVGenerator : public SkImageGenerator {
503 public:
YUVGenerator(const SkImageInfo & ii,SkYUVColorSpace yuvColorSpace,SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],SkBitmap bitmaps[SkYUVASizeInfo::kMaxCount])504     YUVGenerator(const SkImageInfo& ii,
505                  SkYUVColorSpace yuvColorSpace,
506                  SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
507                  SkBitmap bitmaps[SkYUVASizeInfo::kMaxCount])
508             : SkImageGenerator(ii)
509             , fYUVColorSpace(yuvColorSpace) {
510         memcpy(fYUVAIndices, yuvaIndices, sizeof(fYUVAIndices));
511 
512         SkAssertResult(SkYUVAIndex::AreValidIndices(fYUVAIndices, &fNumBitmaps));
513         SkASSERT(fNumBitmaps > 0 && fNumBitmaps <= SkYUVASizeInfo::kMaxCount);
514 
515         for (int i = 0; i < fNumBitmaps; ++i) {
516             fYUVBitmaps[i] = bitmaps[i];
517         }
518     }
519 
520 protected:
onGetPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,const Options &)521     bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
522                      const Options&) override {
523 
524         if (kUnknown_SkColorType == fFlattened.colorType()) {
525             fFlattened.allocPixels(this->getInfo());
526 
527             for (int y = 0; y < info.height(); ++y) {
528                 for (int x = 0; x < info.width(); ++x) {
529 
530                     float x1 = (x + 0.5f) / info.width();
531                     float y1 = (y + 0.5f) / info.height();
532 
533                     uint8_t Y = look_up(x1, y1,
534                                         fYUVBitmaps[fYUVAIndices[0].fIndex],
535                                         fYUVAIndices[0].fChannel);
536 
537                     uint8_t U = look_up(x1, y1,
538                                         fYUVBitmaps[fYUVAIndices[1].fIndex],
539                                         fYUVAIndices[1].fChannel);
540 
541 
542                     uint8_t V = look_up(x1, y1,
543                                         fYUVBitmaps[fYUVAIndices[2].fIndex],
544                                         fYUVAIndices[2].fChannel);
545 
546                     uint8_t A = 255;
547                     if (fYUVAIndices[3].fIndex >= 0) {
548                         A = look_up(x1, y1,
549                                     fYUVBitmaps[fYUVAIndices[3].fIndex],
550                                     fYUVAIndices[3].fChannel);
551                     }
552 
553                     // Making premul here.
554                     switch (fYUVColorSpace) {
555                         case kJPEG_SkYUVColorSpace:
556                             *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_jpeg(Y, U, V, A);
557                             break;
558                         case kRec601_SkYUVColorSpace:
559                             *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_601(Y, U, V, A);
560                             break;
561                         case kRec709_SkYUVColorSpace:
562                             *fFlattened.getAddr32(x, y) = convert_yuva_to_rgba_709(Y, U, V, A);
563                             break;
564                     }
565                 }
566             }
567         }
568 
569         return fFlattened.readPixels(info, pixels, rowBytes, 0, 0);
570     }
571 
onQueryYUVA8(SkYUVASizeInfo * size,SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],SkYUVColorSpace * yuvColorSpace) const572     bool onQueryYUVA8(SkYUVASizeInfo* size,
573                       SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
574                       SkYUVColorSpace* yuvColorSpace) const override {
575 
576         memcpy(yuvaIndices, fYUVAIndices, sizeof(fYUVAIndices));
577         *yuvColorSpace = fYUVColorSpace;
578 
579         int i = 0;
580         for ( ; i < fNumBitmaps; ++i) {
581             size->fSizes[i].fWidth = fYUVBitmaps[i].width();
582             size->fSizes[i].fHeight = fYUVBitmaps[i].height();
583             size->fWidthBytes[i] = fYUVBitmaps[i].rowBytes();
584         }
585         for ( ; i < SkYUVASizeInfo::kMaxCount; ++i) {
586             size->fSizes[i].fWidth = 0;
587             size->fSizes[i].fHeight = 0;
588             size->fWidthBytes[i] = 0;
589         }
590 
591         return true;
592     }
593 
onGetYUVA8Planes(const SkYUVASizeInfo &,const SkYUVAIndex[SkYUVAIndex::kIndexCount],void * planes[SkYUVASizeInfo::kMaxCount])594     bool onGetYUVA8Planes(const SkYUVASizeInfo&, const SkYUVAIndex[SkYUVAIndex::kIndexCount],
595                           void* planes[SkYUVASizeInfo::kMaxCount]) override {
596         for (int i = 0; i < fNumBitmaps; ++i) {
597             planes[i] = fYUVBitmaps[i].getPixels();
598         }
599         return true;
600     }
601 
602 private:
603     SkYUVColorSpace fYUVColorSpace;
604     SkYUVAIndex     fYUVAIndices[SkYUVAIndex::kIndexCount];
605     int             fNumBitmaps;
606     SkBitmap        fYUVBitmaps[SkYUVASizeInfo::kMaxCount];
607     SkBitmap        fFlattened;
608 
609 };
610 
make_yuv_gen_image(const SkImageInfo & ii,SkYUVColorSpace yuvColorSpace,SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],SkBitmap bitmaps[])611 static sk_sp<SkImage> make_yuv_gen_image(const SkImageInfo& ii,
612                                          SkYUVColorSpace yuvColorSpace,
613                                          SkYUVAIndex yuvaIndices[SkYUVAIndex::kIndexCount],
614                                          SkBitmap bitmaps[]) {
615     std::unique_ptr<SkImageGenerator> gen(new YUVGenerator(ii, yuvColorSpace,
616                                                            yuvaIndices, bitmaps));
617 
618     return SkImage::MakeFromGenerator(std::move(gen));
619 }
620 
draw_col_label(SkCanvas * canvas,int x,int yuvColorSpace,bool opaque)621 static void draw_col_label(SkCanvas* canvas, int x, int yuvColorSpace, bool opaque) {
622     static const char* kYUVColorSpaceNames[] = { "JPEG", "601", "709" };
623     GR_STATIC_ASSERT(SK_ARRAY_COUNT(kYUVColorSpaceNames) == kLastEnum_SkYUVColorSpace+1);
624 
625     SkPaint paint;
626     SkFont font(sk_tool_utils::create_portable_typeface(nullptr, SkFontStyle::Bold()), 16);
627     font.setEdging(SkFont::Edging::kAlias);
628 
629     SkRect textRect;
630     SkString colLabel;
631 
632     colLabel.printf("%s", kYUVColorSpaceNames[yuvColorSpace]);
633     font.measureText(colLabel.c_str(), colLabel.size(), kUTF8_SkTextEncoding, &textRect);
634     int y = textRect.height();
635 
636     SkTextUtils::DrawString(canvas, colLabel.c_str(), x, y, font, paint, SkTextUtils::kCenter_Align);
637 
638     colLabel.printf("%s", opaque ? "Opaque" : "Transparent");
639 
640     font.measureText(colLabel.c_str(), colLabel.size(), kUTF8_SkTextEncoding, &textRect);
641     y += textRect.height();
642 
643     SkTextUtils::DrawString(canvas, colLabel.c_str(), x, y, font, paint, SkTextUtils::kCenter_Align);
644 }
645 
draw_row_label(SkCanvas * canvas,int y,int yuvFormat)646 static void draw_row_label(SkCanvas* canvas, int y, int yuvFormat) {
647     static const char* kYUVFormatNames[] = { "AYUV", "NV12", "NV21", "I420", "YV12" };
648     GR_STATIC_ASSERT(SK_ARRAY_COUNT(kYUVFormatNames) == kLast_YUVFormat+1);
649 
650     SkPaint paint;
651     SkFont font(sk_tool_utils::create_portable_typeface(nullptr, SkFontStyle::Bold()), 16);
652     font.setEdging(SkFont::Edging::kAlias);
653 
654     SkRect textRect;
655     SkString rowLabel;
656 
657     rowLabel.printf("%s", kYUVFormatNames[yuvFormat]);
658     font.measureText(rowLabel.c_str(), rowLabel.size(), kUTF8_SkTextEncoding, &textRect);
659     y += kTileWidthHeight/2 + textRect.height()/2;
660 
661     canvas->drawString(rowLabel, 0, y, font, paint);
662 }
663 
create_yuva_texture(GrGpu * gpu,const SkBitmap & bm,SkYUVAIndex yuvaIndices[4],int texIndex)664 static GrBackendTexture create_yuva_texture(GrGpu* gpu, const SkBitmap& bm,
665                                             SkYUVAIndex yuvaIndices[4], int texIndex) {
666     SkASSERT(texIndex >= 0 && texIndex <= 3);
667     int channelCount = 0;
668     for (int i = 0; i < SkYUVAIndex::kIndexCount; ++i) {
669         if (yuvaIndices[i].fIndex == texIndex) {
670             ++channelCount;
671         }
672     }
673     // Need to create an RG texture for two-channel planes
674     GrBackendTexture tex;
675     if (2 == channelCount) {
676         SkASSERT(kRGBA_8888_SkColorType == bm.colorType());
677         SkAutoTMalloc<char> pixels(2 * bm.width()*bm.height());
678         char* currPixel = pixels;
679         for (int y = 0; y < bm.height(); ++y) {
680             for (int x = 0; x < bm.width(); ++x) {
681                 SkColor color = bm.getColor(x, y);
682                 currPixel[0] = SkColorGetR(color);
683                 currPixel[1] = SkColorGetG(color);
684                 currPixel += 2;
685             }
686         }
687         tex = gpu->createTestingOnlyBackendTexture(
688             pixels,
689             bm.width(),
690             bm.height(),
691             GrColorType::kRG_88,
692             false,
693             GrMipMapped::kNo,
694             2*bm.width());
695     }
696     if (!tex.isValid()) {
697         tex = gpu->createTestingOnlyBackendTexture(
698             bm.getPixels(),
699             bm.width(),
700             bm.height(),
701             bm.colorType(),
702             false,
703             GrMipMapped::kNo,
704             bm.rowBytes());
705     }
706     return tex;
707 }
708 
709 namespace skiagm {
710 
711 // This GM creates an opaque and transparent bitmap, extracts the planes and then recombines
712 // them into various YUV formats. It then renders the results in the grid:
713 //
714 //                   JPEG                   601                     709
715 //        Transparent  Opaque       Transparent  Opaque        Transparent  Opaque
716 // AYUV
717 // NV12
718 // NV21
719 // I420
720 // YV12
721 class WackyYUVFormatsGM : public GM {
722 public:
WackyYUVFormatsGM(bool useTargetColorSpace)723     WackyYUVFormatsGM(bool useTargetColorSpace) : fUseTargetColorSpace(useTargetColorSpace) {
724         this->setBGColor(0xFFCCCCCC);
725     }
726 
727 protected:
728 
onShortName()729     SkString onShortName() override {
730         SkString name("wacky_yuv_formats");
731         if (fUseTargetColorSpace) {
732             name += "_cs";
733         }
734         return name;
735     }
736 
onISize()737     SkISize onISize() override {
738         int numCols = 2 * (kLastEnum_SkYUVColorSpace + 1); // opacity x color-space
739         int numRows = 1 + (kLast_YUVFormat + 1);  // origin + # yuv formats
740         return SkISize::Make(kLabelWidth  + numCols * (kTileWidthHeight + kPad),
741                              kLabelHeight + numRows * (kTileWidthHeight + kPad));
742     }
743 
onOnceBeforeDraw()744     void onOnceBeforeDraw() override {
745         SkPoint origin = { kTileWidthHeight/2.0f, kTileWidthHeight/2.0f };
746         float outerRadius = kTileWidthHeight/2.0f - 20.0f;
747         float innerRadius = 20.0f;
748 
749         {
750             // transparent
751             SkTDArray<SkRect> circles;
752             SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 5, &circles);
753             fOriginalBMs[0] = make_bitmap(path, circles, false);
754         }
755 
756         {
757             // opaque
758             SkTDArray<SkRect> circles;
759             SkPath path = create_splat(origin, innerRadius, outerRadius, 1.0f, 7, &circles);
760             fOriginalBMs[1] = make_bitmap(path, circles, true);
761         }
762 
763         if (fUseTargetColorSpace) {
764             fTargetColorSpace = SkColorSpace::MakeSRGB()->makeColorSpin();
765         }
766     }
767 
createImages(GrContext * context)768     void createImages(GrContext* context) {
769         int counter = 0;
770         for (bool opaque : { false, true }) {
771             for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
772                 PlaneData planes;
773                 extract_planes(fOriginalBMs[opaque], (SkYUVColorSpace) cs, &planes);
774 
775                 for (int format = kAYUV_YUVFormat; format <= kLast_YUVFormat; ++format) {
776                     SkBitmap resultBMs[4];
777                     SkYUVAIndex yuvaIndices[4];
778                     create_YUV(planes, (YUVFormat) format, resultBMs, yuvaIndices, opaque);
779                     int numTextures;
780                     if (!SkYUVAIndex::AreValidIndices(yuvaIndices, &numTextures)) {
781                         continue;
782                     }
783 
784                     if (context) {
785                         if (context->abandoned()) {
786                             return;
787                         }
788 
789                         GrGpu* gpu = context->contextPriv().getGpu();
790                         if (!gpu) {
791                             return;
792                         }
793 
794                         GrBackendTexture yuvaTextures[4];
795                         SkPixmap yuvaPixmaps[4];
796 
797                         for (int i = 0; i < numTextures; ++i) {
798                             yuvaTextures[i] = create_yuva_texture(gpu, resultBMs[i],
799                                                                   yuvaIndices, i);
800                             if (yuvaTextures[i].isValid()) {
801                                 fBackendTextures.push_back(yuvaTextures[i]);
802                             }
803                             yuvaPixmaps[i] = resultBMs[i].pixmap();
804                         }
805 
806                         int counterMod = counter % 3;
807                         switch (counterMod) {
808                         case 0:
809                             fImages[opaque][cs][format] = SkImage::MakeFromYUVATexturesCopy(
810                                 context,
811                                 (SkYUVColorSpace)cs,
812                                 yuvaTextures,
813                                 yuvaIndices,
814                                 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
815                                 kTopLeft_GrSurfaceOrigin);
816                             break;
817                         case 1:
818                             fImages[opaque][cs][format] = SkImage::MakeFromYUVATextures(
819                                 context,
820                                 (SkYUVColorSpace)cs,
821                                 yuvaTextures,
822                                 yuvaIndices,
823                                 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
824                                 kTopLeft_GrSurfaceOrigin);
825                             break;
826                         case 2:
827                         default:
828                             fImages[opaque][cs][format] = SkImage::MakeFromYUVAPixmaps(
829                                 context,
830                                 (SkYUVColorSpace)cs,
831                                 yuvaPixmaps,
832                                 yuvaIndices,
833                                 { fOriginalBMs[opaque].width(), fOriginalBMs[opaque].height() },
834                                 kTopLeft_GrSurfaceOrigin, true);
835                             break;
836                         }
837                         ++counter;
838                     } else {
839                         fImages[opaque][cs][format] = make_yuv_gen_image(
840                                                                 fOriginalBMs[opaque].info(),
841                                                                 (SkYUVColorSpace) cs,
842                                                                 yuvaIndices,
843                                                                 resultBMs);
844                     }
845                 }
846             }
847         }
848     }
849 
onDraw(SkCanvas * canvas)850     void onDraw(SkCanvas* canvas) override {
851         this->createImages(canvas->getGrContext());
852 
853         int x = kLabelWidth;
854         for (int cs = kJPEG_SkYUVColorSpace; cs <= kLastEnum_SkYUVColorSpace; ++cs) {
855             for (int opaque : { 0, 1 }) {
856                 int y = kLabelHeight;
857 
858                 draw_col_label(canvas, x+kTileWidthHeight/2, cs, opaque);
859 
860                 canvas->drawBitmap(fOriginalBMs[opaque], x, y);
861                 y += kTileWidthHeight + kPad;
862 
863                 for (int format = kAYUV_YUVFormat; format <= kLast_YUVFormat; ++format) {
864                     draw_row_label(canvas, y, format);
865                     if (fUseTargetColorSpace && fImages[opaque][cs][format]) {
866                         sk_sp<SkImage> csImage =
867                             fImages[opaque][cs][format]->makeColorSpace(fTargetColorSpace);
868                         canvas->drawImage(csImage, x, y);
869                     } else {
870                         canvas->drawImage(fImages[opaque][cs][format], x, y);
871                     }
872                     y += kTileWidthHeight + kPad;
873                 }
874 
875                 x += kTileWidthHeight + kPad;
876             }
877         }
878         if (auto context = canvas->getGrContext()) {
879             if (!context->abandoned()) {
880                 context->flush();
881                 GrGpu* gpu = context->contextPriv().getGpu();
882                 SkASSERT(gpu);
883                 gpu->testingOnly_flushGpuAndSync();
884                 for (const auto& tex : fBackendTextures) {
885                     gpu->deleteTestingOnlyBackendTexture(tex);
886                 }
887                 fBackendTextures.reset();
888             }
889         }
890         SkASSERT(!fBackendTextures.count());
891     }
892 
893 private:
894     SkBitmap fOriginalBMs[2];
895     sk_sp<SkImage> fImages[2][kLastEnum_SkYUVColorSpace + 1][kLast_YUVFormat + 1];
896     SkTArray<GrBackendTexture> fBackendTextures;
897     bool fUseTargetColorSpace;
898     sk_sp<SkColorSpace> fTargetColorSpace;
899 
900     typedef GM INHERITED;
901 };
902 
903 //////////////////////////////////////////////////////////////////////////////
904 
905 DEF_GM(return new WackyYUVFormatsGM(false);)
906 DEF_GM(return new WackyYUVFormatsGM(true);)
907 }
908