| 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 |