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