1 
2 /*
3  * Copyright 2006 The Android Open Source Project
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 
9 
10 #include "SkSVGPaintState.h"
11 #include "SkSVGElements.h"
12 #include "SkSVGParser.h"
13 #include "SkParse.h"
14 
15 SkSVGAttribute SkSVGPaint::gAttributes[] = {
16     SVG_LITERAL_ATTRIBUTE(clip-path, f_clipPath),
17     SVG_LITERAL_ATTRIBUTE(clip-rule, f_clipRule),
18     SVG_LITERAL_ATTRIBUTE(enable-background, f_enableBackground),
19     SVG_ATTRIBUTE(fill),
20     SVG_LITERAL_ATTRIBUTE(fill-rule, f_fillRule),
21     SVG_ATTRIBUTE(filter),
22     SVG_LITERAL_ATTRIBUTE(font-family, f_fontFamily),
23     SVG_LITERAL_ATTRIBUTE(font-size, f_fontSize),
24     SVG_LITERAL_ATTRIBUTE(letter-spacing, f_letterSpacing),
25     SVG_ATTRIBUTE(mask),
26     SVG_ATTRIBUTE(opacity),
27     SVG_LITERAL_ATTRIBUTE(stop-color, f_stopColor),
28     SVG_LITERAL_ATTRIBUTE(stop-opacity, f_stopOpacity),
29     SVG_ATTRIBUTE(stroke),
30     SVG_LITERAL_ATTRIBUTE(stroke-dasharray, f_strokeDasharray),
31     SVG_LITERAL_ATTRIBUTE(stroke-linecap, f_strokeLinecap),
32     SVG_LITERAL_ATTRIBUTE(stroke-linejoin, f_strokeLinejoin),
33     SVG_LITERAL_ATTRIBUTE(stroke-miterlimit, f_strokeMiterlimit),
34     SVG_LITERAL_ATTRIBUTE(stroke-width, f_strokeWidth),
35     SVG_ATTRIBUTE(style),
36     SVG_ATTRIBUTE(transform)
37 };
38 
39 const int SkSVGPaint::kAttributesSize = SK_ARRAY_COUNT(SkSVGPaint::gAttributes);
40 
SkSVGPaint()41 SkSVGPaint::SkSVGPaint() : fNext(nullptr) {
42 }
43 
operator [](int index)44 SkString* SkSVGPaint::operator[](int index) {
45     SkASSERT(index >= 0);
46     SkASSERT(index < &fTerminal - &fInitial);
47     SkASSERT(&fTerminal - &fInitial == kTerminal - kInitial);
48     SkString* result = &fInitial + index + 1;
49     return result;
50 }
51 
addAttribute(SkSVGParser & parser,int attrIndex,const char * attrValue,size_t attrLength)52 void SkSVGPaint::addAttribute(SkSVGParser& parser, int attrIndex,
53         const char* attrValue, size_t attrLength) {
54     SkString* attr = (*this)[attrIndex];
55     switch(attrIndex) {
56         case kClipPath:
57         case kClipRule:
58         case kEnableBackground:
59         case kFill:
60         case kFillRule:
61         case kFilter:
62         case kFontFamily:
63         case kFontSize:
64         case kLetterSpacing:
65         case kMask:
66         case kOpacity:
67         case kStopColor:
68         case kStopOpacity:
69         case kStroke:
70         case kStroke_Dasharray:
71         case kStroke_Linecap:
72         case kStroke_Linejoin:
73         case kStroke_Miterlimit:
74         case kStroke_Width:
75         case kTransform:
76             attr->set(attrValue, attrLength);
77             return;
78         case kStyle: {
79             // iterate through colon / semi-colon delimited pairs
80             int pairs = SkParse::Count(attrValue, ';');
81             const char* attrEnd = attrValue + attrLength;
82             do {
83                 const char* end = strchr(attrValue, ';');
84                 if (end == nullptr)
85                     end = attrEnd;
86                 const char* delimiter = strchr(attrValue, ':');
87                 SkASSERT(delimiter != 0 && delimiter < end);
88                 int index = parser.findAttribute(this, attrValue, (int) (delimiter - attrValue), true);
89                 SkASSERT(index >= 0);
90                 delimiter++;
91                 addAttribute(parser, index, delimiter, (int) (end - delimiter));
92                 attrValue = end + 1;
93             } while (--pairs);
94             return;
95             }
96         default:
97             SkASSERT(0);
98     }
99 }
100 
flush(SkSVGParser & parser,bool isFlushable,bool isDef)101 bool SkSVGPaint::flush(SkSVGParser& parser, bool isFlushable, bool isDef) {
102     SkSVGPaint current;
103     SkSVGPaint* walking = parser.fHead;
104     int index;
105     while (walking != nullptr) {
106         for (index = kInitial + 1; index < kTerminal; index++) {
107             SkString* lastAttr = (*walking)[index];
108             if (lastAttr->size() == 0)
109                 continue;
110             if (current[index]->size() > 0)
111                 continue;
112             current[index]->set(*lastAttr);
113         }
114         walking = walking->fNext;
115     }
116     bool paintChanged = false;
117     SkSVGPaint& lastState = parser.fLastFlush;
118     if (isFlushable == false) {
119         if (isDef == true) {
120             if (current.f_mask.size() > 0 && current.f_mask.equals(lastState.f_mask) == false) {
121                 SkSVGElement* found;
122                 const char* idStart = strchr(current.f_mask.c_str(), '#');
123                 SkASSERT(idStart);
124                 SkString id(idStart + 1, strlen(idStart) - 2);
125                 bool itsFound = parser.fIDs.find(id.c_str(), &found);
126                 SkASSERT(itsFound);
127                 SkSVGElement* gradient = found->getGradient();
128                 if (gradient) {
129                     gradient->write(parser, current.f_fill);
130                     gradient->write(parser, current.f_stroke);
131                 }
132             }
133         }
134         goto setLast;
135     }
136     {
137         bool changed[kTerminal];
138         memset(changed, 0, sizeof(changed));
139         for (index = kInitial + 1; index < kTerminal; index++) {
140             if (index == kTransform || index == kClipPath || index == kStopColor || index == kStopOpacity ||
141                     index == kClipRule || index == kFillRule)
142                 continue;
143             SkString* lastAttr = lastState[index];
144             SkString* currentAttr = current[index];
145             paintChanged |= changed[index] = lastAttr->equals(*currentAttr) == false;
146         }
147         if (paintChanged) {
148             if (current.f_mask.size() > 0) {
149                 if (current.f_fill.equals("none") == false && strncmp(current.f_fill.c_str(), "url(#", 5) != 0) {
150                     SkASSERT(current.f_fill.c_str()[0] == '#');
151                     SkString replacement("url(#mask");
152                     replacement.append(current.f_fill.c_str() + 1);
153                     replacement.appendUnichar(')');
154                     current.f_fill.set(replacement);
155                 }
156                 if (current.f_stroke.equals("none") == false && strncmp(current.f_stroke.c_str(), "url(#", 5) != 0) {
157                     SkASSERT(current.f_stroke.c_str()[0] == '#');
158                     SkString replacement("url(#mask");
159                     replacement.append(current.f_stroke.c_str() + 1);
160                     replacement.appendUnichar(')');
161                     current.f_stroke.set(replacement);
162                 }
163             }
164             if (current.f_fill.equals("none") && current.f_stroke.equals("none"))
165                 current.f_opacity.set("0");
166             if (parser.fSuppressPaint == false) {
167                 parser._startElement("paint");
168                 bool success = writeChangedAttributes(parser, current, changed);
169                 if (success == false)
170                     return paintChanged;
171                 success = writeChangedElements(parser, current, changed);
172                 if (success == false)
173                     return paintChanged;
174                 parser._endElement(); // paint
175             }
176         }
177     }
178 setLast:
179     for (index = kInitial + 1; index < kTerminal; index++) {
180         SkString* lastAttr = lastState[index];
181         SkString* currentAttr = current[index];
182         lastAttr->set(*currentAttr);
183     }
184     return paintChanged;
185 }
186 
getAttributes(const SkSVGAttribute ** attrPtr)187 int SkSVGPaint::getAttributes(const SkSVGAttribute** attrPtr) {
188     *attrPtr = gAttributes;
189     return kAttributesSize;
190 }
191 
setSave(SkSVGParser & parser)192 void SkSVGPaint::setSave(SkSVGParser& parser) {
193     SkTDArray<SkString*> clips;
194     SkSVGPaint* walking = parser.fHead;
195     int index;
196     SkMatrix sum;
197     sum.reset();
198     while (walking != nullptr) {
199         for (index = kInitial + 1; index < kTerminal; index++) {
200             SkString* lastAttr = (*walking)[index];
201             if (lastAttr->size() == 0)
202                 continue;
203             if (index == kTransform) {
204                 const char* str = lastAttr->c_str();
205                 SkASSERT(strncmp(str, "matrix(", 7) == 0);
206                 str += 6;
207                 const char* strEnd = strrchr(str, ')');
208                 SkASSERT(strEnd != nullptr);
209                 SkString mat(str, strEnd - str);
210                 SkSVGParser::ConvertToArray(mat);
211                 SkScalar values[6];
212                 SkParse::FindScalars(mat.c_str() + 1, values, 6);
213                 SkMatrix matrix;
214                 matrix.reset();
215                 matrix.setScaleX(values[0]);
216                 matrix.setSkewY(values[1]);
217                 matrix.setSkewX(values[2]);
218                 matrix.setScaleY(values[3]);
219                 matrix.setTranslateX(values[4]);
220                 matrix.setTranslateY(values[5]);
221                 sum.setConcat(matrix, sum);
222                 continue;
223             }
224             if ( index == kClipPath)
225                 *clips.insert(0) = lastAttr;
226         }
227         walking = walking->fNext;
228     }
229     if ((sum == parser.fLastTransform) == false) {
230         SkMatrix inverse;
231         bool success = parser.fLastTransform.invert(&inverse);
232         SkASSERT(success == true);
233         SkMatrix output;
234         output.setConcat(inverse, sum);
235         parser.fLastTransform = sum;
236         SkString outputStr;
237         outputStr.appendUnichar('[');
238         outputStr.appendScalar(output.getScaleX());
239         outputStr.appendUnichar(',');
240         outputStr.appendScalar(output.getSkewX());
241         outputStr.appendUnichar(',');
242         outputStr.appendScalar(output.getTranslateX());
243         outputStr.appendUnichar(',');
244         outputStr.appendScalar(output.getSkewY());
245         outputStr.appendUnichar(',');
246         outputStr.appendScalar(output.getScaleY());
247         outputStr.appendUnichar(',');
248         outputStr.appendScalar(output.getTranslateY());
249         outputStr.appendUnichar(',');
250         outputStr.appendScalar(output.getPerspX());
251         outputStr.appendUnichar(',');
252         outputStr.appendScalar(output.getPerspY());
253         outputStr.append(",1]");
254         parser._startElement("matrix");
255         parser._addAttributeLen("matrix", outputStr.c_str(), outputStr.size());
256         parser._endElement();
257     }
258 #if 0   // incomplete
259     if (parser.fTransformClips.size() > 0) {
260         // need to reset the clip when the 'g' scope is ended
261         parser._startElement("add");
262         const char* start = strchr(current->f_clipPath.c_str(), '#') + 1;
263         SkASSERT(start);
264         parser._addAttributeLen("use", start, strlen(start) - 1);
265         parser._endElement();   // clip
266     }
267 #endif
268 }
269 
writeChangedAttributes(SkSVGParser & parser,SkSVGPaint & current,bool * changed)270 bool SkSVGPaint::writeChangedAttributes(SkSVGParser& parser,
271         SkSVGPaint& current, bool* changed) {
272     SkSVGPaint& lastState = parser.fLastFlush;
273     for (int index = kInitial + 1; index < kTerminal; index++) {
274         if (changed[index] == false)
275                 continue;
276         SkString* topAttr = current[index];
277         size_t attrLength = topAttr->size();
278         if (attrLength == 0)
279             continue;
280         const char* attrValue = topAttr->c_str();
281         SkString* lastAttr = lastState[index];
282         switch(index) {
283             case kClipPath:
284             case kClipRule:
285             case kEnableBackground:
286                 break;
287             case kFill:
288                 if (topAttr->equals("none") == false && lastAttr->equals("none") == true)
289                     parser._addAttribute("stroke", "false");
290                 goto fillStrokeAttrCommon;
291             case kFillRule:
292             case kFilter:
293             case kFontFamily:
294                 break;
295             case kFontSize:
296                 parser._addAttributeLen("textSize", attrValue, attrLength);
297                 break;
298             case kLetterSpacing:
299                 parser._addAttributeLen("textTracking", attrValue, attrLength);
300                 break;
301             case kMask:
302                 break;
303             case kOpacity:
304                 break;
305             case kStopColor:
306                 break;
307             case kStopOpacity:
308                 break;
309             case kStroke:
310                 if (topAttr->equals("none") == false && lastAttr->equals("none") == true)
311                     parser._addAttribute("stroke", "true");
312 fillStrokeAttrCommon:
313                 if (strncmp(attrValue, "url(", 4) == 0) {
314                     SkASSERT(attrValue[4] == '#');
315                     const char* idStart = attrValue + 5;
316                     const char* idEnd = strrchr(attrValue, ')');
317                     SkASSERT(idStart < idEnd);
318                     SkString id(idStart, idEnd - idStart);
319                     SkSVGElement* found;
320                     if (strncmp(id.c_str(), "mask", 4) != 0) {
321                         bool itsFound = parser.fIDs.find(id.c_str(), &found);
322                         SkASSERT(itsFound);
323                         SkASSERT(found->getType() == SkSVGType_LinearGradient ||
324                             found->getType() == SkSVGType_RadialGradient);
325                     }
326                     parser._addAttribute("shader", id.c_str());
327                 }
328                 break;
329             case kStroke_Dasharray:
330                 break;
331             case kStroke_Linecap:
332                 parser._addAttributeLen("strokeCap", attrValue, attrLength);
333                 break;
334             case kStroke_Linejoin:
335                 parser._addAttributeLen("strokeJoin", attrValue, attrLength);
336                 break;
337             case kStroke_Miterlimit:
338                 parser._addAttributeLen("strokeMiter", attrValue, attrLength);
339                 break;
340             case kStroke_Width:
341                 parser._addAttributeLen("strokeWidth", attrValue, attrLength);
342             case kStyle:
343             case kTransform:
344                 break;
345         default:
346             SkASSERT(0);
347             return false;
348         }
349     }
350     return true;
351 }
352 
writeChangedElements(SkSVGParser & parser,SkSVGPaint & current,bool * changed)353 bool SkSVGPaint::writeChangedElements(SkSVGParser& parser,
354         SkSVGPaint& current, bool* changed) {
355     SkSVGPaint& lastState = parser.fLastFlush;
356     for (int index = kInitial + 1; index < kTerminal; index++) {
357         SkString* topAttr = current[index];
358         size_t attrLength = topAttr->size();
359         if (attrLength == 0)
360             continue;
361         const char* attrValue = topAttr->c_str();
362         SkString* lastAttr = lastState[index];
363         switch(index) {
364             case kClipPath:
365             case kClipRule:
366                 // !!! need to add this outside of paint
367                 break;
368             case kEnableBackground:
369                 // !!! don't know what to do with this
370                 break;
371             case kFill:
372                 goto addColor;
373             case kFillRule:
374             case kFilter:
375                 break;
376             case kFontFamily:
377                 parser._startElement("typeface");
378                 parser._addAttributeLen("fontName", attrValue, attrLength);
379                 parser._endElement();   // typeface
380                 break;
381             case kFontSize:
382             case kLetterSpacing:
383                 break;
384             case kMask:
385             case kOpacity:
386                 if (changed[kStroke] == false && changed[kFill] == false) {
387                     parser._startElement("color");
388                     SkString& opacity = current.f_opacity;
389                     parser._addAttributeLen("color", parser.fLastColor.c_str(), parser.fLastColor.size());
390                     parser._addAttributeLen("alpha", opacity.c_str(), opacity.size());
391                     parser._endElement();   // color
392                 }
393                 break;
394             case kStopColor:
395                 break;
396             case kStopOpacity:
397                 break;
398             case kStroke:
399 addColor:
400                 if (strncmp(lastAttr->c_str(), "url(", 4) == 0 && strncmp(attrValue, "url(", 4) != 0) {
401                     parser._startElement("shader");
402                     parser._endElement();
403                 }
404                 if (topAttr->equals(*lastAttr))
405                     continue;
406                 {
407                     bool urlRef = strncmp(attrValue, "url(", 4) == 0;
408                     bool colorNone = strcmp(attrValue, "none") == 0;
409                     bool lastEqual = parser.fLastColor.equals(attrValue, attrLength);
410                     bool newColor = urlRef == false && colorNone == false && lastEqual == false;
411                     if (newColor || changed[kOpacity]) {
412                         parser._startElement("color");
413                         if (newColor || changed[kOpacity]) {
414                             parser._addAttributeLen("color", attrValue, attrLength);
415                             parser.fLastColor.set(attrValue, attrLength);
416                         }
417                         if (changed[kOpacity]) {
418                             SkString& opacity = current.f_opacity;
419                             parser._addAttributeLen("alpha", opacity.c_str(), opacity.size());
420                         }
421                         parser._endElement();   // color
422                     }
423                 }
424                 break;
425             case kStroke_Dasharray:
426                 parser._startElement("dash");
427                 SkSVGParser::ConvertToArray(*topAttr);
428                 parser._addAttribute("intervals", topAttr->c_str());
429                 parser._endElement();   // dash
430             break;
431             case kStroke_Linecap:
432             case kStroke_Linejoin:
433             case kStroke_Miterlimit:
434             case kStroke_Width:
435             case kStyle:
436             case kTransform:
437                 break;
438         default:
439             SkASSERT(0);
440             return false;
441         }
442     }
443     return true;
444 }
445 
Push(SkSVGPaint ** head,SkSVGPaint * newRecord)446 void SkSVGPaint::Push(SkSVGPaint** head, SkSVGPaint* newRecord) {
447     newRecord->fNext = *head;
448     *head = newRecord;
449 }
450 
Pop(SkSVGPaint ** head)451 void SkSVGPaint::Pop(SkSVGPaint** head) {
452     SkSVGPaint* next = (*head)->fNext;
453     *head = next;
454 }
455