overstroke.cpp 6.0 KB

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