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