Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(475)

Side by Side Diff: experimental/svg/SkSVGDevice.cpp

Issue 902583006: Add SkSVGCanvas (Closed) Base URL: https://chromium.googlesource.com/skia.git@master
Patch Set: fix win warning Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « experimental/svg/SkSVGDevice.h ('k') | experimental/svg/skp2svg.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "SkSVGDevice.h"
9
10 #include "SkBitmap.h"
11 #include "SkDraw.h"
12 #include "SkPaint.h"
13 #include "SkParsePath.h"
14 #include "SkPathOps.h"
15 #include "SkShader.h"
16 #include "SkStream.h"
17 #include "SkTypeface.h"
18 #include "SkUtils.h"
19 #include "SkXMLWriter.h"
20
21 namespace {
22
23 static SkString svg_color(SkColor color) {
24 return SkStringPrintf("rgb(%u,%u,%u)",
25 SkColorGetR(color),
26 SkColorGetG(color),
27 SkColorGetB(color));
28 }
29
30 static SkScalar svg_opacity(SkColor color) {
31 return SkIntToScalar(SkColorGetA(color)) / SK_AlphaOPAQUE;
32 }
33
34 // Keep in sync with SkPaint::Cap
35 static const char* cap_map[] = {
36 NULL, // kButt_Cap (default)
37 "round", // kRound_Cap
38 "square" // kSquare_Cap
39 };
40 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(cap_map) == SkPaint::kCapCount, missing_cap_map _entry);
41
42 static const char* svg_cap(SkPaint::Cap cap) {
43 SkASSERT(cap < SK_ARRAY_COUNT(cap_map));
44 return cap_map[cap];
45 }
46
47 // Keep in sync with SkPaint::Join
48 static const char* join_map[] = {
49 NULL, // kMiter_Join (default)
50 "round", // kRound_Join
51 "bevel" // kBevel_Join
52 };
53 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(join_map) == SkPaint::kJoinCount, missing_join_ map_entry);
54
55 static const char* svg_join(SkPaint::Join join) {
56 SkASSERT(join < SK_ARRAY_COUNT(join_map));
57 return join_map[join];
58 }
59
60 // Keep in sync with SkPaint::Align
61 static const char* text_align_map[] = {
62 NULL, // kLeft_Align (default)
63 "middle", // kCenter_Align
64 "end" // kRight_Align
65 };
66 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(text_align_map) == SkPaint::kAlignCount,
67 missing_text_align_map_entry);
68 static const char* svg_text_align(SkPaint::Align align) {
69 SkASSERT(align < SK_ARRAY_COUNT(text_align_map));
70 return text_align_map[align];
71 }
72
73 static SkString svg_transform(const SkMatrix& t) {
74 SkASSERT(!t.isIdentity());
75
76 SkString tstr;
77 switch (t.getType()) {
78 case SkMatrix::kPerspective_Mask:
79 SkDebugf("Can't handle perspective matrices.");
80 break;
81 case SkMatrix::kTranslate_Mask:
82 tstr.printf("translate(%g %g)", t.getTranslateX(), t.getTranslateY());
83 break;
84 case SkMatrix::kScale_Mask:
85 tstr.printf("scale(%g %g)", t.getScaleX(), t.getScaleY());
86 break;
87 default:
88 // http://www.w3.org/TR/SVG/coords.html#TransformMatrixDefined
89 // | a c e |
90 // | b d f |
91 // | 0 0 1 |
92 tstr.printf("matrix(%g %g %g %g %g %g)",
93 t.getScaleX(), t.getSkewY(),
94 t.getSkewX(), t.getScaleY(),
95 t.getTranslateX(), t.getTranslateY());
96 break;
97 }
98
99 return tstr;
100 }
101
102 static void append_escaped_unichar(SkUnichar c, SkString* text) {
103 switch(c) {
104 case '&':
105 text->append("&amp;");
106 break;
107 case '"':
108 text->append("&quot;");
109 break;
110 case '\'':
111 text->append("&apos;");
112 break;
113 case '<':
114 text->append("&lt;");
115 break;
116 case '>':
117 text->append("&gt;");
118 break;
119 default:
120 text->appendUnichar(c);
121 break;
122 }
123 }
124
125 static SkString svg_text(const void* text, size_t byteLen, const SkPaint& paint) {
126 SkString svgText;
127 int count = paint.countText(text, byteLen);
128
129 switch(paint.getTextEncoding()) {
130 case SkPaint::kGlyphID_TextEncoding: {
131 SkASSERT(count * sizeof(uint16_t) == byteLen);
132 SkAutoSTArray<64, SkUnichar> unichars(count);
133 paint.glyphsToUnichars((const uint16_t*)text, count, unichars.get());
134 for (int i = 0; i < count; ++i) {
135 append_escaped_unichar(unichars[i], &svgText);
136 }
137 } break;
138 case SkPaint::kUTF8_TextEncoding: {
139 const char* c8 = reinterpret_cast<const char*>(text);
140 for (int i = 0; i < count; ++i) {
141 append_escaped_unichar(SkUTF8_NextUnichar(&c8), &svgText);
142 }
143 SkASSERT(reinterpret_cast<const char*>(text) + byteLen == c8);
144 } break;
145 case SkPaint::kUTF16_TextEncoding: {
146 const uint16_t* c16 = reinterpret_cast<const uint16_t*>(text);
147 for (int i = 0; i < count; ++i) {
148 append_escaped_unichar(SkUTF16_NextUnichar(&c16), &svgText);
149 }
150 SkASSERT(SkIsAlign2(byteLen));
151 SkASSERT(reinterpret_cast<const uint16_t*>(text) + (byteLen / 2) == c16) ;
152 } break;
153 case SkPaint::kUTF32_TextEncoding: {
154 SkASSERT(count * sizeof(uint32_t) == byteLen);
155 const uint32_t* c32 = reinterpret_cast<const uint32_t*>(text);
156 for (int i = 0; i < count; ++i) {
157 append_escaped_unichar(c32[i], &svgText);
158 }
159 } break;
160 default:
161 SkFAIL("unknown text encoding");
162 }
163
164 return svgText;
165 }
166
167 struct Resources {
168 Resources(const SkPaint& paint)
169 : fPaintServer(svg_color(paint.getColor())) {}
170
171 SkString fPaintServer;
172 SkString fClip;
173 };
174
175 }
176
177 // For now all this does is serve unique serial IDs, but it will eventually evol ve to track
178 // and deduplicate resources.
179 class SkSVGDevice::ResourceBucket : ::SkNoncopyable {
180 public:
181 ResourceBucket() : fGradientCount(0), fClipCount(0), fPathCount(0) {}
182
183 SkString addLinearGradient() {
184 return SkStringPrintf("gradient_%d", fGradientCount++);
185 }
186
187 SkString addClip() {
188 return SkStringPrintf("clip_%d", fClipCount++);
189 }
190
191 SkString addPath() {
192 return SkStringPrintf("path_%d", fPathCount++);
193 }
194
195 private:
196 uint32_t fGradientCount;
197 uint32_t fClipCount;
198 uint32_t fPathCount;
199 };
200
201 class SkSVGDevice::AutoElement : ::SkNoncopyable {
202 public:
203 AutoElement(const char name[], SkXMLWriter* writer)
204 : fWriter(writer)
205 , fResourceBucket(NULL) {
206 fWriter->startElement(name);
207 }
208
209 AutoElement(const char name[], SkXMLWriter* writer, ResourceBucket* bucket,
210 const SkDraw& draw, const SkPaint& paint)
211 : fWriter(writer)
212 , fResourceBucket(bucket) {
213
214 Resources res = this->addResources(draw, paint);
215
216 fWriter->startElement(name);
217
218 this->addPaint(paint, res);
219
220 if (!draw.fMatrix->isIdentity()) {
221 this->addAttribute("transform", svg_transform(*draw.fMatrix));
222 }
223 }
224
225 ~AutoElement() {
226 fWriter->endElement();
227 }
228
229 void addAttribute(const char name[], const char val[]) {
230 fWriter->addAttribute(name, val);
231 }
232
233 void addAttribute(const char name[], const SkString& val) {
234 fWriter->addAttribute(name, val.c_str());
235 }
236
237 void addAttribute(const char name[], int32_t val) {
238 fWriter->addS32Attribute(name, val);
239 }
240
241 void addAttribute(const char name[], SkScalar val) {
242 fWriter->addScalarAttribute(name, val);
243 }
244
245 void addText(const SkString& text) {
246 fWriter->addText(text.c_str(), text.size());
247 }
248
249 void addRectAttributes(const SkRect&);
250 void addPathAttributes(const SkPath&);
251 void addTextAttributes(const SkPaint&);
252
253 private:
254 Resources addResources(const SkDraw& draw, const SkPaint& paint);
255 void addClipResources(const SkDraw& draw, Resources* resources);
256 void addShaderResources(const SkPaint& paint, Resources* resources);
257
258 void addPaint(const SkPaint& paint, const Resources& resources);
259
260 SkString addLinearGradientDef(const SkShader::GradientInfo& info, const SkSh ader* shader);
261
262 SkXMLWriter* fWriter;
263 ResourceBucket* fResourceBucket;
264 };
265
266 void SkSVGDevice::AutoElement::addPaint(const SkPaint& paint, const Resources& r esources) {
267 SkPaint::Style style = paint.getStyle();
268 if (style == SkPaint::kFill_Style || style == SkPaint::kStrokeAndFill_Style) {
269 this->addAttribute("fill", resources.fPaintServer);
270
271 if (SK_AlphaOPAQUE != SkColorGetA(paint.getColor())) {
272 this->addAttribute("fill-opacity", svg_opacity(paint.getColor()));
273 }
274 } else {
275 SkASSERT(style == SkPaint::kStroke_Style);
276 this->addAttribute("fill", "none");
277 }
278
279 if (style == SkPaint::kStroke_Style || style == SkPaint::kStrokeAndFill_Styl e) {
280 this->addAttribute("stroke", resources.fPaintServer);
281
282 SkScalar strokeWidth = paint.getStrokeWidth();
283 if (strokeWidth == 0) {
284 // Hairline stroke
285 strokeWidth = 1;
286 this->addAttribute("vector-effect", "non-scaling-stroke");
287 }
288 this->addAttribute("stroke-width", strokeWidth);
289
290 if (const char* cap = svg_cap(paint.getStrokeCap())) {
291 this->addAttribute("stroke-linecap", cap);
292 }
293
294 if (const char* join = svg_join(paint.getStrokeJoin())) {
295 this->addAttribute("stroke-linejoin", join);
296 }
297
298 if (paint.getStrokeJoin() == SkPaint::kMiter_Join) {
299 this->addAttribute("stroke-miterlimit", paint.getStrokeMiter());
300 }
301
302 if (SK_AlphaOPAQUE != SkColorGetA(paint.getColor())) {
303 this->addAttribute("stroke-opacity", svg_opacity(paint.getColor()));
304 }
305 } else {
306 SkASSERT(style == SkPaint::kFill_Style);
307 this->addAttribute("stroke", "none");
308 }
309
310 if (!resources.fClip.isEmpty()) {
311 this->addAttribute("clip-path", resources.fClip);
312 }
313 }
314
315 Resources SkSVGDevice::AutoElement::addResources(const SkDraw& draw, const SkPai nt& paint) {
316 Resources resources(paint);
317
318 // FIXME: this is a weak heuristic and we end up with LOTS of redundant clip s.
319 bool hasClip = !draw.fClipStack->isWideOpen();
320 bool hasShader = SkToBool(paint.getShader());
321
322 if (hasClip || hasShader) {
323 AutoElement defs("defs", fWriter);
324
325 if (hasClip) {
326 this->addClipResources(draw, &resources);
327 }
328
329 if (hasShader) {
330 this->addShaderResources(paint, &resources);
331 }
332 }
333
334 return resources;
335 }
336
337 void SkSVGDevice::AutoElement::addShaderResources(const SkPaint& paint, Resource s* resources) {
338 const SkShader* shader = paint.getShader();
339 SkASSERT(SkToBool(shader));
340
341 SkShader::GradientInfo grInfo;
342 grInfo.fColorCount = 0;
343 if (SkShader::kLinear_GradientType != shader->asAGradient(&grInfo)) {
344 // TODO: non-linear gradient support
345 SkDebugf("unsupported shader type\n");
346 return;
347 }
348
349 SkAutoSTArray<16, SkColor> grColors(grInfo.fColorCount);
350 SkAutoSTArray<16, SkScalar> grOffsets(grInfo.fColorCount);
351 grInfo.fColors = grColors.get();
352 grInfo.fColorOffsets = grOffsets.get();
353
354 // One more call to get the actual colors/offsets.
355 shader->asAGradient(&grInfo);
356 SkASSERT(grInfo.fColorCount <= grColors.count());
357 SkASSERT(grInfo.fColorCount <= grOffsets.count());
358
359 resources->fPaintServer.printf("url(#%s)", addLinearGradientDef(grInfo, shad er).c_str());
360 }
361
362 void SkSVGDevice::AutoElement::addClipResources(const SkDraw& draw, Resources* r esources) {
363 SkASSERT(!draw.fClipStack->isWideOpen());
364
365 SkPath clipPath;
366 (void) draw.fClipStack->asPath(&clipPath);
367
368 SkString clipID = fResourceBucket->addClip();
369 const char* clipRule = clipPath.getFillType() == SkPath::kEvenOdd_FillType ?
370 "evenodd" : "nonzero";
371 {
372 // clipPath is in device space, but since we're only pushing transform a ttributes
373 // to the leaf nodes, so are all our elements => SVG userSpaceOnUse == d evice space.
374 AutoElement clipPathElement("clipPath", fWriter);
375 clipPathElement.addAttribute("id", clipID);
376
377 SkRect clipRect = SkRect::MakeEmpty();
378 if (clipPath.isEmpty() || clipPath.isRect(&clipRect)) {
379 AutoElement rectElement("rect", fWriter);
380 rectElement.addRectAttributes(clipRect);
381 rectElement.addAttribute("clip-rule", clipRule);
382 } else {
383 AutoElement pathElement("path", fWriter);
384 pathElement.addPathAttributes(clipPath);
385 pathElement.addAttribute("clip-rule", clipRule);
386 }
387 }
388
389 resources->fClip.printf("url(#%s)", clipID.c_str());
390 }
391
392 SkString SkSVGDevice::AutoElement::addLinearGradientDef(const SkShader::Gradient Info& info,
393 const SkShader* shader) {
394 SkASSERT(fResourceBucket);
395 SkString id = fResourceBucket->addLinearGradient();
396
397 {
398 AutoElement gradient("linearGradient", fWriter);
399
400 gradient.addAttribute("id", id);
401 gradient.addAttribute("gradientUnits", "userSpaceOnUse");
402 gradient.addAttribute("x1", info.fPoint[0].x());
403 gradient.addAttribute("y1", info.fPoint[0].y());
404 gradient.addAttribute("x2", info.fPoint[1].x());
405 gradient.addAttribute("y2", info.fPoint[1].y());
406
407 if (!shader->getLocalMatrix().isIdentity()) {
408 this->addAttribute("gradientTransform", svg_transform(shader->getLoc alMatrix()));
409 }
410
411 SkASSERT(info.fColorCount >= 2);
412 for (int i = 0; i < info.fColorCount; ++i) {
413 SkColor color = info.fColors[i];
414 SkString colorStr(svg_color(color));
415
416 {
417 AutoElement stop("stop", fWriter);
418 stop.addAttribute("offset", info.fColorOffsets[i]);
419 stop.addAttribute("stop-color", colorStr.c_str());
420
421 if (SK_AlphaOPAQUE != SkColorGetA(color)) {
422 stop.addAttribute("stop-opacity", svg_opacity(color));
423 }
424 }
425 }
426 }
427
428 return id;
429 }
430
431 void SkSVGDevice::AutoElement::addRectAttributes(const SkRect& rect) {
432 // x, y default to 0
433 if (rect.x() != 0) {
434 this->addAttribute("x", rect.x());
435 }
436 if (rect.y() != 0) {
437 this->addAttribute("y", rect.y());
438 }
439
440 this->addAttribute("width", rect.width());
441 this->addAttribute("height", rect.height());
442 }
443
444 void SkSVGDevice::AutoElement::addPathAttributes(const SkPath& path) {
445 SkString pathData;
446 SkParsePath::ToSVGString(path, &pathData);
447 this->addAttribute("d", pathData);
448 }
449
450 void SkSVGDevice::AutoElement::addTextAttributes(const SkPaint& paint) {
451 this->addAttribute("font-size", paint.getTextSize());
452
453 SkTypeface::Style style = paint.getTypeface()->style();
454 if (style & SkTypeface::kItalic) {
455 this->addAttribute("font-style", "italic");
456 }
457 if (style & SkTypeface::kBold) {
458 this->addAttribute("font-weight", "bold");
459 }
460
461 SkAutoTUnref<const SkTypeface> tface(paint.getTypeface() ?
462 SkRef(paint.getTypeface()) : SkTypeface::RefDefault(style));
463 SkString familyName;
464 tface->getFamilyName(&familyName);
465 if (!familyName.isEmpty()) {
466 this->addAttribute("font-family", familyName);
467 }
468
469 if (const char* textAlign = svg_text_align(paint.getTextAlign())) {
470 this->addAttribute("text-anchor", textAlign);
471 }
472 }
473
474 SkBaseDevice* SkSVGDevice::Create(const SkISize& size, SkWStream* wstream) {
475 if (!SkToBool(wstream)) {
476 return NULL;
477 }
478
479 return SkNEW_ARGS(SkSVGDevice, (size, wstream));
480 }
481
482 SkSVGDevice::SkSVGDevice(const SkISize& size, SkWStream* wstream)
483 : fWriter(SkNEW_ARGS(SkXMLStreamWriter, (wstream)))
484 , fResourceBucket(SkNEW(ResourceBucket)) {
485
486 fLegacyBitmap.setInfo(SkImageInfo::MakeUnknown(size.width(), size.height())) ;
487
488 fWriter->writeHeader();
489
490 // The root <svg> tag gets closed by the destructor.
491 fRootElement.reset(SkNEW_ARGS(AutoElement, ("svg", fWriter)));
492
493 fRootElement->addAttribute("xmlns", "http://www.w3.org/2000/svg");
494 fRootElement->addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
495 fRootElement->addAttribute("width", size.width());
496 fRootElement->addAttribute("height", size.height());
497 }
498
499 SkSVGDevice::~SkSVGDevice() {
500 }
501
502 SkImageInfo SkSVGDevice::imageInfo() const {
503 return fLegacyBitmap.info();
504 }
505
506 const SkBitmap& SkSVGDevice::onAccessBitmap() {
507 return fLegacyBitmap;
508 }
509
510 void SkSVGDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
511 AutoElement rect("rect", fWriter, fResourceBucket, draw, paint);
512 rect.addRectAttributes(SkRect::MakeWH(SkIntToScalar(this->width()),
513 SkIntToScalar(this->height())));
514 }
515
516 void SkSVGDevice::drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t cou nt,
517 const SkPoint[], const SkPaint& paint) {
518 // todo
519 SkDebugf("unsupported operation: drawPoints()\n");
520 }
521
522 void SkSVGDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& p aint) {
523 AutoElement rect("rect", fWriter, fResourceBucket, draw, paint);
524 rect.addRectAttributes(r);
525 }
526
527 void SkSVGDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint & paint) {
528 AutoElement ellipse("ellipse", fWriter, fResourceBucket, draw, paint);
529 ellipse.addAttribute("cx", oval.centerX());
530 ellipse.addAttribute("cy", oval.centerY());
531 ellipse.addAttribute("rx", oval.width() / 2);
532 ellipse.addAttribute("ry", oval.height() / 2);
533 }
534
535 void SkSVGDevice::drawRRect(const SkDraw&, const SkRRect& rr, const SkPaint& pai nt) {
536 // todo
537 SkDebugf("unsupported operation: drawRRect()\n");
538 }
539
540 void SkSVGDevice::drawPath(const SkDraw& draw, const SkPath& path, const SkPaint & paint,
541 const SkMatrix* prePathMatrix, bool pathIsMutable) {
542 AutoElement elem("path", fWriter, fResourceBucket, draw, paint);
543 elem.addPathAttributes(path);
544 }
545
546 void SkSVGDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap,
547 const SkMatrix& matrix, const SkPaint& paint) {
548 // todo
549 SkDebugf("unsupported operation: drawBitmap()\n");
550 }
551
552 void SkSVGDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap,
553 int x, int y, const SkPaint& paint) {
554 // todo
555 SkDebugf("unsupported operation: drawSprite()\n");
556 }
557
558 void SkSVGDevice::drawBitmapRect(const SkDraw&, const SkBitmap&, const SkRect* s rcOrNull,
559 const SkRect& dst, const SkPaint& paint,
560 SkCanvas::DrawBitmapRectFlags flags) {
561 // todo
562 SkDebugf("unsupported operation: drawBitmapRect()\n");
563 }
564
565 void SkSVGDevice::drawText(const SkDraw& draw, const void* text, size_t len,
566 SkScalar x, SkScalar y, const SkPaint& paint) {
567 AutoElement elem("text", fWriter, fResourceBucket, draw, paint);
568 elem.addTextAttributes(paint);
569 elem.addAttribute("x", x);
570 elem.addAttribute("y", y);
571 elem.addText(svg_text(text, len, paint));
572 }
573
574 void SkSVGDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
575 const SkScalar pos[], int scalarsPerPos, const SkP oint& offset,
576 const SkPaint& paint) {
577 SkASSERT(scalarsPerPos == 1 || scalarsPerPos == 2);
578
579 AutoElement elem("text", fWriter, fResourceBucket, draw, paint);
580 elem.addTextAttributes(paint);
581
582 SkString xStr;
583 SkString yStr;
584 for (int i = 0; i < paint.countText(text, len); ++i) {
585 xStr.appendf("%.8g, ", offset.x() + pos[i * scalarsPerPos]);
586
587 if (scalarsPerPos == 2) {
588 yStr.appendf("%.8g, ", offset.y() + pos[i * scalarsPerPos + 1]);
589 }
590 }
591
592 if (scalarsPerPos != 2) {
593 yStr.appendScalar(offset.y());
594 }
595
596 elem.addAttribute("x", xStr);
597 elem.addAttribute("y", yStr);
598 elem.addText(svg_text(text, len, paint));
599 }
600
601 void SkSVGDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len, co nst SkPath& path,
602 const SkMatrix* matrix, const SkPaint& paint) {
603 SkString pathID = fResourceBucket->addPath();
604
605 {
606 AutoElement defs("defs", fWriter);
607 AutoElement pathElement("path", fWriter);
608 pathElement.addAttribute("id", pathID);
609 pathElement.addPathAttributes(path);
610
611 }
612
613 {
614 AutoElement textElement("text", fWriter);
615 textElement.addTextAttributes(paint);
616
617 if (matrix && !matrix->isIdentity()) {
618 textElement.addAttribute("transform", svg_transform(*matrix));
619 }
620
621 {
622 AutoElement textPathElement("textPath", fWriter);
623 textPathElement.addAttribute("xlink:href", SkStringPrintf("#%s", pat hID.c_str()));
624
625 if (paint.getTextAlign() != SkPaint::kLeft_Align) {
626 SkASSERT(paint.getTextAlign() == SkPaint::kCenter_Align ||
627 paint.getTextAlign() == SkPaint::kRight_Align);
628 textPathElement.addAttribute("startOffset",
629 paint.getTextAlign() == SkPaint::kCenter_Align ? "50%" : "10 0%");
630 }
631
632 textPathElement.addText(svg_text(text, len, paint));
633 }
634 }
635 }
636
637 void SkSVGDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCo unt,
638 const SkPoint verts[], const SkPoint texs[],
639 const SkColor colors[], SkXfermode* xmode,
640 const uint16_t indices[], int indexCount,
641 const SkPaint& paint) {
642 // todo
643 SkDebugf("unsupported operation: drawVertices()\n");
644 }
645
646 void SkSVGDevice::drawDevice(const SkDraw&, SkBaseDevice*, int x, int y,
647 const SkPaint&) {
648 // todo
649 SkDebugf("unsupported operation: drawDevice()\n");
650 }
OLDNEW
« no previous file with comments | « experimental/svg/SkSVGDevice.h ('k') | experimental/svg/skp2svg.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698