blob: bf19a81a1d3670ef193836d668660bbb7f72464d [file] [log] [blame]
fmalita93957f42015-01-30 09:03:29 -08001/*
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"
fmalita1a481fe2015-02-04 07:39:34 -080014#include "SkPathOps.h"
fmalita532faa92015-02-03 05:44:40 -080015#include "SkShader.h"
fmalita93957f42015-01-30 09:03:29 -080016#include "SkStream.h"
fmalitafe3f2602015-02-03 17:47:12 -080017#include "SkTypeface.h"
18#include "SkUtils.h"
fmalita93957f42015-01-30 09:03:29 -080019#include "SkXMLWriter.h"
20
21namespace {
22
fmalita532faa92015-02-03 05:44:40 -080023static SkString svg_color(SkColor color) {
fmalita1a481fe2015-02-04 07:39:34 -080024 return SkStringPrintf("rgb(%u,%u,%u)",
25 SkColorGetR(color),
26 SkColorGetG(color),
27 SkColorGetB(color));
fmalita532faa92015-02-03 05:44:40 -080028}
29
30static SkScalar svg_opacity(SkColor color) {
31 return SkIntToScalar(SkColorGetA(color)) / SK_AlphaOPAQUE;
32}
33
fmalita12753cc2015-02-04 14:56:35 -080034// Keep in sync with SkPaint::Cap
35static const char* cap_map[] = {
36 NULL, // kButt_Cap (default)
37 "round", // kRound_Cap
38 "square" // kSquare_Cap
39};
40SK_COMPILE_ASSERT(SK_ARRAY_COUNT(cap_map) == SkPaint::kCapCount, missing_cap_map_entry);
41
42static 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
48static const char* join_map[] = {
49 NULL, // kMiter_Join (default)
50 "round", // kRound_Join
51 "bevel" // kBevel_Join
52};
53SK_COMPILE_ASSERT(SK_ARRAY_COUNT(join_map) == SkPaint::kJoinCount, missing_join_map_entry);
54
55static const char* svg_join(SkPaint::Join join) {
56 SkASSERT(join < SK_ARRAY_COUNT(join_map));
57 return join_map[join];
58}
59
fmalitaa9d9de42015-02-04 17:54:46 -080060// Keep in sync with SkPaint::Align
61static const char* text_align_map[] = {
62 NULL, // kLeft_Align (default)
63 "middle", // kCenter_Align
64 "end" // kRight_Align
65};
66SK_COMPILE_ASSERT(SK_ARRAY_COUNT(text_align_map) == SkPaint::kAlignCount,
67 missing_text_align_map_entry);
68static 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
73static 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
fmalitafe3f2602015-02-03 17:47:12 -0800102static 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
125static 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
fmalita532faa92015-02-03 05:44:40 -0800167struct Resources {
168 Resources(const SkPaint& paint)
169 : fPaintServer(svg_color(paint.getColor())) {}
170
171 SkString fPaintServer;
fmalita1a481fe2015-02-04 07:39:34 -0800172 SkString fClip;
fmalita532faa92015-02-03 05:44:40 -0800173};
174
175}
176
177// For now all this does is serve unique serial IDs, but it will eventually evolve to track
178// and deduplicate resources.
179class SkSVGDevice::ResourceBucket : ::SkNoncopyable {
180public:
fmalitaa9d9de42015-02-04 17:54:46 -0800181 ResourceBucket() : fGradientCount(0), fClipCount(0), fPathCount(0) {}
fmalita532faa92015-02-03 05:44:40 -0800182
183 SkString addLinearGradient() {
fmalita1a481fe2015-02-04 07:39:34 -0800184 return SkStringPrintf("gradient_%d", fGradientCount++);
185 }
186
187 SkString addClip() {
188 return SkStringPrintf("clip_%d", fClipCount++);
fmalita532faa92015-02-03 05:44:40 -0800189 }
190
fmalitaa9d9de42015-02-04 17:54:46 -0800191 SkString addPath() {
192 return SkStringPrintf("path_%d", fPathCount++);
193 }
194
fmalita532faa92015-02-03 05:44:40 -0800195private:
196 uint32_t fGradientCount;
fmalita1a481fe2015-02-04 07:39:34 -0800197 uint32_t fClipCount;
fmalitaa9d9de42015-02-04 17:54:46 -0800198 uint32_t fPathCount;
fmalita532faa92015-02-03 05:44:40 -0800199};
200
201class SkSVGDevice::AutoElement : ::SkNoncopyable {
fmalita93957f42015-01-30 09:03:29 -0800202public:
203 AutoElement(const char name[], SkXMLWriter* writer)
fmalita532faa92015-02-03 05:44:40 -0800204 : fWriter(writer)
205 , fResourceBucket(NULL) {
fmalita93957f42015-01-30 09:03:29 -0800206 fWriter->startElement(name);
207 }
208
fmalita532faa92015-02-03 05:44:40 -0800209 AutoElement(const char name[], SkXMLWriter* writer, ResourceBucket* bucket,
210 const SkDraw& draw, const SkPaint& paint)
211 : fWriter(writer)
212 , fResourceBucket(bucket) {
213
fmalita1a481fe2015-02-04 07:39:34 -0800214 Resources res = this->addResources(draw, paint);
fmalita532faa92015-02-03 05:44:40 -0800215
216 fWriter->startElement(name);
217
218 this->addPaint(paint, res);
fmalitaa9d9de42015-02-04 17:54:46 -0800219
220 if (!draw.fMatrix->isIdentity()) {
221 this->addAttribute("transform", svg_transform(*draw.fMatrix));
222 }
fmalita532faa92015-02-03 05:44:40 -0800223 }
224
fmalita93957f42015-01-30 09:03:29 -0800225 ~AutoElement() {
226 fWriter->endElement();
227 }
228
fmalita532faa92015-02-03 05:44:40 -0800229 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
fmalitafe3f2602015-02-03 17:47:12 -0800245 void addText(const SkString& text) {
reede73da402015-02-04 18:29:27 -0800246 fWriter->addText(text.c_str(), text.size());
fmalitafe3f2602015-02-03 17:47:12 -0800247 }
248
fmalita1a481fe2015-02-04 07:39:34 -0800249 void addRectAttributes(const SkRect&);
fmalitaa9d9de42015-02-04 17:54:46 -0800250 void addPathAttributes(const SkPath&);
251 void addTextAttributes(const SkPaint&);
fmalitafe3f2602015-02-03 17:47:12 -0800252
fmalita93957f42015-01-30 09:03:29 -0800253private:
fmalita1a481fe2015-02-04 07:39:34 -0800254 Resources addResources(const SkDraw& draw, const SkPaint& paint);
255 void addClipResources(const SkDraw& draw, Resources* resources);
256 void addShaderResources(const SkPaint& paint, Resources* resources);
fmalita532faa92015-02-03 05:44:40 -0800257
258 void addPaint(const SkPaint& paint, const Resources& resources);
fmalita532faa92015-02-03 05:44:40 -0800259
260 SkString addLinearGradientDef(const SkShader::GradientInfo& info, const SkShader* shader);
261
262 SkXMLWriter* fWriter;
263 ResourceBucket* fResourceBucket;
fmalita93957f42015-01-30 09:03:29 -0800264};
265
fmalita532faa92015-02-03 05:44:40 -0800266void SkSVGDevice::AutoElement::addPaint(const SkPaint& paint, const Resources& resources) {
267 SkPaint::Style style = paint.getStyle();
268 if (style == SkPaint::kFill_Style || style == SkPaint::kStrokeAndFill_Style) {
269 this->addAttribute("fill", resources.fPaintServer);
fmalita12753cc2015-02-04 14:56:35 -0800270
271 if (SK_AlphaOPAQUE != SkColorGetA(paint.getColor())) {
272 this->addAttribute("fill-opacity", svg_opacity(paint.getColor()));
273 }
fmalita532faa92015-02-03 05:44:40 -0800274 } else {
fmalita12753cc2015-02-04 14:56:35 -0800275 SkASSERT(style == SkPaint::kStroke_Style);
fmalita532faa92015-02-03 05:44:40 -0800276 this->addAttribute("fill", "none");
277 }
278
279 if (style == SkPaint::kStroke_Style || style == SkPaint::kStrokeAndFill_Style) {
280 this->addAttribute("stroke", resources.fPaintServer);
fmalita532faa92015-02-03 05:44:40 -0800281
fmalita12753cc2015-02-04 14:56:35 -0800282 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");
fmalita532faa92015-02-03 05:44:40 -0800308 }
fmalita1a481fe2015-02-04 07:39:34 -0800309
310 if (!resources.fClip.isEmpty()) {
311 this->addAttribute("clip-path", resources.fClip);
312 }
fmalita532faa92015-02-03 05:44:40 -0800313}
314
fmalita1a481fe2015-02-04 07:39:34 -0800315Resources SkSVGDevice::AutoElement::addResources(const SkDraw& draw, const SkPaint& paint) {
fmalita532faa92015-02-03 05:44:40 -0800316 Resources resources(paint);
fmalita1a481fe2015-02-04 07:39:34 -0800317
318 // FIXME: this is a weak heuristic and we end up with LOTS of redundant clips.
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
fmalita532faa92015-02-03 05:44:40 -0800334 return resources;
335}
336
fmalita1a481fe2015-02-04 07:39:34 -0800337void SkSVGDevice::AutoElement::addShaderResources(const SkPaint& paint, Resources* resources) {
fmalita532faa92015-02-03 05:44:40 -0800338 const SkShader* shader = paint.getShader();
fmalita1a481fe2015-02-04 07:39:34 -0800339 SkASSERT(SkToBool(shader));
fmalita532faa92015-02-03 05:44:40 -0800340
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
fmalita1a481fe2015-02-04 07:39:34 -0800349 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, shader).c_str());
360}
361
362void SkSVGDevice::AutoElement::addClipResources(const SkDraw& draw, Resources* resources) {
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";
fmalita532faa92015-02-03 05:44:40 -0800371 {
fmalita1a481fe2015-02-04 07:39:34 -0800372 // clipPath is in device space, but since we're only pushing transform attributes
373 // to the leaf nodes, so are all our elements => SVG userSpaceOnUse == device space.
374 AutoElement clipPathElement("clipPath", fWriter);
375 clipPathElement.addAttribute("id", clipID);
fmalita532faa92015-02-03 05:44:40 -0800376
fmalita1a481fe2015-02-04 07:39:34 -0800377 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);
fmalitaa9d9de42015-02-04 17:54:46 -0800384 pathElement.addPathAttributes(clipPath);
fmalita1a481fe2015-02-04 07:39:34 -0800385 pathElement.addAttribute("clip-rule", clipRule);
386 }
fmalita532faa92015-02-03 05:44:40 -0800387 }
fmalita1a481fe2015-02-04 07:39:34 -0800388
389 resources->fClip.printf("url(#%s)", clipID.c_str());
fmalita532faa92015-02-03 05:44:40 -0800390}
391
392SkString SkSVGDevice::AutoElement::addLinearGradientDef(const SkShader::GradientInfo& 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());
fmalitaa9d9de42015-02-04 17:54:46 -0800406
407 if (!shader->getLocalMatrix().isIdentity()) {
408 this->addAttribute("gradientTransform", svg_transform(shader->getLocalMatrix()));
409 }
fmalita532faa92015-02-03 05:44:40 -0800410
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;
fmalita93957f42015-01-30 09:03:29 -0800429}
430
fmalita1a481fe2015-02-04 07:39:34 -0800431void 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
fmalitaa9d9de42015-02-04 17:54:46 -0800444void SkSVGDevice::AutoElement::addPathAttributes(const SkPath& path) {
445 SkString pathData;
446 SkParsePath::ToSVGString(path, &pathData);
447 this->addAttribute("d", pathData);
448}
449
450void SkSVGDevice::AutoElement::addTextAttributes(const SkPaint& paint) {
fmalitafe3f2602015-02-03 17:47:12 -0800451 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 }
fmalitaa9d9de42015-02-04 17:54:46 -0800468
469 if (const char* textAlign = svg_text_align(paint.getTextAlign())) {
470 this->addAttribute("text-anchor", textAlign);
471 }
fmalitafe3f2602015-02-03 17:47:12 -0800472}
473
fmalita2aafe6f2015-02-06 12:51:10 -0800474SkBaseDevice* SkSVGDevice::Create(const SkISize& size, SkXMLWriter* writer) {
475 if (!writer) {
fmalita93957f42015-01-30 09:03:29 -0800476 return NULL;
477 }
478
fmalita2aafe6f2015-02-06 12:51:10 -0800479 return SkNEW_ARGS(SkSVGDevice, (size, writer));
fmalita93957f42015-01-30 09:03:29 -0800480}
481
fmalita2aafe6f2015-02-06 12:51:10 -0800482SkSVGDevice::SkSVGDevice(const SkISize& size, SkXMLWriter* writer)
483 : fWriter(writer)
fmalita532faa92015-02-03 05:44:40 -0800484 , fResourceBucket(SkNEW(ResourceBucket)) {
fmalita2aafe6f2015-02-06 12:51:10 -0800485 SkASSERT(writer);
fmalita93957f42015-01-30 09:03:29 -0800486
487 fLegacyBitmap.setInfo(SkImageInfo::MakeUnknown(size.width(), size.height()));
488
489 fWriter->writeHeader();
fmalita532faa92015-02-03 05:44:40 -0800490
491 // The root <svg> tag gets closed by the destructor.
492 fRootElement.reset(SkNEW_ARGS(AutoElement, ("svg", fWriter)));
493
494 fRootElement->addAttribute("xmlns", "http://www.w3.org/2000/svg");
495 fRootElement->addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
496 fRootElement->addAttribute("width", size.width());
497 fRootElement->addAttribute("height", size.height());
fmalita93957f42015-01-30 09:03:29 -0800498}
499
500SkSVGDevice::~SkSVGDevice() {
fmalita93957f42015-01-30 09:03:29 -0800501}
502
503SkImageInfo SkSVGDevice::imageInfo() const {
504 return fLegacyBitmap.info();
505}
506
507const SkBitmap& SkSVGDevice::onAccessBitmap() {
508 return fLegacyBitmap;
509}
510
fmalita532faa92015-02-03 05:44:40 -0800511void SkSVGDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
512 AutoElement rect("rect", fWriter, fResourceBucket, draw, paint);
fmalita1a481fe2015-02-04 07:39:34 -0800513 rect.addRectAttributes(SkRect::MakeWH(SkIntToScalar(this->width()),
514 SkIntToScalar(this->height())));
fmalita93957f42015-01-30 09:03:29 -0800515}
516
517void SkSVGDevice::drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count,
518 const SkPoint[], const SkPaint& paint) {
519 // todo
fmalitafe3f2602015-02-03 17:47:12 -0800520 SkDebugf("unsupported operation: drawPoints()\n");
fmalita93957f42015-01-30 09:03:29 -0800521}
522
523void SkSVGDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& paint) {
fmalita532faa92015-02-03 05:44:40 -0800524 AutoElement rect("rect", fWriter, fResourceBucket, draw, paint);
fmalita1a481fe2015-02-04 07:39:34 -0800525 rect.addRectAttributes(r);
fmalita93957f42015-01-30 09:03:29 -0800526}
527
fmalita532faa92015-02-03 05:44:40 -0800528void SkSVGDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& paint) {
529 AutoElement ellipse("ellipse", fWriter, fResourceBucket, draw, paint);
530 ellipse.addAttribute("cx", oval.centerX());
531 ellipse.addAttribute("cy", oval.centerY());
532 ellipse.addAttribute("rx", oval.width() / 2);
533 ellipse.addAttribute("ry", oval.height() / 2);
fmalita93957f42015-01-30 09:03:29 -0800534}
535
536void SkSVGDevice::drawRRect(const SkDraw&, const SkRRect& rr, const SkPaint& paint) {
537 // todo
fmalitafe3f2602015-02-03 17:47:12 -0800538 SkDebugf("unsupported operation: drawRRect()\n");
fmalita93957f42015-01-30 09:03:29 -0800539}
540
541void SkSVGDevice::drawPath(const SkDraw& draw, const SkPath& path, const SkPaint& paint,
542 const SkMatrix* prePathMatrix, bool pathIsMutable) {
fmalita532faa92015-02-03 05:44:40 -0800543 AutoElement elem("path", fWriter, fResourceBucket, draw, paint);
fmalitaa9d9de42015-02-04 17:54:46 -0800544 elem.addPathAttributes(path);
fmalita93957f42015-01-30 09:03:29 -0800545}
546
547void SkSVGDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap,
548 const SkMatrix& matrix, const SkPaint& paint) {
549 // todo
fmalitafe3f2602015-02-03 17:47:12 -0800550 SkDebugf("unsupported operation: drawBitmap()\n");
fmalita93957f42015-01-30 09:03:29 -0800551}
552
553void SkSVGDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap,
554 int x, int y, const SkPaint& paint) {
555 // todo
fmalitafe3f2602015-02-03 17:47:12 -0800556 SkDebugf("unsupported operation: drawSprite()\n");
fmalita93957f42015-01-30 09:03:29 -0800557}
558
559void SkSVGDevice::drawBitmapRect(const SkDraw&, const SkBitmap&, const SkRect* srcOrNull,
560 const SkRect& dst, const SkPaint& paint,
561 SkCanvas::DrawBitmapRectFlags flags) {
562 // todo
fmalitafe3f2602015-02-03 17:47:12 -0800563 SkDebugf("unsupported operation: drawBitmapRect()\n");
fmalita93957f42015-01-30 09:03:29 -0800564}
565
fmalitafe3f2602015-02-03 17:47:12 -0800566void SkSVGDevice::drawText(const SkDraw& draw, const void* text, size_t len,
fmalita93957f42015-01-30 09:03:29 -0800567 SkScalar x, SkScalar y, const SkPaint& paint) {
fmalitafe3f2602015-02-03 17:47:12 -0800568 AutoElement elem("text", fWriter, fResourceBucket, draw, paint);
fmalitaa9d9de42015-02-04 17:54:46 -0800569 elem.addTextAttributes(paint);
fmalitafe3f2602015-02-03 17:47:12 -0800570 elem.addAttribute("x", x);
571 elem.addAttribute("y", y);
572 elem.addText(svg_text(text, len, paint));
fmalita93957f42015-01-30 09:03:29 -0800573}
574
fmalitafe3f2602015-02-03 17:47:12 -0800575void SkSVGDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
576 const SkScalar pos[], int scalarsPerPos, const SkPoint& offset,
577 const SkPaint& paint) {
578 SkASSERT(scalarsPerPos == 1 || scalarsPerPos == 2);
579
580 AutoElement elem("text", fWriter, fResourceBucket, draw, paint);
fmalitaa9d9de42015-02-04 17:54:46 -0800581 elem.addTextAttributes(paint);
fmalitafe3f2602015-02-03 17:47:12 -0800582
583 SkString xStr;
584 SkString yStr;
585 for (int i = 0; i < paint.countText(text, len); ++i) {
586 xStr.appendf("%.8g, ", offset.x() + pos[i * scalarsPerPos]);
587
588 if (scalarsPerPos == 2) {
589 yStr.appendf("%.8g, ", offset.y() + pos[i * scalarsPerPos + 1]);
590 }
591 }
592
593 if (scalarsPerPos != 2) {
594 yStr.appendScalar(offset.y());
595 }
596
597 elem.addAttribute("x", xStr);
598 elem.addAttribute("y", yStr);
599 elem.addText(svg_text(text, len, paint));
fmalita93957f42015-01-30 09:03:29 -0800600}
601
602void SkSVGDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len, const SkPath& path,
603 const SkMatrix* matrix, const SkPaint& paint) {
fmalitaa9d9de42015-02-04 17:54:46 -0800604 SkString pathID = fResourceBucket->addPath();
605
606 {
607 AutoElement defs("defs", fWriter);
608 AutoElement pathElement("path", fWriter);
609 pathElement.addAttribute("id", pathID);
610 pathElement.addPathAttributes(path);
611
612 }
613
614 {
615 AutoElement textElement("text", fWriter);
616 textElement.addTextAttributes(paint);
617
618 if (matrix && !matrix->isIdentity()) {
619 textElement.addAttribute("transform", svg_transform(*matrix));
620 }
621
622 {
623 AutoElement textPathElement("textPath", fWriter);
624 textPathElement.addAttribute("xlink:href", SkStringPrintf("#%s", pathID.c_str()));
625
626 if (paint.getTextAlign() != SkPaint::kLeft_Align) {
627 SkASSERT(paint.getTextAlign() == SkPaint::kCenter_Align ||
628 paint.getTextAlign() == SkPaint::kRight_Align);
629 textPathElement.addAttribute("startOffset",
630 paint.getTextAlign() == SkPaint::kCenter_Align ? "50%" : "100%");
631 }
632
633 textPathElement.addText(svg_text(text, len, paint));
634 }
635 }
fmalita93957f42015-01-30 09:03:29 -0800636}
637
638void SkSVGDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCount,
639 const SkPoint verts[], const SkPoint texs[],
640 const SkColor colors[], SkXfermode* xmode,
641 const uint16_t indices[], int indexCount,
642 const SkPaint& paint) {
643 // todo
fmalitafe3f2602015-02-03 17:47:12 -0800644 SkDebugf("unsupported operation: drawVertices()\n");
fmalita93957f42015-01-30 09:03:29 -0800645}
646
647void SkSVGDevice::drawDevice(const SkDraw&, SkBaseDevice*, int x, int y,
648 const SkPaint&) {
649 // todo
fmalitafe3f2602015-02-03 17:47:12 -0800650 SkDebugf("unsupported operation: drawDevice()\n");
fmalita93957f42015-01-30 09:03:29 -0800651}