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 if (scalarsPerPos < 2) { |
| 168 SkASSERT(fPosY.isEmpty()); |
| 169 fPosY.appendScalar(offset.y()); // DrawText or DrawPosTextH (fixed Y
). |
| 170 } |
| 171 |
| 172 if (scalarsPerPos < 1) { |
| 173 SkASSERT(fPosX.isEmpty()); |
| 174 fPosX.appendScalar(offset.x()); // DrawText (X also fixed). |
| 175 } |
| 176 } |
| 177 |
| 178 const SkString& text() const { return fText; } |
| 179 const SkString& posX() const { return fPosX; } |
| 180 const SkString& posY() const { return fPosY; } |
| 181 |
| 182 private: |
| 183 void appendUnichar(SkUnichar c) { |
| 184 bool discardPos = false; |
| 185 bool isWhitespace = false; |
| 186 |
| 187 switch(c) { |
| 188 case ' ': |
| 189 case '\t': |
| 190 // consolidate whitespace to match SVG's xml:space=default munging |
| 191 // (http://www.w3.org/TR/SVG/text.html#WhiteSpace) |
| 192 if (fLastCharWasWhitespace) { |
| 193 discardPos = true; |
| 194 } else { |
| 195 fText.appendUnichar(c); |
| 196 } |
| 197 isWhitespace = true; |
| 198 break; |
| 199 case '\0': |
| 200 // SkPaint::glyphsToUnichars() returns \0 for inconvertible glyphs,
but these |
| 201 // are not legal XML characters (http://www.w3.org/TR/REC-xml/#chars
ets) |
| 202 discardPos = true; |
| 203 isWhitespace = fLastCharWasWhitespace; // preserve whitespace consol
idation |
| 204 break; |
| 205 case '&': |
| 206 fText.append("&"); |
| 207 break; |
| 208 case '"': |
| 209 fText.append("""); |
| 210 break; |
| 211 case '\'': |
| 212 fText.append("'"); |
| 213 break; |
| 214 case '<': |
| 215 fText.append("<"); |
| 216 break; |
| 217 case '>': |
| 218 fText.append(">"); |
| 219 break; |
| 220 default: |
| 221 fText.appendUnichar(c); |
| 222 break; |
| 223 } |
| 224 |
| 225 this->advancePos(discardPos); |
| 226 fLastCharWasWhitespace = isWhitespace; |
| 227 } |
| 228 |
| 229 void advancePos(bool discard) { |
| 230 if (!discard && fScalarsPerPos > 0) { |
| 231 fPosX.appendf("%.8g, ", fOffset.x() + fPos[0]); |
| 232 if (fScalarsPerPos > 1) { |
| 233 SkASSERT(fScalarsPerPos == 2); |
| 234 fPosY.appendf("%.8g, ", fOffset.y() + fPos[1]); |
| 235 } |
| 236 } |
| 237 fPos += fScalarsPerPos; |
| 238 } |
| 239 |
| 240 const SkPoint& fOffset; |
| 241 const unsigned fScalarsPerPos; |
| 242 const SkScalar* fPos; |
| 243 |
| 244 SkString fText, fPosX, fPosY; |
| 245 bool fLastCharWasWhitespace; |
| 246 }; |
| 247 |
183 } | 248 } |
184 | 249 |
185 // For now all this does is serve unique serial IDs, but it will eventually evol
ve to track | 250 // For now all this does is serve unique serial IDs, but it will eventually evol
ve to track |
186 // and deduplicate resources. | 251 // and deduplicate resources. |
187 class SkSVGDevice::ResourceBucket : ::SkNoncopyable { | 252 class SkSVGDevice::ResourceBucket : ::SkNoncopyable { |
188 public: | 253 public: |
189 ResourceBucket() : fGradientCount(0), fClipCount(0), fPathCount(0) {} | 254 ResourceBucket() : fGradientCount(0), fClipCount(0), fPathCount(0) {} |
190 | 255 |
191 SkString addLinearGradient() { | 256 SkString addLinearGradient() { |
192 return SkStringPrintf("gradient_%d", fGradientCount++); | 257 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, | 643 const SkRect& dst, const SkPaint& paint, |
579 SkCanvas::DrawBitmapRectFlags flags) { | 644 SkCanvas::DrawBitmapRectFlags flags) { |
580 // todo | 645 // todo |
581 SkDebugf("unsupported operation: drawBitmapRect()\n"); | 646 SkDebugf("unsupported operation: drawBitmapRect()\n"); |
582 } | 647 } |
583 | 648 |
584 void SkSVGDevice::drawText(const SkDraw& draw, const void* text, size_t len, | 649 void SkSVGDevice::drawText(const SkDraw& draw, const void* text, size_t len, |
585 SkScalar x, SkScalar y, const SkPaint& paint) { | 650 SkScalar x, SkScalar y, const SkPaint& paint) { |
586 AutoElement elem("text", fWriter, fResourceBucket, draw, paint); | 651 AutoElement elem("text", fWriter, fResourceBucket, draw, paint); |
587 elem.addTextAttributes(paint); | 652 elem.addTextAttributes(paint); |
588 elem.addAttribute("x", x); | 653 |
589 elem.addAttribute("y", y); | 654 SVGTextBuilder builder(text, len, paint, SkPoint::Make(x, y), 0); |
590 elem.addText(svg_text(text, len, paint)); | 655 elem.addAttribute("x", builder.posX()); |
| 656 elem.addAttribute("y", builder.posY()); |
| 657 elem.addText(builder.text()); |
591 } | 658 } |
592 | 659 |
593 void SkSVGDevice::drawPosText(const SkDraw& draw, const void* text, size_t len, | 660 void SkSVGDevice::drawPosText(const SkDraw& draw, const void* text, size_t len, |
594 const SkScalar pos[], int scalarsPerPos, const SkP
oint& offset, | 661 const SkScalar pos[], int scalarsPerPos, const SkP
oint& offset, |
595 const SkPaint& paint) { | 662 const SkPaint& paint) { |
596 SkASSERT(scalarsPerPos == 1 || scalarsPerPos == 2); | 663 SkASSERT(scalarsPerPos == 1 || scalarsPerPos == 2); |
597 | 664 |
598 AutoElement elem("text", fWriter, fResourceBucket, draw, paint); | 665 AutoElement elem("text", fWriter, fResourceBucket, draw, paint); |
599 elem.addTextAttributes(paint); | 666 elem.addTextAttributes(paint); |
600 | 667 |
601 SkString xStr; | 668 SVGTextBuilder builder(text, len, paint, offset, scalarsPerPos, pos); |
602 SkString yStr; | 669 elem.addAttribute("x", builder.posX()); |
603 for (int i = 0; i < paint.countText(text, len); ++i) { | 670 elem.addAttribute("y", builder.posY()); |
604 xStr.appendf("%.8g, ", offset.x() + pos[i * scalarsPerPos]); | 671 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 } | 672 } |
619 | 673 |
620 void SkSVGDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len, co
nst SkPath& path, | 674 void SkSVGDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len, co
nst SkPath& path, |
621 const SkMatrix* matrix, const SkPaint& paint) { | 675 const SkMatrix* matrix, const SkPaint& paint) { |
622 SkString pathID = fResourceBucket->addPath(); | 676 SkString pathID = fResourceBucket->addPath(); |
623 | 677 |
624 { | 678 { |
625 AutoElement defs("defs", fWriter); | 679 AutoElement defs("defs", fWriter); |
626 AutoElement pathElement("path", fWriter); | 680 AutoElement pathElement("path", fWriter); |
627 pathElement.addAttribute("id", pathID); | 681 pathElement.addAttribute("id", pathID); |
(...skipping 13 matching lines...) Expand all Loading... |
641 AutoElement textPathElement("textPath", fWriter); | 695 AutoElement textPathElement("textPath", fWriter); |
642 textPathElement.addAttribute("xlink:href", SkStringPrintf("#%s", pat
hID.c_str())); | 696 textPathElement.addAttribute("xlink:href", SkStringPrintf("#%s", pat
hID.c_str())); |
643 | 697 |
644 if (paint.getTextAlign() != SkPaint::kLeft_Align) { | 698 if (paint.getTextAlign() != SkPaint::kLeft_Align) { |
645 SkASSERT(paint.getTextAlign() == SkPaint::kCenter_Align || | 699 SkASSERT(paint.getTextAlign() == SkPaint::kCenter_Align || |
646 paint.getTextAlign() == SkPaint::kRight_Align); | 700 paint.getTextAlign() == SkPaint::kRight_Align); |
647 textPathElement.addAttribute("startOffset", | 701 textPathElement.addAttribute("startOffset", |
648 paint.getTextAlign() == SkPaint::kCenter_Align ? "50%" : "10
0%"); | 702 paint.getTextAlign() == SkPaint::kCenter_Align ? "50%" : "10
0%"); |
649 } | 703 } |
650 | 704 |
651 textPathElement.addText(svg_text(text, len, paint)); | 705 SVGTextBuilder builder(text, len, paint, SkPoint::Make(0, 0), 0); |
| 706 textPathElement.addText(builder.text()); |
652 } | 707 } |
653 } | 708 } |
654 } | 709 } |
655 | 710 |
656 void SkSVGDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCo
unt, | 711 void SkSVGDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCo
unt, |
657 const SkPoint verts[], const SkPoint texs[], | 712 const SkPoint verts[], const SkPoint texs[], |
658 const SkColor colors[], SkXfermode* xmode, | 713 const SkColor colors[], SkXfermode* xmode, |
659 const uint16_t indices[], int indexCount, | 714 const uint16_t indices[], int indexCount, |
660 const SkPaint& paint) { | 715 const SkPaint& paint) { |
661 // todo | 716 // todo |
662 SkDebugf("unsupported operation: drawVertices()\n"); | 717 SkDebugf("unsupported operation: drawVertices()\n"); |
663 } | 718 } |
664 | 719 |
665 void SkSVGDevice::drawDevice(const SkDraw&, SkBaseDevice*, int x, int y, | 720 void SkSVGDevice::drawDevice(const SkDraw&, SkBaseDevice*, int x, int y, |
666 const SkPaint&) { | 721 const SkPaint&) { |
667 // todo | 722 // todo |
668 SkDebugf("unsupported operation: drawDevice()\n"); | 723 SkDebugf("unsupported operation: drawDevice()\n"); |
669 } | 724 } |
OLD | NEW |