Home | History | Annotate | Download | only in svg
      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 
     49 SkSVGPaint::SkSVGPaint() : fNext(NULL) {
     50 }
     51 
     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 
     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 
    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 
    195 int SkSVGPaint::getAttributes(const SkSVGAttribute** attrPtr) {
    196     *attrPtr = gAttributes;
    197     return kAttributesSize;
    198 }
    199 
    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 
    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                     const 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 
    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 
    454 void SkSVGPaint::Push(SkSVGPaint** head, SkSVGPaint* newRecord) {
    455     newRecord->fNext = *head;
    456     *head = newRecord;
    457 }
    458 
    459 void SkSVGPaint::Pop(SkSVGPaint** head) {
    460     SkSVGPaint* next = (*head)->fNext;
    461     *head = next;
    462 }
    463 
    464