SVGDeviceTest.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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. #define ABORT_TEST(r, cond, ...) \
  8. do { \
  9. if (cond) { \
  10. REPORT_FAILURE(r, #cond, SkStringPrintf(__VA_ARGS__)); \
  11. return; \
  12. } \
  13. } while (0)
  14. #include "include/core/SkBitmap.h"
  15. #include "include/core/SkCanvas.h"
  16. #include "include/core/SkColorFilter.h"
  17. #include "include/core/SkData.h"
  18. #include "include/core/SkImage.h"
  19. #include "include/core/SkShader.h"
  20. #include "include/core/SkStream.h"
  21. #include "include/core/SkTextBlob.h"
  22. #include "include/private/SkTo.h"
  23. #include "include/svg/SkSVGCanvas.h"
  24. #include "include/utils/SkParse.h"
  25. #include "src/core/SkMakeUnique.h"
  26. #include "src/shaders/SkImageShader.h"
  27. #include "tests/Test.h"
  28. #include "tools/ToolUtils.h"
  29. #include <string.h>
  30. #ifdef SK_XML
  31. #include "src/svg/SkSVGDevice.h"
  32. #include "src/xml/SkDOM.h"
  33. #include "src/xml/SkXMLWriter.h"
  34. static std::unique_ptr<SkCanvas> MakeDOMCanvas(SkDOM* dom, uint32_t flags = 0) {
  35. auto svgDevice = SkSVGDevice::Make(SkISize::Make(100, 100),
  36. skstd::make_unique<SkXMLParserWriter>(dom->beginParsing()),
  37. flags);
  38. return svgDevice ? skstd::make_unique<SkCanvas>(svgDevice)
  39. : nullptr;
  40. }
  41. namespace {
  42. void check_text_node(skiatest::Reporter* reporter,
  43. const SkDOM& dom,
  44. const SkDOM::Node* root,
  45. const SkPoint& offset,
  46. unsigned scalarsPerPos,
  47. const char* txt,
  48. const char* expected) {
  49. if (root == nullptr) {
  50. ERRORF(reporter, "root element not found.");
  51. return;
  52. }
  53. const SkDOM::Node* textElem = dom.getFirstChild(root, "text");
  54. if (textElem == nullptr) {
  55. ERRORF(reporter, "<text> element not found.");
  56. return;
  57. }
  58. REPORTER_ASSERT(reporter, dom.getType(textElem) == SkDOM::kElement_Type);
  59. const SkDOM::Node* textNode= dom.getFirstChild(textElem);
  60. REPORTER_ASSERT(reporter, textNode != nullptr);
  61. if (textNode != nullptr) {
  62. REPORTER_ASSERT(reporter, dom.getType(textNode) == SkDOM::kText_Type);
  63. REPORTER_ASSERT(reporter, strcmp(expected, dom.getName(textNode)) == 0);
  64. }
  65. int textLen = SkToInt(strlen(expected));
  66. const char* x = dom.findAttr(textElem, "x");
  67. REPORTER_ASSERT(reporter, x != nullptr);
  68. if (x != nullptr) {
  69. int xposCount = textLen;
  70. REPORTER_ASSERT(reporter, SkParse::Count(x) == xposCount);
  71. SkAutoTMalloc<SkScalar> xpos(xposCount);
  72. SkParse::FindScalars(x, xpos.get(), xposCount);
  73. if (scalarsPerPos < 1) {
  74. // For default-positioned text, we cannot make any assumptions regarding
  75. // the first glyph position when the string has leading whitespace (to be stripped).
  76. if (txt[0] != ' ' && txt[0] != '\t') {
  77. REPORTER_ASSERT(reporter, xpos[0] == offset.x());
  78. }
  79. } else {
  80. for (int i = 0; i < xposCount; ++i) {
  81. REPORTER_ASSERT(reporter, xpos[i] == SkIntToScalar(expected[i]));
  82. }
  83. }
  84. }
  85. const char* y = dom.findAttr(textElem, "y");
  86. REPORTER_ASSERT(reporter, y != nullptr);
  87. if (y != nullptr) {
  88. int yposCount = (scalarsPerPos < 2) ? 1 : textLen;
  89. REPORTER_ASSERT(reporter, SkParse::Count(y) == yposCount);
  90. SkAutoTMalloc<SkScalar> ypos(yposCount);
  91. SkParse::FindScalars(y, ypos.get(), yposCount);
  92. if (scalarsPerPos < 2) {
  93. REPORTER_ASSERT(reporter, ypos[0] == offset.y());
  94. } else {
  95. for (int i = 0; i < yposCount; ++i) {
  96. REPORTER_ASSERT(reporter, ypos[i] == 150 - SkIntToScalar(expected[i]));
  97. }
  98. }
  99. }
  100. }
  101. void test_whitespace_pos(skiatest::Reporter* reporter,
  102. const char* txt,
  103. const char* expected) {
  104. size_t len = strlen(txt);
  105. SkDOM dom;
  106. SkPaint paint;
  107. SkFont font(ToolUtils::create_portable_typeface());
  108. SkPoint offset = SkPoint::Make(10, 20);
  109. {
  110. MakeDOMCanvas(&dom)->drawSimpleText(txt, len, SkTextEncoding::kUTF8,
  111. offset.x(), offset.y(), font, paint);
  112. }
  113. check_text_node(reporter, dom, dom.finishParsing(), offset, 0, txt, expected);
  114. {
  115. SkAutoTMalloc<SkScalar> xpos(len);
  116. for (int i = 0; i < SkToInt(len); ++i) {
  117. xpos[i] = SkIntToScalar(txt[i]);
  118. }
  119. auto blob = SkTextBlob::MakeFromPosTextH(txt, len, &xpos[0], offset.y(), font);
  120. MakeDOMCanvas(&dom)->drawTextBlob(blob, 0, 0, paint);
  121. }
  122. check_text_node(reporter, dom, dom.finishParsing(), offset, 1, txt, expected);
  123. {
  124. SkAutoTMalloc<SkPoint> pos(len);
  125. for (int i = 0; i < SkToInt(len); ++i) {
  126. pos[i] = SkPoint::Make(SkIntToScalar(txt[i]), 150 - SkIntToScalar(txt[i]));
  127. }
  128. auto blob = SkTextBlob::MakeFromPosText(txt, len, &pos[0], font);
  129. MakeDOMCanvas(&dom)->drawTextBlob(blob, 0, 0, paint);
  130. }
  131. check_text_node(reporter, dom, dom.finishParsing(), offset, 2, txt, expected);
  132. }
  133. } // namespace
  134. DEF_TEST(SVGDevice_whitespace_pos, reporter) {
  135. static const struct {
  136. const char* tst_in;
  137. const char* tst_out;
  138. } tests[] = {
  139. { "abcd" , "abcd" },
  140. { "ab cd" , "ab cd" },
  141. { "ab \t\t cd", "ab cd" },
  142. { " abcd" , "abcd" },
  143. { " abcd" , "abcd" },
  144. { " \t\t abcd", "abcd" },
  145. { "abcd " , "abcd " }, // we allow one trailing whitespace char
  146. { "abcd " , "abcd " }, // because it makes no difference and
  147. { "abcd\t " , "abcd " }, // simplifies the implementation
  148. { "\t\t \t ab \t\t \t cd \t\t \t ", "ab cd " },
  149. };
  150. for (unsigned i = 0; i < SK_ARRAY_COUNT(tests); ++i) {
  151. test_whitespace_pos(reporter, tests[i].tst_in, tests[i].tst_out);
  152. }
  153. }
  154. void SetImageShader(SkPaint* paint, int imageWidth, int imageHeight, SkTileMode xTile,
  155. SkTileMode yTile) {
  156. auto surface = SkSurface::MakeRasterN32Premul(imageWidth, imageHeight);
  157. paint->setShader(surface->makeImageSnapshot()->makeShader(xTile, yTile, nullptr));
  158. }
  159. // Attempt to find the three nodes on which we have expectations:
  160. // the pattern node, the image within that pattern, and the rect which
  161. // uses the pattern as a fill.
  162. // returns false if not all nodes are found.
  163. bool FindImageShaderNodes(skiatest::Reporter* reporter, const SkDOM* dom, const SkDOM::Node* root,
  164. const SkDOM::Node** patternOut, const SkDOM::Node** imageOut,
  165. const SkDOM::Node** rectOut) {
  166. if (root == nullptr || dom == nullptr) {
  167. ERRORF(reporter, "root element not found");
  168. return false;
  169. }
  170. const SkDOM::Node* rect = dom->getFirstChild(root, "rect");
  171. if (rect == nullptr) {
  172. ERRORF(reporter, "rect not found");
  173. return false;
  174. }
  175. *rectOut = rect;
  176. const SkDOM::Node* defs = dom->getFirstChild(root, "defs");
  177. if (defs == nullptr) {
  178. ERRORF(reporter, "defs not found");
  179. return false;
  180. }
  181. const SkDOM::Node* pattern = dom->getFirstChild(defs, "pattern");
  182. if (pattern == nullptr) {
  183. ERRORF(reporter, "pattern not found");
  184. return false;
  185. }
  186. *patternOut = pattern;
  187. const SkDOM::Node* image = dom->getFirstChild(pattern, "image");
  188. if (image == nullptr) {
  189. ERRORF(reporter, "image not found");
  190. return false;
  191. }
  192. *imageOut = image;
  193. return true;
  194. }
  195. void ImageShaderTestSetup(SkDOM* dom, SkPaint* paint, int imageWidth, int imageHeight,
  196. int rectWidth, int rectHeight, SkTileMode xTile, SkTileMode yTile) {
  197. SetImageShader(paint, imageWidth, imageHeight, xTile, yTile);
  198. auto svgCanvas = MakeDOMCanvas(dom);
  199. SkRect bounds{0, 0, SkIntToScalar(rectWidth), SkIntToScalar(rectHeight)};
  200. svgCanvas->drawRect(bounds, *paint);
  201. }
  202. DEF_TEST(SVGDevice_image_shader_norepeat, reporter) {
  203. SkDOM dom;
  204. SkPaint paint;
  205. int imageWidth = 3, imageHeight = 3;
  206. int rectWidth = 10, rectHeight = 10;
  207. ImageShaderTestSetup(&dom, &paint, imageWidth, imageHeight, rectWidth, rectHeight,
  208. SkTileMode::kClamp, SkTileMode::kClamp);
  209. const SkDOM::Node* root = dom.finishParsing();
  210. const SkDOM::Node *patternNode, *imageNode, *rectNode;
  211. bool structureAppropriate =
  212. FindImageShaderNodes(reporter, &dom, root, &patternNode, &imageNode, &rectNode);
  213. REPORTER_ASSERT(reporter, structureAppropriate);
  214. // the image should always maintain its size.
  215. REPORTER_ASSERT(reporter, atoi(dom.findAttr(imageNode, "width")) == imageWidth);
  216. REPORTER_ASSERT(reporter, atoi(dom.findAttr(imageNode, "height")) == imageHeight);
  217. // making the pattern as large as the container prevents
  218. // it from repeating.
  219. REPORTER_ASSERT(reporter, strcmp(dom.findAttr(patternNode, "width"), "100%") == 0);
  220. REPORTER_ASSERT(reporter, strcmp(dom.findAttr(patternNode, "height"), "100%") == 0);
  221. }
  222. DEF_TEST(SVGDevice_image_shader_tilex, reporter) {
  223. SkDOM dom;
  224. SkPaint paint;
  225. int imageWidth = 3, imageHeight = 3;
  226. int rectWidth = 10, rectHeight = 10;
  227. ImageShaderTestSetup(&dom, &paint, imageWidth, imageHeight, rectWidth, rectHeight,
  228. SkTileMode::kRepeat, SkTileMode::kClamp);
  229. const SkDOM::Node* root = dom.finishParsing();
  230. const SkDOM::Node* innerSvg = dom.getFirstChild(root, "svg");
  231. if (innerSvg == nullptr) {
  232. ERRORF(reporter, "inner svg element not found");
  233. return;
  234. }
  235. const SkDOM::Node *patternNode, *imageNode, *rectNode;
  236. bool structureAppropriate =
  237. FindImageShaderNodes(reporter, &dom, innerSvg, &patternNode, &imageNode, &rectNode);
  238. REPORTER_ASSERT(reporter, structureAppropriate);
  239. // the imageNode should always maintain its size.
  240. REPORTER_ASSERT(reporter, atoi(dom.findAttr(imageNode, "width")) == imageWidth);
  241. REPORTER_ASSERT(reporter, atoi(dom.findAttr(imageNode, "height")) == imageHeight);
  242. // if the patternNode width matches the imageNode width,
  243. // it will repeat in along the x axis.
  244. REPORTER_ASSERT(reporter, atoi(dom.findAttr(patternNode, "width")) == imageWidth);
  245. REPORTER_ASSERT(reporter, strcmp(dom.findAttr(patternNode, "height"), "100%") == 0);
  246. }
  247. DEF_TEST(SVGDevice_image_shader_tiley, reporter) {
  248. SkDOM dom;
  249. SkPaint paint;
  250. int imageNodeWidth = 3, imageNodeHeight = 3;
  251. int rectNodeWidth = 10, rectNodeHeight = 10;
  252. ImageShaderTestSetup(&dom, &paint, imageNodeWidth, imageNodeHeight, rectNodeWidth,
  253. rectNodeHeight, SkTileMode::kClamp, SkTileMode::kRepeat);
  254. const SkDOM::Node* root = dom.finishParsing();
  255. const SkDOM::Node* innerSvg = dom.getFirstChild(root, "svg");
  256. if (innerSvg == nullptr) {
  257. ERRORF(reporter, "inner svg element not found");
  258. return;
  259. }
  260. const SkDOM::Node *patternNode, *imageNode, *rectNode;
  261. bool structureAppropriate =
  262. FindImageShaderNodes(reporter, &dom, innerSvg, &patternNode, &imageNode, &rectNode);
  263. REPORTER_ASSERT(reporter, structureAppropriate);
  264. // the imageNode should always maintain its size.
  265. REPORTER_ASSERT(reporter, atoi(dom.findAttr(imageNode, "width")) == imageNodeWidth);
  266. REPORTER_ASSERT(reporter, atoi(dom.findAttr(imageNode, "height")) == imageNodeHeight);
  267. // making the patternNode as large as the container prevents
  268. // it from repeating.
  269. REPORTER_ASSERT(reporter, strcmp(dom.findAttr(patternNode, "width"), "100%") == 0);
  270. REPORTER_ASSERT(reporter, atoi(dom.findAttr(patternNode, "height")) == imageNodeHeight);
  271. }
  272. DEF_TEST(SVGDevice_image_shader_tileboth, reporter) {
  273. SkDOM dom;
  274. SkPaint paint;
  275. int imageWidth = 3, imageHeight = 3;
  276. int rectWidth = 10, rectHeight = 10;
  277. ImageShaderTestSetup(&dom, &paint, imageWidth, imageHeight, rectWidth, rectHeight,
  278. SkTileMode::kRepeat, SkTileMode::kRepeat);
  279. const SkDOM::Node* root = dom.finishParsing();
  280. const SkDOM::Node *patternNode, *imageNode, *rectNode;
  281. const SkDOM::Node* innerSvg = dom.getFirstChild(root, "svg");
  282. if (innerSvg == nullptr) {
  283. ERRORF(reporter, "inner svg element not found");
  284. return;
  285. }
  286. bool structureAppropriate =
  287. FindImageShaderNodes(reporter, &dom, innerSvg, &patternNode, &imageNode, &rectNode);
  288. REPORTER_ASSERT(reporter, structureAppropriate);
  289. // the imageNode should always maintain its size.
  290. REPORTER_ASSERT(reporter, atoi(dom.findAttr(imageNode, "width")) == imageWidth);
  291. REPORTER_ASSERT(reporter, atoi(dom.findAttr(imageNode, "height")) == imageHeight);
  292. REPORTER_ASSERT(reporter, atoi(dom.findAttr(patternNode, "width")) == imageWidth);
  293. REPORTER_ASSERT(reporter, atoi(dom.findAttr(patternNode, "height")) == imageHeight);
  294. }
  295. DEF_TEST(SVGDevice_ColorFilters, reporter) {
  296. SkDOM dom;
  297. SkPaint paint;
  298. paint.setColorFilter(SkColorFilters::Blend(SK_ColorRED, SkBlendMode::kSrcIn));
  299. {
  300. auto svgCanvas = MakeDOMCanvas(&dom);
  301. SkRect bounds{0, 0, SkIntToScalar(100), SkIntToScalar(100)};
  302. svgCanvas->drawRect(bounds, paint);
  303. }
  304. const SkDOM::Node* rootElement = dom.finishParsing();
  305. ABORT_TEST(reporter, !rootElement, "root element not found");
  306. const SkDOM::Node* filterElement = dom.getFirstChild(rootElement, "filter");
  307. ABORT_TEST(reporter, !filterElement, "filter element not found");
  308. const SkDOM::Node* floodElement = dom.getFirstChild(filterElement, "feFlood");
  309. ABORT_TEST(reporter, !floodElement, "feFlood element not found");
  310. const SkDOM::Node* compositeElement = dom.getFirstChild(filterElement, "feComposite");
  311. ABORT_TEST(reporter, !compositeElement, "feComposite element not found");
  312. REPORTER_ASSERT(reporter, strcmp(dom.findAttr(filterElement, "width"), "100%") == 0);
  313. REPORTER_ASSERT(reporter, strcmp(dom.findAttr(filterElement, "height"), "100%") == 0);
  314. REPORTER_ASSERT(reporter,
  315. strcmp(dom.findAttr(floodElement, "flood-color"), "rgb(255,0,0)") == 0);
  316. REPORTER_ASSERT(reporter, atoi(dom.findAttr(floodElement, "flood-opacity")) == 1);
  317. REPORTER_ASSERT(reporter, strcmp(dom.findAttr(compositeElement, "in"), "flood") == 0);
  318. REPORTER_ASSERT(reporter, strcmp(dom.findAttr(compositeElement, "operator"), "in") == 0);
  319. }
  320. DEF_TEST(SVGDevice_textpath, reporter) {
  321. SkDOM dom;
  322. SkFont font(ToolUtils::create_portable_typeface());
  323. SkPaint paint;
  324. // By default, we emit <text> nodes.
  325. {
  326. auto svgCanvas = MakeDOMCanvas(&dom);
  327. svgCanvas->drawString("foo", 100, 100, font, paint);
  328. }
  329. const auto* rootElement = dom.finishParsing();
  330. REPORTER_ASSERT(reporter, rootElement, "root element not found");
  331. const auto* textElement = dom.getFirstChild(rootElement, "text");
  332. REPORTER_ASSERT(reporter, textElement, "text element not found");
  333. const auto* pathElement = dom.getFirstChild(rootElement, "path");
  334. REPORTER_ASSERT(reporter, !pathElement, "path element found");
  335. // With kConvertTextToPaths_Flag, we emit <path> nodes.
  336. {
  337. auto svgCanvas = MakeDOMCanvas(&dom, SkSVGCanvas::kConvertTextToPaths_Flag);
  338. svgCanvas->drawString("foo", 100, 100, font, paint);
  339. }
  340. rootElement = dom.finishParsing();
  341. REPORTER_ASSERT(reporter, rootElement, "root element not found");
  342. textElement = dom.getFirstChild(rootElement, "text");
  343. REPORTER_ASSERT(reporter, !textElement, "text element found");
  344. pathElement = dom.getFirstChild(rootElement, "path");
  345. REPORTER_ASSERT(reporter, pathElement, "path element not found");
  346. }
  347. #endif