Add fixes & test for isConfigTexturable and isConfigRenderable
[platform/upstream/libSkiaSharp.git] / gm / reveal.cpp
1 /*
2  * Copyright 2016 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 #include "SkAnimTimer.h"
11 #include "SkBlurMaskFilter.h"
12 #include "SkGaussianEdgeShader.h"
13 #include "SkRRectsGaussianEdgeMaskFilter.h"
14 #include "SkPath.h"
15 #include "SkPathOps.h"
16 #include "SkRRect.h"
17 #include "SkStroke.h"
18
19 constexpr int kNumCols = 2;
20 constexpr int kNumRows = 5;
21 constexpr int kCellSize = 128;
22 constexpr SkScalar kPad = 8.0f;
23 constexpr SkScalar kInitialBlurRadius = 8.0f;
24 constexpr SkScalar kPeriod = 8.0f;
25 constexpr int kClipOffset = 32;
26
27 ///////////////////////////////////////////////////////////////////////////////////////////////////
28
29 class Object {
30 public:
31     virtual ~Object() {}
32     // When it returns true, this call will have placed a device-space _circle, rect or 
33     // simple circular_ RRect in "rr"
34     virtual bool asDevSpaceRRect(const SkMatrix& ctm, SkRRect* rr) const = 0;
35     virtual SkPath asPath(SkScalar inset) const = 0;
36     virtual void draw(SkCanvas* canvas, const SkPaint& paint) const = 0;
37     virtual void clip(SkCanvas* canvas) const = 0;
38     virtual bool contains(const SkRect& r) const = 0;
39     virtual const SkRect& bounds() const = 0;
40 };
41
42 typedef Object* (*PFMakeMthd)(const SkRect& r);
43
44 class RRect : public Object {
45 public:
46     RRect(const SkRect& r) {
47         fRRect = SkRRect::MakeRectXY(r, 4*kPad, 4*kPad);
48     }
49
50     bool asDevSpaceRRect(const SkMatrix& ctm, SkRRect* rr) const override {
51         if (!ctm.isSimilarity()) { // the corners have to remain circular
52             return false;
53         }
54
55         SkScalar scales[2];
56         if (!ctm.getMinMaxScales(scales)) {
57             return false;
58         }
59
60         SkASSERT(SkScalarNearlyEqual(scales[0], scales[1]));
61
62         SkRect devRect;
63         ctm.mapRect(&devRect, fRRect.rect());
64
65         SkScalar scaledRad = scales[0] * fRRect.getSimpleRadii().fX;
66
67         *rr = SkRRect::MakeRectXY(devRect, scaledRad, scaledRad);
68         return true;
69     }
70
71     SkPath asPath(SkScalar inset) const override {
72         SkRRect tmp = fRRect;
73         tmp.inset(inset, inset);
74         SkPath p;
75         p.addRRect(tmp);
76         return p;
77     }
78
79     void draw(SkCanvas* canvas, const SkPaint& paint) const override {
80         canvas->drawRRect(fRRect, paint);
81     }
82
83     void clip(SkCanvas* canvas) const override {
84         canvas->clipRRect(fRRect);
85     }
86
87     bool contains(const SkRect& r) const override {
88         return fRRect.contains(r);
89     }
90
91     const SkRect& bounds() const override {
92         return fRRect.getBounds();
93     }
94
95     static Object* Make(const SkRect& r) {
96         return new RRect(r);
97     }
98
99 private:
100     SkRRect  fRRect;
101 };
102
103 class StrokedRRect : public Object {
104 public:
105     StrokedRRect(const SkRect& r) {
106         fRRect = SkRRect::MakeRectXY(r, 2*kPad, 2*kPad);
107         fStrokedBounds = r.makeOutset(kPad, kPad);
108     }
109
110     bool asDevSpaceRRect(const SkMatrix& ctm, SkRRect* rr) const override {
111         return false;
112     }
113
114     SkPath asPath(SkScalar inset) const override {
115         SkRRect tmp = fRRect;
116         tmp.inset(inset, inset);
117
118         // In this case we want the outline of the stroked rrect
119         SkPaint paint;
120         paint.setAntiAlias(true);
121         paint.setStyle(SkPaint::kStroke_Style);
122         paint.setStrokeWidth(kPad);
123
124         SkPath p, stroked;
125         p.addRRect(tmp);
126         SkStroke stroke(paint);
127         stroke.strokePath(p, &stroked);
128         return stroked;
129     }
130
131     void draw(SkCanvas* canvas, const SkPaint& paint) const override {
132         SkPaint stroke(paint);
133         stroke.setStyle(SkPaint::kStroke_Style);
134         stroke.setStrokeWidth(kPad);
135
136         canvas->drawRRect(fRRect, stroke);
137     }
138
139     void clip(SkCanvas* canvas) const override {
140         canvas->clipPath(this->asPath(0.0f));
141     }
142
143     bool contains(const SkRect& r) const override {
144         return false;
145     }
146
147     const SkRect& bounds() const override {
148         return fStrokedBounds;
149     }
150
151     static Object* Make(const SkRect& r) {
152         return new StrokedRRect(r);
153     }
154
155 private:
156     SkRRect  fRRect;
157     SkRect   fStrokedBounds;
158 };
159
160 class Oval : public Object {
161 public:
162     Oval(const SkRect& r) {
163         fRRect = SkRRect::MakeOval(r);
164     }
165
166     bool asDevSpaceRRect(const SkMatrix& ctm, SkRRect* rr) const override {
167         if (!ctm.isSimilarity()) { // circles have to remain circles
168             return false;
169         }
170
171         SkRect devRect;
172         ctm.mapRect(&devRect, fRRect.rect());
173         *rr = SkRRect::MakeOval(devRect);
174         return true;
175     }
176
177     SkPath asPath(SkScalar inset) const override { 
178         SkRRect tmp = fRRect;
179         tmp.inset(inset, inset);
180
181         SkPath p;
182         p.addRRect(tmp);
183         return p;
184     }
185
186     void draw(SkCanvas* canvas, const SkPaint& paint) const override {
187         canvas->drawRRect(fRRect, paint);
188     }
189
190     void clip(SkCanvas* canvas) const override {
191         canvas->clipRRect(fRRect);
192     }
193
194     bool contains(const SkRect& r) const override {
195         return fRRect.contains(r);
196     }
197
198     const SkRect& bounds() const override {
199         return fRRect.getBounds();
200     }
201
202     static Object* Make(const SkRect& r) {
203         return new Oval(r);
204     }
205
206 private:
207     SkRRect  fRRect;
208 };
209
210 class Rect : public Object {
211 public:
212     Rect(const SkRect& r) : fRect(r) { }
213
214     bool asDevSpaceRRect(const SkMatrix& ctm, SkRRect* rr) const override {
215         if (!ctm.rectStaysRect()) {
216             return false;
217         }
218
219         SkRect devRect;
220         ctm.mapRect(&devRect, fRect);
221         *rr = SkRRect::MakeRect(devRect);
222         return true;
223     }
224
225     SkPath asPath(SkScalar inset) const override { 
226         SkRect tmp = fRect;
227         tmp.inset(inset, inset);
228
229         SkPath p;
230         p.addRect(tmp);
231         return p;
232     }
233
234     void draw(SkCanvas* canvas, const SkPaint& paint) const override {
235         canvas->drawRect(fRect, paint);
236     }
237
238     void clip(SkCanvas* canvas) const override {
239         canvas->clipRect(fRect);
240     }
241
242     bool contains(const SkRect& r) const override {
243         return fRect.contains(r);
244     }
245
246     const SkRect& bounds() const override {
247         return fRect;
248     }
249
250     static Object* Make(const SkRect& r) {
251         return new Rect(r);
252     }
253
254 private:
255     SkRect  fRect;
256 };
257
258 class Pentagon : public Object {
259 public:
260     Pentagon(const SkRect& r) {
261         SkPoint points[5] = {
262             {  0.000000f, -1.000000f },
263             { -0.951056f, -0.309017f },
264             { -0.587785f,  0.809017f },
265             {  0.587785f,  0.809017f },
266             {  0.951057f, -0.309017f },
267         };
268
269         SkScalar height = r.height()/2.0f;
270         SkScalar width = r.width()/2.0f;
271
272         fPath.moveTo(r.centerX() + points[0].fX * width, r.centerY() + points[0].fY * height);
273         fPath.lineTo(r.centerX() + points[1].fX * width, r.centerY() + points[1].fY * height);
274         fPath.lineTo(r.centerX() + points[2].fX * width, r.centerY() + points[2].fY * height);
275         fPath.lineTo(r.centerX() + points[3].fX * width, r.centerY() + points[3].fY * height);
276         fPath.lineTo(r.centerX() + points[4].fX * width, r.centerY() + points[4].fY * height);
277         fPath.close();
278     }
279
280     bool asDevSpaceRRect(const SkMatrix& ctm, SkRRect* rr) const override {
281         return false;
282     }
283
284     SkPath asPath(SkScalar inset) const override { return fPath; }
285
286     void draw(SkCanvas* canvas, const SkPaint& paint) const override {
287         canvas->drawPath(fPath, paint);
288     }
289
290     void clip(SkCanvas* canvas) const override {
291         canvas->clipPath(this->asPath(0.0f));
292     }
293
294     bool contains(const SkRect& r) const override {
295         return false;
296     }
297
298     const SkRect& bounds() const override {
299         return fPath.getBounds();
300     }
301
302     static Object* Make(const SkRect& r) {
303         return new Pentagon(r);
304     }
305
306 private:
307     SkPath fPath;
308 };
309
310 ///////////////////////////////////////////////////////////////////////////////////////////////////
311 namespace skiagm {
312
313 // This GM attempts to mimic Android's reveal animation
314 class RevealGM : public GM {
315 public:
316     enum Mode {
317         kGaussianEdge_Mode,
318         kBlurMask_Mode,
319         kRRectsGaussianEdge_Mode,
320
321         kLast_Mode = kRRectsGaussianEdge_Mode
322     };
323     static const int kModeCount = kLast_Mode + 1;
324
325     enum CoverageGeom {
326         kRect_CoverageGeom,
327         kRRect_CoverageGeom,
328         kDRRect_CoverageGeom,
329         kPath_CoverageGeom,
330
331         kLast_CoverageGeom = kPath_CoverageGeom
332     };
333     static const int kCoverageGeomCount = kLast_CoverageGeom + 1;
334
335     RevealGM()
336         : fFraction(0.5f)
337         , fMode(kRRectsGaussianEdge_Mode)
338         , fPause(false)
339         , fBlurRadius(kInitialBlurRadius)
340         , fCoverageGeom(kRect_CoverageGeom) {
341         this->setBGColor(sk_tool_utils::color_to_565(0xFFCCCCCC));
342     }
343
344 protected:
345     bool runAsBench() const override { return true; }
346
347     SkString onShortName() override {
348         return SkString("reveal");
349     }
350
351     SkISize onISize() override {
352         return SkISize::Make(kNumCols * kCellSize, kNumRows * kCellSize);
353     }
354
355     void onDraw(SkCanvas* canvas) override {
356         PFMakeMthd clipMakes[kNumCols] = { Oval::Make, Rect::Make };
357         PFMakeMthd drawMakes[kNumRows] = {
358             RRect::Make, StrokedRRect::Make, Oval::Make, Rect::Make, Pentagon::Make
359         };
360
361         SkPaint strokePaint;
362         strokePaint.setStyle(SkPaint::kStroke_Style);
363         strokePaint.setStrokeWidth(0.0f);
364
365         for (int y = 0; y < kNumRows; ++y) {
366             for (int x = 0; x < kNumCols; ++x) {
367                 SkRect cell = SkRect::MakeXYWH(SkIntToScalar(x*kCellSize),
368                                                SkIntToScalar(y*kCellSize),
369                                                SkIntToScalar(kCellSize),
370                                                SkIntToScalar(kCellSize));
371
372                 canvas->save();
373                 canvas->clipRect(cell);
374
375                 cell.inset(kPad, kPad);
376                 SkPoint clipCenter = SkPoint::Make(cell.centerX() - kClipOffset,
377                                                    cell.centerY() + kClipOffset);
378                 SkScalar curSize = kCellSize * fFraction;
379                 const SkRect clipRect = SkRect::MakeLTRB(clipCenter.fX - curSize,
380                                                          clipCenter.fY - curSize,
381                                                          clipCenter.fX + curSize,
382                                                          clipCenter.fY + curSize);
383
384                 std::unique_ptr<Object> clipObj((*clipMakes[x])(clipRect));
385                 std::unique_ptr<Object> drawObj((*drawMakes[y])(cell));
386
387                 // The goal is to replace this clipped draw (which clips the 
388                 // shadow) with a draw using the geometric clip
389                 if (kGaussianEdge_Mode == fMode) {
390                     canvas->save();
391                         clipObj->clip(canvas);
392
393                         // Draw with GaussianEdgeShader
394                         SkPaint paint;
395                         paint.setAntiAlias(true);
396                         // G channel is an F6.2 radius
397                         int iBlurRad = (int)(4.0f * fBlurRadius);
398                         paint.setColor(SkColorSetARGB(255, iBlurRad >> 8, iBlurRad & 0xFF, 0));
399                         paint.setShader(SkGaussianEdgeShader::Make());
400                         drawObj->draw(canvas, paint);
401                     canvas->restore();
402                 } else if (kBlurMask_Mode == fMode) {
403                     SkPath clippedPath;
404
405                     SkScalar sigma = fBlurRadius / 4.0f;
406
407                     if (clipObj->contains(drawObj->bounds())) {
408                         clippedPath = drawObj->asPath(2.0f*sigma);
409                     } else {
410                         SkPath drawnPath = drawObj->asPath(2.0f*sigma);
411                         SkPath clipPath  = clipObj->asPath(2.0f*sigma);
412
413                         SkAssertResult(Op(clipPath, drawnPath, kIntersect_SkPathOp, &clippedPath));
414                     }
415
416                     SkPaint blurPaint;
417                     blurPaint.setAntiAlias(true);
418                     blurPaint.setMaskFilter(SkBlurMaskFilter::Make(kNormal_SkBlurStyle, sigma));
419                     canvas->drawPath(clippedPath, blurPaint);
420                 } else {
421                     SkASSERT(kRRectsGaussianEdge_Mode == fMode);
422
423                     SkRect cover = drawObj->bounds();
424                     SkAssertResult(cover.intersect(clipObj->bounds()));
425
426                     SkPaint paint;
427
428                     SkRRect devSpaceClipRR, devSpaceDrawnRR;
429
430                     if (clipObj->asDevSpaceRRect(canvas->getTotalMatrix(), &devSpaceClipRR) &&
431                         drawObj->asDevSpaceRRect(canvas->getTotalMatrix(), &devSpaceDrawnRR)) {
432                         paint.setMaskFilter(SkRRectsGaussianEdgeMaskFilter::Make(devSpaceClipRR,
433                                                                                  devSpaceDrawnRR,
434                                                                                  fBlurRadius));
435                     }
436
437                     strokePaint.setColor(SK_ColorBLUE);
438
439                     switch (fCoverageGeom) {
440                         case kRect_CoverageGeom:
441                             canvas->drawRect(cover, paint);
442                             canvas->drawRect(cover, strokePaint);
443                             break;
444                         case kRRect_CoverageGeom: {
445                             const SkRRect rrect = SkRRect::MakeRectXY(
446                                                                     cover.makeOutset(10.0f, 10.0f),
447                                                                     10.0f, 10.0f);
448                             canvas->drawRRect(rrect, paint);
449                             canvas->drawRRect(rrect, strokePaint);
450                             break;
451                         }
452                         case kDRRect_CoverageGeom: {
453                             const SkRRect inner = SkRRect::MakeRectXY(cover.makeInset(10.0f, 10.0f),
454                                                                       10.0f, 10.0f);
455                             const SkRRect outer = SkRRect::MakeRectXY(
456                                                                     cover.makeOutset(10.0f, 10.0f),
457                                                                     10.0f, 10.0f);
458                             canvas->drawDRRect(outer, inner, paint);
459                             canvas->drawDRRect(outer, inner, strokePaint);
460                             break;
461                         }
462                         case kPath_CoverageGeom: {
463                             SkPath path;
464                             path.moveTo(cover.fLeft, cover.fTop);
465                             path.lineTo(cover.centerX(), cover.centerY());
466                             path.lineTo(cover.fRight, cover.fTop);
467                             path.lineTo(cover.fRight, cover.fBottom);
468                             path.lineTo(cover.centerX(), cover.centerY());
469                             path.lineTo(cover.fLeft, cover.fBottom);
470                             path.close();
471                             canvas->drawPath(path, paint);
472                             canvas->drawPath(path, strokePaint);
473                             break;
474                         }
475                     }
476                 }
477
478                 // Draw the clip and draw objects for reference
479                 strokePaint.setColor(SK_ColorRED);
480                 canvas->drawPath(drawObj->asPath(0.0f), strokePaint);
481                 strokePaint.setColor(SK_ColorGREEN);
482                 canvas->drawPath(clipObj->asPath(0.0f), strokePaint);
483
484                 canvas->restore();
485             }
486         }
487     }
488
489     bool onHandleKey(SkUnichar uni) override {
490         switch (uni) {
491             case 'C':
492                 fMode = (Mode)((fMode + 1) % kModeCount);
493                 return true;
494             case '+':
495                 fBlurRadius += 1.0f;
496                 return true;
497             case '-':
498                 fBlurRadius = SkTMax(1.0f, fBlurRadius - 1.0f);
499                 return true;
500             case 'p':
501                 fPause = !fPause;
502                 return true;
503             case 'G':
504                 fCoverageGeom = (CoverageGeom) ((fCoverageGeom+1) % kCoverageGeomCount);
505                 return true;
506         }        
507     
508         return false;
509     }
510
511     bool onAnimate(const SkAnimTimer& timer) override {
512         if (!fPause) {
513             fFraction = timer.pingPong(kPeriod, 0.0f, 0.0f, 1.0f);
514         }
515         return true;
516     }
517
518 private:
519     SkScalar     fFraction;
520     Mode         fMode;
521     bool         fPause;
522     float        fBlurRadius;
523     CoverageGeom fCoverageGeom;
524
525     typedef GM INHERITED;
526 };
527
528 //////////////////////////////////////////////////////////////////////////////
529
530 DEF_GM(return new RevealGM;)
531 }