• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2012 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/gm.h"
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkBlurTypes.h"
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkColor.h"
13 #include "include/core/SkFont.h"
14 #include "include/core/SkMaskFilter.h"
15 #include "include/core/SkPaint.h"
16 #include "include/core/SkPath.h"
17 #include "include/core/SkPixmap.h"
18 #include "include/core/SkPoint.h"
19 #include "include/core/SkRRect.h"
20 #include "include/core/SkRect.h"
21 #include "include/core/SkScalar.h"
22 #include "include/core/SkSize.h"
23 #include "include/core/SkString.h"
24 #include "include/core/SkTypes.h"
25 #include "include/effects/SkGradientShader.h"
26 #include "tools/ToolUtils.h"
27 #include "tools/fonts/FontToolUtils.h"
28 
29 typedef void (*InsetProc)(const SkRRect&, SkScalar dx, SkScalar dy, SkRRect*);
30 
inset0(const SkRRect & src,SkScalar dx,SkScalar dy,SkRRect * dst)31 static void inset0(const SkRRect& src, SkScalar dx, SkScalar dy, SkRRect* dst) {
32     SkRect r = src.rect();
33 
34     r.inset(dx, dy);
35     if (r.isEmpty()) {
36         dst->setEmpty();
37         return;
38     }
39 
40     SkVector radii[4];
41     for (int i = 0; i < 4; ++i) {
42         radii[i] = src.radii((SkRRect::Corner)i);
43     }
44     for (int i = 0; i < 4; ++i) {
45         radii[i].fX -= dx;
46         radii[i].fY -= dy;
47     }
48     dst->setRectRadii(r, radii);
49 }
50 
inset1(const SkRRect & src,SkScalar dx,SkScalar dy,SkRRect * dst)51 static void inset1(const SkRRect& src, SkScalar dx, SkScalar dy, SkRRect* dst) {
52     SkRect r = src.rect();
53 
54     r.inset(dx, dy);
55     if (r.isEmpty()) {
56         dst->setEmpty();
57         return;
58     }
59 
60     SkVector radii[4];
61     for (int i = 0; i < 4; ++i) {
62         radii[i] = src.radii((SkRRect::Corner)i);
63     }
64     dst->setRectRadii(r, radii);
65 }
66 
inset2(const SkRRect & src,SkScalar dx,SkScalar dy,SkRRect * dst)67 static void inset2(const SkRRect& src, SkScalar dx, SkScalar dy, SkRRect* dst) {
68     SkRect r = src.rect();
69 
70     r.inset(dx, dy);
71     if (r.isEmpty()) {
72         dst->setEmpty();
73         return;
74     }
75 
76     SkVector radii[4];
77     for (int i = 0; i < 4; ++i) {
78         radii[i] = src.radii((SkRRect::Corner)i);
79     }
80     for (int i = 0; i < 4; ++i) {
81         if (radii[i].fX) {
82             radii[i].fX -= dx;
83         }
84         if (radii[i].fY) {
85             radii[i].fY -= dy;
86         }
87     }
88     dst->setRectRadii(r, radii);
89 }
90 
prop(SkScalar radius,SkScalar newSize,SkScalar oldSize)91 static SkScalar prop(SkScalar radius, SkScalar newSize, SkScalar oldSize) {
92     return newSize * radius / oldSize;
93 }
94 
inset3(const SkRRect & src,SkScalar dx,SkScalar dy,SkRRect * dst)95 static void inset3(const SkRRect& src, SkScalar dx, SkScalar dy, SkRRect* dst) {
96     SkRect r = src.rect();
97 
98     r.inset(dx, dy);
99     if (r.isEmpty()) {
100         dst->setEmpty();
101         return;
102     }
103 
104     SkVector radii[4];
105     for (int i = 0; i < 4; ++i) {
106         radii[i] = src.radii((SkRRect::Corner)i);
107     }
108     for (int i = 0; i < 4; ++i) {
109         radii[i].fX = prop(radii[i].fX, r.width(), src.rect().width());
110         radii[i].fY = prop(radii[i].fY, r.height(), src.rect().height());
111     }
112     dst->setRectRadii(r, radii);
113 }
114 
draw_rrect_color(SkCanvas * canvas,const SkRRect & rrect)115 static void draw_rrect_color(SkCanvas* canvas, const SkRRect& rrect) {
116     SkPaint paint;
117     paint.setAntiAlias(true);
118     paint.setStyle(SkPaint::kStroke_Style);
119 
120     if (rrect.isRect()) {
121         paint.setColor(SK_ColorRED);
122     } else if (rrect.isOval()) {
123         paint.setColor(ToolUtils::color_to_565(0xFF008800));
124     } else if (rrect.isSimple()) {
125         paint.setColor(SK_ColorBLUE);
126     } else {
127         paint.setColor(SK_ColorBLACK);
128     }
129     canvas->drawRRect(rrect, paint);
130 }
131 
drawrr(SkCanvas * canvas,const SkRRect & rrect,InsetProc proc)132 static void drawrr(SkCanvas* canvas, const SkRRect& rrect, InsetProc proc) {
133     SkRRect rr;
134     for (SkScalar d = -30; d <= 30; d += 5) {
135         proc(rrect, d, d, &rr);
136         draw_rrect_color(canvas, rr);
137     }
138 }
139 
140 class RRectGM : public skiagm::GM {
141 public:
RRectGM()142     RRectGM() {}
143 
144 protected:
getName() const145     SkString getName() const override { return SkString("rrect"); }
146 
getISize()147     SkISize getISize() override { return SkISize::Make(820, 710); }
148 
onDraw(SkCanvas * canvas)149     void onDraw(SkCanvas* canvas) override {
150         constexpr InsetProc insetProcs[] = {
151             inset0, inset1, inset2, inset3
152         };
153 
154         SkRRect rrect[4];
155         SkRect r = { 0, 0, 120, 100 };
156         SkVector radii[4] = {
157             { 0, 0 }, { 30, 1 }, { 10, 40 }, { 40, 40 }
158         };
159 
160         rrect[0].setRect(r);
161         rrect[1].setOval(r);
162         rrect[2].setRectXY(r, 20, 20);
163         rrect[3].setRectRadii(r, radii);
164 
165         canvas->translate(50.5f, 50.5f);
166         for (size_t j = 0; j < std::size(insetProcs); ++j) {
167             canvas->save();
168             for (size_t i = 0; i < std::size(rrect); ++i) {
169                 drawrr(canvas, rrect[i], insetProcs[j]);
170                 canvas->translate(200, 0);
171             }
172             canvas->restore();
173             canvas->translate(0, 170);
174         }
175     }
176 
177 private:
178     using INHERITED = GM;
179 };
180 
181 DEF_GM( return new RRectGM; )
182 
183 class RRectBlurGM : public skiagm::GM {
184 public:
RRectBlurGM()185     RRectBlurGM() {}
186 
187 protected:
getName() const188     SkString getName() const override { return SkString("rrect_blurs"); }
189 
190     static constexpr int kWidth = 300;
191     static constexpr int kHeight = 400;
192     // how much to exagerate the diffs
193     static constexpr int kDiffMaginification = 16;
194     static constexpr bool kPrintDiffMetrics = false;
195 
getISize()196     SkISize getISize() override { return SkISize::Make(kWidth, kHeight); }
197 
draw_blurry_rrect(SkCanvas * canvas,int cellY,sk_sp<SkMaskFilter> mf,SkColor color,const SkRRect & rr)198     static void draw_blurry_rrect(
199             SkCanvas* canvas, int cellY, sk_sp<SkMaskFilter> mf, SkColor color, const SkRRect& rr) {
200         const int kCellSize = 100;
201         SkPaint rrectPaint;
202         rrectPaint.setColor(color);
203         rrectPaint.setMaskFilter(mf);
204 
205         const int paddingX = (kCellSize - rr.width()) / 2;
206         const int paddingY = (kCellSize - rr.height()) / 2;
207         const SkRRect left = rr.makeOffset(paddingX, paddingY + cellY);
208         canvas->drawRRect(left, rrectPaint);
209 
210         const SkRRect right = rr.makeOffset(2 * kCellSize + paddingX, paddingY + cellY);
211         SkPath rightPath;
212         rightPath.addRRect(right);
213         canvas->drawPath(rightPath, rrectPaint);
214 
215         // In an ideal world, there would be no diffs at all between the two drawing
216         // methods. The point of this gm is to show those differences and allow us to
217         // measure the differences.
218         SkBitmap leftBitmap;
219         leftBitmap.allocPixels(SkImageInfo::MakeN32Premul(kCellSize, kCellSize));
220         SkImageInfo infoLeft = leftBitmap.info();
221         if (!canvas->readPixels(infoLeft,
222                                 leftBitmap.pixmap().writable_addr(),
223                                 infoLeft.minRowBytes(),
224                                 0,
225                                 cellY)) {
226             return;
227         }
228 
229         SkBitmap rightBitmap;
230         rightBitmap.allocPixels(SkImageInfo::MakeN32Premul(kCellSize, kCellSize));
231         SkImageInfo infoRight = rightBitmap.info();
232         if (!canvas->readPixels(infoRight,
233                                 rightBitmap.pixmap().writable_addr(),
234                                 infoRight.minRowBytes(),
235                                 2 * kCellSize,
236                                 cellY)) {
237             return;
238         }
239 
240         int diffPixels = 0;
241         SkBitmap diffBitmap;
242         diffBitmap.allocPixels(SkImageInfo::MakeN32Premul(kCellSize, kCellSize));
243         for (int y = 0; y < kCellSize; ++y) {
244             for (int x = 0; x < kCellSize; ++x) {
245                 SkColor leftColor = leftBitmap.getColor(x, y);
246                 SkColor rightColor = rightBitmap.getColor(x, y);
247                 // Add up the diffs in the 4 channels, then treat that as how bright
248                 // to draw the diff
249                 int diff = abs((int)(SkColorGetA(leftColor) - SkColorGetA(rightColor))) +
250                            abs((int)(SkColorGetR(leftColor) - SkColorGetR(rightColor))) +
251                            abs((int)(SkColorGetG(leftColor) - SkColorGetG(rightColor))) +
252                            abs((int)(SkColorGetB(leftColor) - SkColorGetB(rightColor)));
253                 SkASSERT(diff >= 0);
254                 const U8CPU grey = std::min(diff * kDiffMaginification, 255);
255                 if (grey > 0) {
256                     diffPixels++;
257                 }
258                 *diffBitmap.pixmap().writable_addr32(x, y) = SkColorSetARGB(0xFF, grey, grey, grey);
259             }
260         }
261         if (kPrintDiffMetrics) {
262             SkDebugf("%d pixels diff\n", diffPixels);
263         }
264 
265         canvas->writePixels(diffBitmap, kCellSize, cellY);
266     }
267 
onDraw(SkCanvas * canvas)268     void onDraw(SkCanvas* canvas) override {
269         // Because of the read/write pixels, this doesn't draw right if viewer zooms in.
270         canvas->resetMatrix();
271         canvas->clear(SK_ColorDKGRAY);
272 
273         draw_blurry_rrect(canvas, 0,
274                           SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 1.0f, false /*=respectCTM*/),
275                           SK_ColorWHITE,
276                           SkRRect::MakeRectXY(SkRect::MakeWH(50, 50), 10, 15));
277 
278         draw_blurry_rrect(canvas, 100,
279                           SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 0.5f, false /*=respectCTM*/),
280                           SK_ColorYELLOW,
281                           SkRRect::MakeRectXY(SkRect::MakeWH(60, 80), 3.1f, 1.5f));
282 
283         SkRRect rr;
284         rr.setNinePatch(SkRect::MakeWH(70, 80),
285                         5,   // left
286                         10,  // top
287                         13,  // right
288                         7);  // bottom
289         draw_blurry_rrect(canvas, 200,
290                           SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 2.5f, false /*=respectCTM*/),
291                           SkColorSetARGB(255, 200, 100, 30),
292                           rr);
293 
294         SkVector radii[4] = {{0, 0}, {20, 1}, {10, 30}, {30, 30}};
295         rr.setRectRadii(SkRect::MakeWH(90, 90), radii);
296         draw_blurry_rrect(canvas, 300,
297                           SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 1.1f, false /*=respectCTM*/),
298                           SkColorSetARGB(255, 35, 120, 220),
299                           rr);
300 
301         // labels after to avoid contaminating the diffs
302         SkPaint labelPaint;
303         labelPaint.setColor(SK_ColorWHITE);
304         labelPaint.setAntiAlias(true);
305         SkFont font = ToolUtils::DefaultPortableFont();
306         canvas->drawString("drawRRect", 15, 15, font, labelPaint);
307         canvas->drawString("diff", 140, 15, font, labelPaint);
308         canvas->drawString("drawPath", 220, 15, font, labelPaint);
309         canvas->drawLine(100, 0, 100, kHeight, labelPaint);
310         canvas->drawLine(200, 0, 200, kHeight, labelPaint);
311         canvas->drawLine(0, 100, kWidth, 100, labelPaint);
312         canvas->drawLine(0, 200, kWidth, 200, labelPaint);
313         canvas->drawLine(0, 300, kWidth, 300, labelPaint);
314     }
315 };
316 
317 DEF_GM(return new RRectBlurGM;)
318