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";
102 if (
const auto* nc = named_color(
color)) {
113 uint8_t rl = r & 0xf;
115 uint8_t
gl = g & 0xf;
117 uint8_t bl =
b & 0xf;
118 if ((rh == rl) && (gh == gl) && (bh == bl)) {
130static const char* cap_map[] = {
138 SkASSERT(
static_cast<size_t>(cap) < std::size(cap_map));
143static const char* join_map[] = {
151 SkASSERT(join < std::size(join_map));
152 return join_map[
join];
174 tstr.
printf(
"matrix(%g %g %g %g %g %g)",
186 : fPaintServer(svg_color(
paint.getColor())) {}
206 for (
int i = 0; i < 2; i++) {
222 Rec* rec = reinterpret_cast<Rec*>(ctx);
225 total.postTranslate(rec->fPos->fX + rec->fOffset.fX,
226 rec->fPos->fY + rec->fOffset.fY);
227 rec->fPath->addPath(*path, total);
246 , fColorFilterCount(0) {}
267 uint32_t fGradientCount;
269 uint32_t fImageCount;
270 uint32_t fPatternCount;
271 uint32_t fColorFilterCount;
286 , fResourceBucket(nullptr) {
295 : fWriter(svgdev->fWriter.get())
296 , fResourceBucket(bucket) {
299 Resources res = this->addResources(mc,
paint);
303 this->addPaint(
paint, res);
340 void addShaderResources(
const SkPaint&
paint, Resources* resources);
342 Resources* resources);
343 void addColorFilterResources(
const SkColorFilter& cf, Resources* resources);
345 Resources* resources);
347 void addPatternDef(
const SkBitmap& bm);
349 void addPaint(
const SkPaint&
paint,
const Resources& resources);
359void SkSVGDevice::AutoElement::addPaint(
const SkPaint&
paint,
const Resources& resources) {
364 if (
paint.getPathEffect() !=
nullptr) {
365 SkDebugf(
"Unsupported path effect in addPaint.");
369 static constexpr char kDefaultFill[] =
"black";
370 if (!resources.fPaintServer.equals(kDefaultFill)) {
381 if (!resources.fColorFilter.isEmpty()) {
382 this->
addAttribute(
"filter", resources.fColorFilter.c_str());
392 this->
addAttribute(
"vector-effect",
"non-scaling-stroke");
396 if (
const char* cap = svg_cap(
paint.getStrokeCap())) {
400 if (
const char* join = svg_join(
paint.getStrokeJoin())) {
417Resources SkSVGDevice::AutoElement::addResources(
const MxCp& mc,
const SkPaint&
paint) {
418 Resources resources(
paint);
420 if (
paint.getShader()) {
421 AutoElement
defs(
"defs", fWriter);
423 this->addShaderResources(
paint, &resources);
430 this->addColorFilterResources(*cf, &resources);
437void SkSVGDevice::AutoElement::addGradientShaderResources(
const SkShader* shader,
439 Resources* resources) {
441 if (
as_SB(shader)->
type() == SkShaderBase::ShaderType::kColor) {
442 auto colorShader =
static_cast<const SkColorShader*
>(shader);
443 resources->fPaintServer = svg_color(colorShader->color());
450 if (gradient_type != SkShaderBase::GradientType::kLinear) {
457 grInfo.
fColors = grColors.get();
467 resources->fPaintServer =
468 SkStringPrintf(
"url(#%s)", addLinearGradientDef(grInfo, shader, localMatrix).c_str());
471void SkSVGDevice::AutoElement::addColorFilterResources(
const SkColorFilter& cf,
472 Resources* resources) {
473 SkString colorfilterID = fResourceBucket->addColorFilter();
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%");
484 bool asAColorMode = cf.
asAColorMode(&filterColor, &mode);
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");
498 AutoElement compositeElement(
"feComposite", fWriter);
499 compositeElement.addAttribute(
"in",
"flood");
500 compositeElement.addAttribute(
"operator",
"in");
503 resources->fColorFilter.printf(
"url(#%s)", colorfilterID.
c_str());
507 static constexpr uint8_t pngSig[] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
508 return length >=
sizeof(pngSig) && !memcmp(bytes, pngSig,
sizeof(pngSig));
512 static constexpr uint8_t jpegSig[] = {0xFF, 0xD8, 0xFF};
513 return length >=
sizeof(jpegSig) && !memcmp(bytes, jpegSig,
sizeof(jpegSig));
520 static constexpr char jpgDataPrefix[] =
"data:image/jpeg;base64,";
521 static constexpr char pngDataPrefix[] =
"data:image/png;base64,";
525 const char* selectedPrefix = pngDataPrefix;
526 size_t selectedPrefixLength =
sizeof(pngDataPrefix);
530 if (
is_jpeg(imageData->data(), imageData->size())) {
531 selectedPrefix = jpgDataPrefix;
532 selectedPrefixLength =
sizeof(jpgDataPrefix);
533 }
else if (!
is_png(imageData->data(), imageData->size())) {
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;
560void SkSVGDevice::AutoElement::addImageShaderResources(
const SkShader* shader,
const SkPaint&
paint,
561 Resources* resources) {
575 for (
int i = 0; i < 2; i++) {
576 int imageDimension = i == 0 ? imageSize.
width() : imageSize.
height();
583 patternDims[i] =
"100%";
587 SkString patternID = fResourceBucket->addPattern();
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);
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());
606 imageTag.addAttribute(
"xlink:href",
static_cast<const char*
>(dataUri->data()));
609 resources->fPaintServer.printf(
"url(#%s)", patternID.
c_str());
612void SkSVGDevice::AutoElement::addShaderResources(
const SkPaint&
paint, Resources* resources) {
617 if (shaderType == SkShaderBase::ShaderType::kColor ||
618 shaderType == SkShaderBase::ShaderType::kGradientBase) {
619 this->addGradientShaderResources(shader,
paint, resources);
621 this->addImageShaderResources(shader,
paint, resources);
630 SkString id = fResourceBucket->addLinearGradient();
633 AutoElement gradient(
"linearGradient", fWriter);
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());
643 this->addAttribute(
"gradientTransform", svg_transform(localMatrix));
647 for (
int i = 0; i <
info.fColorCount; ++i) {
652 AutoElement stop(
"stop", fWriter);
653 stop.addAttribute(
"offset",
info.fColorOffsets[i]);
654 stop.addAttribute(
"stop-color", colorStr.c_str());
657 stop.addAttribute(
"stop-opacity", svg_opacity(
color));
669 this->addAttribute(
"x", rect.x());
672 this->addAttribute(
"y", rect.y());
675 this->addAttribute(
"width", rect.width());
676 this->addAttribute(
"height", rect.height());
685 this->addAttribute(
"font-size", font.getSize());
694 this->addAttribute(
"font-style",
"italic");
696 this->addAttribute(
"font-style",
"oblique");
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"
703 this->addAttribute(
"font-weight", weights[weightIndex]);
705 int stretchIndex = style.
width() - 1;
706 if (stretchIndex != 4) {
707 static constexpr const char* stretches[] = {
708 "ultra-condensed",
"extra-condensed",
"condensed",
"semi-condensed",
710 "semi-expanded",
"expanded",
"extra-expanded",
"ultra-expanded"
712 this->addAttribute(
"font-stretch", stretches[stretchIndex]);
717 if (familyNameIter) {
718 while (familyNameIter->next(&familyString)) {
727 this->addAttribute(
"font-family", familyName);
732 std::unique_ptr<SkXMLWriter> writer,
738SkSVGDevice::SkSVGDevice(
const SkISize&
size, std::unique_ptr<SkXMLWriter> writer, uint32_t
flags)
742 , fWriter(
std::move(writer))
743 , fResourceBucket(new ResourceBucket)
748 fWriter->writeHeader();
751 fRootElement = std::make_unique<AutoElement>(
"svg", fWriter);
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());
761 while (!fClipStack.
empty()) {
772void SkSVGDevice::syncClipStack(
const SkClipStack& cs) {
779 while ((elem = iter.next()) && (rec_idx < fClipStack.
size())) {
787 while (fClipStack.
size() > rec_idx) {
794 AutoElement clip_path(
"clipPath", fWriter);
795 clip_path.addAttribute(
"id", cid);
799 switch (
e->getDeviceSpaceType()) {
802 AutoElement
rect(
"rect", fWriter);
805 AutoElement
rect(
"rect", fWriter);
806 rect.addRectAttributes(
e->getDeviceSpaceRect());
810 const auto& rr =
e->getDeviceSpaceRRect();
811 const auto radii = rr.getSimpleRadii();
813 AutoElement
rrect(
"rect", fWriter);
814 rrect.addRectAttributes(rr.rect());
815 rrect.addAttribute(
"rx", radii.x());
816 rrect.addAttribute(
"ry", radii.y());
819 const auto&
p =
e->getDeviceSpacePath();
820 AutoElement
path(
"path", fWriter);
821 path.addPathAttributes(p, this->pathEncoding());
823 path.addAttribute(
"clip-rule",
"evenodd");
836 const auto cid = define_clip(elem);
838 auto clip_grp = std::make_unique<AutoElement>(
"g", fWriter);
864 if (transformedRect.
isEmpty()) {
870 a.addAttribute(
"xlink:href", url.
c_str());
890 for (
size_t i = 0; i <
count; i += 2) {
892 path.lineTo(pts[i+1]);
906 if (
paint.getPathEffect()) {
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);
920 rect.addAttribute(
"x", 0);
921 rect.addAttribute(
"y", 0);
922 rect.addAttribute(
"width",
"100%");
923 rect.addAttribute(
"height",
"100%");
925 rect.addRectAttributes(r);
930 if (
paint.getPathEffect()) {
943 if (
paint.getPathEffect()) {
953 if (path.isInverseFillType()) {
954 SkDebugf(
"Inverse path fill type not yet implemented.");
963 if (path_paint->getPathEffect()) {
964 if (!pathIsMutable) {
965 pathPtr = &pathStorage;
974 path_paint.
writable()->setStrokeWidth(0);
977 path_paint.
writable()->setPathEffect(
nullptr);
981 AutoElement elem(
"path",
this, fResourceBucket.get(),
MxCp(
this), *path_paint);
1005 SkString svgImageData(
"data:image/png;base64,");
1006 svgImageData.append(b64Data.get(), b64Size);
1008 SkString imageID = fResourceBucket->addImage();
1010 AutoElement
defs(
"defs", fWriter);
1012 AutoElement
image(
"image", fWriter);
1013 image.addAttribute(
"id", imageID);
1016 image.addAttribute(
"xlink:href", svgImageData);
1021 AutoElement imageUse(
"use",
this, fResourceBucket.get(), mc,
paint);
1031 if (!
as_IB(
image)->getROPixels(
nullptr, &bm)) {
1045 drawBitmapCommon(
MxCp(&adjustedMatrix,
cs), bm,
paint);
1052 auto runSize = glyphRun.
runSize();
1055 runSize, unichars.
get());
1057 for (
size_t i = 0; i < runSize; ++i) {
1058 this->appendUnichar(unichars[i], positions[i]);
1064 const SkString&
posY()
const {
return fHasConstY ? fConstYStr : fPosYStr; }
1068 bool discardPos =
false;
1069 bool isWhitespace =
false;
1076 if (fLastCharWasWhitespace) {
1081 isWhitespace =
true;
1087 isWhitespace = fLastCharWasWhitespace;
1109 fLastCharWasWhitespace = isWhitespace;
1115 position += fOrigin;
1116 fPosXStr.
appendf(
"%.8g, ", position.
fX);
1117 fPosYStr.
appendf(
"%.8g, ", position.
fY);
1120 fConstYStr = fPosYStr;
1121 fConstY = position.
fY;
1133 bool fLastCharWasWhitespace =
true,
1141 const auto draw_as_path =
1147 for (
auto& glyphRun : glyphRunList) {
1148 AddPath(glyphRun, glyphRunList.
origin(), &path);
1157 for (
auto& glyphRun : glyphRunList) {
static const int strokeWidth
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
#define SkAssertResult(cond)
#define SkColorGetR(color)
#define SkColorGetG(color)
constexpr SkAlpha SK_AlphaOPAQUE
#define SkColorGetA(color)
#define SkColorGetB(color)
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])
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)
SkShaderBase * as_SB(SkShader *shader)
SK_API SkString static SkString SkStringPrintf()
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
constexpr int SkToInt(S x)
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()
@ kLines_PointMode
draw each pair of points as a line segment
@ kPolygon_PointMode
draw the array of points as a open polygon
@ kPoints_PointMode
draw each point separately
@ 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)
const SkMatrix & localToDevice() const
SkIRect getGlobalBounds() const
sk_sp< SkData > detachAsData()
static void GlyphsToUnichars(const SkFont &, const uint16_t glyphs[], int count, SkUnichar[])
void getPaths(const SkGlyphID glyphIDs[], int count, void(*glyphPathProc)(const SkPath *pathOrNull, const SkMatrix &mx, void *ctx), void *ctx) const
virtual bool isTextureBacked() const =0
sk_sp< SkData > refEncodedData() const
static SkMatrix RectToRect(const SkRect &src, const SkRect &dst, ScaleToFit mode=kFill_ScaleToFit)
SkScalar getSkewY() const
SkScalar getTranslateY() const
SkScalar getSkewX() const
SkScalar getScaleX() const
SkScalar getScaleY() const
SkScalar getTranslateX() const
@ kPerspective_Mask
perspective SkMatrix
@ kTranslate_Mask
translation SkMatrix
@ kScale_Mask
scale SkMatrix
SkScalar getStrokeMiter() const
static constexpr int kCapCount
@ kStroke_Style
set to stroke geometry
@ kFill_Style
set to fill geometry
@ kStrokeAndFill_Style
sets to stroke and fill geometry
static constexpr int kJoinCount
@ kMiter_Join
extends to miter limit
static SkString ToSVGString(const SkPath &, PathEncoding=PathEncoding::Absolute)
static SkPath RRect(const SkRRect &, SkPathDirection dir=SkPathDirection::kCW)
static SkPath Rect(const SkRect &, SkPathDirection=SkPathDirection::kCW, unsigned startIndex=0)
SkPathFillType getFillType() const
static SkPath Oval(const SkRect &, SkPathDirection=SkPathDirection::kCW)
@ kRelativePathEncoding_Flag
@ kConvertTextToPaths_Flag
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)
SkString addColorFilter()
SkString addLinearGradient()
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
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
void appendUnichar(SkUnichar uni)
void printf(const char format[],...) SK_PRINTF_LIKE(2
void append(const char text[])
void appendScalar(SkScalar value)
const char * c_str() const
void void void appendf(const char format[],...) SK_PRINTF_LIKE(2
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 startElement(const char elem[])
bool contains(const T &item) const
const SkFont & font() const
SkSpan< const SkGlyphID > glyphsIDs() const
SkSpan< const SkPoint > positions() const
FlutterSemanticsFlag flags
SK_API bool Encode(SkWStream *dst, const SkPixmap &src, const Options &options)
sk_sp< SkBlender > blender SkRect rect
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
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
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)
static size_t EncodedSize(size_t srcDataLength)
static size_t Encode(const void *src, size_t length, void *dst, const char *encode=nullptr)
constexpr int32_t height() const
constexpr int32_t width() const
constexpr int32_t width() const
constexpr int32_t height() const
static SkRect Make(const SkISize &size)
constexpr float centerX() const
constexpr float height() const
constexpr float centerY() const
constexpr float width() const
static constexpr SkRect MakeWH(float w, float h)
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.