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(NULL) {
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 == 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
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 != 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 }
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 != 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
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