| OLD | NEW |
| (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("&"); | |
| 106 break; | |
| 107 case '"': | |
| 108 text->append("""); | |
| 109 break; | |
| 110 case '\'': | |
| 111 text->append("'"); | |
| 112 break; | |
| 113 case '<': | |
| 114 text->append("<"); | |
| 115 break; | |
| 116 case '>': | |
| 117 text->append(">"); | |
| 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 } | |
| OLD | NEW |