blob: b55a35fc5653607aec1956ce6e27601c3c9b43ec [file] [log] [blame]
fmalita7a048692015-02-20 13:54:40 -08001/*
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
Hal Canaryff2742e2018-01-30 11:35:47 -05008
Alexander Midlash77e3afc2018-03-06 17:21:28 -08009#include "SkBitmap.h"
fmalita7a048692015-02-20 13:54:40 -080010#include "SkCanvas.h"
Alexander Midlash77e3afc2018-03-06 17:21:28 -080011#include "SkData.h"
12#include "SkImage.h"
13#include "SkImageShader.h"
fmalita7a048692015-02-20 13:54:40 -080014#include "SkParse.h"
Alexander Midlash77e3afc2018-03-06 17:21:28 -080015#include "SkShader.h"
16#include "SkStream.h"
fmalita7a048692015-02-20 13:54:40 -080017#include "Test.h"
18
19#include <string.h>
20
Alexander Midlash77e3afc2018-03-06 17:21:28 -080021#ifdef SK_XML
22
Ben Wagner6ce482a2018-03-27 10:57:43 -040023#include "SkDOM.h"
24#include "SkSVGCanvas.h"
25#include "SkXMLWriter.h"
26
fmalita7a048692015-02-20 13:54:40 -080027namespace {
28
Alexander Midlash77e3afc2018-03-06 17:21:28 -080029
fmalita7a048692015-02-20 13:54:40 -080030void check_text_node(skiatest::Reporter* reporter,
31 const SkDOM& dom,
32 const SkDOM::Node* root,
33 const SkPoint& offset,
34 unsigned scalarsPerPos,
35 const char* expected) {
halcanary96fcdcc2015-08-27 07:41:13 -070036 if (root == nullptr) {
fmalita7a048692015-02-20 13:54:40 -080037 ERRORF(reporter, "root element not found.");
38 return;
39 }
40
41 const SkDOM::Node* textElem = dom.getFirstChild(root, "text");
halcanary96fcdcc2015-08-27 07:41:13 -070042 if (textElem == nullptr) {
fmalita7a048692015-02-20 13:54:40 -080043 ERRORF(reporter, "<text> element not found.");
44 return;
45 }
46 REPORTER_ASSERT(reporter, dom.getType(textElem) == SkDOM::kElement_Type);
47
48 const SkDOM::Node* textNode= dom.getFirstChild(textElem);
halcanary96fcdcc2015-08-27 07:41:13 -070049 REPORTER_ASSERT(reporter, textNode != nullptr);
50 if (textNode != nullptr) {
fmalita7a048692015-02-20 13:54:40 -080051 REPORTER_ASSERT(reporter, dom.getType(textNode) == SkDOM::kText_Type);
52 REPORTER_ASSERT(reporter, strcmp(expected, dom.getName(textNode)) == 0);
53 }
54
55 int textLen = SkToInt(strlen(expected));
56
57 const char* x = dom.findAttr(textElem, "x");
halcanary96fcdcc2015-08-27 07:41:13 -070058 REPORTER_ASSERT(reporter, x != nullptr);
59 if (x != nullptr) {
fmalita7a048692015-02-20 13:54:40 -080060 int xposCount = (scalarsPerPos < 1) ? 1 : textLen;
61 REPORTER_ASSERT(reporter, SkParse::Count(x) == xposCount);
62
63 SkAutoTMalloc<SkScalar> xpos(xposCount);
64 SkParse::FindScalars(x, xpos.get(), xposCount);
65 if (scalarsPerPos < 1) {
66 REPORTER_ASSERT(reporter, xpos[0] == offset.x());
67 } else {
68 for (int i = 0; i < xposCount; ++i) {
69 REPORTER_ASSERT(reporter, xpos[i] == SkIntToScalar(expected[i]));
70 }
71 }
72 }
73
74 const char* y = dom.findAttr(textElem, "y");
halcanary96fcdcc2015-08-27 07:41:13 -070075 REPORTER_ASSERT(reporter, y != nullptr);
76 if (y != nullptr) {
fmalita7a048692015-02-20 13:54:40 -080077 int yposCount = (scalarsPerPos < 2) ? 1 : textLen;
78 REPORTER_ASSERT(reporter, SkParse::Count(y) == yposCount);
79
80 SkAutoTMalloc<SkScalar> ypos(yposCount);
81 SkParse::FindScalars(y, ypos.get(), yposCount);
82 if (scalarsPerPos < 2) {
83 REPORTER_ASSERT(reporter, ypos[0] == offset.y());
84 } else {
85 for (int i = 0; i < yposCount; ++i) {
86 REPORTER_ASSERT(reporter, ypos[i] == -SkIntToScalar(expected[i]));
87 }
88 }
89 }
90}
91
92void test_whitespace_pos(skiatest::Reporter* reporter,
93 const char* txt,
94 const char* expected) {
95 size_t len = strlen(txt);
96
97 SkDOM dom;
98 SkPaint paint;
99 SkPoint offset = SkPoint::Make(10, 20);
100
101 {
102 SkXMLParserWriter writer(dom.beginParsing());
Mike Reed5df49342016-11-12 08:06:55 -0600103 std::unique_ptr<SkCanvas> svgCanvas = SkSVGCanvas::Make(SkRect::MakeWH(100, 100), &writer);
fmalita7a048692015-02-20 13:54:40 -0800104 svgCanvas->drawText(txt, len, offset.x(), offset.y(), paint);
105 }
106 check_text_node(reporter, dom, dom.finishParsing(), offset, 0, expected);
107
108 {
109 SkAutoTMalloc<SkScalar> xpos(len);
110 for (int i = 0; i < SkToInt(len); ++i) {
111 xpos[i] = SkIntToScalar(txt[i]);
112 }
113
114 SkXMLParserWriter writer(dom.beginParsing());
Mike Reed5df49342016-11-12 08:06:55 -0600115 std::unique_ptr<SkCanvas> svgCanvas = SkSVGCanvas::Make(SkRect::MakeWH(100, 100), &writer);
fmalita7a048692015-02-20 13:54:40 -0800116 svgCanvas->drawPosTextH(txt, len, xpos, offset.y(), paint);
117 }
118 check_text_node(reporter, dom, dom.finishParsing(), offset, 1, expected);
119
120 {
121 SkAutoTMalloc<SkPoint> pos(len);
122 for (int i = 0; i < SkToInt(len); ++i) {
123 pos[i] = SkPoint::Make(SkIntToScalar(txt[i]), -SkIntToScalar(txt[i]));
124 }
125
126 SkXMLParserWriter writer(dom.beginParsing());
Mike Reed5df49342016-11-12 08:06:55 -0600127 std::unique_ptr<SkCanvas> svgCanvas = SkSVGCanvas::Make(SkRect::MakeWH(100, 100), &writer);
fmalita7a048692015-02-20 13:54:40 -0800128 svgCanvas->drawPosText(txt, len, pos, paint);
129 }
130 check_text_node(reporter, dom, dom.finishParsing(), offset, 2, expected);
131}
132
133}
134
135DEF_TEST(SVGDevice_whitespace_pos, reporter) {
136 static const struct {
137 const char* tst_in;
138 const char* tst_out;
139 } tests[] = {
140 { "abcd" , "abcd" },
141 { "ab cd" , "ab cd" },
142 { "ab \t\t cd", "ab cd" },
143 { " abcd" , "abcd" },
144 { " abcd" , "abcd" },
145 { " \t\t abcd", "abcd" },
146 { "abcd " , "abcd " }, // we allow one trailing whitespace char
147 { "abcd " , "abcd " }, // because it makes no difference and
148 { "abcd\t " , "abcd\t" }, // simplifies the implementation
149 { "\t\t \t ab \t\t \t cd \t\t \t ", "ab cd " },
150 };
151
152 for (unsigned i = 0; i < SK_ARRAY_COUNT(tests); ++i) {
153 test_whitespace_pos(reporter, tests[i].tst_in, tests[i].tst_out);
154 }
155}
Hal Canaryff2742e2018-01-30 11:35:47 -0500156
Alexander Midlash77e3afc2018-03-06 17:21:28 -0800157
158void SetImageShader(SkPaint* paint, int imageWidth, int imageHeight, SkShader::TileMode xTile,
159 SkShader::TileMode yTile) {
160 auto surface = SkSurface::MakeRasterN32Premul(imageWidth, imageHeight);
161 paint->setShader(SkImageShader::Make(surface->makeImageSnapshot(), xTile, yTile, nullptr));
162}
163
164// Attempt to find the three nodes on which we have expectations:
165// the pattern node, the image within that pattern, and the rect which
166// uses the pattern as a fill.
167// returns false if not all nodes are found.
168bool FindImageShaderNodes(skiatest::Reporter* reporter, const SkDOM* dom, const SkDOM::Node* root,
169 const SkDOM::Node** patternOut, const SkDOM::Node** imageOut,
170 const SkDOM::Node** rectOut) {
171 if (root == nullptr || dom == nullptr) {
172 ERRORF(reporter, "root element not found");
173 return false;
174 }
175
176
177 const SkDOM::Node* rect = dom->getFirstChild(root, "rect");
178 if (rect == nullptr) {
179 ERRORF(reporter, "rect not found");
180 return false;
181 }
182 *rectOut = rect;
183
184 const SkDOM::Node* defs = dom->getFirstChild(root, "defs");
185 if (defs == nullptr) {
186 ERRORF(reporter, "defs not found");
187 return false;
188 }
189
190 const SkDOM::Node* pattern = dom->getFirstChild(defs, "pattern");
191 if (pattern == nullptr) {
192 ERRORF(reporter, "pattern not found");
193 return false;
194 }
195 *patternOut = pattern;
196
197 const SkDOM::Node* image = dom->getFirstChild(pattern, "image");
198 if (image == nullptr) {
199 ERRORF(reporter, "image not found");
200 return false;
201 }
202 *imageOut = image;
203
204 return true;
205}
206
207void ImageShaderTestSetup(SkDOM* dom, SkPaint* paint, int imageWidth, int imageHeight,
208 int rectWidth, int rectHeight, SkShader::TileMode xTile,
209 SkShader::TileMode yTile) {
210 SetImageShader(paint, imageWidth, imageHeight, xTile, yTile);
211 SkXMLParserWriter writer(dom->beginParsing());
212 std::unique_ptr<SkCanvas> svgCanvas = SkSVGCanvas::Make(SkRect::MakeWH(100, 100), &writer);
213
214 SkRect bounds{0, 0, SkIntToScalar(rectWidth), SkIntToScalar(rectHeight)};
215 svgCanvas->drawRect(bounds, *paint);
216}
217
218
219DEF_TEST(SVGDevice_image_shader_norepeat, reporter) {
220 SkDOM dom;
221 SkPaint paint;
222 int imageWidth = 3, imageHeight = 3;
223 int rectWidth = 10, rectHeight = 10;
224 ImageShaderTestSetup(&dom, &paint, imageWidth, imageHeight, rectWidth, rectHeight,
225 SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
226
227 const SkDOM::Node* root = dom.finishParsing();
228
229 const SkDOM::Node *patternNode, *imageNode, *rectNode;
230 bool structureAppropriate =
231 FindImageShaderNodes(reporter, &dom, root, &patternNode, &imageNode, &rectNode);
232 REPORTER_ASSERT(reporter, structureAppropriate);
233
234 // the image should always maintain its size.
235 REPORTER_ASSERT(reporter, atoi(dom.findAttr(imageNode, "width")) == imageWidth);
236 REPORTER_ASSERT(reporter, atoi(dom.findAttr(imageNode, "height")) == imageHeight);
237
238 // making the pattern as large as the container prevents
239 // it from repeating.
240 REPORTER_ASSERT(reporter, strcmp(dom.findAttr(patternNode, "width"), "100%") == 0);
241 REPORTER_ASSERT(reporter, strcmp(dom.findAttr(patternNode, "height"), "100%") == 0);
242}
243
244DEF_TEST(SVGDevice_image_shader_tilex, reporter) {
245 SkDOM dom;
246 SkPaint paint;
247 int imageWidth = 3, imageHeight = 3;
248 int rectWidth = 10, rectHeight = 10;
249 ImageShaderTestSetup(&dom, &paint, imageWidth, imageHeight, rectWidth, rectHeight,
250 SkShader::kRepeat_TileMode, SkShader::kClamp_TileMode);
251
252 const SkDOM::Node* root = dom.finishParsing();
253 const SkDOM::Node* innerSvg = dom.getFirstChild(root, "svg");
254 if (innerSvg == nullptr) {
255 ERRORF(reporter, "inner svg element not found");
256 return;
257 }
258
259 const SkDOM::Node *patternNode, *imageNode, *rectNode;
260 bool structureAppropriate =
261 FindImageShaderNodes(reporter, &dom, innerSvg, &patternNode, &imageNode, &rectNode);
262 REPORTER_ASSERT(reporter, structureAppropriate);
263
264 // the imageNode should always maintain its size.
265 REPORTER_ASSERT(reporter, atoi(dom.findAttr(imageNode, "width")) == imageWidth);
266 REPORTER_ASSERT(reporter, atoi(dom.findAttr(imageNode, "height")) == imageHeight);
267
268 // if the patternNode width matches the imageNode width,
269 // it will repeat in along the x axis.
270 REPORTER_ASSERT(reporter, atoi(dom.findAttr(patternNode, "width")) == imageWidth);
271 REPORTER_ASSERT(reporter, strcmp(dom.findAttr(patternNode, "height"), "100%") == 0);
272}
273
274DEF_TEST(SVGDevice_image_shader_tiley, reporter) {
275 SkDOM dom;
276 SkPaint paint;
277 int imageNodeWidth = 3, imageNodeHeight = 3;
278 int rectNodeWidth = 10, rectNodeHeight = 10;
279 ImageShaderTestSetup(&dom, &paint, imageNodeWidth, imageNodeHeight, rectNodeWidth,
280 rectNodeHeight, SkShader::kClamp_TileMode, SkShader::kRepeat_TileMode);
281
282 const SkDOM::Node* root = dom.finishParsing();
283 const SkDOM::Node* innerSvg = dom.getFirstChild(root, "svg");
284 if (innerSvg == nullptr) {
285 ERRORF(reporter, "inner svg element not found");
286 return;
287 }
288
289 const SkDOM::Node *patternNode, *imageNode, *rectNode;
290 bool structureAppropriate =
291 FindImageShaderNodes(reporter, &dom, innerSvg, &patternNode, &imageNode, &rectNode);
292 REPORTER_ASSERT(reporter, structureAppropriate);
293
294 // the imageNode should always maintain its size.
295 REPORTER_ASSERT(reporter, atoi(dom.findAttr(imageNode, "width")) == imageNodeWidth);
296 REPORTER_ASSERT(reporter, atoi(dom.findAttr(imageNode, "height")) == imageNodeHeight);
297
298 // making the patternNode as large as the container prevents
299 // it from repeating.
300 REPORTER_ASSERT(reporter, strcmp(dom.findAttr(patternNode, "width"), "100%") == 0);
301 REPORTER_ASSERT(reporter, atoi(dom.findAttr(patternNode, "height")) == imageNodeHeight);
302}
303
304DEF_TEST(SVGDevice_image_shader_tileboth, reporter) {
305 SkDOM dom;
306 SkPaint paint;
307 int imageWidth = 3, imageHeight = 3;
308 int rectWidth = 10, rectHeight = 10;
309 ImageShaderTestSetup(&dom, &paint, imageWidth, imageHeight, rectWidth, rectHeight,
310 SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);
311
312 const SkDOM::Node* root = dom.finishParsing();
313
314 const SkDOM::Node *patternNode, *imageNode, *rectNode;
315 const SkDOM::Node* innerSvg = dom.getFirstChild(root, "svg");
316 if (innerSvg == nullptr) {
317 ERRORF(reporter, "inner svg element not found");
318 return;
319 }
320 bool structureAppropriate =
321 FindImageShaderNodes(reporter, &dom, innerSvg, &patternNode, &imageNode, &rectNode);
322 REPORTER_ASSERT(reporter, structureAppropriate);
323
324 // the imageNode should always maintain its size.
325 REPORTER_ASSERT(reporter, atoi(dom.findAttr(imageNode, "width")) == imageWidth);
326 REPORTER_ASSERT(reporter, atoi(dom.findAttr(imageNode, "height")) == imageHeight);
327
328 REPORTER_ASSERT(reporter, atoi(dom.findAttr(patternNode, "width")) == imageWidth);
329 REPORTER_ASSERT(reporter, atoi(dom.findAttr(patternNode, "height")) == imageHeight);
330}
331
Hal Canaryff2742e2018-01-30 11:35:47 -0500332#endif