Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkSVGDevice.cpp
Go to the documentation of this file.
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
9
15#include "include/core/SkData.h"
17#include "include/core/SkFont.h"
23#include "include/core/SkPath.h"
30#include "include/core/SkRect.h"
33#include "include/core/SkSize.h"
34#include "include/core/SkSpan.h"
47#include "src/base/SkBase64.h"
48#include "src/base/SkTLazy.h"
51#include "src/core/SkDevice.h"
52#include "src/core/SkFontPriv.h"
53#include "src/core/SkTHash.h"
57#include "src/text/GlyphRun.h"
58#include "src/xml/SkXMLWriter.h"
59
60#include <cstring>
61#include <memory>
62#include <string>
63#include <utility>
64
65using namespace skia_private;
66
67#if defined(SK_GANESH)
68class SkMesh;
69#endif
70class SkBlender;
71class SkVertices;
73
74namespace {
75
76static SkString svg_color(SkColor color) {
77 // https://www.w3.org/TR/css-color-3/#html4
78 auto named_color = [](SkColor c) -> const char* {
79 switch (c & 0xffffff) {
80 case 0x000000: return "black";
81 case 0x000080: return "navy";
82 case 0x0000ff: return "blue";
83 case 0x008000: return "green";
84 case 0x008080: return "teal";
85 case 0x00ff00: return "lime";
86 case 0x00ffff: return "aqua";
87 case 0x800000: return "maroon";
88 case 0x800080: return "purple";
89 case 0x808000: return "olive";
90 case 0x808080: return "gray";
91 case 0xc0c0c0: return "silver";
92 case 0xff0000: return "red";
93 case 0xff00ff: return "fuchsia";
94 case 0xffff00: return "yellow";
95 case 0xffffff: return "white";
96 default: break;
97 }
98
99 return nullptr;
100 };
101
102 if (const auto* nc = named_color(color)) {
103 return SkString(nc);
104 }
105
106 uint8_t r = SkColorGetR(color);
107 uint8_t g = SkColorGetG(color);
108 uint8_t b = SkColorGetB(color);
109
110 // Some users care about every byte here, so we'll use hex colors with single-digit channels
111 // when possible.
112 uint8_t rh = r >> 4;
113 uint8_t rl = r & 0xf;
114 uint8_t gh = g >> 4;
115 uint8_t gl = g & 0xf;
116 uint8_t bh = b >> 4;
117 uint8_t bl = b & 0xf;
118 if ((rh == rl) && (gh == gl) && (bh == bl)) {
119 return SkStringPrintf("#%1X%1X%1X", rh, gh, bh);
120 }
121
122 return SkStringPrintf("#%02X%02X%02X", r, g, b);
123}
124
125static SkScalar svg_opacity(SkColor color) {
127}
128
129// Keep in sync with SkPaint::Cap
130static const char* cap_map[] = {
131 nullptr, // kButt_Cap (default)
132 "round", // kRound_Cap
133 "square" // kSquare_Cap
134};
135static_assert(std::size(cap_map) == SkPaint::kCapCount, "missing_cap_map_entry");
136
137static const char* svg_cap(SkPaint::Cap cap) {
138 SkASSERT(static_cast<size_t>(cap) < std::size(cap_map));
139 return cap_map[cap];
140}
141
142// Keep in sync with SkPaint::Join
143static const char* join_map[] = {
144 nullptr, // kMiter_Join (default)
145 "round", // kRound_Join
146 "bevel" // kBevel_Join
147};
148static_assert(std::size(join_map) == SkPaint::kJoinCount, "missing_join_map_entry");
149
150static const char* svg_join(SkPaint::Join join) {
151 SkASSERT(join < std::size(join_map));
152 return join_map[join];
153}
154
155static SkString svg_transform(const SkMatrix& t) {
156 SkASSERT(!t.isIdentity());
157
158 SkString tstr;
159 switch (t.getType()) {
161 // TODO: handle perspective matrices?
162 break;
164 tstr.printf("translate(%g %g)", t.getTranslateX(), t.getTranslateY());
165 break;
167 tstr.printf("scale(%g %g)", t.getScaleX(), t.getScaleY());
168 break;
169 default:
170 // http://www.w3.org/TR/SVG/coords.html#TransformMatrixDefined
171 // | a c e |
172 // | b d f |
173 // | 0 0 1 |
174 tstr.printf("matrix(%g %g %g %g %g %g)",
175 t.getScaleX(), t.getSkewY(),
176 t.getSkewX(), t.getScaleY(),
178 break;
179 }
180
181 return tstr;
182}
183
184struct Resources {
185 Resources(const SkPaint& paint)
186 : fPaintServer(svg_color(paint.getColor())) {}
187
188 SkString fPaintServer;
189 SkString fColorFilter;
190};
191
192// Determine if the paint requires us to reset the viewport.
193// Currently, we do this whenever the paint shader calls
194// for a repeating image.
195bool RequiresViewportReset(const SkPaint& paint) {
196 SkShader* shader = paint.getShader();
197 if (!shader)
198 return false;
199
200 SkTileMode xy[2];
201 SkImage* image = shader->isAImage(nullptr, xy);
202
203 if (!image)
204 return false;
205
206 for (int i = 0; i < 2; i++) {
207 if (xy[i] == SkTileMode::kRepeat)
208 return true;
209 }
210 return false;
211}
212
213void AddPath(const sktext::GlyphRun& glyphRun, const SkPoint& offset, SkPath* path) {
214 struct Rec {
215 SkPath* fPath;
216 const SkPoint fOffset;
217 const SkPoint* fPos;
218 } rec = { path, offset, glyphRun.positions().data() };
219
220 glyphRun.font().getPaths(glyphRun.glyphsIDs().data(), SkToInt(glyphRun.glyphsIDs().size()),
221 [](const SkPath* path, const SkMatrix& mx, void* ctx) {
222 Rec* rec = reinterpret_cast<Rec*>(ctx);
223 if (path) {
224 SkMatrix total = mx;
225 total.postTranslate(rec->fPos->fX + rec->fOffset.fX,
226 rec->fPos->fY + rec->fOffset.fY);
227 rec->fPath->addPath(*path, total);
228 } else {
229 // TODO: this is going to drop color emojis.
230 }
231 rec->fPos += 1; // move to the next glyph's position
232 }, &rec);
233}
234
235} // namespace
236
237// For now all this does is serve unique serial IDs, but it will eventually evolve to track
238// and deduplicate resources.
240public:
242 : fGradientCount(0)
243 , fPathCount(0)
244 , fImageCount(0)
245 , fPatternCount(0)
246 , fColorFilterCount(0) {}
247
249 return SkStringPrintf("gradient_%u", fGradientCount++);
250 }
251
253 return SkStringPrintf("path_%u", fPathCount++);
254 }
255
257 return SkStringPrintf("img_%u", fImageCount++);
258 }
259
260 SkString addColorFilter() { return SkStringPrintf("cfilter_%u", fColorFilterCount++); }
261
263 return SkStringPrintf("pattern_%u", fPatternCount++);
264 }
265
266private:
267 uint32_t fGradientCount;
268 uint32_t fPathCount;
269 uint32_t fImageCount;
270 uint32_t fPatternCount;
271 uint32_t fColorFilterCount;
272};
273
281
283public:
284 AutoElement(const char name[], SkXMLWriter* writer)
285 : fWriter(writer)
286 , fResourceBucket(nullptr) {
287 fWriter->startElement(name);
288 }
289
290 AutoElement(const char name[], const std::unique_ptr<SkXMLWriter>& writer)
291 : AutoElement(name, writer.get()) {}
292
293 AutoElement(const char name[], SkSVGDevice* svgdev,
294 ResourceBucket* bucket, const MxCp& mc, const SkPaint& paint)
295 : fWriter(svgdev->fWriter.get())
296 , fResourceBucket(bucket) {
297
298 svgdev->syncClipStack(*mc.fClipStack);
299 Resources res = this->addResources(mc, paint);
300
301 fWriter->startElement(name);
302
303 this->addPaint(paint, res);
304
305 if (!mc.fMatrix->isIdentity()) {
306 this->addAttribute("transform", svg_transform(*mc.fMatrix));
307 }
308 }
309
311 fWriter->endElement();
312 }
313
314 void addAttribute(const char name[], const char val[]) {
315 fWriter->addAttribute(name, val);
316 }
317
318 void addAttribute(const char name[], const SkString& val) {
319 fWriter->addAttribute(name, val.c_str());
320 }
321
322 void addAttribute(const char name[], int32_t val) {
323 fWriter->addS32Attribute(name, val);
324 }
325
326 void addAttribute(const char name[], SkScalar val) {
327 fWriter->addScalarAttribute(name, val);
328 }
329
330 void addText(const SkString& text) {
331 fWriter->addText(text.c_str(), text.size());
332 }
333
334 void addRectAttributes(const SkRect&);
336 void addTextAttributes(const SkFont&);
337
338private:
339 Resources addResources(const MxCp&, const SkPaint& paint);
340 void addShaderResources(const SkPaint& paint, Resources* resources);
341 void addGradientShaderResources(const SkShader* shader, const SkPaint& paint,
342 Resources* resources);
343 void addColorFilterResources(const SkColorFilter& cf, Resources* resources);
344 void addImageShaderResources(const SkShader* shader, const SkPaint& paint,
345 Resources* resources);
346
347 void addPatternDef(const SkBitmap& bm);
348
349 void addPaint(const SkPaint& paint, const Resources& resources);
350
351 SkString addLinearGradientDef(const SkShaderBase::GradientInfo& info,
352 const SkShader* shader,
353 const SkMatrix& localMatrix);
354
355 SkXMLWriter* fWriter;
356 ResourceBucket* fResourceBucket;
357};
358
359void SkSVGDevice::AutoElement::addPaint(const SkPaint& paint, const Resources& resources) {
360 // Path effects are applied to all vector graphics (rects, rrects, ovals,
361 // paths etc). This should only happen when a path effect is attached to
362 // non-vector graphics (text, image) or a new vector graphics primitive is
363 //added that is not handled by base drawPath() routine.
364 if (paint.getPathEffect() != nullptr) {
365 SkDebugf("Unsupported path effect in addPaint.");
366 }
367 SkPaint::Style style = paint.getStyle();
368 if (style == SkPaint::kFill_Style || style == SkPaint::kStrokeAndFill_Style) {
369 static constexpr char kDefaultFill[] = "black";
370 if (!resources.fPaintServer.equals(kDefaultFill)) {
371 this->addAttribute("fill", resources.fPaintServer);
372 }
373 if (SK_AlphaOPAQUE != SkColorGetA(paint.getColor())) {
374 this->addAttribute("fill-opacity", svg_opacity(paint.getColor()));
375 }
376 } else {
378 this->addAttribute("fill", "none");
379 }
380
381 if (!resources.fColorFilter.isEmpty()) {
382 this->addAttribute("filter", resources.fColorFilter.c_str());
383 }
384
385 if (style == SkPaint::kStroke_Style || style == SkPaint::kStrokeAndFill_Style) {
386 this->addAttribute("stroke", resources.fPaintServer);
387
388 SkScalar strokeWidth = paint.getStrokeWidth();
389 if (strokeWidth == 0) {
390 // Hairline stroke
391 strokeWidth = 1;
392 this->addAttribute("vector-effect", "non-scaling-stroke");
393 }
394 this->addAttribute("stroke-width", strokeWidth);
395
396 if (const char* cap = svg_cap(paint.getStrokeCap())) {
397 this->addAttribute("stroke-linecap", cap);
398 }
399
400 if (const char* join = svg_join(paint.getStrokeJoin())) {
401 this->addAttribute("stroke-linejoin", join);
402 }
403
404 if (paint.getStrokeJoin() == SkPaint::kMiter_Join) {
405 this->addAttribute("stroke-miterlimit", paint.getStrokeMiter());
406 }
407
408 if (SK_AlphaOPAQUE != SkColorGetA(paint.getColor())) {
409 this->addAttribute("stroke-opacity", svg_opacity(paint.getColor()));
410 }
411 } else {
413 // SVG default stroke value is "none".
414 }
415}
416
417Resources SkSVGDevice::AutoElement::addResources(const MxCp& mc, const SkPaint& paint) {
418 Resources resources(paint);
419
420 if (paint.getShader()) {
421 AutoElement defs("defs", fWriter);
422
423 this->addShaderResources(paint, &resources);
424 }
425
426 if (const SkColorFilter* cf = paint.getColorFilter()) {
427 // TODO: Implement skia color filters for blend modes other than SrcIn
429 if (cf->asAColorMode(nullptr, &mode) && mode == SkBlendMode::kSrcIn) {
430 this->addColorFilterResources(*cf, &resources);
431 }
432 }
433
434 return resources;
435}
436
437void SkSVGDevice::AutoElement::addGradientShaderResources(const SkShader* shader,
438 const SkPaint& paint,
439 Resources* resources) {
440 SkASSERT(shader);
441 if (as_SB(shader)->type() == SkShaderBase::ShaderType::kColor) {
442 auto colorShader = static_cast<const SkColorShader*>(shader);
443 resources->fPaintServer = svg_color(colorShader->color());
444 return;
445 }
446
448 const auto gradient_type = as_SB(shader)->asGradient(&grInfo);
449
450 if (gradient_type != SkShaderBase::GradientType::kLinear) {
451 // TODO: other gradient support
452 return;
453 }
454
455 AutoSTArray<16, SkColor> grColors(grInfo.fColorCount);
456 AutoSTArray<16, SkScalar> grOffsets(grInfo.fColorCount);
457 grInfo.fColors = grColors.get();
458 grInfo.fColorOffsets = grOffsets.get();
459
460 // One more call to get the actual colors/offsets and local matrix.
461 SkMatrix localMatrix;
462 as_SB(shader)->asGradient(&grInfo, &localMatrix);
463 SkASSERT(grInfo.fColorCount <= grColors.count());
464 SkASSERT(grInfo.fColorCount <= grOffsets.count());
465
466 SkASSERT(grColors.size() > 0);
467 resources->fPaintServer =
468 SkStringPrintf("url(#%s)", addLinearGradientDef(grInfo, shader, localMatrix).c_str());
469}
470
471void SkSVGDevice::AutoElement::addColorFilterResources(const SkColorFilter& cf,
472 Resources* resources) {
473 SkString colorfilterID = fResourceBucket->addColorFilter();
474 {
475 AutoElement filterElement("filter", fWriter);
476 filterElement.addAttribute("id", colorfilterID);
477 filterElement.addAttribute("x", "0%");
478 filterElement.addAttribute("y", "0%");
479 filterElement.addAttribute("width", "100%");
480 filterElement.addAttribute("height", "100%");
481
482 SkColor filterColor;
484 bool asAColorMode = cf.asAColorMode(&filterColor, &mode);
485 SkAssertResult(asAColorMode);
487
488 {
489 // first flood with filter color
490 AutoElement floodElement("feFlood", fWriter);
491 floodElement.addAttribute("flood-color", svg_color(filterColor));
492 floodElement.addAttribute("flood-opacity", svg_opacity(filterColor));
493 floodElement.addAttribute("result", "flood");
494 }
495
496 {
497 // apply the transform to filter color
498 AutoElement compositeElement("feComposite", fWriter);
499 compositeElement.addAttribute("in", "flood");
500 compositeElement.addAttribute("operator", "in");
501 }
502 }
503 resources->fColorFilter.printf("url(#%s)", colorfilterID.c_str());
504}
505
506static bool is_png(const void* bytes, size_t length) {
507 static constexpr uint8_t pngSig[] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
508 return length >= sizeof(pngSig) && !memcmp(bytes, pngSig, sizeof(pngSig));
509}
510
511static bool is_jpeg(const void* bytes, size_t length) {
512 static constexpr uint8_t jpegSig[] = {0xFF, 0xD8, 0xFF};
513 return length >= sizeof(jpegSig) && !memcmp(bytes, jpegSig, sizeof(jpegSig));
514}
515
516// Returns data uri from bytes.
517// it will use any cached data if available, otherwise will
518// encode as png.
520 static constexpr char jpgDataPrefix[] = "data:image/jpeg;base64,";
521 static constexpr char pngDataPrefix[] = "data:image/png;base64,";
522
524
525 const char* selectedPrefix = pngDataPrefix;
526 size_t selectedPrefixLength = sizeof(pngDataPrefix);
527
528 sk_sp<SkData> imageData = image->refEncodedData();
529 if (imageData) { // Already encoded as something
530 if (is_jpeg(imageData->data(), imageData->size())) {
531 selectedPrefix = jpgDataPrefix;
532 selectedPrefixLength = sizeof(jpgDataPrefix);
533 } else if (!is_png(imageData->data(), imageData->size())) {
534 // re-encode the image as a PNG.
535 // GrDirectContext is nullptr because we shouldn't have any texture-based images
536 // passed in.
537 imageData = SkPngEncoder::Encode(nullptr, image, {});
538 if (!imageData) {
539 return nullptr;
540 }
541 }
542 // else, it's already encoded as a PNG - we don't need to do anything.
543 } else {
544 // It was not encoded as something, so we need to encode it as a PNG.
545 imageData = SkPngEncoder::Encode(nullptr, image, {});
546 if (!imageData) {
547 return nullptr;
548 }
549 }
550
551 size_t b64Size = SkBase64::EncodedSize(imageData->size());
552 sk_sp<SkData> dataUri = SkData::MakeUninitialized(selectedPrefixLength + b64Size);
553 char* dest = (char*)dataUri->writable_data();
554 memcpy(dest, selectedPrefix, selectedPrefixLength);
555 SkBase64::Encode(imageData->data(), imageData->size(), dest + selectedPrefixLength - 1);
556 dest[dataUri->size() - 1] = 0;
557 return dataUri;
558}
559
560void SkSVGDevice::AutoElement::addImageShaderResources(const SkShader* shader, const SkPaint& paint,
561 Resources* resources) {
562 SkMatrix outMatrix;
563
564 SkTileMode xy[2];
565 SkImage* image = shader->isAImage(&outMatrix, xy);
567
568 SkString patternDims[2]; // width, height
569
570 sk_sp<SkData> dataUri = AsDataUri(image);
571 if (!dataUri) {
572 return;
573 }
574 SkIRect imageSize = image->bounds();
575 for (int i = 0; i < 2; i++) {
576 int imageDimension = i == 0 ? imageSize.width() : imageSize.height();
577 switch (xy[i]) {
579 patternDims[i].appendScalar(imageDimension);
580 break;
581 default:
582 // TODO: other tile modes?
583 patternDims[i] = "100%";
584 }
585 }
586
587 SkString patternID = fResourceBucket->addPattern();
588 {
589 AutoElement pattern("pattern", fWriter);
590 pattern.addAttribute("id", patternID);
591 pattern.addAttribute("patternUnits", "userSpaceOnUse");
592 pattern.addAttribute("patternContentUnits", "userSpaceOnUse");
593 pattern.addAttribute("width", patternDims[0]);
594 pattern.addAttribute("height", patternDims[1]);
595 pattern.addAttribute("x", 0);
596 pattern.addAttribute("y", 0);
597
598 {
599 SkString imageID = fResourceBucket->addImage();
600 AutoElement imageTag("image", fWriter);
601 imageTag.addAttribute("id", imageID);
602 imageTag.addAttribute("x", 0);
603 imageTag.addAttribute("y", 0);
604 imageTag.addAttribute("width", image->width());
605 imageTag.addAttribute("height", image->height());
606 imageTag.addAttribute("xlink:href", static_cast<const char*>(dataUri->data()));
607 }
608 }
609 resources->fPaintServer.printf("url(#%s)", patternID.c_str());
610}
611
612void SkSVGDevice::AutoElement::addShaderResources(const SkPaint& paint, Resources* resources) {
613 const SkShader* shader = paint.getShader();
614 SkASSERT(shader);
615
616 auto shaderType = as_SB(shader)->type();
617 if (shaderType == SkShaderBase::ShaderType::kColor ||
618 shaderType == SkShaderBase::ShaderType::kGradientBase) {
619 this->addGradientShaderResources(shader, paint, resources);
620 } else if (shader->isAImage()) {
621 this->addImageShaderResources(shader, paint, resources);
622 }
623 // TODO: other shader types?
624}
625
626SkString SkSVGDevice::AutoElement::addLinearGradientDef(const SkShaderBase::GradientInfo& info,
627 const SkShader* shader,
628 const SkMatrix& localMatrix) {
629 SkASSERT(fResourceBucket);
630 SkString id = fResourceBucket->addLinearGradient();
631
632 {
633 AutoElement gradient("linearGradient", fWriter);
634
635 gradient.addAttribute("id", id);
636 gradient.addAttribute("gradientUnits", "userSpaceOnUse");
637 gradient.addAttribute("x1", info.fPoint[0].x());
638 gradient.addAttribute("y1", info.fPoint[0].y());
639 gradient.addAttribute("x2", info.fPoint[1].x());
640 gradient.addAttribute("y2", info.fPoint[1].y());
641
642 if (!localMatrix.isIdentity()) {
643 this->addAttribute("gradientTransform", svg_transform(localMatrix));
644 }
645
646 SkASSERT(info.fColorCount >= 2);
647 for (int i = 0; i < info.fColorCount; ++i) {
648 SkColor color = info.fColors[i];
649 SkString colorStr(svg_color(color));
650
651 {
652 AutoElement stop("stop", fWriter);
653 stop.addAttribute("offset", info.fColorOffsets[i]);
654 stop.addAttribute("stop-color", colorStr.c_str());
655
657 stop.addAttribute("stop-opacity", svg_opacity(color));
658 }
659 }
660 }
661 }
662
663 return id;
664}
665
667 // x, y default to 0
668 if (rect.x() != 0) {
669 this->addAttribute("x", rect.x());
670 }
671 if (rect.y() != 0) {
672 this->addAttribute("y", rect.y());
673 }
674
675 this->addAttribute("width", rect.width());
676 this->addAttribute("height", rect.height());
677}
678
680 SkParsePath::PathEncoding encoding) {
681 this->addAttribute("d", SkParsePath::ToSVGString(path, encoding));
682}
683
685 this->addAttribute("font-size", font.getSize());
686
687 SkString familyName;
688 THashSet<SkString> familySet;
689 sk_sp<SkTypeface> tface = font.refTypeface();
690
691 SkASSERT(tface);
692 SkFontStyle style = tface->fontStyle();
693 if (style.slant() == SkFontStyle::kItalic_Slant) {
694 this->addAttribute("font-style", "italic");
695 } else if (style.slant() == SkFontStyle::kOblique_Slant) {
696 this->addAttribute("font-style", "oblique");
697 }
698 int weightIndex = (SkTPin(style.weight(), 100, 900) - 50) / 100;
699 if (weightIndex != 3) {
700 static constexpr const char* weights[] = {
701 "100", "200", "300", "normal", "400", "500", "600", "bold", "800", "900"
702 };
703 this->addAttribute("font-weight", weights[weightIndex]);
704 }
705 int stretchIndex = style.width() - 1;
706 if (stretchIndex != 4) {
707 static constexpr const char* stretches[] = {
708 "ultra-condensed", "extra-condensed", "condensed", "semi-condensed",
709 "normal",
710 "semi-expanded", "expanded", "extra-expanded", "ultra-expanded"
711 };
712 this->addAttribute("font-stretch", stretches[stretchIndex]);
713 }
714
715 sk_sp<SkTypeface::LocalizedStrings> familyNameIter(tface->createFamilyNameIterator());
716 SkTypeface::LocalizedString familyString;
717 if (familyNameIter) {
718 while (familyNameIter->next(&familyString)) {
719 if (familySet.contains(familyString.fString)) {
720 continue;
721 }
722 familySet.add(familyString.fString);
723 familyName.appendf((familyName.isEmpty() ? "%s" : ", %s"), familyString.fString.c_str());
724 }
725 }
726 if (!familyName.isEmpty()) {
727 this->addAttribute("font-family", familyName);
728 }
729}
730
732 std::unique_ptr<SkXMLWriter> writer,
733 uint32_t flags) {
734 return writer ? sk_sp<SkDevice>(new SkSVGDevice(size, std::move(writer), flags))
735 : nullptr;
736}
737
738SkSVGDevice::SkSVGDevice(const SkISize& size, std::unique_ptr<SkXMLWriter> writer, uint32_t flags)
740 SkImageInfo::MakeUnknown(size.fWidth, size.fHeight),
742 , fWriter(std::move(writer))
743 , fResourceBucket(new ResourceBucket)
744 , fFlags(flags)
745{
746 SkASSERT(fWriter);
747
748 fWriter->writeHeader();
749
750 // The root <svg> tag gets closed by the destructor.
751 fRootElement = std::make_unique<AutoElement>("svg", fWriter);
752
753 fRootElement->addAttribute("xmlns", "http://www.w3.org/2000/svg");
754 fRootElement->addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
755 fRootElement->addAttribute("width", size.width());
756 fRootElement->addAttribute("height", size.height());
757}
758
760 // Pop order is important.
761 while (!fClipStack.empty()) {
762 fClipStack.pop_back();
763 }
764}
765
766SkParsePath::PathEncoding SkSVGDevice::pathEncoding() const {
770}
771
772void SkSVGDevice::syncClipStack(const SkClipStack& cs) {
774
775 const SkClipStack::Element* elem;
776 int rec_idx = 0;
777
778 // First, find/preserve the common bottom.
779 while ((elem = iter.next()) && (rec_idx < fClipStack.size())) {
780 if (fClipStack[SkToInt(rec_idx)].fGenID != elem->getGenID()) {
781 break;
782 }
783 rec_idx++;
784 }
785
786 // Discard out-of-date stack top.
787 while (fClipStack.size() > rec_idx) {
788 fClipStack.pop_back();
789 }
790
791 auto define_clip = [this](const SkClipStack::Element* e) {
792 const auto cid = SkStringPrintf("cl_%x", e->getGenID());
793
794 AutoElement clip_path("clipPath", fWriter);
795 clip_path.addAttribute("id", cid);
796
797 // TODO: handle non-intersect clips.
798
799 switch (e->getDeviceSpaceType()) {
801 // TODO: can we skip this?
802 AutoElement rect("rect", fWriter);
803 } break;
805 AutoElement rect("rect", fWriter);
806 rect.addRectAttributes(e->getDeviceSpaceRect());
807 } break;
809 // TODO: complex rrect handling?
810 const auto& rr = e->getDeviceSpaceRRect();
811 const auto radii = rr.getSimpleRadii();
812
813 AutoElement rrect("rect", fWriter);
814 rrect.addRectAttributes(rr.rect());
815 rrect.addAttribute("rx", radii.x());
816 rrect.addAttribute("ry", radii.y());
817 } break;
819 const auto& p = e->getDeviceSpacePath();
820 AutoElement path("path", fWriter);
821 path.addPathAttributes(p, this->pathEncoding());
822 if (p.getFillType() == SkPathFillType::kEvenOdd) {
823 path.addAttribute("clip-rule", "evenodd");
824 }
825 } break;
827 // TODO: handle shader clipping, perhaps rasterize and apply as a mask image?
828 break;
829 }
830
831 return cid;
832 };
833
834 // Rebuild the top.
835 while (elem) {
836 const auto cid = define_clip(elem);
837
838 auto clip_grp = std::make_unique<AutoElement>("g", fWriter);
839 clip_grp->addAttribute("clip-path", SkStringPrintf("url(#%s)", cid.c_str()));
840
841 fClipStack.push_back({ std::move(clip_grp), elem->getGenID() });
842
843 elem = iter.next();
844 }
845}
846
848 AutoElement rect("rect", this, fResourceBucket.get(), MxCp(this), paint);
849 rect.addRectAttributes(SkRect::MakeWH(SkIntToScalar(this->width()),
850 SkIntToScalar(this->height())));
851}
852
853void SkSVGDevice::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
854 if (!value) {
855 return;
856 }
857
858 if (!strcmp(SkAnnotationKeys::URL_Key(), key) ||
860 this->cs().save();
861 this->cs().clipRect(rect, this->localToDevice(), SkClipOp::kIntersect, true);
862 SkRect transformedRect = this->cs().bounds(this->getGlobalBounds());
863 this->cs().restore();
864 if (transformedRect.isEmpty()) {
865 return;
866 }
867
868 SkString url(static_cast<const char*>(value->data()), value->size() - 1);
869 AutoElement a("a", fWriter);
870 a.addAttribute("xlink:href", url.c_str());
871 {
872 AutoElement r("rect", fWriter);
873 r.addAttribute("fill-opacity", "0.0");
874 r.addRectAttributes(transformedRect);
875 }
876 }
877}
878
880 const SkPoint pts[], const SkPaint& paint) {
881 SkPathBuilder path;
882
883 switch (mode) {
884 // todo
886 // TODO?
887 break;
889 count -= 1;
890 for (size_t i = 0; i < count; i += 2) {
891 path.moveTo(pts[i]);
892 path.lineTo(pts[i+1]);
893 }
894 break;
896 if (count > 1) {
897 path.addPolygon(pts, SkToInt(count), false);
898 }
899 break;
900 }
901
902 this->drawPath(path.detach(), paint, true);
903}
904
906 if (paint.getPathEffect()) {
907 this->drawPath(SkPath::Rect(r), paint, true);
908 return;
909 }
910
911 std::unique_ptr<AutoElement> svg;
912 if (RequiresViewportReset(paint)) {
913 svg = std::make_unique<AutoElement>("svg", this, fResourceBucket.get(), MxCp(this), paint);
914 svg->addRectAttributes(r);
915 }
916
917 AutoElement rect("rect", this, fResourceBucket.get(), MxCp(this), paint);
918
919 if (svg) {
920 rect.addAttribute("x", 0);
921 rect.addAttribute("y", 0);
922 rect.addAttribute("width", "100%");
923 rect.addAttribute("height", "100%");
924 } else {
925 rect.addRectAttributes(r);
926 }
927}
928
929void SkSVGDevice::drawOval(const SkRect& oval, const SkPaint& paint) {
930 if (paint.getPathEffect()) {
931 this->drawPath(SkPath::Oval(oval), paint, true);
932 return;
933 }
934
935 AutoElement ellipse("ellipse", this, fResourceBucket.get(), MxCp(this), paint);
936 ellipse.addAttribute("cx", oval.centerX());
937 ellipse.addAttribute("cy", oval.centerY());
938 ellipse.addAttribute("rx", oval.width() / 2);
939 ellipse.addAttribute("ry", oval.height() / 2);
940}
941
943 if (paint.getPathEffect()) {
944 this->drawPath(SkPath::RRect(rr), paint, true);
945 return;
946 }
947
948 AutoElement elem("path", this, fResourceBucket.get(), MxCp(this), paint);
949 elem.addPathAttributes(SkPath::RRect(rr), this->pathEncoding());
950}
951
952void SkSVGDevice::drawPath(const SkPath& path, const SkPaint& paint, bool pathIsMutable) {
953 if (path.isInverseFillType()) {
954 SkDebugf("Inverse path fill type not yet implemented.");
955 return;
956 }
957
958 SkPath pathStorage;
959 SkPath* pathPtr = const_cast<SkPath*>(&path);
961
962 // Apply path effect from paint to path.
963 if (path_paint->getPathEffect()) {
964 if (!pathIsMutable) {
965 pathPtr = &pathStorage;
966 }
967 bool fill = skpathutils::FillPathWithPaint(path, *path_paint, pathPtr);
968 if (fill) {
969 // Path should be filled.
970 path_paint.writable()->setStyle(SkPaint::kFill_Style);
971 } else {
972 // Path should be drawn with a hairline (width == 0).
973 path_paint.writable()->setStyle(SkPaint::kStroke_Style);
974 path_paint.writable()->setStrokeWidth(0);
975 }
976
977 path_paint.writable()->setPathEffect(nullptr); // path effect processed
978 }
979
980 // Create path element.
981 AutoElement elem("path", this, fResourceBucket.get(), MxCp(this), *path_paint);
982 elem.addPathAttributes(*pathPtr, this->pathEncoding());
983
984 // TODO: inverse fill types?
985 if (pathPtr->getFillType() == SkPathFillType::kEvenOdd) {
986 elem.addAttribute("fill-rule", "evenodd");
987 }
988}
989
990static sk_sp<SkData> encode(const SkBitmap& src) {
992 return SkPngEncoder::Encode(&buf, src.pixmap(), {}) ? buf.detachAsData() : nullptr;
993}
994
995void SkSVGDevice::drawBitmapCommon(const MxCp& mc, const SkBitmap& bm, const SkPaint& paint) {
996 sk_sp<SkData> pngData = encode(bm);
997 if (!pngData) {
998 return;
999 }
1000
1001 size_t b64Size = SkBase64::EncodedSize(pngData->size());
1002 AutoTMalloc<char> b64Data(b64Size);
1003 SkBase64::Encode(pngData->data(), pngData->size(), b64Data.get());
1004
1005 SkString svgImageData("data:image/png;base64,");
1006 svgImageData.append(b64Data.get(), b64Size);
1007
1008 SkString imageID = fResourceBucket->addImage();
1009 {
1010 AutoElement defs("defs", fWriter);
1011 {
1012 AutoElement image("image", fWriter);
1013 image.addAttribute("id", imageID);
1014 image.addAttribute("width", bm.width());
1015 image.addAttribute("height", bm.height());
1016 image.addAttribute("xlink:href", svgImageData);
1017 }
1018 }
1019
1020 {
1021 AutoElement imageUse("use", this, fResourceBucket.get(), mc, paint);
1022 imageUse.addAttribute("xlink:href", SkStringPrintf("#%s", imageID.c_str()));
1023 }
1024}
1025
1026void SkSVGDevice::drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
1027 const SkSamplingOptions& sampling, const SkPaint& paint,
1028 SkCanvas::SrcRectConstraint constraint) {
1029 SkBitmap bm;
1030 // TODO: support gpu images
1031 if (!as_IB(image)->getROPixels(nullptr, &bm)) {
1032 return;
1033 }
1034
1035 SkClipStack* cs = &this->cs();
1036 SkClipStack::AutoRestore ar(cs, false);
1037 if (src && *src != SkRect::Make(bm.bounds())) {
1038 cs->save();
1039 cs->clipRect(dst, this->localToDevice(), SkClipOp::kIntersect, paint.isAntiAlias());
1040 }
1041
1042 SkMatrix adjustedMatrix = this->localToDevice()
1043 * SkMatrix::RectToRect(src ? *src : SkRect::Make(bm.bounds()), dst);
1044
1045 drawBitmapCommon(MxCp(&adjustedMatrix, cs), bm, paint);
1046}
1047
1049public:
1050 SVGTextBuilder(SkPoint origin, const sktext::GlyphRun& glyphRun)
1051 : fOrigin(origin) {
1052 auto runSize = glyphRun.runSize();
1053 AutoSTArray<64, SkUnichar> unichars(runSize);
1054 SkFontPriv::GlyphsToUnichars(glyphRun.font(), glyphRun.glyphsIDs().data(),
1055 runSize, unichars.get());
1056 auto positions = glyphRun.positions();
1057 for (size_t i = 0; i < runSize; ++i) {
1058 this->appendUnichar(unichars[i], positions[i]);
1059 }
1060 }
1061
1062 const SkString& text() const { return fText; }
1063 const SkString& posX() const { return fPosXStr; }
1064 const SkString& posY() const { return fHasConstY ? fConstYStr : fPosYStr; }
1065
1066private:
1067 void appendUnichar(SkUnichar c, SkPoint position) {
1068 bool discardPos = false;
1069 bool isWhitespace = false;
1070
1071 switch(c) {
1072 case ' ':
1073 case '\t':
1074 // consolidate whitespace to match SVG's xml:space=default munging
1075 // (http://www.w3.org/TR/SVG/text.html#WhiteSpace)
1076 if (fLastCharWasWhitespace) {
1077 discardPos = true;
1078 } else {
1079 fText.appendUnichar(c);
1080 }
1081 isWhitespace = true;
1082 break;
1083 case '\0':
1084 // SkPaint::glyphsToUnichars() returns \0 for inconvertible glyphs, but these
1085 // are not legal XML characters (http://www.w3.org/TR/REC-xml/#charsets)
1086 discardPos = true;
1087 isWhitespace = fLastCharWasWhitespace; // preserve whitespace consolidation
1088 break;
1089 case '&':
1090 fText.append("&amp;");
1091 break;
1092 case '"':
1093 fText.append("&quot;");
1094 break;
1095 case '\'':
1096 fText.append("&apos;");
1097 break;
1098 case '<':
1099 fText.append("&lt;");
1100 break;
1101 case '>':
1102 fText.append("&gt;");
1103 break;
1104 default:
1105 fText.appendUnichar(c);
1106 break;
1107 }
1108
1109 fLastCharWasWhitespace = isWhitespace;
1110
1111 if (discardPos) {
1112 return;
1113 }
1114
1115 position += fOrigin;
1116 fPosXStr.appendf("%.8g, ", position.fX);
1117 fPosYStr.appendf("%.8g, ", position.fY);
1118
1119 if (fConstYStr.isEmpty()) {
1120 fConstYStr = fPosYStr;
1121 fConstY = position.fY;
1122 } else {
1123 fHasConstY &= SkScalarNearlyEqual(fConstY, position.fY);
1124 }
1125 }
1126
1127 const SkPoint fOrigin;
1128
1129 SkString fText,
1130 fPosXStr, fPosYStr,
1131 fConstYStr;
1132 SkScalar fConstY;
1133 bool fLastCharWasWhitespace = true, // start off in whitespace mode to strip leading space
1134 fHasConstY = true;
1135};
1136
1138 const sktext::GlyphRunList& glyphRunList,
1139 const SkPaint& paint) {
1140 SkASSERT(!glyphRunList.hasRSXForm());
1141 const auto draw_as_path =
1142 (fFlags & SkSVGCanvas::kConvertTextToPaths_Flag) || paint.getPathEffect();
1143
1144 if (draw_as_path) {
1145 // Emit a single <path> element.
1146 SkPath path;
1147 for (auto& glyphRun : glyphRunList) {
1148 AddPath(glyphRun, glyphRunList.origin(), &path);
1149 }
1150
1151 this->drawPath(path, paint);
1152
1153 return;
1154 }
1155
1156 // Emit one <text> element for each run.
1157 for (auto& glyphRun : glyphRunList) {
1158 AutoElement elem("text", this, fResourceBucket.get(), MxCp(this), paint);
1159 elem.addTextAttributes(glyphRun.font());
1160
1161 SVGTextBuilder builder(glyphRunList.origin(), glyphRun);
1162 elem.addAttribute("x", builder.posX());
1163 elem.addAttribute("y", builder.posY());
1164 elem.addText(builder.text());
1165 }
1166}
1167
1169 // todo
1170}
1171
1173 // todo
1174}
SkPath fPath
static const int strokeWidth
Definition BlurTest.cpp:60
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
int count
SkColor4f color
#define SkAssertResult(cond)
Definition SkAssert.h:123
#define SkASSERT(cond)
Definition SkAssert.h:116
SkBlendMode
Definition SkBlendMode.h:38
@ kSrcIn
r = s * da
#define SkColorGetR(color)
Definition SkColor.h:65
#define SkColorGetG(color)
Definition SkColor.h:69
uint32_t SkColor
Definition SkColor.h:37
constexpr SkAlpha SK_AlphaOPAQUE
Definition SkColor.h:94
#define SkColorGetA(color)
Definition SkColor.h:61
#define SkColorGetB(color)
Definition SkColor.h:73
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static SkImage_Base * as_IB(SkImage *image)
static void encode(uint8_t output[16], const uint32_t input[4])
Definition SkMD5.cpp:240
static bool is_png(const void *bytes, size_t length)
sk_sp< SkData > AsDataUri(SkImage *image)
static sk_sp< SkData > encode(const SkBitmap &src)
static bool is_jpeg(const void *bytes, size_t length)
static bool SkScalarNearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance=SK_ScalarNearlyZero)
Definition SkScalar.h:107
#define SkIntToScalar(x)
Definition SkScalar.h:57
SkShaderBase * as_SB(SkShader *shader)
SK_API SkString static SkString SkStringPrintf()
Definition SkString.h:287
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
Definition SkTPin.h:19
SkTileMode
Definition SkTileMode.h:13
constexpr int SkToInt(S x)
Definition SkTo.h:29
int32_t SkUnichar
Definition SkTypes.h:175
SVGTextBuilder(SkPoint origin, const sktext::GlyphRun &glyphRun)
const SkString & text() const
const SkString & posX() const
const SkString & posY() const
static const char * Link_Named_Dest_Key()
static const char * URL_Key()
int width() const
Definition SkBitmap.h:149
SkIRect bounds() const
Definition SkBitmap.h:382
int height() const
Definition SkBitmap.h:158
SrcRectConstraint
Definition SkCanvas.h:1541
@ kLines_PointMode
draw each pair of points as a line segment
Definition SkCanvas.h:1242
@ kPolygon_PointMode
draw the array of points as a open polygon
Definition SkCanvas.h:1243
@ kPoints_PointMode
draw each point separately
Definition SkCanvas.h:1241
SkClipStack & cs()
@ kPath
This element does not have geometry, but applies a shader to the clip.
@ kEmpty
This element makes the clip empty (regardless of previous elements).
@ kRect
This element combines a device space round-rect with the current clip.
@ kRRect
This element combines a device space path with the current clip.
uint32_t getGenID() const
void clipRect(const SkRect &, const SkMatrix &matrix, SkClipOp, bool doAA)
SkRect bounds(const SkIRect &deviceBounds) const
bool asAColorMode(SkColor *color, SkBlendMode *mode) const
static sk_sp< SkData > MakeUninitialized(size_t length)
Definition SkData.cpp:116
int height() const
Definition SkDevice.h:120
SkISize size() const
Definition SkDevice.h:126
const SkMatrix & localToDevice() const
Definition SkDevice.h:179
int width() const
Definition SkDevice.h:119
SkIRect getGlobalBounds() const
Definition SkDevice.h:230
sk_sp< SkData > detachAsData()
Definition SkStream.cpp:707
static void GlyphsToUnichars(const SkFont &, const uint16_t glyphs[], int count, SkUnichar[])
Definition SkFont.cpp:396
Slant slant() const
Definition SkFontStyle.h:64
int width() const
Definition SkFontStyle.h:63
int weight() const
Definition SkFontStyle.h:62
void getPaths(const SkGlyphID glyphIDs[], int count, void(*glyphPathProc)(const SkPath *pathOrNull, const SkMatrix &mx, void *ctx), void *ctx) const
Definition SkFont.cpp:285
int width() const
Definition SkImage.h:285
virtual bool isTextureBacked() const =0
int height() const
Definition SkImage.h:291
SkIRect bounds() const
Definition SkImage.h:303
sk_sp< SkData > refEncodedData() const
Definition SkImage.cpp:214
static SkMatrix RectToRect(const SkRect &src, const SkRect &dst, ScaleToFit mode=kFill_ScaleToFit)
Definition SkMatrix.h:157
SkScalar getSkewY() const
Definition SkMatrix.h:430
SkScalar getTranslateY() const
Definition SkMatrix.h:452
SkScalar getSkewX() const
Definition SkMatrix.h:438
SkScalar getScaleX() const
Definition SkMatrix.h:415
SkScalar getScaleY() const
Definition SkMatrix.h:422
SkScalar getTranslateX() const
Definition SkMatrix.h:445
@ kPerspective_Mask
perspective SkMatrix
Definition SkMatrix.h:196
@ kTranslate_Mask
translation SkMatrix
Definition SkMatrix.h:193
@ kScale_Mask
scale SkMatrix
Definition SkMatrix.h:194
TypeMask getType() const
Definition SkMatrix.h:207
bool isIdentity() const
Definition SkMatrix.h:223
SkScalar getStrokeMiter() const
Definition SkPaint.h:318
static constexpr int kCapCount
Definition SkPaint.h:343
@ kStroke_Style
set to stroke geometry
Definition SkPaint.h:194
@ kFill_Style
set to fill geometry
Definition SkPaint.h:193
@ kStrokeAndFill_Style
sets to stroke and fill geometry
Definition SkPaint.h:195
static constexpr int kJoinCount
Definition SkPaint.h:368
bool isAntiAlias() const
Definition SkPaint.h:162
@ kMiter_Join
extends to miter limit
Definition SkPaint.h:359
static SkString ToSVGString(const SkPath &, PathEncoding=PathEncoding::Absolute)
static SkPath RRect(const SkRRect &, SkPathDirection dir=SkPathDirection::kCW)
Definition SkPath.cpp:3534
static SkPath Rect(const SkRect &, SkPathDirection=SkPathDirection::kCW, unsigned startIndex=0)
Definition SkPath.cpp:3518
SkPathFillType getFillType() const
Definition SkPath.h:230
static SkPath Oval(const SkRect &, SkPathDirection=SkPathDirection::kCW)
Definition SkPath.cpp:3522
@ kRelativePathEncoding_Flag
Definition SkSVGCanvas.h:25
@ kConvertTextToPaths_Flag
Definition SkSVGCanvas.h:23
void addTextAttributes(const SkFont &)
void addAttribute(const char name[], int32_t val)
AutoElement(const char name[], SkSVGDevice *svgdev, ResourceBucket *bucket, const MxCp &mc, const SkPaint &paint)
void addAttribute(const char name[], const SkString &val)
void addAttribute(const char name[], const char val[])
void addText(const SkString &text)
AutoElement(const char name[], SkXMLWriter *writer)
void addPathAttributes(const SkPath &, SkParsePath::PathEncoding)
void addRectAttributes(const SkRect &)
void addAttribute(const char name[], SkScalar val)
AutoElement(const char name[], const std::unique_ptr< SkXMLWriter > &writer)
void drawOval(const SkRect &oval, const SkPaint &paint) override
void drawPath(const SkPath &path, const SkPaint &paint, bool pathIsMutable=false) override
void drawPoints(SkCanvas::PointMode mode, size_t count, const SkPoint[], const SkPaint &paint) override
void drawPaint(const SkPaint &paint) override
void onDrawGlyphRunList(SkCanvas *, const sktext::GlyphRunList &, const SkPaint &paint) override
static sk_sp< SkDevice > Make(const SkISize &size, std::unique_ptr< SkXMLWriter >, uint32_t flags)
void drawVertices(const SkVertices *, sk_sp< SkBlender >, const SkPaint &, bool) override
void drawAnnotation(const SkRect &rect, const char key[], SkData *value) override
~SkSVGDevice() override
void drawRRect(const SkRRect &rr, const SkPaint &paint) override
void drawImageRect(const SkImage *image, const SkRect *src, const SkRect &dst, const SkSamplingOptions &, const SkPaint &paint, SkCanvas::SrcRectConstraint constraint) override
void drawMesh(const SkMesh &, sk_sp< SkBlender >, const SkPaint &) override
void drawRect(const SkRect &r, const SkPaint &paint) override
virtual GradientType asGradient(GradientInfo *info=nullptr, SkMatrix *localMatrix=nullptr) const
virtual ShaderType type() const =0
SkImage * isAImage(SkMatrix *localMatrix, SkTileMode xy[2]) const
Definition SkShader.cpp:22
void appendUnichar(SkUnichar uni)
Definition SkString.h:207
void printf(const char format[],...) SK_PRINTF_LIKE(2
Definition SkString.cpp:534
bool isEmpty() const
Definition SkString.h:130
void append(const char text[])
Definition SkString.h:203
void appendScalar(SkScalar value)
Definition SkString.h:213
const char * c_str() const
Definition SkString.h:133
void void void appendf(const char format[],...) SK_PRINTF_LIKE(2
Definition SkString.cpp:550
void addText(const char text[], size_t length)
void addScalarAttribute(const char name[], SkScalar value)
void addS32Attribute(const char name[], int32_t value)
void addAttribute(const char name[], const char value[])
void endElement()
Definition SkXMLWriter.h:29
void startElement(const char elem[])
T * get() const
Definition SkRefCnt.h:303
bool empty() const
Definition SkTArray.h:194
int size() const
Definition SkTArray.h:416
void add(T item)
Definition SkTHash.h:573
bool contains(const T &item) const
Definition SkTHash.h:576
bool hasRSXForm() const
Definition GlyphRun.h:105
SkPoint origin() const
Definition GlyphRun.h:114
const SkFont & font() const
Definition GlyphRun.h:51
size_t runSize() const
Definition GlyphRun.h:47
SkSpan< const SkGlyphID > glyphsIDs() const
Definition GlyphRun.h:49
SkSpan< const SkPoint > positions() const
Definition GlyphRun.h:48
const Paint & paint
VkDevice device
Definition main.cc:53
sk_sp< SkImage > image
Definition examples.cpp:29
float SkScalar
Definition extension.cpp:12
static bool b
struct MyStruct a[10]
FlutterSemanticsFlag flags
uint8_t value
const char * name
Definition fuchsia.cc:50
size_t length
std::u16string text
SK_API bool Encode(SkWStream *dst, const SkPixmap &src, const Options &options)
SkRRect rrect
Definition SkRecords.h:232
sk_sp< SkBlender > blender SkRect rect
Definition SkRecords.h:350
const intptr_t cid
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switches.h:57
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
Definition switches.h:228
SK_API bool FillPathWithPaint(const SkPath &src, const SkPaint &paint, SkPath *dst, const SkRect *cullRect, SkScalar resScale=1)
SINT Vec< 2 *N, T > join(const Vec< N, T > &lo, const Vec< N, T > &hi)
Definition SkVx.h:242
Definition ref_ptr.h:256
Point offset
static size_t EncodedSize(size_t srcDataLength)
Definition SkBase64.h:40
static size_t Encode(const void *src, size_t length, void *dst, const char *encode=nullptr)
Definition SkBase64.cpp:113
constexpr int32_t height() const
Definition SkRect.h:165
constexpr int32_t width() const
Definition SkRect.h:158
constexpr int32_t width() const
Definition SkSize.h:36
constexpr int32_t height() const
Definition SkSize.h:37
float fX
x-axis value
float fY
y-axis value
static SkRect Make(const SkISize &size)
Definition SkRect.h:669
constexpr float centerX() const
Definition SkRect.h:776
constexpr float height() const
Definition SkRect.h:769
constexpr float centerY() const
Definition SkRect.h:785
constexpr float width() const
Definition SkRect.h:762
bool isEmpty() const
Definition SkRect.h:693
static constexpr SkRect MakeWH(float w, float h)
Definition SkRect.h:609
const SkMatrix * fMatrix
const SkClipStack * fClipStack
MxCp(SkSVGDevice *device)
MxCp(const SkMatrix *mx, const SkClipStack *cs)
SkColor * fColors
The colors in the gradient.
int fColorCount
In-out parameter, specifies passed size.
SkScalar * fColorOffsets
The unit offset for color transitions.
const char * svg
const uintptr_t id