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