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