|
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 |
|
41 SkSVGPaint::SkSVGPaint() : fNext(NULL) { |
|
42 } |
|
43 |
|
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 |
|
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 |
|
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 |
|
187 int SkSVGPaint::getAttributes(const SkSVGAttribute** attrPtr) { |
|
188 *attrPtr = gAttributes; |
|
189 return kAttributesSize; |
|
190 } |
|
191 |
|
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 |
|
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 |
|
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 |
|
446 void SkSVGPaint::Push(SkSVGPaint** head, SkSVGPaint* newRecord) { |
|
447 newRecord->fNext = *head; |
|
448 *head = newRecord; |
|
449 } |
|
450 |
|
451 void SkSVGPaint::Pop(SkSVGPaint** head) { |
|
452 SkSVGPaint* next = (*head)->fNext; |
|
453 *head = next; |
|
454 } |