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