OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2016 Google Inc. | 2 * Copyright 2016 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 | 8 |
9 /* | 9 /* |
10 * This GM exercises stroking of paths with large stroke lengths, which is | 10 * This GM exercises stroking of paths with large stroke lengths, which is |
11 * referred to as "overstroke" for brevity. In Skia as of 8/2016 we offset | 11 * referred to as "overstroke" for brevity. In Skia as of 8/2016 we offset |
12 * each part of the curve the request amount even if it makes the offsets | 12 * each part of the curve the request amount even if it makes the offsets |
13 * overlap and create holes. There is not a really great algorithm for this | 13 * overlap and create holes. There is not a really great algorithm for this |
14 * and several other 2D graphics engines have the same bug. | 14 * and several other 2D graphics engines have the same bug. |
15 * | 15 * |
| 16 * If we run this using Nvidia Path Renderer with: |
| 17 * `path/to/dm --match OverStroke -w gm_out --gpu --config nvpr16` |
| 18 * then we get correct results, so that is a possible direction of attack - |
| 19 * use the GPU and a completely different algorithm to get correctness in |
| 20 * Skia. |
| 21 * |
16 * See crbug.com/589769 skbug.com/5405 skbug.com/5406 | 22 * See crbug.com/589769 skbug.com/5405 skbug.com/5406 |
17 */ | 23 */ |
18 | 24 |
19 | 25 |
20 #include "gm.h" | 26 #include "gm.h" |
21 #include "SkPaint.h" | 27 #include "SkPaint.h" |
22 #include "SkPath.h" | 28 #include "SkPath.h" |
| 29 #include "SkPathMeasure.h" |
| 30 |
| 31 const SkScalar OVERSTROKE_WIDTH = 500.0f; |
| 32 const SkScalar NORMALSTROKE_WIDTH = 3.0f; |
23 | 33 |
24 //////// path and paint builders | 34 //////// path and paint builders |
25 | 35 |
| 36 SkPaint make_normal_paint() { |
| 37 SkPaint p; |
| 38 p.setAntiAlias(true); |
| 39 p.setStyle(SkPaint::kStroke_Style); |
| 40 p.setStrokeWidth(NORMALSTROKE_WIDTH); |
| 41 p.setColor(SK_ColorBLUE); |
| 42 |
| 43 return p; |
| 44 } |
| 45 |
26 SkPaint make_overstroke_paint() { | 46 SkPaint make_overstroke_paint() { |
27 SkPaint p; | 47 SkPaint p; |
28 p.setAntiAlias(true); | 48 p.setAntiAlias(true); |
29 p.setStyle(SkPaint::kStroke_Style); | 49 p.setStyle(SkPaint::kStroke_Style); |
30 p.setStrokeWidth(500); | 50 p.setStrokeWidth(OVERSTROKE_WIDTH); |
31 | 51 |
32 return p; | 52 return p; |
33 } | 53 } |
34 | 54 |
35 SkPath quad_path() { | 55 SkPath quad_path() { |
36 SkPath path; | 56 SkPath path; |
37 path.moveTo(0, 0); | 57 path.moveTo(0, 0); |
38 path.lineTo(100, 0); | 58 path.lineTo(100, 0); |
39 path.quadTo(50, -40, | 59 path.quadTo(50, -40, |
40 0, 0); | 60 0, 0); |
| 61 path.close(); |
41 | 62 |
42 return path; | 63 return path; |
43 } | 64 } |
44 | 65 |
45 SkPath cubic_path() { | 66 SkPath cubic_path() { |
46 SkPath path; | 67 SkPath path; |
47 path.moveTo(0, 0); | 68 path.moveTo(0, 0); |
48 path.cubicTo(25, 75, | 69 path.cubicTo(25, 75, |
49 75, -50, | 70 75, -50, |
50 100, 0); | 71 100, 0); |
51 | 72 |
52 return path; | 73 return path; |
53 } | 74 } |
54 | 75 |
55 SkPath oval_path() { | 76 SkPath oval_path() { |
56 SkRect oval = SkRect::MakeXYWH(0, -25, 100, 50); | 77 SkRect oval = SkRect::MakeXYWH(0, -25, 100, 50); |
57 | 78 |
58 SkPath path; | 79 SkPath path; |
59 path.arcTo(oval, 0, 359, true); | 80 path.arcTo(oval, 0, 359, true); |
60 path.close(); | 81 path.close(); |
61 | 82 |
62 return path; | 83 return path; |
63 } | 84 } |
64 | 85 |
| 86 SkPath ribs_path(SkPath path, SkScalar radius) { |
| 87 SkPath ribs; |
| 88 |
| 89 const SkScalar spacing = 5.0f; |
| 90 float accum = 0.0f; |
| 91 |
| 92 SkPathMeasure meas(path, false); |
| 93 SkScalar length = meas.getLength(); |
| 94 SkPoint pos; |
| 95 SkVector tan; |
| 96 while (accum < length) { |
| 97 if (meas.getPosTan(accum, &pos, &tan)) { |
| 98 tan.scale(radius); |
| 99 tan.rotateCCW(); |
| 100 |
| 101 ribs.moveTo(pos.x() + tan.x(), pos.y() + tan.y()); |
| 102 ribs.lineTo(pos.x() - tan.x(), pos.y() - tan.y()); |
| 103 } |
| 104 accum += spacing; |
| 105 } |
| 106 |
| 107 return ribs; |
| 108 } |
| 109 |
| 110 void draw_ribs(SkCanvas *canvas, SkPath path) { |
| 111 SkPath ribs = ribs_path(path, OVERSTROKE_WIDTH/2.0f); |
| 112 SkPaint p = make_normal_paint(); |
| 113 p.setStrokeWidth(1); |
| 114 p.setColor(SK_ColorBLUE); |
| 115 p.setColor(SK_ColorGREEN); |
| 116 |
| 117 canvas->drawPath(ribs, p); |
| 118 } |
| 119 |
65 ///////// quads | 120 ///////// quads |
66 | 121 |
67 void draw_small_quad(SkCanvas *canvas) { | 122 void draw_small_quad(SkCanvas *canvas) { |
68 // scaled so it's visible | 123 // scaled so it's visible |
69 canvas->scale(8, 8); | 124 // canvas->scale(8, 8); |
70 | 125 |
71 SkPaint p; | 126 SkPaint p = make_normal_paint(); |
72 p.setAntiAlias(true); | |
73 p.setStyle(SkPaint::kStroke_Style); | |
74 p.setStrokeWidth(3); | |
75 | |
76 SkPath path = quad_path(); | 127 SkPath path = quad_path(); |
77 | 128 |
| 129 draw_ribs(canvas, path); |
78 canvas->drawPath(path, p); | 130 canvas->drawPath(path, p); |
79 } | 131 } |
80 | 132 |
81 void draw_large_quad(SkCanvas *canvas) { | 133 void draw_large_quad(SkCanvas *canvas) { |
82 SkPaint p = make_overstroke_paint(); | 134 SkPaint p = make_overstroke_paint(); |
83 SkPath path = quad_path(); | 135 SkPath path = quad_path(); |
84 | 136 |
85 canvas->drawPath(path, p); | 137 canvas->drawPath(path, p); |
| 138 draw_ribs(canvas, path); |
86 } | 139 } |
87 | 140 |
88 void draw_quad_fillpath(SkCanvas *canvas) { | 141 void draw_quad_fillpath(SkCanvas *canvas) { |
89 SkPath path = quad_path(); | 142 SkPath path = quad_path(); |
90 SkPaint p = make_overstroke_paint(); | 143 SkPaint p = make_overstroke_paint(); |
91 | 144 |
92 SkPaint fillp; | 145 SkPaint fillp = make_normal_paint(); |
93 fillp.setAntiAlias(true); | |
94 fillp.setStyle(SkPaint::kStroke_Style); | |
95 fillp.setColor(SK_ColorMAGENTA); | 146 fillp.setColor(SK_ColorMAGENTA); |
96 | 147 |
97 SkPath fillpath; | 148 SkPath fillpath; |
98 p.getFillPath(path, &fillpath); | 149 p.getFillPath(path, &fillpath); |
99 | 150 |
100 canvas->drawPath(fillpath, fillp); | 151 canvas->drawPath(fillpath, fillp); |
101 } | 152 } |
102 | 153 |
103 void draw_stroked_quad(SkCanvas *canvas) { | 154 void draw_stroked_quad(SkCanvas *canvas) { |
104 canvas->translate(200, 0); | 155 canvas->translate(400, 0); |
105 draw_large_quad(canvas); | 156 draw_large_quad(canvas); |
106 draw_quad_fillpath(canvas); | 157 draw_quad_fillpath(canvas); |
107 } | 158 } |
108 | 159 |
109 ////////// cubics | 160 ////////// cubics |
110 | 161 |
111 void draw_small_cubic(SkCanvas *canvas) { | 162 void draw_small_cubic(SkCanvas *canvas) { |
112 // scaled so it's visible | 163 SkPaint p = make_normal_paint(); |
113 canvas->scale(8, 8); | |
114 | |
115 SkPaint p; | |
116 p.setAntiAlias(true); | |
117 p.setStyle(SkPaint::kStroke_Style); | |
118 p.setStrokeWidth(3); | |
119 | |
120 SkPath path = cubic_path(); | 164 SkPath path = cubic_path(); |
121 | 165 |
| 166 draw_ribs(canvas, path); |
122 canvas->drawPath(path, p); | 167 canvas->drawPath(path, p); |
123 } | 168 } |
124 | 169 |
125 void draw_large_cubic(SkCanvas *canvas) { | 170 void draw_large_cubic(SkCanvas *canvas) { |
126 SkPaint p = make_overstroke_paint(); | 171 SkPaint p = make_overstroke_paint(); |
127 SkPath path = cubic_path(); | 172 SkPath path = cubic_path(); |
128 | 173 |
129 canvas->drawPath(path, p); | 174 canvas->drawPath(path, p); |
| 175 draw_ribs(canvas, path); |
130 } | 176 } |
131 | 177 |
132 void draw_cubic_fillpath(SkCanvas *canvas) { | 178 void draw_cubic_fillpath(SkCanvas *canvas) { |
133 SkPath path = cubic_path(); | 179 SkPath path = cubic_path(); |
134 SkPaint p = make_overstroke_paint(); | 180 SkPaint p = make_overstroke_paint(); |
135 | 181 |
136 SkPaint fillp; | 182 SkPaint fillp = make_normal_paint(); |
137 fillp.setAntiAlias(true); | |
138 fillp.setStyle(SkPaint::kStroke_Style); | |
139 fillp.setColor(SK_ColorMAGENTA); | 183 fillp.setColor(SK_ColorMAGENTA); |
140 | 184 |
141 SkPath fillpath; | 185 SkPath fillpath; |
142 p.getFillPath(path, &fillpath); | 186 p.getFillPath(path, &fillpath); |
143 | 187 |
144 canvas->drawPath(fillpath, fillp); | 188 canvas->drawPath(fillpath, fillp); |
145 } | 189 } |
146 | 190 |
147 void draw_stroked_cubic(SkCanvas *canvas) { | 191 void draw_stroked_cubic(SkCanvas *canvas) { |
148 canvas->translate(400, 0); | 192 canvas->translate(400, 0); |
149 draw_large_cubic(canvas); | 193 draw_large_cubic(canvas); |
150 draw_cubic_fillpath(canvas); | 194 draw_cubic_fillpath(canvas); |
151 } | 195 } |
152 | 196 |
153 ////////// ovals | 197 ////////// ovals |
154 | 198 |
155 void draw_small_oval(SkCanvas *canvas) { | 199 void draw_small_oval(SkCanvas *canvas) { |
156 // scaled so it's visible | 200 SkPaint p = make_normal_paint(); |
157 canvas->scale(8, 8); | |
158 | |
159 SkPaint p; | |
160 p.setAntiAlias(true); | |
161 p.setStyle(SkPaint::kStroke_Style); | |
162 p.setStrokeWidth(3); | |
163 | 201 |
164 SkPath path = oval_path(); | 202 SkPath path = oval_path(); |
165 | 203 |
| 204 draw_ribs(canvas, path); |
166 canvas->drawPath(path, p); | 205 canvas->drawPath(path, p); |
167 } | 206 } |
168 | 207 |
169 void draw_large_oval(SkCanvas *canvas) { | 208 void draw_large_oval(SkCanvas *canvas) { |
170 SkPaint p = make_overstroke_paint(); | 209 SkPaint p = make_overstroke_paint(); |
171 SkPath path = oval_path(); | 210 SkPath path = oval_path(); |
172 | 211 |
173 canvas->drawPath(path, p); | 212 canvas->drawPath(path, p); |
| 213 draw_ribs(canvas, path); |
174 } | 214 } |
175 | 215 |
176 void draw_oval_fillpath(SkCanvas *canvas) { | 216 void draw_oval_fillpath(SkCanvas *canvas) { |
177 SkPath path = oval_path(); | 217 SkPath path = oval_path(); |
178 SkPaint p = make_overstroke_paint(); | 218 SkPaint p = make_overstroke_paint(); |
179 | 219 |
180 SkPaint fillp; | 220 SkPaint fillp = make_normal_paint(); |
181 fillp.setAntiAlias(true); | |
182 fillp.setStyle(SkPaint::kStroke_Style); | |
183 fillp.setColor(SK_ColorMAGENTA); | 221 fillp.setColor(SK_ColorMAGENTA); |
184 | 222 |
185 SkPath fillpath; | 223 SkPath fillpath; |
186 p.getFillPath(path, &fillpath); | 224 p.getFillPath(path, &fillpath); |
187 | 225 |
188 canvas->drawPath(fillpath, fillp); | 226 canvas->drawPath(fillpath, fillp); |
189 } | 227 } |
190 | 228 |
191 void draw_stroked_oval(SkCanvas *canvas) { | 229 void draw_stroked_oval(SkCanvas *canvas) { |
192 canvas->translate(400, 0); | 230 canvas->translate(400, 0); |
(...skipping 10 matching lines...) Expand all Loading... |
203 | 241 |
204 DEF_SIMPLE_GM(OverStroke, canvas, 500, 500) { | 242 DEF_SIMPLE_GM(OverStroke, canvas, 500, 500) { |
205 const size_t length = sizeof(examples) / sizeof(examples[0]); | 243 const size_t length = sizeof(examples) / sizeof(examples[0]); |
206 const size_t width = 2; | 244 const size_t width = 2; |
207 | 245 |
208 for (size_t i = 0; i < length; i++) { | 246 for (size_t i = 0; i < length; i++) { |
209 int x = (int)(i % width); | 247 int x = (int)(i % width); |
210 int y = (int)(i / width); | 248 int y = (int)(i / width); |
211 | 249 |
212 canvas->save(); | 250 canvas->save(); |
213 canvas->translate(200.0f * x, 150.0f * y); | 251 canvas->translate(150.0f * x, 150.0f * y); |
214 canvas->scale(0.25f, 0.25f); | 252 canvas->scale(0.2f, 0.2f); |
215 canvas->translate(100.0f, 400.0f); | 253 canvas->translate(300.0f, 400.0f); |
216 | 254 |
217 examples[i](canvas); | 255 examples[i](canvas); |
218 | 256 |
219 canvas->restore(); | 257 canvas->restore(); |
220 } | 258 } |
221 } | 259 } |
OLD | NEW |