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 "SkBase64.h" |
10 #include "SkBitmap.h" | 11 #include "SkBitmap.h" |
11 #include "SkChecksum.h" | 12 #include "SkChecksum.h" |
| 13 #include "SkData.h" |
12 #include "SkDraw.h" | 14 #include "SkDraw.h" |
| 15 #include "SkImageEncoder.h" |
13 #include "SkPaint.h" | 16 #include "SkPaint.h" |
14 #include "SkParsePath.h" | 17 #include "SkParsePath.h" |
15 #include "SkPathOps.h" | |
16 #include "SkShader.h" | 18 #include "SkShader.h" |
17 #include "SkStream.h" | 19 #include "SkStream.h" |
18 #include "SkTHash.h" | 20 #include "SkTHash.h" |
19 #include "SkTypeface.h" | 21 #include "SkTypeface.h" |
20 #include "SkUtils.h" | 22 #include "SkUtils.h" |
21 #include "SkXMLWriter.h" | 23 #include "SkXMLWriter.h" |
22 | 24 |
23 namespace { | 25 namespace { |
24 | 26 |
25 static SkString svg_color(SkColor color) { | 27 static SkString svg_color(SkColor color) { |
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
244 SkString fText, fPosX, fPosY; | 246 SkString fText, fPosX, fPosY; |
245 bool fLastCharWasWhitespace; | 247 bool fLastCharWasWhitespace; |
246 }; | 248 }; |
247 | 249 |
248 } | 250 } |
249 | 251 |
250 // For now all this does is serve unique serial IDs, but it will eventually evol
ve to track | 252 // For now all this does is serve unique serial IDs, but it will eventually evol
ve to track |
251 // and deduplicate resources. | 253 // and deduplicate resources. |
252 class SkSVGDevice::ResourceBucket : ::SkNoncopyable { | 254 class SkSVGDevice::ResourceBucket : ::SkNoncopyable { |
253 public: | 255 public: |
254 ResourceBucket() : fGradientCount(0), fClipCount(0), fPathCount(0) {} | 256 ResourceBucket() : fGradientCount(0), fClipCount(0), fPathCount(0), fImageCo
unt(0) {} |
255 | 257 |
256 SkString addLinearGradient() { | 258 SkString addLinearGradient() { |
257 return SkStringPrintf("gradient_%d", fGradientCount++); | 259 return SkStringPrintf("gradient_%d", fGradientCount++); |
258 } | 260 } |
259 | 261 |
260 SkString addClip() { | 262 SkString addClip() { |
261 return SkStringPrintf("clip_%d", fClipCount++); | 263 return SkStringPrintf("clip_%d", fClipCount++); |
262 } | 264 } |
263 | 265 |
264 SkString addPath() { | 266 SkString addPath() { |
265 return SkStringPrintf("path_%d", fPathCount++); | 267 return SkStringPrintf("path_%d", fPathCount++); |
266 } | 268 } |
267 | 269 |
| 270 SkString addImage() { |
| 271 return SkStringPrintf("img_%d", fImageCount++); |
| 272 } |
| 273 |
268 private: | 274 private: |
269 uint32_t fGradientCount; | 275 uint32_t fGradientCount; |
270 uint32_t fClipCount; | 276 uint32_t fClipCount; |
271 uint32_t fPathCount; | 277 uint32_t fPathCount; |
| 278 uint32_t fImageCount; |
272 }; | 279 }; |
273 | 280 |
274 class SkSVGDevice::AutoElement : ::SkNoncopyable { | 281 class SkSVGDevice::AutoElement : ::SkNoncopyable { |
275 public: | 282 public: |
276 AutoElement(const char name[], SkXMLWriter* writer) | 283 AutoElement(const char name[], SkXMLWriter* writer) |
277 : fWriter(writer) | 284 : fWriter(writer) |
278 , fResourceBucket(NULL) { | 285 , fResourceBucket(NULL) { |
279 fWriter->startElement(name); | 286 fWriter->startElement(name); |
280 } | 287 } |
281 | 288 |
282 AutoElement(const char name[], SkXMLWriter* writer, ResourceBucket* bucket, | 289 AutoElement(const char name[], SkXMLWriter* writer, ResourceBucket* bucket, |
283 const SkDraw& draw, const SkPaint& paint) | 290 const SkDraw& draw, const SkPaint& paint) |
284 : fWriter(writer) | 291 : fWriter(writer) |
285 , fResourceBucket(bucket) { | 292 , fResourceBucket(bucket) { |
286 | 293 |
287 Resources res = this->addResources(draw, paint); | 294 Resources res = this->addResources(draw, paint); |
| 295 if (!res.fClip.isEmpty()) { |
| 296 // The clip is in device space. Apply it via a <g> wrapper to avoid
local transform |
| 297 // interference. |
| 298 fClipGroup.reset(SkNEW_ARGS(AutoElement, ("g", fWriter))); |
| 299 fClipGroup->addAttribute("clip-path",res.fClip); |
| 300 } |
288 | 301 |
289 fWriter->startElement(name); | 302 fWriter->startElement(name); |
290 | 303 |
291 this->addPaint(paint, res); | 304 this->addPaint(paint, res); |
292 | 305 |
293 if (!draw.fMatrix->isIdentity()) { | 306 if (!draw.fMatrix->isIdentity()) { |
294 this->addAttribute("transform", svg_transform(*draw.fMatrix)); | 307 this->addAttribute("transform", svg_transform(*draw.fMatrix)); |
295 } | 308 } |
296 } | 309 } |
297 | 310 |
(...skipping 27 matching lines...) Expand all Loading... |
325 | 338 |
326 private: | 339 private: |
327 Resources addResources(const SkDraw& draw, const SkPaint& paint); | 340 Resources addResources(const SkDraw& draw, const SkPaint& paint); |
328 void addClipResources(const SkDraw& draw, Resources* resources); | 341 void addClipResources(const SkDraw& draw, Resources* resources); |
329 void addShaderResources(const SkPaint& paint, Resources* resources); | 342 void addShaderResources(const SkPaint& paint, Resources* resources); |
330 | 343 |
331 void addPaint(const SkPaint& paint, const Resources& resources); | 344 void addPaint(const SkPaint& paint, const Resources& resources); |
332 | 345 |
333 SkString addLinearGradientDef(const SkShader::GradientInfo& info, const SkSh
ader* shader); | 346 SkString addLinearGradientDef(const SkShader::GradientInfo& info, const SkSh
ader* shader); |
334 | 347 |
335 SkXMLWriter* fWriter; | 348 SkXMLWriter* fWriter; |
336 ResourceBucket* fResourceBucket; | 349 ResourceBucket* fResourceBucket; |
| 350 SkAutoTDelete<AutoElement> fClipGroup; |
337 }; | 351 }; |
338 | 352 |
339 void SkSVGDevice::AutoElement::addPaint(const SkPaint& paint, const Resources& r
esources) { | 353 void SkSVGDevice::AutoElement::addPaint(const SkPaint& paint, const Resources& r
esources) { |
340 SkPaint::Style style = paint.getStyle(); | 354 SkPaint::Style style = paint.getStyle(); |
341 if (style == SkPaint::kFill_Style || style == SkPaint::kStrokeAndFill_Style)
{ | 355 if (style == SkPaint::kFill_Style || style == SkPaint::kStrokeAndFill_Style)
{ |
342 this->addAttribute("fill", resources.fPaintServer); | 356 this->addAttribute("fill", resources.fPaintServer); |
343 | 357 |
344 if (SK_AlphaOPAQUE != SkColorGetA(paint.getColor())) { | 358 if (SK_AlphaOPAQUE != SkColorGetA(paint.getColor())) { |
345 this->addAttribute("fill-opacity", svg_opacity(paint.getColor())); | 359 this->addAttribute("fill-opacity", svg_opacity(paint.getColor())); |
346 } | 360 } |
(...skipping 25 matching lines...) Expand all Loading... |
372 this->addAttribute("stroke-miterlimit", paint.getStrokeMiter()); | 386 this->addAttribute("stroke-miterlimit", paint.getStrokeMiter()); |
373 } | 387 } |
374 | 388 |
375 if (SK_AlphaOPAQUE != SkColorGetA(paint.getColor())) { | 389 if (SK_AlphaOPAQUE != SkColorGetA(paint.getColor())) { |
376 this->addAttribute("stroke-opacity", svg_opacity(paint.getColor())); | 390 this->addAttribute("stroke-opacity", svg_opacity(paint.getColor())); |
377 } | 391 } |
378 } else { | 392 } else { |
379 SkASSERT(style == SkPaint::kFill_Style); | 393 SkASSERT(style == SkPaint::kFill_Style); |
380 this->addAttribute("stroke", "none"); | 394 this->addAttribute("stroke", "none"); |
381 } | 395 } |
382 | |
383 if (!resources.fClip.isEmpty()) { | |
384 this->addAttribute("clip-path", resources.fClip); | |
385 } | |
386 } | 396 } |
387 | 397 |
388 Resources SkSVGDevice::AutoElement::addResources(const SkDraw& draw, const SkPai
nt& paint) { | 398 Resources SkSVGDevice::AutoElement::addResources(const SkDraw& draw, const SkPai
nt& paint) { |
389 Resources resources(paint); | 399 Resources resources(paint); |
390 | 400 |
391 // FIXME: this is a weak heuristic and we end up with LOTS of redundant clip
s. | 401 // FIXME: this is a weak heuristic and we end up with LOTS of redundant clip
s. |
392 bool hasClip = !draw.fClipStack->isWideOpen(); | 402 bool hasClip = !draw.fClipStack->isWideOpen(); |
393 bool hasShader = SkToBool(paint.getShader()); | 403 bool hasShader = SkToBool(paint.getShader()); |
394 | 404 |
395 if (hasClip || hasShader) { | 405 if (hasClip || hasShader) { |
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
622 // todo | 632 // todo |
623 SkDebugf("unsupported operation: drawRRect()\n"); | 633 SkDebugf("unsupported operation: drawRRect()\n"); |
624 } | 634 } |
625 | 635 |
626 void SkSVGDevice::drawPath(const SkDraw& draw, const SkPath& path, const SkPaint
& paint, | 636 void SkSVGDevice::drawPath(const SkDraw& draw, const SkPath& path, const SkPaint
& paint, |
627 const SkMatrix* prePathMatrix, bool pathIsMutable) { | 637 const SkMatrix* prePathMatrix, bool pathIsMutable) { |
628 AutoElement elem("path", fWriter, fResourceBucket, draw, paint); | 638 AutoElement elem("path", fWriter, fResourceBucket, draw, paint); |
629 elem.addPathAttributes(path); | 639 elem.addPathAttributes(path); |
630 } | 640 } |
631 | 641 |
632 void SkSVGDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap, | 642 void SkSVGDevice::drawBitmapCommon(const SkDraw& draw, const SkBitmap& bm, |
633 const SkMatrix& matrix, const SkPaint& paint) { | 643 const SkPaint& paint) { |
634 // todo | 644 SkAutoTUnref<const SkData> pngData( |
635 SkDebugf("unsupported operation: drawBitmap()\n"); | 645 SkImageEncoder::EncodeData(bm, SkImageEncoder::kPNG_Type, SkImageEncoder
::kDefaultQuality)); |
| 646 if (!pngData) { |
| 647 return; |
| 648 } |
| 649 |
| 650 size_t b64Size = SkBase64::Encode(pngData->data(), pngData->size(), NULL); |
| 651 SkAutoTMalloc<char> b64Data(b64Size); |
| 652 SkBase64::Encode(pngData->data(), pngData->size(), b64Data.get()); |
| 653 |
| 654 SkString svgImageData("data:image/png;base64,"); |
| 655 svgImageData.append(b64Data.get(), b64Size); |
| 656 |
| 657 SkString imageID = fResourceBucket->addImage(); |
| 658 { |
| 659 AutoElement defs("defs", fWriter); |
| 660 { |
| 661 AutoElement image("image", fWriter); |
| 662 image.addAttribute("id", imageID); |
| 663 image.addAttribute("width", bm.width()); |
| 664 image.addAttribute("height", bm.height()); |
| 665 image.addAttribute("xlink:href", svgImageData); |
| 666 } |
| 667 } |
| 668 |
| 669 { |
| 670 AutoElement imageUse("use", fWriter, fResourceBucket, draw, paint); |
| 671 imageUse.addAttribute("xlink:href", SkStringPrintf("#%s", imageID.c_str(
))); |
| 672 } |
636 } | 673 } |
637 | 674 |
638 void SkSVGDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap, | 675 void SkSVGDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap, |
639 int x, int y, const SkPaint& paint) { | 676 const SkMatrix& matrix, const SkPaint& paint) { |
640 // todo | 677 SkMatrix adjustedMatrix = *draw.fMatrix; |
641 SkDebugf("unsupported operation: drawSprite()\n"); | 678 adjustedMatrix.preConcat(matrix); |
| 679 SkDraw adjustedDraw(draw); |
| 680 adjustedDraw.fMatrix = &adjustedMatrix; |
| 681 |
| 682 drawBitmapCommon(adjustedDraw, bitmap, paint); |
642 } | 683 } |
643 | 684 |
644 void SkSVGDevice::drawBitmapRect(const SkDraw&, const SkBitmap&, const SkRect* s
rcOrNull, | 685 void SkSVGDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, |
| 686 int x, int y, const SkPaint& paint) { |
| 687 SkMatrix adjustedMatrix = *draw.fMatrix; |
| 688 adjustedMatrix.preTranslate(SkIntToScalar(x), SkIntToScalar(y)); |
| 689 SkDraw adjustedDraw(draw); |
| 690 adjustedDraw.fMatrix = &adjustedMatrix; |
| 691 |
| 692 drawBitmapCommon(adjustedDraw, bitmap, paint); |
| 693 } |
| 694 |
| 695 void SkSVGDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bm, const S
kRect* srcOrNull, |
645 const SkRect& dst, const SkPaint& paint, | 696 const SkRect& dst, const SkPaint& paint, |
646 SkCanvas::DrawBitmapRectFlags flags) { | 697 SkCanvas::DrawBitmapRectFlags) { |
647 // todo | 698 SkMatrix adjustedMatrix; |
648 SkDebugf("unsupported operation: drawBitmapRect()\n"); | 699 adjustedMatrix.setRectToRect(srcOrNull ? *srcOrNull : SkRect::Make(bm.bounds
()), |
| 700 dst, |
| 701 SkMatrix::kFill_ScaleToFit); |
| 702 adjustedMatrix.postConcat(*draw.fMatrix); |
| 703 |
| 704 SkDraw adjustedDraw(draw); |
| 705 adjustedDraw.fMatrix = &adjustedMatrix; |
| 706 |
| 707 SkClipStack adjustedClipStack; |
| 708 if (srcOrNull && *srcOrNull != SkRect::Make(bm.bounds())) { |
| 709 SkRect devClipRect; |
| 710 draw.fMatrix->mapRect(&devClipRect, dst); |
| 711 |
| 712 adjustedClipStack = *draw.fClipStack; |
| 713 adjustedClipStack.clipDevRect(devClipRect, SkRegion::kIntersect_Op, pain
t.isAntiAlias()); |
| 714 adjustedDraw.fClipStack = &adjustedClipStack; |
| 715 } |
| 716 |
| 717 drawBitmapCommon(adjustedDraw, bm, paint); |
649 } | 718 } |
650 | 719 |
651 void SkSVGDevice::drawText(const SkDraw& draw, const void* text, size_t len, | 720 void SkSVGDevice::drawText(const SkDraw& draw, const void* text, size_t len, |
652 SkScalar x, SkScalar y, const SkPaint& paint) { | 721 SkScalar x, SkScalar y, const SkPaint& paint) { |
653 AutoElement elem("text", fWriter, fResourceBucket, draw, paint); | 722 AutoElement elem("text", fWriter, fResourceBucket, draw, paint); |
654 elem.addTextAttributes(paint); | 723 elem.addTextAttributes(paint); |
655 | 724 |
656 SVGTextBuilder builder(text, len, paint, SkPoint::Make(x, y), 0); | 725 SVGTextBuilder builder(text, len, paint, SkPoint::Make(x, y), 0); |
657 elem.addAttribute("x", builder.posX()); | 726 elem.addAttribute("x", builder.posX()); |
658 elem.addAttribute("y", builder.posY()); | 727 elem.addAttribute("y", builder.posY()); |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
717 const SkPaint& paint) { | 786 const SkPaint& paint) { |
718 // todo | 787 // todo |
719 SkDebugf("unsupported operation: drawVertices()\n"); | 788 SkDebugf("unsupported operation: drawVertices()\n"); |
720 } | 789 } |
721 | 790 |
722 void SkSVGDevice::drawDevice(const SkDraw&, SkBaseDevice*, int x, int y, | 791 void SkSVGDevice::drawDevice(const SkDraw&, SkBaseDevice*, int x, int y, |
723 const SkPaint&) { | 792 const SkPaint&) { |
724 // todo | 793 // todo |
725 SkDebugf("unsupported operation: drawDevice()\n"); | 794 SkDebugf("unsupported operation: drawDevice()\n"); |
726 } | 795 } |
OLD | NEW |