SkSVGPaintState.cpp revision 80bacfeb4bda06541e8695bd502229727bccfeab
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
15SkSVGAttribute 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
39const int SkSVGPaint::kAttributesSize = SK_ARRAY_COUNT(SkSVGPaint::gAttributes);
40
41SkSVGPaint::SkSVGPaint() : fNext(NULL) {
42}
43
44SkString* 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
52void 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 == NULL)
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
101bool SkSVGPaint::flush(SkSVGParser& parser, bool isFlushable, bool isDef) {
102    SkSVGPaint current;
103    SkSVGPaint* walking = parser.fHead;
104    int index;
105    while (walking != NULL) {
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    }
178setLast:
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
187int SkSVGPaint::getAttributes(const SkSVGAttribute** attrPtr) {
188    *attrPtr = gAttributes;
189    return kAttributesSize;
190}
191
192void 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 != NULL) {
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 != NULL);
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
270bool 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");
312fillStrokeAttrCommon:
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
353bool 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:
399addColor:
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
446void SkSVGPaint::Push(SkSVGPaint** head, SkSVGPaint* newRecord) {
447    newRecord->fNext = *head;
448    *head = newRecord;
449}
450
451void SkSVGPaint::Pop(SkSVGPaint** head) {
452    SkSVGPaint* next = (*head)->fNext;
453    *head = next;
454}
455
456