Chromium Code Reviews| 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 |