path_stroke_with_zero_length.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. /*
  2. * Copyright 2015 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. #include "gm/gm.h"
  8. #include "include/core/SkCanvas.h"
  9. #include "include/core/SkColor.h"
  10. #include "include/core/SkColorPriv.h"
  11. #include "include/core/SkImage.h"
  12. #include "include/core/SkImageInfo.h"
  13. #include "include/core/SkPaint.h"
  14. #include "include/core/SkPath.h"
  15. #include "include/core/SkRect.h"
  16. #include "include/core/SkRefCnt.h"
  17. #include "include/core/SkScalar.h"
  18. #include "include/core/SkString.h"
  19. #include "include/core/SkSurface.h"
  20. #include "include/core/SkTypes.h"
  21. #include "include/utils/SkParsePath.h"
  22. #include "src/core/SkAutoPixmapStorage.h"
  23. // GM to test combinations of stroking zero length paths with different caps and other settings
  24. // Variables:
  25. // * Antialiasing: On, Off
  26. // * Caps: Butt, Round, Square
  27. // * Stroke width: 0, 0.9, 1, 1.1, 15, 25
  28. // * Path form: M, ML, MLZ, MZ
  29. // * Path contours: 1 or 2
  30. // * Path verbs: Line, Quad, Cubic, Conic
  31. //
  32. // Each test is drawn to a 50x20 offscreen surface, and expected to produce some number (0 - 2) of
  33. // visible pieces of cap geometry. These are counted by scanning horizontally for peaks (blobs).
  34. static bool draw_path_cell(SkCanvas* canvas, SkImage* img, int expectedCaps) {
  35. // Draw the image
  36. canvas->drawImage(img, 0, 0);
  37. int w = img->width(), h = img->height();
  38. // Read the pixels back
  39. SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);
  40. SkAutoPixmapStorage pmap;
  41. pmap.alloc(info);
  42. if (!img->readPixels(pmap, 0, 0)) {
  43. return false;
  44. }
  45. // To account for rasterization differences, we scan the middle two rows [y, y+1] of the image
  46. SkASSERT(h % 2 == 0);
  47. int y = (h - 1) / 2;
  48. bool inBlob = false;
  49. int numBlobs = 0;
  50. for (int x = 0; x < w; ++x) {
  51. // We drew white-on-black. We can look for any non-zero value. Just check red.
  52. // And we care if either row is non-zero, so just add them to simplify everything.
  53. uint32_t v = SkGetPackedR32(*pmap.addr32(x, y)) + SkGetPackedR32(*pmap.addr32(x, y + 1));
  54. if (!inBlob && v) {
  55. ++numBlobs;
  56. }
  57. inBlob = SkToBool(v);
  58. }
  59. SkPaint outline;
  60. outline.setStyle(SkPaint::kStroke_Style);
  61. if (numBlobs == expectedCaps) {
  62. outline.setColor(0xFF007F00); // Green
  63. } else if (numBlobs > expectedCaps) {
  64. outline.setColor(0xFF7F7F00); // Yellow -- more geometry than expected
  65. } else {
  66. outline.setColor(0xFF7F0000); // Red -- missing some geometry
  67. }
  68. canvas->drawRect(SkRect::MakeWH(w, h), outline);
  69. return numBlobs == expectedCaps;
  70. }
  71. static const SkPaint::Cap kCaps[] = {
  72. SkPaint::kButt_Cap,
  73. SkPaint::kRound_Cap,
  74. SkPaint::kSquare_Cap
  75. };
  76. static const SkScalar kWidths[] = { 0.0f, 0.9f, 1.0f, 1.1f, 15.0f, 25.0f };
  77. // Full set of path structures for single contour case (each primitive with and without a close)
  78. static const char* kAllVerbs[] = {
  79. nullptr,
  80. "z ",
  81. "l 0 0 ",
  82. "l 0 0 z ",
  83. "q 0 0 0 0 ",
  84. "q 0 0 0 0 z ",
  85. "c 0 0 0 0 0 0 ",
  86. "c 0 0 0 0 0 0 z ",
  87. "a 0 0 0 0 0 0 0 ",
  88. "a 0 0 0 0 0 0 0 z "
  89. };
  90. // Reduced set of path structures for double contour case, to keep total number of cases down
  91. static const char* kSomeVerbs[] = {
  92. nullptr,
  93. "z ",
  94. "l 0 0 ",
  95. "l 0 0 z ",
  96. "q 0 0 0 0 ",
  97. "q 0 0 0 0 z ",
  98. };
  99. static const int kCellWidth = 50;
  100. static const int kCellHeight = 20;
  101. static const int kCellPad = 2;
  102. static const int kNumRows = SK_ARRAY_COUNT(kCaps) * SK_ARRAY_COUNT(kWidths);
  103. static const int kNumColumns = SK_ARRAY_COUNT(kAllVerbs);
  104. static const int kTotalWidth = kNumColumns * (kCellWidth + kCellPad) + kCellPad;
  105. static const int kTotalHeight = kNumRows * (kCellHeight + kCellPad) + kCellPad;
  106. static const int kDblContourNumColums = SK_ARRAY_COUNT(kSomeVerbs) * SK_ARRAY_COUNT(kSomeVerbs);
  107. static const int kDblContourTotalWidth = kDblContourNumColums * (kCellWidth + kCellPad) + kCellPad;
  108. // 50% transparent versions of the colors used for positive/negative triage icons on gold.skia.org
  109. static const SkColor kFailureRed = 0x7FE7298A;
  110. static const SkColor kSuccessGreen = 0x7F1B9E77;
  111. static void draw_zero_length_capped_paths(SkCanvas* canvas, bool aa) {
  112. canvas->translate(kCellPad, kCellPad);
  113. SkImageInfo info = canvas->imageInfo().makeWH(kCellWidth, kCellHeight);
  114. auto surface = canvas->makeSurface(info);
  115. if (!surface) {
  116. surface = SkSurface::MakeRasterN32Premul(kCellWidth, kCellHeight);
  117. }
  118. SkPaint paint;
  119. paint.setColor(SK_ColorWHITE);
  120. paint.setAntiAlias(aa);
  121. paint.setStyle(SkPaint::kStroke_Style);
  122. int numFailedTests = 0;
  123. for (auto cap : kCaps) {
  124. for (auto width : kWidths) {
  125. paint.setStrokeCap(cap);
  126. paint.setStrokeWidth(width);
  127. canvas->save();
  128. for (auto verb : kAllVerbs) {
  129. SkString pathStr;
  130. pathStr.appendf("M %f %f ", (kCellWidth - 1) * 0.5f, (kCellHeight - 1) * 0.5f);
  131. if (verb) {
  132. pathStr.append(verb);
  133. }
  134. SkPath path;
  135. SkParsePath::FromSVGString(pathStr.c_str(), &path);
  136. surface->getCanvas()->clear(SK_ColorTRANSPARENT);
  137. surface->getCanvas()->drawPath(path, paint);
  138. auto img = surface->makeImageSnapshot();
  139. // All cases should draw one cap, except for butt capped, and dangling moves
  140. // (without a verb or close), which shouldn't draw anything.
  141. int expectedCaps = ((SkPaint::kButt_Cap == cap) || !verb) ? 0 : 1;
  142. if (!draw_path_cell(canvas, img.get(), expectedCaps)) {
  143. ++numFailedTests;
  144. }
  145. canvas->translate(kCellWidth + kCellPad, 0);
  146. }
  147. canvas->restore();
  148. canvas->translate(0, kCellHeight + kCellPad);
  149. }
  150. }
  151. canvas->drawColor(numFailedTests > 0 ? kFailureRed : kSuccessGreen);
  152. }
  153. DEF_SIMPLE_GM_BG(zero_length_paths_aa, canvas, kTotalWidth, kTotalHeight, SK_ColorBLACK) {
  154. draw_zero_length_capped_paths(canvas, true);
  155. }
  156. DEF_SIMPLE_GM_BG(zero_length_paths_bw, canvas, kTotalWidth, kTotalHeight, SK_ColorBLACK) {
  157. draw_zero_length_capped_paths(canvas, false);
  158. }
  159. static void draw_zero_length_capped_paths_dbl_contour(SkCanvas* canvas, bool aa) {
  160. canvas->translate(kCellPad, kCellPad);
  161. SkImageInfo info = canvas->imageInfo().makeWH(kCellWidth, kCellHeight);
  162. auto surface = canvas->makeSurface(info);
  163. if (!surface) {
  164. surface = SkSurface::MakeRasterN32Premul(kCellWidth, kCellHeight);
  165. }
  166. SkPaint paint;
  167. paint.setColor(SK_ColorWHITE);
  168. paint.setAntiAlias(aa);
  169. paint.setStyle(SkPaint::kStroke_Style);
  170. int numFailedTests = 0;
  171. for (auto cap : kCaps) {
  172. for (auto width : kWidths) {
  173. paint.setStrokeCap(cap);
  174. paint.setStrokeWidth(width);
  175. canvas->save();
  176. for (auto firstVerb : kSomeVerbs) {
  177. for (auto secondVerb : kSomeVerbs) {
  178. int expectedCaps = 0;
  179. SkString pathStr;
  180. pathStr.append("M 9.5 9.5 ");
  181. if (firstVerb) {
  182. pathStr.append(firstVerb);
  183. ++expectedCaps;
  184. }
  185. pathStr.append("M 40.5 9.5 ");
  186. if (secondVerb) {
  187. pathStr.append(secondVerb);
  188. ++expectedCaps;
  189. }
  190. SkPath path;
  191. SkParsePath::FromSVGString(pathStr.c_str(), &path);
  192. surface->getCanvas()->clear(SK_ColorTRANSPARENT);
  193. surface->getCanvas()->drawPath(path, paint);
  194. auto img = surface->makeImageSnapshot();
  195. if (SkPaint::kButt_Cap == cap) {
  196. expectedCaps = 0;
  197. }
  198. if (!draw_path_cell(canvas, img.get(), expectedCaps)) {
  199. ++numFailedTests;
  200. }
  201. canvas->translate(kCellWidth + kCellPad, 0);
  202. }
  203. }
  204. canvas->restore();
  205. canvas->translate(0, kCellHeight + kCellPad);
  206. }
  207. }
  208. canvas->drawColor(numFailedTests > 0 ? kFailureRed : kSuccessGreen);
  209. }
  210. DEF_SIMPLE_GM_BG(zero_length_paths_dbl_aa, canvas, kDblContourTotalWidth, kTotalHeight,
  211. SK_ColorBLACK) {
  212. draw_zero_length_capped_paths_dbl_contour(canvas, true);
  213. }
  214. DEF_SIMPLE_GM_BG(zero_length_paths_dbl_bw, canvas, kDblContourTotalWidth, kTotalHeight,
  215. SK_ColorBLACK) {
  216. draw_zero_length_capped_paths_dbl_contour(canvas, false);
  217. }