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