OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkSVGDevice.h" | 8 #include "SkSVGDevice.h" |
9 | 9 |
10 #include "SkBitmap.h" | 10 #include "SkBitmap.h" |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
94 tstr.printf("matrix(%g %g %g %g %g %g)", | 94 tstr.printf("matrix(%g %g %g %g %g %g)", |
95 t.getScaleX(), t.getSkewY(), | 95 t.getScaleX(), t.getSkewY(), |
96 t.getSkewX(), t.getScaleY(), | 96 t.getSkewX(), t.getScaleY(), |
97 t.getTranslateX(), t.getTranslateY()); | 97 t.getTranslateX(), t.getTranslateY()); |
98 break; | 98 break; |
99 } | 99 } |
100 | 100 |
101 return tstr; | 101 return tstr; |
102 } | 102 } |
103 | 103 |
104 static void append_escaped_unichar(SkUnichar c, SkString* text) { | |
105 switch(c) { | |
106 case '&': | |
107 text->append("&"); | |
108 break; | |
109 case '"': | |
110 text->append("""); | |
111 break; | |
112 case '\'': | |
113 text->append("'"); | |
114 break; | |
115 case '<': | |
116 text->append("<"); | |
117 break; | |
118 case '>': | |
119 text->append(">"); | |
120 break; | |
121 default: | |
122 text->appendUnichar(c); | |
123 break; | |
124 } | |
125 } | |
126 | |
127 static SkString svg_text(const void* text, size_t byteLen, const SkPaint& paint) { | |
128 SkString svgText; | |
129 int count = paint.countText(text, byteLen); | |
130 | |
131 switch(paint.getTextEncoding()) { | |
132 case SkPaint::kGlyphID_TextEncoding: { | |
133 SkASSERT(count * sizeof(uint16_t) == byteLen); | |
134 SkAutoSTArray<64, SkUnichar> unichars(count); | |
135 paint.glyphsToUnichars((const uint16_t*)text, count, unichars.get()); | |
136 for (int i = 0; i < count; ++i) { | |
137 append_escaped_unichar(unichars[i], &svgText); | |
138 } | |
139 } break; | |
140 case SkPaint::kUTF8_TextEncoding: { | |
141 const char* c8 = reinterpret_cast<const char*>(text); | |
142 for (int i = 0; i < count; ++i) { | |
143 append_escaped_unichar(SkUTF8_NextUnichar(&c8), &svgText); | |
144 } | |
145 SkASSERT(reinterpret_cast<const char*>(text) + byteLen == c8); | |
146 } break; | |
147 case SkPaint::kUTF16_TextEncoding: { | |
148 const uint16_t* c16 = reinterpret_cast<const uint16_t*>(text); | |
149 for (int i = 0; i < count; ++i) { | |
150 append_escaped_unichar(SkUTF16_NextUnichar(&c16), &svgText); | |
151 } | |
152 SkASSERT(SkIsAlign2(byteLen)); | |
153 SkASSERT(reinterpret_cast<const uint16_t*>(text) + (byteLen / 2) == c16) ; | |
154 } break; | |
155 case SkPaint::kUTF32_TextEncoding: { | |
156 SkASSERT(count * sizeof(uint32_t) == byteLen); | |
157 const uint32_t* c32 = reinterpret_cast<const uint32_t*>(text); | |
158 for (int i = 0; i < count; ++i) { | |
159 append_escaped_unichar(c32[i], &svgText); | |
160 } | |
161 } break; | |
162 default: | |
163 SkFAIL("unknown text encoding"); | |
164 } | |
165 | |
166 return svgText; | |
167 } | |
168 | |
169 uint32_t hash_family_string(const SkString& family) { | 104 uint32_t hash_family_string(const SkString& family) { |
170 // This is a lame hash function, but we don't really expect to see more than 1-2 | 105 // This is a lame hash function, but we don't really expect to see more than 1-2 |
171 // family names under normal circumstances. | 106 // family names under normal circumstances. |
172 return SkChecksum::Mix(SkToU32(family.size())); | 107 return SkChecksum::Mix(SkToU32(family.size())); |
173 } | 108 } |
174 | 109 |
175 struct Resources { | 110 struct Resources { |
176 Resources(const SkPaint& paint) | 111 Resources(const SkPaint& paint) |
177 : fPaintServer(svg_color(paint.getColor())) {} | 112 : fPaintServer(svg_color(paint.getColor())) {} |
178 | 113 |
179 SkString fPaintServer; | 114 SkString fPaintServer; |
180 SkString fClip; | 115 SkString fClip; |
181 }; | 116 }; |
182 | 117 |
118 class SVGTextBuilder : SkNoncopyable { | |
119 public: | |
120 SVGTextBuilder(const void* text, size_t byteLen, const SkPaint& paint, const SkPoint& offset, | |
121 unsigned scalarsPerPos, const SkScalar pos[] = NULL) | |
122 : fOffset(offset) | |
123 , fScalarsPerPos(scalarsPerPos) | |
124 , fPos(pos) | |
125 , fLastCharWasWhitespace(true) // start off in whitespace mode to strip all leading space | |
126 { | |
127 SkASSERT(scalarsPerPos <= 2); | |
128 SkASSERT(scalarsPerPos == 0 || SkToBool(pos)); | |
129 | |
130 int count = paint.countText(text, byteLen); | |
131 | |
132 switch(paint.getTextEncoding()) { | |
133 case SkPaint::kGlyphID_TextEncoding: { | |
134 SkASSERT(count * sizeof(uint16_t) == byteLen); | |
135 SkAutoSTArray<64, SkUnichar> unichars(count); | |
136 paint.glyphsToUnichars((const uint16_t*)text, count, unichars.get()) ; | |
137 for (int i = 0; i < count; ++i) { | |
138 this->appendUnichar(unichars[i]); | |
139 } | |
140 } break; | |
141 case SkPaint::kUTF8_TextEncoding: { | |
142 const char* c8 = reinterpret_cast<const char*>(text); | |
143 for (int i = 0; i < count; ++i) { | |
144 this->appendUnichar(SkUTF8_NextUnichar(&c8)); | |
145 } | |
146 SkASSERT(reinterpret_cast<const char*>(text) + byteLen == c8); | |
147 } break; | |
148 case SkPaint::kUTF16_TextEncoding: { | |
149 const uint16_t* c16 = reinterpret_cast<const uint16_t*>(text); | |
150 for (int i = 0; i < count; ++i) { | |
151 this->appendUnichar(SkUTF16_NextUnichar(&c16)); | |
152 } | |
153 SkASSERT(SkIsAlign2(byteLen)); | |
154 SkASSERT(reinterpret_cast<const uint16_t*>(text) + (byteLen / 2) == c16); | |
155 } break; | |
156 case SkPaint::kUTF32_TextEncoding: { | |
157 SkASSERT(count * sizeof(uint32_t) == byteLen); | |
158 const uint32_t* c32 = reinterpret_cast<const uint32_t*>(text); | |
159 for (int i = 0; i < count; ++i) { | |
160 this->appendUnichar(c32[i]); | |
161 } | |
162 } break; | |
163 default: | |
164 SkFAIL("unknown text encoding"); | |
165 } | |
166 | |
167 switch (scalarsPerPos) { | |
mtklein
2015/02/18 19:55:44
This might be clearer as two if-statements:
if (s
f(malita)
2015/02/19 20:58:38
Done.
| |
168 case 0: | |
169 SkASSERT(fPosX.isEmpty()); | |
170 fPosX.appendScalar(offset.x()); | |
171 // fall through | |
172 case 1: | |
173 SkASSERT(fPosY.isEmpty()); | |
174 fPosY.appendScalar(offset.y()); | |
175 break; | |
176 case 2: | |
177 SkASSERT(fPosX.isEmpty() == fText.isEmpty()); | |
178 SkASSERT(fPosY.isEmpty() == fText.isEmpty()); | |
179 break; | |
180 default: | |
181 SkFAIL("illegal scalarsPer argument"); | |
182 } | |
183 } | |
184 | |
185 const SkString& text() const { return fText; } | |
186 const SkString& posX() const { return fPosX; } | |
187 const SkString& posY() const { return fPosY; } | |
188 | |
189 private: | |
190 void appendUnichar(SkUnichar c) { | |
191 bool discardPos = false; | |
192 bool isWhitespace = false; | |
193 | |
194 switch(c) { | |
195 case ' ': | |
196 case '\t': | |
197 // consolidate whitespace to match SVG's xml:space=default munging | |
198 // (http://www.w3.org/TR/SVG/text.html#WhiteSpace) | |
199 if (fLastCharWasWhitespace) { | |
200 discardPos = true; | |
201 } else { | |
202 fText.appendUnichar(c); | |
203 } | |
204 isWhitespace = true; | |
205 break; | |
206 case '\0': | |
207 // SkPaint::glyphsToUnichars() returns \0 for inconcertible glyphs, but these | |
mtklein
2015/02/18 19:55:44
inconvertible?
f(malita)
2015/02/19 20:58:38
Done.
| |
208 // are not legal XML characters (http://www.w3.org/TR/REC-xml/#chars ets) | |
209 discardPos = true; | |
210 isWhitespace = fLastCharWasWhitespace; // preserve whitespace consol idation | |
211 break; | |
212 case '&': | |
213 fText.append("&"); | |
214 break; | |
215 case '"': | |
216 fText.append("""); | |
217 break; | |
218 case '\'': | |
219 fText.append("'"); | |
220 break; | |
221 case '<': | |
222 fText.append("<"); | |
223 break; | |
224 case '>': | |
225 fText.append(">"); | |
226 break; | |
227 default: | |
228 fText.appendUnichar(c); | |
229 break; | |
230 } | |
231 | |
232 this->advancePos(discardPos); | |
233 fLastCharWasWhitespace = isWhitespace; | |
234 } | |
235 | |
236 void advancePos(bool discard) { | |
237 if (!discard && fScalarsPerPos > 0) { | |
238 fPosX.appendf("%.8g, ", fOffset.x() + fPos[0]); | |
239 if (fScalarsPerPos > 1) { | |
240 SkASSERT(fScalarsPerPos == 2); | |
241 fPosY.appendf("%.8g, ", fOffset.y() + fPos[1]); | |
242 } | |
243 } | |
244 fPos += fScalarsPerPos; | |
245 } | |
246 | |
247 const SkPoint& fOffset; | |
248 const unsigned fScalarsPerPos; | |
249 const SkScalar* fPos; | |
250 | |
251 SkString fText, fPosX, fPosY; | |
252 bool fLastCharWasWhitespace; | |
253 }; | |
254 | |
183 } | 255 } |
184 | 256 |
185 // For now all this does is serve unique serial IDs, but it will eventually evol ve to track | 257 // For now all this does is serve unique serial IDs, but it will eventually evol ve to track |
186 // and deduplicate resources. | 258 // and deduplicate resources. |
187 class SkSVGDevice::ResourceBucket : ::SkNoncopyable { | 259 class SkSVGDevice::ResourceBucket : ::SkNoncopyable { |
188 public: | 260 public: |
189 ResourceBucket() : fGradientCount(0), fClipCount(0), fPathCount(0) {} | 261 ResourceBucket() : fGradientCount(0), fClipCount(0), fPathCount(0) {} |
190 | 262 |
191 SkString addLinearGradient() { | 263 SkString addLinearGradient() { |
192 return SkStringPrintf("gradient_%d", fGradientCount++); | 264 return SkStringPrintf("gradient_%d", fGradientCount++); |
(...skipping 385 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
578 const SkRect& dst, const SkPaint& paint, | 650 const SkRect& dst, const SkPaint& paint, |
579 SkCanvas::DrawBitmapRectFlags flags) { | 651 SkCanvas::DrawBitmapRectFlags flags) { |
580 // todo | 652 // todo |
581 SkDebugf("unsupported operation: drawBitmapRect()\n"); | 653 SkDebugf("unsupported operation: drawBitmapRect()\n"); |
582 } | 654 } |
583 | 655 |
584 void SkSVGDevice::drawText(const SkDraw& draw, const void* text, size_t len, | 656 void SkSVGDevice::drawText(const SkDraw& draw, const void* text, size_t len, |
585 SkScalar x, SkScalar y, const SkPaint& paint) { | 657 SkScalar x, SkScalar y, const SkPaint& paint) { |
586 AutoElement elem("text", fWriter, fResourceBucket, draw, paint); | 658 AutoElement elem("text", fWriter, fResourceBucket, draw, paint); |
587 elem.addTextAttributes(paint); | 659 elem.addTextAttributes(paint); |
588 elem.addAttribute("x", x); | 660 |
589 elem.addAttribute("y", y); | 661 SVGTextBuilder builder(text, len, paint, SkPoint::Make(x, y), 0); |
590 elem.addText(svg_text(text, len, paint)); | 662 elem.addAttribute("x", builder.posX()); |
663 elem.addAttribute("y", builder.posY()); | |
664 elem.addText(builder.text()); | |
591 } | 665 } |
592 | 666 |
593 void SkSVGDevice::drawPosText(const SkDraw& draw, const void* text, size_t len, | 667 void SkSVGDevice::drawPosText(const SkDraw& draw, const void* text, size_t len, |
594 const SkScalar pos[], int scalarsPerPos, const SkP oint& offset, | 668 const SkScalar pos[], int scalarsPerPos, const SkP oint& offset, |
595 const SkPaint& paint) { | 669 const SkPaint& paint) { |
596 SkASSERT(scalarsPerPos == 1 || scalarsPerPos == 2); | 670 SkASSERT(scalarsPerPos == 1 || scalarsPerPos == 2); |
597 | 671 |
598 AutoElement elem("text", fWriter, fResourceBucket, draw, paint); | 672 AutoElement elem("text", fWriter, fResourceBucket, draw, paint); |
599 elem.addTextAttributes(paint); | 673 elem.addTextAttributes(paint); |
600 | 674 |
601 SkString xStr; | 675 SVGTextBuilder builder(text, len, paint, offset, scalarsPerPos, pos); |
602 SkString yStr; | 676 elem.addAttribute("x", builder.posX()); |
603 for (int i = 0; i < paint.countText(text, len); ++i) { | 677 elem.addAttribute("y", builder.posY()); |
604 xStr.appendf("%.8g, ", offset.x() + pos[i * scalarsPerPos]); | 678 elem.addText(builder.text()); |
605 | |
606 if (scalarsPerPos == 2) { | |
607 yStr.appendf("%.8g, ", offset.y() + pos[i * scalarsPerPos + 1]); | |
608 } | |
609 } | |
610 | |
611 if (scalarsPerPos != 2) { | |
612 yStr.appendScalar(offset.y()); | |
613 } | |
614 | |
615 elem.addAttribute("x", xStr); | |
616 elem.addAttribute("y", yStr); | |
617 elem.addText(svg_text(text, len, paint)); | |
618 } | 679 } |
619 | 680 |
620 void SkSVGDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len, co nst SkPath& path, | 681 void SkSVGDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len, co nst SkPath& path, |
621 const SkMatrix* matrix, const SkPaint& paint) { | 682 const SkMatrix* matrix, const SkPaint& paint) { |
622 SkString pathID = fResourceBucket->addPath(); | 683 SkString pathID = fResourceBucket->addPath(); |
623 | 684 |
624 { | 685 { |
625 AutoElement defs("defs", fWriter); | 686 AutoElement defs("defs", fWriter); |
626 AutoElement pathElement("path", fWriter); | 687 AutoElement pathElement("path", fWriter); |
627 pathElement.addAttribute("id", pathID); | 688 pathElement.addAttribute("id", pathID); |
(...skipping 13 matching lines...) Expand all Loading... | |
641 AutoElement textPathElement("textPath", fWriter); | 702 AutoElement textPathElement("textPath", fWriter); |
642 textPathElement.addAttribute("xlink:href", SkStringPrintf("#%s", pat hID.c_str())); | 703 textPathElement.addAttribute("xlink:href", SkStringPrintf("#%s", pat hID.c_str())); |
643 | 704 |
644 if (paint.getTextAlign() != SkPaint::kLeft_Align) { | 705 if (paint.getTextAlign() != SkPaint::kLeft_Align) { |
645 SkASSERT(paint.getTextAlign() == SkPaint::kCenter_Align || | 706 SkASSERT(paint.getTextAlign() == SkPaint::kCenter_Align || |
646 paint.getTextAlign() == SkPaint::kRight_Align); | 707 paint.getTextAlign() == SkPaint::kRight_Align); |
647 textPathElement.addAttribute("startOffset", | 708 textPathElement.addAttribute("startOffset", |
648 paint.getTextAlign() == SkPaint::kCenter_Align ? "50%" : "10 0%"); | 709 paint.getTextAlign() == SkPaint::kCenter_Align ? "50%" : "10 0%"); |
649 } | 710 } |
650 | 711 |
651 textPathElement.addText(svg_text(text, len, paint)); | 712 SVGTextBuilder builder(text, len, paint, SkPoint::Make(0, 0), 0); |
713 textPathElement.addText(builder.text()); | |
652 } | 714 } |
653 } | 715 } |
654 } | 716 } |
655 | 717 |
656 void SkSVGDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCo unt, | 718 void SkSVGDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCo unt, |
657 const SkPoint verts[], const SkPoint texs[], | 719 const SkPoint verts[], const SkPoint texs[], |
658 const SkColor colors[], SkXfermode* xmode, | 720 const SkColor colors[], SkXfermode* xmode, |
659 const uint16_t indices[], int indexCount, | 721 const uint16_t indices[], int indexCount, |
660 const SkPaint& paint) { | 722 const SkPaint& paint) { |
661 // todo | 723 // todo |
662 SkDebugf("unsupported operation: drawVertices()\n"); | 724 SkDebugf("unsupported operation: drawVertices()\n"); |
663 } | 725 } |
664 | 726 |
665 void SkSVGDevice::drawDevice(const SkDraw&, SkBaseDevice*, int x, int y, | 727 void SkSVGDevice::drawDevice(const SkDraw&, SkBaseDevice*, int x, int y, |
666 const SkPaint&) { | 728 const SkPaint&) { |
667 // todo | 729 // todo |
668 SkDebugf("unsupported operation: drawDevice()\n"); | 730 SkDebugf("unsupported operation: drawDevice()\n"); |
669 } | 731 } |
OLD | NEW |