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 9 /* 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 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 14 * and several other 2D graphics engines have the same bug. 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 * 22 * See crbug.com/589769 skbug.com/5405 skbug.com/5406 23 */ 24 25 26 #include "gm.h" 27 #include "SkPaint.h" 28 #include "SkPath.h" 29 #include "SkPathMeasure.h" 30 #include "SkPointPriv.h" 31 32 const SkScalar OVERSTROKE_WIDTH = 500.0f; 33 const SkScalar NORMALSTROKE_WIDTH = 3.0f; 34 35 //////// path and paint builders 36 make_normal_paint()37 SkPaint make_normal_paint() { 38 SkPaint p; 39 p.setAntiAlias(true); 40 p.setStyle(SkPaint::kStroke_Style); 41 p.setStrokeWidth(NORMALSTROKE_WIDTH); 42 p.setColor(SK_ColorBLUE); 43 44 return p; 45 } 46 make_overstroke_paint()47 SkPaint make_overstroke_paint() { 48 SkPaint p; 49 p.setAntiAlias(true); 50 p.setStyle(SkPaint::kStroke_Style); 51 p.setStrokeWidth(OVERSTROKE_WIDTH); 52 53 return p; 54 } 55 quad_path()56 SkPath quad_path() { 57 SkPath path; 58 path.moveTo(0, 0); 59 path.lineTo(100, 0); 60 path.quadTo(50, -40, 61 0, 0); 62 path.close(); 63 64 return path; 65 } 66 cubic_path()67 SkPath cubic_path() { 68 SkPath path; 69 path.moveTo(0, 0); 70 path.cubicTo(25, 75, 71 75, -50, 72 100, 0); 73 74 return path; 75 } 76 oval_path()77 SkPath oval_path() { 78 SkRect oval = SkRect::MakeXYWH(0, -25, 100, 50); 79 80 SkPath path; 81 path.arcTo(oval, 0, 359, true); 82 path.close(); 83 84 return path; 85 } 86 ribs_path(SkPath path,SkScalar radius)87 SkPath ribs_path(SkPath path, SkScalar radius) { 88 SkPath ribs; 89 90 const SkScalar spacing = 5.0f; 91 float accum = 0.0f; 92 93 SkPathMeasure meas(path, false); 94 SkScalar length = meas.getLength(); 95 SkPoint pos; 96 SkVector tan; 97 while (accum < length) { 98 if (meas.getPosTan(accum, &pos, &tan)) { 99 tan.scale(radius); 100 SkPointPriv::RotateCCW(&tan); 101 102 ribs.moveTo(pos.x() + tan.x(), pos.y() + tan.y()); 103 ribs.lineTo(pos.x() - tan.x(), pos.y() - tan.y()); 104 } 105 accum += spacing; 106 } 107 108 return ribs; 109 } 110 draw_ribs(SkCanvas * canvas,SkPath path)111 void draw_ribs(SkCanvas *canvas, SkPath path) { 112 SkPath ribs = ribs_path(path, OVERSTROKE_WIDTH/2.0f); 113 SkPaint p = make_normal_paint(); 114 p.setStrokeWidth(1); 115 p.setColor(SK_ColorBLUE); 116 p.setColor(SK_ColorGREEN); 117 118 canvas->drawPath(ribs, p); 119 } 120 121 ///////// quads 122 draw_small_quad(SkCanvas * canvas)123 void draw_small_quad(SkCanvas *canvas) { 124 // scaled so it's visible 125 // canvas->scale(8, 8); 126 127 SkPaint p = make_normal_paint(); 128 SkPath path = quad_path(); 129 130 draw_ribs(canvas, path); 131 canvas->drawPath(path, p); 132 } 133 draw_large_quad(SkCanvas * canvas)134 void draw_large_quad(SkCanvas *canvas) { 135 SkPaint p = make_overstroke_paint(); 136 SkPath path = quad_path(); 137 138 canvas->drawPath(path, p); 139 draw_ribs(canvas, path); 140 } 141 draw_quad_fillpath(SkCanvas * canvas)142 void draw_quad_fillpath(SkCanvas *canvas) { 143 SkPath path = quad_path(); 144 SkPaint p = make_overstroke_paint(); 145 146 SkPaint fillp = make_normal_paint(); 147 fillp.setColor(SK_ColorMAGENTA); 148 149 SkPath fillpath; 150 p.getFillPath(path, &fillpath); 151 152 canvas->drawPath(fillpath, fillp); 153 } 154 draw_stroked_quad(SkCanvas * canvas)155 void draw_stroked_quad(SkCanvas *canvas) { 156 canvas->translate(400, 0); 157 draw_large_quad(canvas); 158 draw_quad_fillpath(canvas); 159 } 160 161 ////////// cubics 162 draw_small_cubic(SkCanvas * canvas)163 void draw_small_cubic(SkCanvas *canvas) { 164 SkPaint p = make_normal_paint(); 165 SkPath path = cubic_path(); 166 167 draw_ribs(canvas, path); 168 canvas->drawPath(path, p); 169 } 170 draw_large_cubic(SkCanvas * canvas)171 void draw_large_cubic(SkCanvas *canvas) { 172 SkPaint p = make_overstroke_paint(); 173 SkPath path = cubic_path(); 174 175 canvas->drawPath(path, p); 176 draw_ribs(canvas, path); 177 } 178 draw_cubic_fillpath(SkCanvas * canvas)179 void draw_cubic_fillpath(SkCanvas *canvas) { 180 SkPath path = cubic_path(); 181 SkPaint p = make_overstroke_paint(); 182 183 SkPaint fillp = make_normal_paint(); 184 fillp.setColor(SK_ColorMAGENTA); 185 186 SkPath fillpath; 187 p.getFillPath(path, &fillpath); 188 189 canvas->drawPath(fillpath, fillp); 190 } 191 draw_stroked_cubic(SkCanvas * canvas)192 void draw_stroked_cubic(SkCanvas *canvas) { 193 canvas->translate(400, 0); 194 draw_large_cubic(canvas); 195 draw_cubic_fillpath(canvas); 196 } 197 198 ////////// ovals 199 draw_small_oval(SkCanvas * canvas)200 void draw_small_oval(SkCanvas *canvas) { 201 SkPaint p = make_normal_paint(); 202 203 SkPath path = oval_path(); 204 205 draw_ribs(canvas, path); 206 canvas->drawPath(path, p); 207 } 208 draw_large_oval(SkCanvas * canvas)209 void draw_large_oval(SkCanvas *canvas) { 210 SkPaint p = make_overstroke_paint(); 211 SkPath path = oval_path(); 212 213 canvas->drawPath(path, p); 214 draw_ribs(canvas, path); 215 } 216 draw_oval_fillpath(SkCanvas * canvas)217 void draw_oval_fillpath(SkCanvas *canvas) { 218 SkPath path = oval_path(); 219 SkPaint p = make_overstroke_paint(); 220 221 SkPaint fillp = make_normal_paint(); 222 fillp.setColor(SK_ColorMAGENTA); 223 224 SkPath fillpath; 225 p.getFillPath(path, &fillpath); 226 227 canvas->drawPath(fillpath, fillp); 228 } 229 draw_stroked_oval(SkCanvas * canvas)230 void draw_stroked_oval(SkCanvas *canvas) { 231 canvas->translate(400, 0); 232 draw_large_oval(canvas); 233 draw_oval_fillpath(canvas); 234 } 235 236 ////////// gm 237 238 void (*examples[])(SkCanvas *canvas) = { 239 draw_small_quad, draw_stroked_quad, draw_small_cubic, 240 draw_stroked_cubic, draw_small_oval, draw_stroked_oval, 241 }; 242 243 DEF_SIMPLE_GM(OverStroke, canvas, 500, 500) { 244 const size_t length = sizeof(examples) / sizeof(examples[0]); 245 const size_t width = 2; 246 247 for (size_t i = 0; i < length; i++) { 248 int x = (int)(i % width); 249 int y = (int)(i / width); 250 251 canvas->save(); 252 canvas->translate(150.0f * x, 150.0f * y); 253 canvas->scale(0.2f, 0.2f); 254 canvas->translate(300.0f, 400.0f); 255 256 examples[i](canvas); 257 258 canvas->restore(); 259 } 260 } 261