From af6427b6c4501ab8637c60762c8c636addf18d89 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Thu, 14 Nov 2024 20:25:41 +0800 Subject: [PATCH 01/31] svg to dom,and svg render --- include/tgfx/core/Color.h | 8 + include/tgfx/svg/SVGAttribute.h | 120 ++ include/tgfx/svg/SVGAttributeParser.h | 184 +++ include/tgfx/svg/SVGDOM.h | 116 ++ include/tgfx/svg/SVGFontManager.h | 119 ++ include/tgfx/svg/SVGIDMapper.h | 28 + include/tgfx/svg/SVGParse.h | 56 + include/tgfx/svg/SVGRenderContext.h | 354 +++++ include/tgfx/svg/SVGTypes.h | 893 ++++++++++++ include/tgfx/svg/SVGValue.h | 99 ++ include/tgfx/svg/node/SVGCircle.h | 63 + include/tgfx/svg/node/SVGClipPath.h | 53 + include/tgfx/svg/node/SVGContainer.h | 63 + include/tgfx/svg/node/SVGDefs.h | 39 + include/tgfx/svg/node/SVGEllipse.h | 63 + include/tgfx/svg/node/SVGFe.h | 107 ++ include/tgfx/svg/node/SVGFeBlend.h | 67 + include/tgfx/svg/node/SVGFeColorMatrix.h | 70 + .../tgfx/svg/node/SVGFeComponentTransfer.h | 96 ++ include/tgfx/svg/node/SVGFeComposite.h | 65 + include/tgfx/svg/node/SVGFeDisplacementMap.h | 68 + include/tgfx/svg/node/SVGFeFlood.h | 62 + include/tgfx/svg/node/SVGFeGaussianBlur.h | 64 + include/tgfx/svg/node/SVGFeImage.h | 60 + include/tgfx/svg/node/SVGFeLightSource.h | 105 ++ include/tgfx/svg/node/SVGFeLighting.h | 141 ++ include/tgfx/svg/node/SVGFeMerge.h | 76 ++ include/tgfx/svg/node/SVGFeMorphology.h | 70 + include/tgfx/svg/node/SVGFeOffset.h | 60 + include/tgfx/svg/node/SVGFeTurbulence.h | 60 + include/tgfx/svg/node/SVGFilter.h | 57 + include/tgfx/svg/node/SVGFilterContext.h | 87 ++ include/tgfx/svg/node/SVGG.h | 37 + include/tgfx/svg/node/SVGGradient.h | 59 + include/tgfx/svg/node/SVGHiddenContainer.h | 39 + include/tgfx/svg/node/SVGImage.h | 84 ++ include/tgfx/svg/node/SVGLine.h | 63 + include/tgfx/svg/node/SVGLinearGradient.h | 54 + include/tgfx/svg/node/SVGMask.h | 61 + include/tgfx/svg/node/SVGNode.h | 264 ++++ include/tgfx/svg/node/SVGPath.h | 54 + include/tgfx/svg/node/SVGPattern.h | 68 + include/tgfx/svg/node/SVGPoly.h | 62 + include/tgfx/svg/node/SVGRadialGradient.h | 56 + include/tgfx/svg/node/SVGRect.h | 68 + include/tgfx/svg/node/SVGSVG.h | 71 + include/tgfx/svg/node/SVGShape.h | 50 + include/tgfx/svg/node/SVGStop.h | 47 + include/tgfx/svg/node/SVGText.h | 152 +++ include/tgfx/svg/node/SVGTransformableNode.h | 58 + include/tgfx/svg/node/SVGUse.h | 63 + qt/src/TGFXView.cpp | 6 +- qt/src/main.cpp | 4 +- src/gpu/RenderContext.cpp | 2 +- src/svg/SVGAttribute.cpp | 63 + src/svg/SVGAttributeParser.cpp | 1200 +++++++++++++++++ src/svg/SVGDOM.cpp | 536 ++++++++ src/svg/SVGFontManager.cpp | 89 ++ src/svg/SVGParse.cpp | 472 +++++++ src/svg/SVGParseColor.cpp | 334 +++++ src/svg/SVGRenderContext.cpp | 539 ++++++++ src/svg/node/SVGCircle.cpp | 70 + src/svg/node/SVGClipPath.cpp | 47 + src/svg/node/SVGContainer.cpp | 76 ++ src/svg/node/SVGEllipse.cpp | 68 + src/svg/node/SVGFe.cpp | 147 ++ src/svg/node/SVGFeBlend.cpp | 76 ++ src/svg/node/SVGFeColorMatrix.cpp | 158 +++ src/svg/node/SVGFeComponentTransfer.cpp | 178 +++ src/svg/node/SVGFeComposite.cpp | 90 ++ src/svg/node/SVGFeDisplacementMap.cpp | 85 ++ src/svg/node/SVGFeFlood.cpp | 42 + src/svg/node/SVGFeGaussianBlur.cpp | 59 + src/svg/node/SVGFeImage.cpp | 57 + src/svg/node/SVGFeLightSource.cpp | 63 + src/svg/node/SVGFeLighting.cpp | 180 +++ src/svg/node/SVGFeMerge.cpp | 59 + src/svg/node/SVGFeMorphology.cpp | 73 + src/svg/node/SVGFeOffset.cpp | 45 + src/svg/node/SVGFeTurbulence.cpp | 81 ++ src/svg/node/SVGFilter.cpp | 91 ++ src/svg/node/SVGFilterContext.cpp | 174 +++ src/svg/node/SVGGradient.cpp | 130 ++ src/svg/node/SVGImage.cpp | 184 +++ src/svg/node/SVGLine.cpp | 64 + src/svg/node/SVGLinearGradient.cpp | 57 + src/svg/node/SVGMask.cpp | 82 ++ src/svg/node/SVGNode.cpp | 189 +++ src/svg/node/SVGPath.cpp | 69 + src/svg/node/SVGPattern.cpp | 200 +++ src/svg/node/SVGPoly.cpp | 66 + src/svg/node/SVGRadialGradient.cpp | 68 + src/svg/node/SVGRect.cpp | 138 ++ src/svg/node/SVGRectPriv.h | 30 + src/svg/node/SVGSVG.cpp | 135 ++ src/svg/node/SVGShape.cpp | 59 + src/svg/node/SVGStop.cpp | 31 + src/svg/node/SVGText.cpp | 262 ++++ src/svg/node/SVGTransformableNode.cpp | 73 + src/svg/node/SVGUse.cpp | 97 ++ src/svg/xml/XMLWriter.cpp | 6 +- 101 files changed, 12080 insertions(+), 7 deletions(-) create mode 100644 include/tgfx/svg/SVGAttribute.h create mode 100644 include/tgfx/svg/SVGAttributeParser.h create mode 100644 include/tgfx/svg/SVGDOM.h create mode 100644 include/tgfx/svg/SVGFontManager.h create mode 100644 include/tgfx/svg/SVGIDMapper.h create mode 100644 include/tgfx/svg/SVGParse.h create mode 100644 include/tgfx/svg/SVGRenderContext.h create mode 100644 include/tgfx/svg/SVGTypes.h create mode 100644 include/tgfx/svg/SVGValue.h create mode 100644 include/tgfx/svg/node/SVGCircle.h create mode 100644 include/tgfx/svg/node/SVGClipPath.h create mode 100644 include/tgfx/svg/node/SVGContainer.h create mode 100644 include/tgfx/svg/node/SVGDefs.h create mode 100644 include/tgfx/svg/node/SVGEllipse.h create mode 100644 include/tgfx/svg/node/SVGFe.h create mode 100644 include/tgfx/svg/node/SVGFeBlend.h create mode 100644 include/tgfx/svg/node/SVGFeColorMatrix.h create mode 100644 include/tgfx/svg/node/SVGFeComponentTransfer.h create mode 100644 include/tgfx/svg/node/SVGFeComposite.h create mode 100644 include/tgfx/svg/node/SVGFeDisplacementMap.h create mode 100644 include/tgfx/svg/node/SVGFeFlood.h create mode 100644 include/tgfx/svg/node/SVGFeGaussianBlur.h create mode 100644 include/tgfx/svg/node/SVGFeImage.h create mode 100644 include/tgfx/svg/node/SVGFeLightSource.h create mode 100644 include/tgfx/svg/node/SVGFeLighting.h create mode 100644 include/tgfx/svg/node/SVGFeMerge.h create mode 100644 include/tgfx/svg/node/SVGFeMorphology.h create mode 100644 include/tgfx/svg/node/SVGFeOffset.h create mode 100644 include/tgfx/svg/node/SVGFeTurbulence.h create mode 100644 include/tgfx/svg/node/SVGFilter.h create mode 100644 include/tgfx/svg/node/SVGFilterContext.h create mode 100644 include/tgfx/svg/node/SVGG.h create mode 100644 include/tgfx/svg/node/SVGGradient.h create mode 100644 include/tgfx/svg/node/SVGHiddenContainer.h create mode 100644 include/tgfx/svg/node/SVGImage.h create mode 100644 include/tgfx/svg/node/SVGLine.h create mode 100644 include/tgfx/svg/node/SVGLinearGradient.h create mode 100644 include/tgfx/svg/node/SVGMask.h create mode 100644 include/tgfx/svg/node/SVGNode.h create mode 100644 include/tgfx/svg/node/SVGPath.h create mode 100644 include/tgfx/svg/node/SVGPattern.h create mode 100644 include/tgfx/svg/node/SVGPoly.h create mode 100644 include/tgfx/svg/node/SVGRadialGradient.h create mode 100644 include/tgfx/svg/node/SVGRect.h create mode 100644 include/tgfx/svg/node/SVGSVG.h create mode 100644 include/tgfx/svg/node/SVGShape.h create mode 100644 include/tgfx/svg/node/SVGStop.h create mode 100644 include/tgfx/svg/node/SVGText.h create mode 100644 include/tgfx/svg/node/SVGTransformableNode.h create mode 100644 include/tgfx/svg/node/SVGUse.h create mode 100644 src/svg/SVGAttribute.cpp create mode 100644 src/svg/SVGAttributeParser.cpp create mode 100644 src/svg/SVGDOM.cpp create mode 100644 src/svg/SVGFontManager.cpp create mode 100644 src/svg/SVGParse.cpp create mode 100644 src/svg/SVGParseColor.cpp create mode 100644 src/svg/SVGRenderContext.cpp create mode 100644 src/svg/node/SVGCircle.cpp create mode 100644 src/svg/node/SVGClipPath.cpp create mode 100644 src/svg/node/SVGContainer.cpp create mode 100644 src/svg/node/SVGEllipse.cpp create mode 100644 src/svg/node/SVGFe.cpp create mode 100644 src/svg/node/SVGFeBlend.cpp create mode 100644 src/svg/node/SVGFeColorMatrix.cpp create mode 100644 src/svg/node/SVGFeComponentTransfer.cpp create mode 100644 src/svg/node/SVGFeComposite.cpp create mode 100644 src/svg/node/SVGFeDisplacementMap.cpp create mode 100644 src/svg/node/SVGFeFlood.cpp create mode 100644 src/svg/node/SVGFeGaussianBlur.cpp create mode 100644 src/svg/node/SVGFeImage.cpp create mode 100644 src/svg/node/SVGFeLightSource.cpp create mode 100644 src/svg/node/SVGFeLighting.cpp create mode 100644 src/svg/node/SVGFeMerge.cpp create mode 100644 src/svg/node/SVGFeMorphology.cpp create mode 100644 src/svg/node/SVGFeOffset.cpp create mode 100644 src/svg/node/SVGFeTurbulence.cpp create mode 100644 src/svg/node/SVGFilter.cpp create mode 100644 src/svg/node/SVGFilterContext.cpp create mode 100644 src/svg/node/SVGGradient.cpp create mode 100644 src/svg/node/SVGImage.cpp create mode 100644 src/svg/node/SVGLine.cpp create mode 100644 src/svg/node/SVGLinearGradient.cpp create mode 100644 src/svg/node/SVGMask.cpp create mode 100644 src/svg/node/SVGNode.cpp create mode 100644 src/svg/node/SVGPath.cpp create mode 100644 src/svg/node/SVGPattern.cpp create mode 100644 src/svg/node/SVGPoly.cpp create mode 100644 src/svg/node/SVGRadialGradient.cpp create mode 100644 src/svg/node/SVGRect.cpp create mode 100644 src/svg/node/SVGRectPriv.h create mode 100644 src/svg/node/SVGSVG.cpp create mode 100644 src/svg/node/SVGShape.cpp create mode 100644 src/svg/node/SVGStop.cpp create mode 100644 src/svg/node/SVGText.cpp create mode 100644 src/svg/node/SVGTransformableNode.cpp create mode 100644 src/svg/node/SVGUse.cpp diff --git a/include/tgfx/core/Color.h b/include/tgfx/core/Color.h index d66777eb..e60c1c18 100644 --- a/include/tgfx/core/Color.h +++ b/include/tgfx/core/Color.h @@ -24,6 +24,14 @@ namespace tgfx { +enum class ColorChannel { + R, // the red channel + G, // the green channel + B, // the blue channel + A, // the alpha channel + LastEnum = A, +}; + /** * RGBA color value, holding four floating point components. Color components are always in a known * order. diff --git a/include/tgfx/svg/SVGAttribute.h b/include/tgfx/svg/SVGAttribute.h new file mode 100644 index 00000000..84266bdc --- /dev/null +++ b/include/tgfx/svg/SVGAttribute.h @@ -0,0 +1,120 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "tgfx/svg/SVGTypes.h" + +namespace tgfx { +enum class SVGAttribute { + kClipRule, + kColor, + kColorInterpolation, + kColorInterpolationFilters, + kCx, // , , : center x position + kCy, // , , : center y position + kFill, + kFillOpacity, + kFillRule, + kFilter, + kFilterUnits, + kFontFamily, + kFontSize, + kFontStyle, + kFontWeight, + kFx, // : focal point x position + kFy, // : focal point y position + kGradientUnits, + kGradientTransform, + kHeight, + kHref, + kOpacity, + kPoints, + kPreserveAspectRatio, + kR, // , : radius + kRx, // ,: horizontal (corner) radius + kRy, // ,: vertical (corner) radius + kSpreadMethod, + kStroke, + kStrokeDashArray, + kStrokeDashOffset, + kStrokeOpacity, + kStrokeLineCap, + kStrokeLineJoin, + kStrokeMiterLimit, + kStrokeWidth, + kTransform, + kText, + kTextAnchor, + kViewBox, + kVisibility, + kWidth, + kX, + kX1, // : first endpoint x + kX2, // : second endpoint x + kY, + kY1, // : first endpoint y + kY2, // : second endpoint y + + kUnknown, +}; + +struct SkSVGPresentationAttributes { + static SkSVGPresentationAttributes MakeInitial(); + + // TODO: SVGProperty adds an extra ptr per attribute; refactor to reduce overhead. + + SVGProperty fFill; + SVGProperty fFillOpacity; + SVGProperty fFillRule; + SVGProperty fClipRule; + + SVGProperty fStroke; + SVGProperty fStrokeDashArray; + SVGProperty fStrokeDashOffset; + SVGProperty fStrokeLineCap; + SVGProperty fStrokeLineJoin; + SVGProperty fStrokeMiterLimit; + SVGProperty fStrokeOpacity; + SVGProperty fStrokeWidth; + + SVGProperty fVisibility; + + SVGProperty fColor; + SVGProperty fColorInterpolation; + SVGProperty fColorInterpolationFilters; + + SVGProperty fFontFamily; + SVGProperty fFontStyle; + SVGProperty fFontSize; + SVGProperty fFontWeight; + SVGProperty fTextAnchor; + + // uninherited + SVGProperty fOpacity; + SVGProperty fClipPath; + SVGProperty fDisplay; + SVGProperty fMask; + SVGProperty fFilter; + SVGProperty fStopColor; + SVGProperty fStopOpacity; + SVGProperty fFloodColor; + SVGProperty fFloodOpacity; + SVGProperty fLightingColor; +}; +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGAttributeParser.h b/include/tgfx/svg/SVGAttributeParser.h new file mode 100644 index 00000000..0817d0ea --- /dev/null +++ b/include/tgfx/svg/SVGAttributeParser.h @@ -0,0 +1,184 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include +#include +#include "tgfx/core/Color.h" +#include "tgfx/core/Matrix.h" +#include "tgfx/core/Typeface.h" +#include "tgfx/svg/SVGTypes.h" + +namespace tgfx { + +class SVGAttributeParser { + public: + explicit SVGAttributeParser(const char[]); + + bool parseInteger(SVGIntegerType*); + bool parseViewBox(SVGViewBoxType*); + bool parsePreserveAspectRatio(SVGPreserveAspectRatio*); + + // TODO: Migrate all parse*() functions to this style (and delete the old version) + // so they can be used by parse(): + bool parse(SVGIntegerType* v) { + return parseInteger(v); + } + + template + using ParseResult = std::optional; + + template + static ParseResult parse(const char* value) { + ParseResult result; + T parsedValue; + if (SVGAttributeParser(value).parse(&parsedValue)) { + result = parsedValue; + } + return result; + } + + template + static ParseResult parse(const char* expectedName, const char* name, const char* value) { + if (!strcmp(name, expectedName)) { + return parse(value); + } + + return ParseResult(); + } + + template + static ParseResult parseProperty(const char* expectedName, const char* name, + const char* value) { + if (strcmp(name, expectedName) != 0) { + return ParseResult(); + } + + if (!strcmp(value, "inherit")) { + PropertyT result(SVGPropertyState::kInherit); + return ParseResult(result); + } + + auto pr = parse(value); + if (pr.has_value()) { + PropertyT result(*pr); + return ParseResult(result); + } + + return ParseResult(); + } + + // Stack-only + void* operator new(size_t) = delete; + void* operator new(size_t, void*) = delete; + + private: + class RestoreCurPos { + public: + explicit RestoreCurPos(SVGAttributeParser* self) : fSelf(self), fCurPos(self->fCurPos) { + } + + ~RestoreCurPos() { + if (fSelf) { + fSelf->fCurPos = this->fCurPos; + } + } + + void clear() { + fSelf = nullptr; + } + + private: + SVGAttributeParser* fSelf; + const char* fCurPos; + + public: + RestoreCurPos(const RestoreCurPos&) = delete; + RestoreCurPos& operator=(const RestoreCurPos&) = delete; + }; + + template + bool parse(T*); + + template + bool advanceWhile(F func); + + bool matchStringToken(const char* token, const char** newPos = nullptr) const; + bool matchHexToken(const char** newPos) const; + + bool parseWSToken(); + bool parseEOSToken(); + bool parseSepToken(); + bool parseCommaWspToken(); + bool parseExpectedStringToken(const char*); + bool parseScalarToken(float*); + bool parseInt32Token(int32_t*); + bool parseEscape(Unichar*); + bool parseIdentToken(std::string*); + bool parseLengthUnitToken(SVGLength::Unit&); + bool parseNamedColorToken(Color*); + bool parseHexColorToken(Color*); + bool parseColorComponentScalarToken(int32_t*); + bool parseColorComponentIntegralToken(int32_t*); + bool parseColorComponentFractionalToken(int32_t*); + bool parseColorComponentToken(int32_t*); + bool parseColorToken(Color*); + bool parseRGBColorToken(Color*); + bool parseRGBAColorToken(Color*); + bool parseSVGColor(SVGColor*, SVGColor::Vars&&); + bool parseSVGColorType(SVGColorType*); + bool parseFuncIRI(SVGFuncIRI*); + + // Transform helpers + bool parseMatrixToken(Matrix*); + bool parseTranslateToken(Matrix*); + bool parseScaleToken(Matrix*); + bool parseRotateToken(Matrix*); + bool parseSkewXToken(Matrix*); + bool parseSkewYToken(Matrix*); + + // Parses a sequence of 'WS* WS* ()', where the nested sequence + // is handled by the passed functor. + template + bool parseParenthesized(const char* prefix, Func, T* result); + + template + bool parseList(std::vector*); + + template + bool parseEnumMap(const TArray& arr, T* result) { + for (size_t i = 0; i < std::size(arr); ++i) { + if (this->parseExpectedStringToken(std::get<0>(arr[i]))) { + *result = std::get<1>(arr[i]); + return true; + } + } + return false; + } + + // The current position in the input string. + const char* fCurPos; + const char* fEndPos; + + // using INHERITED = SkNoncopyable; +}; +} // namespace tgfx diff --git a/include/tgfx/svg/SVGDOM.h b/include/tgfx/svg/SVGDOM.h new file mode 100644 index 00000000..cc8f2cb0 --- /dev/null +++ b/include/tgfx/svg/SVGDOM.h @@ -0,0 +1,116 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/core/Canvas.h" +#include "tgfx/core/Data.h" +#include "tgfx/core/Size.h" +#include "tgfx/svg/SVGFontManager.h" +#include "tgfx/svg/SVGIDMapper.h" +#include "tgfx/svg/node/SVGSVG.h" + +namespace tgfx { + +class SVGDOM { + public: + class Builder final { + public: + // /** + // * Specify a font manager for loading fonts (e.g. from the system) to render + // * SVG nodes. + // * If this is not set, but a font is required as part of rendering, the text will + // * not be displayed. + // */ + // Builder& setFontManager(sk_sp); + + // /** + // * Specify a resource provider for loading images etc. + // */ + // Builder& setResourceProvider(sk_sp); + + // /** + // * Specify the callbacks for dealing with shaping text. See also + // * modules/skshaper/utils/FactoryHelpers.h + // */ + // Builder& setTextShapingFactory(sk_sp); + + std::shared_ptr make(Data&, std::shared_ptr) const; + + private: + // sk_sp fFontMgr; + // sk_sp fResourceProvider; + // sk_sp fTextShapingFactory; + }; + + // static std::shared_ptr MakeFromData(Data& data) { + // return Builder().make(data); + // } + + /** + * Returns the root (outermost) SVG element. + */ + const std::shared_ptr& getRoot() const { + return fRoot; + } + + /** + * Specify a "container size" for the SVG dom. + * + * This is used to resolve the initial viewport when the root SVG width/height are specified + * in relative units. + * + * If the root dimensions are in absolute units, then the container size has no effect since + * the initial viewport is fixed. + */ + void setContainerSize(const Size&); + + /** + * DEPRECATED: use getRoot()->intrinsicSize() to query the root element intrinsic size. + * + * Returns the SVG dom container size. + * + * If the client specified a container size via setContainerSize(), then the same size is + * returned. + * + * When unspecified by clients, this returns the intrinsic size of the root element, as defined + * by its width/height attributes. If either width or height is specified in relative units + * (e.g. "100%"), then the corresponding intrinsic size dimension is zero. + */ + const Size& containerSize() const; + + // Returns the node with the given id, or nullptr if not found. + std::shared_ptr findNodeById(const char* id); + + void render(Canvas*) const; + + /** Render the node with the given id as if it were the only child of the root. */ + void renderNode(Canvas*, SkSVGPresentationContext&, const char* id) const; + + private: + SVGDOM(std::shared_ptr, SVGIDMapper&&, std::shared_ptr fontManager); + + const std::shared_ptr fRoot; + const std::shared_ptr fFontMgr; + // const sk_sp fTextShapingFactory; + const std::shared_ptr fResourceProvider; + const SVGIDMapper fIDMapper; + Size fContainerSize; +}; +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGFontManager.h b/include/tgfx/svg/SVGFontManager.h new file mode 100644 index 00000000..be10646f --- /dev/null +++ b/include/tgfx/svg/SVGFontManager.h @@ -0,0 +1,119 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include +#include "tgfx/core/Typeface.h" +namespace tgfx { + +class FontStyle { + public: + struct Hash { + std::size_t operator()(const FontStyle& style) const { + return std::hash()(style.weight()) ^ std::hash()(style.width()) ^ + std::hash()(style.slant()); + } + }; + + enum Weight { + kInvisible_Weight = 0, + kThin_Weight = 100, + kExtraLight_Weight = 200, + kLight_Weight = 300, + kNormal_Weight = 400, + kMedium_Weight = 500, + kSemiBold_Weight = 600, + kBold_Weight = 700, + kExtraBold_Weight = 800, + kBlack_Weight = 900, + kExtraBlack_Weight = 1000, + }; + + enum Width { + kUltraCondensed_Width = 1, + kExtraCondensed_Width = 2, + kCondensed_Width = 3, + kSemiCondensed_Width = 4, + kNormal_Width = 5, + kSemiExpanded_Width = 6, + kExpanded_Width = 7, + kExtraExpanded_Width = 8, + kUltraExpanded_Width = 9, + }; + + enum Slant { + kUpright_Slant, + kItalic_Slant, + kOblique_Slant, + }; + + constexpr FontStyle(Weight weight, Width width, Slant slant) + : _value(weight + (width << 16) + (slant << 24)) { + } + + constexpr FontStyle() : FontStyle{kNormal_Weight, kNormal_Width, kUpright_Slant} { + } + + bool operator==(const FontStyle& rhs) const { + return _value == rhs._value; + } + + int weight() const { + return _value & 0xFFFF; + } + int width() const { + return (_value >> 16) & 0xFF; + } + Slant slant() const { + return static_cast((_value >> 24) & 0xFF); + } + + private: + int _value; +}; + +class SVGFontManager { + public: + SVGFontManager() = default; + ~SVGFontManager() = default; + + void setDefaultTypeface(const std::shared_ptr& typeface); + + void addFontStyle(const std::string& fontFamily, FontStyle style); + void setTypeface(const std::string& fontFamily, FontStyle style, + const std::shared_ptr& typeface); + std::vector getFontFamilies() const; + std::vector getFontStyles(const std::string& fontFamily) const; + + std::shared_ptr getTypefaceForConfig(const std::string& fontFamily, + FontStyle style) const; + std::shared_ptr getTypefaceForRender(const std::string& fontFamily, + FontStyle style) const; + + private: + std::unordered_map, FontStyle::Hash>> + _typefaces; + std::shared_ptr _defaultTypeface; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGIDMapper.h b/include/tgfx/svg/SVGIDMapper.h new file mode 100644 index 00000000..85a96b22 --- /dev/null +++ b/include/tgfx/svg/SVGIDMapper.h @@ -0,0 +1,28 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include + +namespace tgfx { +class SVGNode; +using SVGIDMapper = std::unordered_map>; +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGParse.h b/include/tgfx/svg/SVGParse.h new file mode 100644 index 00000000..f04f2468 --- /dev/null +++ b/include/tgfx/svg/SVGParse.h @@ -0,0 +1,56 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include +#include "tgfx/core/Color.h" +#include "tgfx/core/Path.h" + +namespace tgfx { + +class SVGParse { + public: + static const char* FindHex(const char str[], uint32_t* value); + static const char* FindNamedColor(const char str[], Color* color); + static const char* FindS32(const char str[], int32_t* value); + static const char* FindScalar(const char str[], float* value); + static const char* FindScalars(const char str[], float value[], int count); + static bool FindBool(const char str[], bool* value); + // return the index of str in list[], or -1 if not found + static int FindList(const char target[], const char list[]); + + static inline Color Uint32ToColor(uint32_t value) { + return Color::FromRGBA((value >> 16) & 0xff, (value >> 8) & 0xff, (value >> 0) & 0xff, + (value >> 24) & 0xff); + } +}; + +class PathParse { + public: + enum class PathEncoding { Absolute, Relative }; + + static std::tuple> FromSVGString(const char data[]); + + static std::string ToSVGString(const std::shared_ptr& path, + PathEncoding = PathEncoding::Absolute); +}; +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGRenderContext.h b/include/tgfx/svg/SVGRenderContext.h new file mode 100644 index 00000000..f3e28d07 --- /dev/null +++ b/include/tgfx/svg/SVGRenderContext.h @@ -0,0 +1,354 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include +#include +#include "tgfx/core/Canvas.h" +#include "tgfx/core/Paint.h" +#include "tgfx/core/Path.h" +#include "tgfx/core/Point.h" +#include "tgfx/core/Rect.h" +#include "tgfx/core/Size.h" +#include "tgfx/svg/SVGAttribute.h" +#include "tgfx/svg/SVGFontManager.h" +#include "tgfx/svg/SVGIDMapper.h" +#include "tgfx/svg/SVGTypes.h" + +namespace tgfx { + +// class SkCanvas; +// class SkPaint; +// class SkString; +// namespace skresources { +// class ResourceProvider; +// } + +class SVGNode; + +template +class CopyOnWrite { + public: + explicit CopyOnWrite(const T& initial) : fObj(&initial) { + } + + explicit CopyOnWrite(const T* initial) : fObj(initial) { + } + + // Constructor for delayed initialization. + CopyOnWrite() : fObj(nullptr) { + } + + CopyOnWrite(const CopyOnWrite& that) { + *this = that; + } + + CopyOnWrite(CopyOnWrite&& that) noexcept { + *this = std::move(that); + } + + CopyOnWrite& operator=(const CopyOnWrite& that) { + fLazy = that.fLazy; + fObj = fLazy.has_value() ? &fLazy.value() : that.fObj; + return *this; + } + + CopyOnWrite& operator=(CopyOnWrite&& that) { + fLazy = std::move(that.fLazy); + fObj = fLazy.has_value() ? &fLazy.value() : that.fObj; + return *this; + } + + // Should only be called once, and only if the default constructor was used. + void init(const T& initial) { + SkASSERT(!fObj); + SkASSERT(!fLazy.has_value()); + fObj = &initial; + } + + // If not already initialized, in-place instantiates the writable object + template + void initIfNeeded(Args&&... args) { + if (!fObj) { + SkASSERT(!fLazy.has_value()); + fObj = &fLazy.emplace(std::forward(args)...); + } + } + + /** + * Returns a writable T*. The first time this is called the initial object is cloned. + */ + T* writable() { + if (!fLazy.has_value()) { + fLazy = *fObj; + fObj = &fLazy.value(); + } + return &fLazy.value(); + } + + const T* get() const { + return fObj; + } + + /** + * Operators for treating this as though it were a const pointer. + */ + + const T* operator->() const { + return fObj; + } + + operator const T*() const { + return fObj; + } + + const T& operator*() const { + return *fObj; + } + + private: + const T* fObj; + std::optional fLazy; +}; + +class SVGLengthContext { + public: + explicit SVGLengthContext(const Size& viewport, float dpi = 90) : fViewport(viewport), fDPI(dpi) { + } + + enum class LengthType { + kHorizontal, + kVertical, + kOther, + }; + + const Size& viewPort() const { + return fViewport; + } + void setViewPort(const Size& viewport) { + fViewport = viewport; + } + + float resolve(const SVGLength&, LengthType) const; + Rect resolveRect(const SVGLength& x, const SVGLength& y, const SVGLength& w, + const SVGLength& h) const; + + void setPatternUnits(SVGPatternUnits unit) { + fPatternUnit = unit; + } + + void clearPatternUnits() { + fPatternUnit.reset(); + } + + std::optional getPatternUnits() const { + return fPatternUnit; + } + + private: + Size fViewport; + float fDPI; + std::optional fPatternUnit; +}; + +struct SkSVGPresentationContext { + SkSVGPresentationContext(); + SkSVGPresentationContext(const SkSVGPresentationContext&) = default; + SkSVGPresentationContext& operator=(const SkSVGPresentationContext&) = default; + + const std::unordered_map* fNamedColors = nullptr; + + // Inherited presentation attributes, computed for the current node. + SkSVGPresentationAttributes fInherited; +}; + +class SVGRenderContext { + public: + // Captures data required for object bounding box resolution. + struct OBBScope { + const SVGNode* fNode; + const SVGRenderContext* fCtx; + }; + + SVGRenderContext(Canvas*, const std::shared_ptr&, + const std::shared_ptr&, const SVGIDMapper&, + const SVGLengthContext&, const SkSVGPresentationContext&, const OBBScope&); + SVGRenderContext(const SVGRenderContext&); + SVGRenderContext(const SVGRenderContext&, Canvas*); + SVGRenderContext(const SVGRenderContext&, const SVGLengthContext&); + SVGRenderContext(const SVGRenderContext&, Canvas*, const SVGLengthContext&); + // Establish a new OBB scope. Normally used when entering a node's render scope. + SVGRenderContext(const SVGRenderContext&, const SVGNode*); + ~SVGRenderContext(); + + const SVGLengthContext& lengthContext() const { + return *fLengthContext; + } + SVGLengthContext* writableLengthContext() { + return fLengthContext.writable(); + } + + const SkSVGPresentationContext& presentationContext() const { + return *fPresentationContext; + } + + Canvas* canvas() const { + return fCanvas; + } + void saveOnce(); + + enum ApplyFlags { + kLeaf = 1 << 0, // the target node doesn't have descendants + }; + void applyPresentationAttributes(const SkSVGPresentationAttributes&, uint32_t flags); + + // Scoped wrapper that temporarily clears the original node reference. + // class BorrowedNode { + // public: + // explicit BorrowedNode(std::shared_ptr* node) : fOwner(node) { + // if (fOwner) { + // fBorrowed = std::move(*fOwner); + // *fOwner = nullptr; + // } + // } + + // ~BorrowedNode() { + // if (fOwner) { + // *fOwner = std::move(fBorrowed); + // } + // } + + // const SVGNode* get() const { + // return fBorrowed.get(); + // } + // const SVGNode* operator->() const { + // return fBorrowed.get(); + // } + // const SVGNode& operator*() const { + // return *fBorrowed; + // } + + // explicit operator bool() const { + // return !!fBorrowed; + // } + + // // noncopyable + // BorrowedNode(const BorrowedNode&) = delete; + // BorrowedNode& operator=(BorrowedNode&) = delete; + + // private: + // std::shared_ptr* fOwner; + // std::shared_ptr fBorrowed; + // }; + + // Note: the id->node association is cleared for the lifetime of the returned value + // (effectively breaks reference cycles, assuming appropriate return value scoping). + std::shared_ptr findNodeById(const SVGIRI&) const; + + std::optional fillPaint() const; + std::optional strokePaint() const; + + SVGColorType resolveSvgColor(const SVGColor&) const; + + // The local computed clip path (not inherited). + Path clipPath() const { + return fClipPath.value_or(Path()); + } + + const std::shared_ptr& resourceProvider() const { + return fResourceProvider; + } + + const std::shared_ptr& fontMgr() const { + return fFontMgr; + } + + std::shared_ptr& fontMgr() { + // It is probably an oversight to try to render without having set the SkFontMgr. + // We will assert this in debug mode, but fallback to an empty fontmgr in release builds. + return fFontMgr; + } + + // Returns the translate/scale transformation required to map into the current OBB scope, + // with the specified units. + struct OBBTransform { + Point offset, scale; + }; + + OBBTransform transformForCurrentOBB(SVGObjectBoundingBoxUnits) const; + + Rect resolveOBBRect(const SVGLength& x, const SVGLength& y, const SVGLength& w, + const SVGLength& h, SVGObjectBoundingBoxUnits) const; + + // std::unique_ptr makeShaper() const { + // SkASSERT(fTextShapingFactory); + // return fTextShapingFactory->makeShaper(this->fontMgr()); + // } + + // std::unique_ptr makeBidiRunIterator(const char* utf8, size_t utf8Bytes, + // uint8_t bidiLevel) const { + // SkASSERT(fTextShapingFactory); + // return fTextShapingFactory->makeBidiRunIterator(utf8, utf8Bytes, bidiLevel); + // } + + // std::unique_ptr makeScriptRunIterator(const char* utf8, + // size_t utf8Bytes) const { + // SkASSERT(fTextShapingFactory); + // constexpr SkFourByteTag unknownScript = SkSetFourByteTag('Z', 'z', 'z', 'z'); + // return fTextShapingFactory->makeScriptRunIterator(utf8, utf8Bytes, unknownScript); + // } + + private: + // Stack-only + void* operator new(size_t) = delete; + void* operator new(size_t, void*) = delete; + SVGRenderContext& operator=(const SVGRenderContext&) = delete; + + void applyOpacity(float opacity, uint32_t flags, bool hasFilter); + void applyFilter(const SVGFuncIRI&); + void applyClip(const SVGFuncIRI&); + void applyMask(const SVGFuncIRI&); + + std::optional commonPaint(const SVGPaint&, float opacity) const; + + std::shared_ptr fFontMgr; + // const sk_sp& fTextShapingFactory; + const std::shared_ptr& fResourceProvider; + + const SVGIDMapper& fIDMapper; + CopyOnWrite fLengthContext; + CopyOnWrite fPresentationContext; + Canvas* fCanvas; + // The save count on 'fCanvas' at construction time. + // A restoreToCount() will be issued on destruction. + size_t fCanvasSaveCount; + + // clipPath, if present for the current context (not inherited). + std::optional fClipPath; + + // Deferred opacity optimization for leaf nodes. + float fDeferredPaintOpacity = 1; + + // Current object bounding box scope. + const OBBScope fOBBScope; +}; +} // namespace tgfx diff --git a/include/tgfx/svg/SVGTypes.h b/include/tgfx/svg/SVGTypes.h new file mode 100644 index 00000000..8309feec --- /dev/null +++ b/include/tgfx/svg/SVGTypes.h @@ -0,0 +1,893 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include +#include +#include "tgfx/core/Color.h" +#include "tgfx/core/Matrix.h" +#include "tgfx/core/PathTypes.h" +#include "tgfx/core/Point.h" +#include "tgfx/core/Rect.h" + +// #define RENDER_SVG + +namespace tgfx { + +using SVGColorType = Color; +using SVGIntegerType = int; +using SVGNumberType = float; +using SVGStringType = std::string; +using SVGViewBoxType = Rect; +using SVGTransformType = Matrix; +using SVGPointsType = std::vector; + +enum class SVGPropertyState { + kUnspecified, + kInherit, + kValue, +}; + +// https://www.w3.org/TR/SVG11/intro.html#TermProperty +template +class SVGProperty { + public: + using ValueT = T; + + SVGProperty() : fState(SVGPropertyState::kUnspecified) { + } + + explicit SVGProperty(SVGPropertyState state) : fState(state) { + } + + explicit SVGProperty(const T& value) : fState(SVGPropertyState::kValue) { + fValue = value; + } + + explicit SVGProperty(T&& value) : fState(SVGPropertyState::kValue) { + fValue = std::move(value); + } + + template + void init(Args&&... args) { + fState = SVGPropertyState::kValue; + fValue.emplace(std::forward(args)...); + } + + constexpr bool isInheritable() const { + return kInheritable; + } + + bool isValue() const { + return fState == SVGPropertyState::kValue; + } + + T* getMaybeNull() const { + return fValue.has_value() ? &fValue.value() : nullptr; + } + + void set(SVGPropertyState state) { + fState = state; + if (fState != SVGPropertyState::kValue) { + fValue.reset(); + } + } + + void set(const T& value) { + fState = SVGPropertyState::kValue; + fValue = value; + } + + void set(T&& value) { + fState = SVGPropertyState::kValue; + fValue = std::move(value); + } + + T* operator->() { + // ASSERT(fState == SVGPropertyState::kValue); + // ASSERT(fValue.has_value()); + return &fValue.value(); + } + + const T* operator->() const { + // ASSERT(fState == SVGPropertyState::kValue); + // ASSERT(fValue.has_value()); + return &fValue.value(); + } + + T& operator*() { + // ASSERT(fState == SVGPropertyState::kValue); + // ASSERT(fValue.has_value()); + return *fValue; + } + + const T& operator*() const { + // ASSERT(fState == SVGPropertyState::kValue); + // ASSERT(fValue.has_value()); + return *fValue; + } + + private: + SVGPropertyState fState; + std::optional fValue; +}; + +class SVGLength { + public: + enum class Unit { + kUnknown, + kNumber, + kPercentage, + kEMS, + kEXS, + kPX, + kCM, + kMM, + kIN, + kPT, + kPC, + }; + + constexpr SVGLength() : fValue(0), fUnit(Unit::kUnknown) { + } + explicit constexpr SVGLength(float v, Unit u = Unit::kNumber) : fValue(v), fUnit(u) { + } + SVGLength(const SVGLength&) = default; + SVGLength& operator=(const SVGLength&) = default; + + bool operator==(const SVGLength& other) const { + return fUnit == other.fUnit && fValue == other.fValue; + } + bool operator!=(const SVGLength& other) const { + return !(*this == other); + } + + const float& value() const { + return fValue; + } + const Unit& unit() const { + return fUnit; + } + + private: + float fValue; + Unit fUnit; +}; + +// https://www.w3.org/TR/SVG11/linking.html#IRIReference +class SVGIRI { + public: + enum class Type { + kLocal, + kNonlocal, + kDataURI, + }; + + SVGIRI() : fType(Type::kLocal) { + } + SVGIRI(Type t, SVGStringType iri) : fType(t), fIRI(std::move(iri)) { + } + + Type type() const { + return fType; + } + const SVGStringType& iri() const { + return fIRI; + } + + bool operator==(const SVGIRI& other) const { + return fType == other.fType && fIRI == other.fIRI; + } + bool operator!=(const SVGIRI& other) const { + return !(*this == other); + } + + private: + Type fType; + SVGStringType fIRI; +}; + +// https://www.w3.org/TR/SVG11/types.html#InterfaceSVGColor +class SVGColor { + public: + enum class Type { + kCurrentColor, + kColor, + kICCColor, + }; + using Vars = std::vector; + + SVGColor() : SVGColor(Color::Black()) { + } + explicit SVGColor(const SVGColorType& c) : fType(Type::kColor), fColor(c), fVars(nullptr) { + } + explicit SVGColor(Type t, Vars&& vars) + : fType(t), fColor(Color::Black()), + fVars(vars.empty() ? nullptr : std::make_shared(std::move(vars))) { + } + explicit SVGColor(const SVGColorType& c, Vars&& vars) + : fType(Type::kColor), fColor(c), + fVars(vars.empty() ? nullptr : std::make_shared(std::move(vars))) { + } + + SVGColor(const SVGColor&) = default; + SVGColor& operator=(const SVGColor&) = default; + SVGColor(SVGColor&&) = default; + SVGColor& operator=(SVGColor&&) = default; + + bool operator==(const SVGColor& other) const { + return fType == other.fType && fColor == other.fColor && fVars == other.fVars; + } + bool operator!=(const SVGColor& other) const { + return !(*this == other); + } + + Type type() const { + return fType; + } + const SVGColorType& color() const { + return fColor; + } + const std::shared_ptr vars() const { + return fVars ? fVars : nullptr; + } + + std::shared_ptr vars() { + return fVars ? fVars : nullptr; + } + + private: + Type fType; + SVGColorType fColor; + std::shared_ptr fVars; +}; + +class SVGPaint { + public: + enum class Type { + kNone, + kColor, + kIRI, + }; + + SVGPaint() : fType(Type::kNone), fColor(Color::Black()) { + } + explicit SVGPaint(Type t) : fType(t), fColor(Color::Black()) { + } + explicit SVGPaint(SVGColor c) : fType(Type::kColor), fColor(std::move(c)) { + } + SVGPaint(SVGIRI iri, SVGColor fallback_color) + : fType(Type::kIRI), fColor(std::move(fallback_color)), fIRI(std::move(iri)) { + } + + SVGPaint(const SVGPaint&) = default; + SVGPaint& operator=(const SVGPaint&) = default; + SVGPaint(SVGPaint&&) = default; + SVGPaint& operator=(SVGPaint&&) = default; + + bool operator==(const SVGPaint& other) const { + return fType == other.fType && fColor == other.fColor && fIRI == other.fIRI; + } + bool operator!=(const SVGPaint& other) const { + return !(*this == other); + } + + Type type() const { + return fType; + } + const SVGColor& color() const { + return fColor; + } + const SVGIRI& iri() const { + return fIRI; + } + + private: + Type fType; + + // Logical union. + SVGColor fColor; + SVGIRI fIRI; +}; + +// | none (used for clip/mask/filter properties) +class SVGFuncIRI { + public: + enum class Type { + kNone, + kIRI, + }; + + SVGFuncIRI() : fType(Type::kNone) { + } + explicit SVGFuncIRI(Type t) : fType(t) { + } + explicit SVGFuncIRI(SVGIRI&& iri) : fType(Type::kIRI), fIRI(std::move(iri)) { + } + + bool operator==(const SVGFuncIRI& other) const { + return fType == other.fType && fIRI == other.fIRI; + } + bool operator!=(const SVGFuncIRI& other) const { + return !(*this == other); + } + + Type type() const { + return fType; + } + const SVGIRI& iri() const { + return fIRI; + } + + private: + Type fType; + SVGIRI fIRI; +}; + +enum class SVGLineCap { + kButt, + kRound, + kSquare, +}; + +class SVGLineJoin { + public: + enum class Type { + kMiter, + kRound, + kBevel, + kInherit, + }; + + constexpr SVGLineJoin() : fType(Type::kInherit) { + } + constexpr explicit SVGLineJoin(Type t) : fType(t) { + } + + SVGLineJoin(const SVGLineJoin&) = default; + SVGLineJoin& operator=(const SVGLineJoin&) = default; + + bool operator==(const SVGLineJoin& other) const { + return fType == other.fType; + } + bool operator!=(const SVGLineJoin& other) const { + return !(*this == other); + } + + Type type() const { + return fType; + } + + private: + Type fType; +}; + +class SVGSpreadMethod { + public: + enum class Type { + kPad, + kRepeat, + kReflect, + }; + + constexpr SVGSpreadMethod() : fType(Type::kPad) { + } + constexpr explicit SVGSpreadMethod(Type t) : fType(t) { + } + + SVGSpreadMethod(const SVGSpreadMethod&) = default; + SVGSpreadMethod& operator=(const SVGSpreadMethod&) = default; + + bool operator==(const SVGSpreadMethod& other) const { + return fType == other.fType; + } + bool operator!=(const SVGSpreadMethod& other) const { + return !(*this == other); + } + + Type type() const { + return fType; + } + + private: + Type fType; +}; + +class SVGFillRule { + public: + enum class Type { + kNonZero, + kEvenOdd, + kInherit, + }; + + constexpr SVGFillRule() : fType(Type::kInherit) { + } + constexpr explicit SVGFillRule(Type t) : fType(t) { + } + + SVGFillRule(const SVGFillRule&) = default; + SVGFillRule& operator=(const SVGFillRule&) = default; + + bool operator==(const SVGFillRule& other) const { + return fType == other.fType; + } + bool operator!=(const SVGFillRule& other) const { + return !(*this == other); + } + + Type type() const { + return fType; + } + + PathFillType asFillType() const { + return fType == Type::kEvenOdd ? PathFillType::EvenOdd : PathFillType::Winding; + } + + private: + Type fType; +}; + +class SVGVisibility { + public: + enum class Type { + kVisible, + kHidden, + kCollapse, + kInherit, + }; + + constexpr SVGVisibility() : fType(Type::kVisible) { + } + constexpr explicit SVGVisibility(Type t) : fType(t) { + } + + SVGVisibility(const SVGVisibility&) = default; + SVGVisibility& operator=(const SVGVisibility&) = default; + + bool operator==(const SVGVisibility& other) const { + return fType == other.fType; + } + bool operator!=(const SVGVisibility& other) const { + return !(*this == other); + } + + Type type() const { + return fType; + } + + private: + Type fType; +}; + +class SVGDashArray { + public: + enum class Type { + kNone, + kDashArray, + kInherit, + }; + + SVGDashArray() : fType(Type::kNone) { + } + explicit SVGDashArray(Type t) : fType(t) { + } + explicit SVGDashArray(std::vector&& dashArray) + : fType(Type::kDashArray), fDashArray(std::move(dashArray)) { + } + + SVGDashArray(const SVGDashArray&) = default; + SVGDashArray& operator=(const SVGDashArray&) = default; + + bool operator==(const SVGDashArray& other) const { + return fType == other.fType && fDashArray == other.fDashArray; + } + bool operator!=(const SVGDashArray& other) const { + return !(*this == other); + } + + Type type() const { + return fType; + } + + const std::vector& dashArray() const { + return fDashArray; + } + + private: + Type fType; + std::vector fDashArray; +}; + +class SVGStopColor { + public: + enum class Type { + kColor, + kCurrentColor, + kICCColor, + kInherit, + }; + + SVGStopColor() : fType(Type::kColor), fColor(Color::Black()) { + } + explicit SVGStopColor(Type t) : fType(t), fColor(Color::Black()) { + } + explicit SVGStopColor(const SVGColorType& c) : fType(Type::kColor), fColor(c) { + } + + SVGStopColor(const SVGStopColor&) = default; + SVGStopColor& operator=(const SVGStopColor&) = default; + + bool operator==(const SVGStopColor& other) const { + return fType == other.fType && fColor == other.fColor; + } + bool operator!=(const SVGStopColor& other) const { + return !(*this == other); + } + + Type type() const { + return fType; + } + const SVGColorType& color() const { + return fColor; + } + + private: + Type fType; + SVGColorType fColor; +}; + +class SVGObjectBoundingBoxUnits { + public: + enum class Type { + kUserSpaceOnUse, + kObjectBoundingBox, + }; + + SVGObjectBoundingBoxUnits() : fType(Type::kUserSpaceOnUse) { + } + explicit SVGObjectBoundingBoxUnits(Type t) : fType(t) { + } + + bool operator==(const SVGObjectBoundingBoxUnits& other) const { + return fType == other.fType; + } + bool operator!=(const SVGObjectBoundingBoxUnits& other) const { + return !(*this == other); + } + + Type type() const { + return fType; + } + + private: + Type fType; +}; + +class SVGFontFamily { + public: + enum class Type { + kFamily, + kInherit, + }; + + SVGFontFamily() : fType(Type::kInherit) { + } + explicit SVGFontFamily(const char family[]) : fType(Type::kFamily), fFamily(family) { + } + + bool operator==(const SVGFontFamily& other) const { + return fType == other.fType && fFamily == other.fFamily; + } + bool operator!=(const SVGFontFamily& other) const { + return !(*this == other); + } + + Type type() const { + return fType; + } + + const std::string& family() const { + return fFamily; + } + + private: + Type fType; + std::string fFamily; +}; + +class SVGFontStyle { + public: + enum class Type { + kNormal, + kItalic, + kOblique, + kInherit, + }; + + SVGFontStyle() : fType(Type::kInherit) { + } + explicit SVGFontStyle(Type t) : fType(t) { + } + + bool operator==(const SVGFontStyle& other) const { + return fType == other.fType; + } + bool operator!=(const SVGFontStyle& other) const { + return !(*this == other); + } + + Type type() const { + return fType; + } + + private: + Type fType; +}; + +class SVGFontSize { + public: + enum class Type { + kLength, + kInherit, + }; + + SVGFontSize() : fType(Type::kInherit), fSize(0) { + } + explicit SVGFontSize(const SVGLength& s) : fType(Type::kLength), fSize(s) { + } + + bool operator==(const SVGFontSize& other) const { + return fType == other.fType && fSize == other.fSize; + } + bool operator!=(const SVGFontSize& other) const { + return !(*this == other); + } + + Type type() const { + return fType; + } + + const SVGLength& size() const { + return fSize; + } + + private: + Type fType; + SVGLength fSize; +}; + +class SVGFontWeight { + public: + enum class Type { + k100, + k200, + k300, + k400, + k500, + k600, + k700, + k800, + k900, + kNormal, + kBold, + kBolder, + kLighter, + kInherit, + }; + + SVGFontWeight() : fType(Type::kInherit) { + } + explicit SVGFontWeight(Type t) : fType(t) { + } + + bool operator==(const SVGFontWeight& other) const { + return fType == other.fType; + } + bool operator!=(const SVGFontWeight& other) const { + return !(*this == other); + } + + Type type() const { + return fType; + } + + private: + Type fType; +}; + +struct SVGPreserveAspectRatio { + enum Align : uint8_t { + // These values are chosen such that bits [0,1] encode X alignment, and + // bits [2,3] encode Y alignment. + kXMinYMin = 0x00, + kXMidYMin = 0x01, + kXMaxYMin = 0x02, + kXMinYMid = 0x04, + kXMidYMid = 0x05, + kXMaxYMid = 0x06, + kXMinYMax = 0x08, + kXMidYMax = 0x09, + kXMaxYMax = 0x0a, + + kNone = 0x10, + }; + + enum Scale { + kMeet, + kSlice, + }; + + Align fAlign = kXMidYMid; + Scale fScale = kMeet; +}; + +class SVGTextAnchor { + public: + enum class Type { + kStart, + kMiddle, + kEnd, + kInherit, + }; + + SVGTextAnchor() : fType(Type::kInherit) { + } + explicit SVGTextAnchor(Type t) : fType(t) { + } + + bool operator==(const SVGTextAnchor& other) const { + return fType == other.fType; + } + bool operator!=(const SVGTextAnchor& other) const { + return !(*this == other); + } + + Type type() const { + return fType; + } + + private: + Type fType; +}; + +// https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveInAttribute +class SVGFeInputType { + public: + enum class Type { + kSourceGraphic, + kSourceAlpha, + kBackgroundImage, + kBackgroundAlpha, + kFillPaint, + kStrokePaint, + kFilterPrimitiveReference, + kUnspecified, + }; + + SVGFeInputType() : fType(Type::kUnspecified) { + } + explicit SVGFeInputType(Type t) : fType(t) { + } + explicit SVGFeInputType(SVGStringType id) + : fType(Type::kFilterPrimitiveReference), fId(std::move(id)) { + } + + bool operator==(const SVGFeInputType& other) const { + return fType == other.fType && fId == other.fId; + } + bool operator!=(const SVGFeInputType& other) const { + return !(*this == other); + } + + const std::string& id() const { + return fId; + } + + Type type() const { + return fType; + } + + private: + Type fType; + std::string fId; +}; + +enum class SVGFeColorMatrixType { + kMatrix, + kSaturate, + kHueRotate, + kLuminanceToAlpha, +}; + +using SVGFeColorMatrixValues = std::vector; + +enum class SVGFeCompositeOperator { + kOver, + kIn, + kOut, + kAtop, + kXor, + kArithmetic, +}; + +class SVGFeTurbulenceBaseFrequency { + public: + SVGFeTurbulenceBaseFrequency() : fFreqX(0), fFreqY(0) { + } + SVGFeTurbulenceBaseFrequency(SVGNumberType freqX, SVGNumberType freqY) + : fFreqX(freqX), fFreqY(freqY) { + } + + SVGNumberType freqX() const { + return fFreqX; + } + SVGNumberType freqY() const { + return fFreqY; + } + + private: + SVGNumberType fFreqX; + SVGNumberType fFreqY; +}; + +struct SVGFeTurbulenceType { + enum Type { + kFractalNoise, + kTurbulence, + }; + + Type fType; + + SVGFeTurbulenceType() : fType(kTurbulence) { + } + explicit SVGFeTurbulenceType(Type type) : fType(type) { + } +}; + +enum class SVGColorspace { + kAuto, + kSRGB, + kLinearRGB, +}; + +// https://www.w3.org/TR/SVG11/painting.html#DisplayProperty +enum class SVGDisplay { + kInline, + kNone, +}; + +// https://www.w3.org/TR/SVG11/filters.html#TransferFunctionElementAttributes +enum class SVGFeFuncType { + kIdentity, + kTable, + kDiscrete, + kLinear, + kGamma, +}; + +//https://www.w3.org/TR/SVG2/pservers.html#PatternElementPatternUnitsAttribute +enum class SVGPatternUnits { + UserSpaceOnUse, + ObjectBoundingBox, +}; +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGValue.h b/include/tgfx/svg/SVGValue.h new file mode 100644 index 00000000..1eab0dfb --- /dev/null +++ b/include/tgfx/svg/SVGValue.h @@ -0,0 +1,99 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "tgfx/svg/SVGTypes.h" + +namespace tgfx { + +class SVGValue { + public: + enum class Type { + kColor, + kFilter, + kLength, + kNumber, + kObjectBoundingBoxUnits, + kPreserveAspectRatio, + kStopColor, + kString, + kTransform, + kViewBox, + }; + + Type type() const { + return fType; + } + + template + const T* as() const { + return fType == T::_type ? static_cast(this) : nullptr; + } + + protected: + explicit SVGValue(Type t) : fType(t) { + } + + private: + Type fType; +}; + +template +class SVGWrapperValue final : public SVGValue { + public: + static constexpr Type _type = ValueType; + + explicit SVGWrapperValue(const T& v) : INHERITED(ValueType), _wrappedValue(v) { + } + + // NOLINTBEGIN + // Allow implicit conversion to the wrapped type operator. + operator const T&() const { + return _wrappedValue; + } + // NOLINTEND + + const T* operator->() const { + return &_wrappedValue; + } + + private: + // Stack-only + void* operator new(size_t) = delete; + void* operator new(size_t, void*) = delete; + + const T& _wrappedValue; + + using INHERITED = SVGValue; +}; + +using SVGColorValue = SVGWrapperValue; +using SVGLengthValue = SVGWrapperValue; +using SVGTransformValue = SVGWrapperValue; +using SVGViewBoxValue = SVGWrapperValue; +using SVGNumberValue = SVGWrapperValue; +using SVGStringValue = SVGWrapperValue; +using SVGStopColorValue = SVGWrapperValue; + +using SVGPreserveAspectRatioValue = + SVGWrapperValue; + +using SVGObjectBoundingBoxUnitsValue = + SVGWrapperValue; +} // namespace tgfx diff --git a/include/tgfx/svg/node/SVGCircle.h b/include/tgfx/svg/node/SVGCircle.h new file mode 100644 index 00000000..86e59729 --- /dev/null +++ b/include/tgfx/svg/node/SVGCircle.h @@ -0,0 +1,63 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/core/Paint.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGNode.h" +#include "tgfx/svg/node/SVGShape.h" + +namespace tgfx { + +class SVGLengthContext; +class SVGRenderContext; + +class SVGCircle final : public SVGShape { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SVGCircle()); + } + + SVG_ATTR(Cx, SVGLength, SVGLength(0)) + SVG_ATTR(Cy, SVGLength, SVGLength(0)) + SVG_ATTR(R, SVGLength, SVGLength(0)) + + protected: + bool parseAndSetAttribute(const char*, const char*) override; + +#ifndef RENDER_SVG + void onDraw(Canvas*, const SVGLengthContext&, const Paint&, PathFillType) const override; + + Path onAsPath(const SVGRenderContext&) const override; + + Rect onObjectBoundingBox(const SVGRenderContext&) const override; + + private: + // resolve and return the center and radius values + std::tuple resolve(const SVGLengthContext&) const; +#endif + + SVGCircle(); + + using INHERITED = SVGShape; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGClipPath.h b/include/tgfx/svg/node/SVGClipPath.h new file mode 100644 index 00000000..b973af6b --- /dev/null +++ b/include/tgfx/svg/node/SVGClipPath.h @@ -0,0 +1,53 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/core/Path.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGHiddenContainer.h" +#include "tgfx/svg/node/SVGNode.h" + +namespace tgfx { + +class SVGRenderContext; + +class SVGClipPath final : public SVGHiddenContainer { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SVGClipPath()); + } + + SVG_ATTR(ClipPathUnits, SVGObjectBoundingBoxUnits, + SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse)) + + private: + friend class SVGRenderContext; + + SVGClipPath(); + + bool parseAndSetAttribute(const char*, const char*) override; + +#ifndef RENDER_SVG + Path resolveClip(const SVGRenderContext&) const; +#endif + + using INHERITED = SVGHiddenContainer; +}; +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGContainer.h b/include/tgfx/svg/node/SVGContainer.h new file mode 100644 index 00000000..5e9571fc --- /dev/null +++ b/include/tgfx/svg/node/SVGContainer.h @@ -0,0 +1,63 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/core/Path.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/node/SVGNode.h" +#include "tgfx/svg/node/SVGTransformableNode.h" + +namespace tgfx { + +class SVGRenderContext; + +class SkSVGContainer : public SkSVGTransformableNode { + public: + void appendChild(std::shared_ptr) override; + const std::vector>& getChildren() const; + bool hasChildren() const final; + + protected: + explicit SkSVGContainer(SVGTag); +#ifndef RENDER_SVG + void onRender(const SVGRenderContext&) const override; + + Path onAsPath(const SVGRenderContext&) const override; + + Rect onObjectBoundingBox(const SVGRenderContext&) const override; +#endif + + template + void forEachChild(Func func) const { + for (const auto& child : fChildren) { + if (child->tag() == NodeType::tag) { + func(static_cast(child.get())); + } + } + } + + // TODO: convert remaining direct users to iterators, and make the container private. + std::vector> fChildren; + + private: + using INHERITED = SkSVGTransformableNode; +}; + +} // namespace tgfx diff --git a/include/tgfx/svg/node/SVGDefs.h b/include/tgfx/svg/node/SVGDefs.h new file mode 100644 index 00000000..ce070fe9 --- /dev/null +++ b/include/tgfx/svg/node/SVGDefs.h @@ -0,0 +1,39 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/svg/node/SVGHiddenContainer.h" + +namespace tgfx { + +class SkSVGDefs : public SVGHiddenContainer { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGDefs()); + } + + private: + SkSVGDefs() : INHERITED(SVGTag::kDefs) { + } + + using INHERITED = SVGHiddenContainer; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGEllipse.h b/include/tgfx/svg/node/SVGEllipse.h new file mode 100644 index 00000000..7b00e134 --- /dev/null +++ b/include/tgfx/svg/node/SVGEllipse.h @@ -0,0 +1,63 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/core/Canvas.h" +#include "tgfx/core/Path.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGNode.h" +#include "tgfx/svg/node/SVGShape.h" + +namespace tgfx { + +class SVGLengthContext; +class SVGRenderContext; + +class SkSVGEllipse final : public SVGShape { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGEllipse()); + } + + SVG_ATTR(Cx, SVGLength, SVGLength(0)) + SVG_ATTR(Cy, SVGLength, SVGLength(0)) + + SVG_OPTIONAL_ATTR(Rx, SVGLength) + SVG_OPTIONAL_ATTR(Ry, SVGLength) + + protected: + bool parseAndSetAttribute(const char*, const char*) override; + +#ifndef RENDER_SVG + void onDraw(Canvas*, const SVGLengthContext&, const Paint&, PathFillType) const override; + + Path onAsPath(const SVGRenderContext&) const override; +#endif + + private: + SkSVGEllipse(); + +#ifndef RENDER_SVG + Rect resolve(const SVGLengthContext&) const; +#endif + + using INHERITED = SVGShape; +}; +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGFe.h b/include/tgfx/svg/node/SVGFe.h new file mode 100644 index 00000000..1b76999d --- /dev/null +++ b/include/tgfx/svg/node/SVGFe.h @@ -0,0 +1,107 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include "tgfx/core/ImageFilter.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGFilterContext.h" +#include "tgfx/svg/node/SVGHiddenContainer.h" +#include "tgfx/svg/node/SVGNode.h" + +class SkSVGFilterContext; +class SVGRenderContext; + +namespace tgfx { + +class SkSVGFe : public SVGHiddenContainer { + public: + static bool IsFilterEffect(const std::shared_ptr& node) { + switch (node->tag()) { + case SVGTag::kFeBlend: + case SVGTag::kFeColorMatrix: + case SVGTag::kFeComponentTransfer: + case SVGTag::kFeComposite: + case SVGTag::kFeDiffuseLighting: + case SVGTag::kFeDisplacementMap: + case SVGTag::kFeFlood: + case SVGTag::kFeGaussianBlur: + case SVGTag::kFeImage: + case SVGTag::kFeMerge: + case SVGTag::kFeMorphology: + case SVGTag::kFeOffset: + case SVGTag::kFeSpecularLighting: + case SVGTag::kFeTurbulence: + return true; + default: + return false; + } + } + + std::shared_ptr makeImageFilter(const SVGRenderContext& ctx, + const SkSVGFilterContext& filterContext) const; + + // https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveSubRegion + Rect resolveFilterSubregion(const SVGRenderContext&, const SkSVGFilterContext&) const; + + /** + * Resolves the colorspace within which this filter effect should be applied. + * Spec: https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperties + * 'color-interpolation-filters' property. + */ + virtual SVGColorspace resolveColorspace(const SVGRenderContext&, const SkSVGFilterContext&) const; + + /** Propagates any inherited presentation attributes in the given context. */ + void applyProperties(SVGRenderContext*) const; + + SVG_ATTR(In, SVGFeInputType, SVGFeInputType()) + SVG_ATTR(Result, SVGStringType, SVGStringType()) + SVG_OPTIONAL_ATTR(X, SVGLength) + SVG_OPTIONAL_ATTR(Y, SVGLength) + SVG_OPTIONAL_ATTR(Width, SVGLength) + SVG_OPTIONAL_ATTR(Height, SVGLength) + + protected: + explicit SkSVGFe(SVGTag t) : INHERITED(t) { + } + +#ifndef RENDER_SVG + virtual std::shared_ptr onMakeImageFilter(const SVGRenderContext&, + const SkSVGFilterContext&) const = 0; +#endif + + virtual std::vector getInputs() const = 0; + + bool parseAndSetAttribute(const char*, const char*) override; + + private: +#ifndef RENDER_SVG + /** + * Resolves the rect specified by the x, y, width and height attributes (if specified) on this + * filter effect. These attributes are resolved according to the given length context and + * the value of 'primitiveUnits' on the parent element. + */ + Rect resolveBoundaries(const SVGRenderContext&, const SkSVGFilterContext&) const; +#endif + + using INHERITED = SVGHiddenContainer; +}; +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGFeBlend.h b/include/tgfx/svg/node/SVGFeBlend.h new file mode 100644 index 00000000..6c41e579 --- /dev/null +++ b/include/tgfx/svg/node/SVGFeBlend.h @@ -0,0 +1,67 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGFe.h" +#include "tgfx/svg/node/SVGNode.h" + +class SkSVGFilterContext; +class SVGRenderContext; + +namespace tgfx { + +class SkSVGFeBlend : public SkSVGFe { + public: + enum class Mode { + kNormal, + kMultiply, + kScreen, + kDarken, + kLighten, + }; + + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGFeBlend()); + } + + SVG_ATTR(Mode, Mode, Mode::kNormal) + SVG_ATTR(In2, SVGFeInputType, SVGFeInputType()) + + protected: +#ifndef RENDER_SVG + std::shared_ptr onMakeImageFilter(const SVGRenderContext&, + const SkSVGFilterContext&) const override; +#endif + + std::vector getInputs() const override { + return {this->getIn(), this->getIn2()}; + } + + bool parseAndSetAttribute(const char*, const char*) override; + + private: + SkSVGFeBlend() : INHERITED(SVGTag::kFeBlend) { + } + + using INHERITED = SkSVGFe; +}; +} // namespace tgfx diff --git a/include/tgfx/svg/node/SVGFeColorMatrix.h b/include/tgfx/svg/node/SVGFeColorMatrix.h new file mode 100644 index 00000000..cd5fea93 --- /dev/null +++ b/include/tgfx/svg/node/SVGFeColorMatrix.h @@ -0,0 +1,70 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGFe.h" +#include "tgfx/svg/node/SVGNode.h" + +class SkSVGFilterContext; +class SVGRenderContext; + +namespace tgfx { + +using ColorMatrix = std::array; + +class SkSVGFeColorMatrix final : public SkSVGFe { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGFeColorMatrix()); + } + + SVG_ATTR(Type, SVGFeColorMatrixType, SVGFeColorMatrixType(SVGFeColorMatrixType::kMatrix)) + SVG_ATTR(Values, SVGFeColorMatrixValues, SVGFeColorMatrixValues()) + + protected: +#ifndef RENDER_SVG + std::shared_ptr onMakeImageFilter(const SVGRenderContext&, + const SkSVGFilterContext&) const override; +#endif + std::vector getInputs() const override { + return {this->getIn()}; + } + + bool parseAndSetAttribute(const char*, const char*) override; + + private: + SkSVGFeColorMatrix() : INHERITED(SVGTag::kFeColorMatrix) { + } + +#ifndef RENDER_SVG + ColorMatrix makeMatrixForType() const; + + static ColorMatrix MakeSaturate(SVGNumberType s); + + static ColorMatrix MakeHueRotate(SVGNumberType degrees); + + static ColorMatrix MakeLuminanceToAlpha(); +#endif + + using INHERITED = SkSVGFe; +}; +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGFeComponentTransfer.h b/include/tgfx/svg/node/SVGFeComponentTransfer.h new file mode 100644 index 00000000..718fed5c --- /dev/null +++ b/include/tgfx/svg/node/SVGFeComponentTransfer.h @@ -0,0 +1,96 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include <__memory/shared_ptr.h> +#include +#include +#include +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGFe.h" +#include "tgfx/svg/node/SVGHiddenContainer.h" +#include "tgfx/svg/node/SVGNode.h" + +class SkSVGFilterContext; +class SVGRenderContext; + +namespace tgfx { + +class SkSVGFeFunc final : public SVGHiddenContainer { + public: + static std::shared_ptr MakeFuncA() { + return std::shared_ptr(new SkSVGFeFunc(SVGTag::kFeFuncA)); + } + + static std::shared_ptr MakeFuncR() { + return std::shared_ptr(new SkSVGFeFunc(SVGTag::kFeFuncR)); + } + + static std::shared_ptr MakeFuncG() { + return std::shared_ptr(new SkSVGFeFunc(SVGTag::kFeFuncG)); + } + + static std::shared_ptr MakeFuncB() { + return std::shared_ptr(new SkSVGFeFunc(SVGTag::kFeFuncB)); + } + + SVG_ATTR(Amplitude, SVGNumberType, 1) + SVG_ATTR(Exponent, SVGNumberType, 1) + SVG_ATTR(Intercept, SVGNumberType, 0) + SVG_ATTR(Offset, SVGNumberType, 0) + SVG_ATTR(Slope, SVGNumberType, 1) + SVG_ATTR(TableValues, std::vector, {}) + SVG_ATTR(Type, SVGFeFuncType, SVGFeFuncType::kIdentity) + + std::vector getTable() const; + + protected: + bool parseAndSetAttribute(const char*, const char*) override; + + private: + SkSVGFeFunc(SVGTag tag) : INHERITED(tag) { + } + + using INHERITED = SVGHiddenContainer; +}; + +class SkSVGFeComponentTransfer final : public SkSVGFe { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGFeComponentTransfer()); + } + + protected: +#ifndef RENDER_SVG + std::shared_ptr onMakeImageFilter(const SVGRenderContext&, + const SkSVGFilterContext&) const override { + return nullptr; + }; +#endif + std::vector getInputs() const override { + return {this->getIn()}; + } + + private: + SkSVGFeComponentTransfer() : INHERITED(SVGTag::kFeComponentTransfer) { + } + + using INHERITED = SkSVGFe; +}; +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGFeComposite.h b/include/tgfx/svg/node/SVGFeComposite.h new file mode 100644 index 00000000..d82c809d --- /dev/null +++ b/include/tgfx/svg/node/SVGFeComposite.h @@ -0,0 +1,65 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGFe.h" +#include "tgfx/svg/node/SVGNode.h" + +namespace tgfx { + +class SkSVGFilterContext; +class SVGRenderContext; + +class SkSVGFeComposite final : public SkSVGFe { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGFeComposite()); + } + + SVG_ATTR(In2, SVGFeInputType, SVGFeInputType()) + SVG_ATTR(K1, SVGNumberType, SVGNumberType(0)) + SVG_ATTR(K2, SVGNumberType, SVGNumberType(0)) + SVG_ATTR(K3, SVGNumberType, SVGNumberType(0)) + SVG_ATTR(K4, SVGNumberType, SVGNumberType(0)) + SVG_ATTR(Operator, SVGFeCompositeOperator, SVGFeCompositeOperator::kOver) + + protected: + std::vector getInputs() const override { + return {this->getIn(), this->getIn2()}; + } + + bool parseAndSetAttribute(const char*, const char*) override; + + private: + SkSVGFeComposite() : INHERITED(SVGTag::kFeComposite) { + } + + using INHERITED = SkSVGFe; + +#ifndef RENDER_SVG + protected: + std::shared_ptr onMakeImageFilter(const SVGRenderContext&, + const SkSVGFilterContext&) const override; + + private: + static BlendMode BlendModeForOperator(SVGFeCompositeOperator); +#endif +}; +} // namespace tgfx diff --git a/include/tgfx/svg/node/SVGFeDisplacementMap.h b/include/tgfx/svg/node/SVGFeDisplacementMap.h new file mode 100644 index 00000000..49a751ac --- /dev/null +++ b/include/tgfx/svg/node/SVGFeDisplacementMap.h @@ -0,0 +1,68 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGFe.h" +#include "tgfx/svg/node/SVGNode.h" + +namespace tgfx { + +class SkSVGFilterContext; +class SVGRenderContext; + +class SkSVGFeDisplacementMap : public SkSVGFe { + public: + using ChannelSelector = ColorChannel; + + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGFeDisplacementMap()); + } + + SVG_ATTR(In2, SVGFeInputType, SVGFeInputType()) + SVG_ATTR(XChannelSelector, ChannelSelector, ChannelSelector::A) + SVG_ATTR(YChannelSelector, ChannelSelector, ChannelSelector::A) + SVG_ATTR(Scale, SVGNumberType, SVGNumberType(0)) + + protected: + std::vector getInputs() const override { + return {this->getIn(), this->getIn2()}; + } + + bool parseAndSetAttribute(const char*, const char*) override; + + private: + SkSVGFeDisplacementMap() : INHERITED(SVGTag::kFeDisplacementMap) { + } + + using INHERITED = SkSVGFe; + +#ifndef RENDER_SVG + public: + SVGColorspace resolveColorspace(const SVGRenderContext&, const SkSVGFilterContext&) const final; + + protected: + std::shared_ptr onMakeImageFilter(const SVGRenderContext&, + const SkSVGFilterContext&) const override; +#endif +}; + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGFeFlood.h b/include/tgfx/svg/node/SVGFeFlood.h new file mode 100644 index 00000000..4ff2555b --- /dev/null +++ b/include/tgfx/svg/node/SVGFeFlood.h @@ -0,0 +1,62 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGFe.h" +#include "tgfx/svg/node/SVGNode.h" + +namespace tgfx { + +class SkImageFilter; +class SkSVGFilterContext; +class SVGRenderContext; + +class SkSVGFeFlood : public SkSVGFe { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGFeFlood()); + } + + protected: + std::shared_ptr onMakeImageFilter(const SVGRenderContext&, + const SkSVGFilterContext&) const override { + return nullptr; + }; +#ifdef RENDER_SVG + sk_sp onMakeImageFilter(const SVGRenderContext&, + const SkSVGFilterContext&) const override; +#endif + + std::vector getInputs() const override { + return {}; + } + + private: + SkSVGFeFlood() : INHERITED(SVGTag::kFeFlood) { + } +#ifdef RENDER_SVG + SkColor resolveFloodColor(const SVGRenderContext&) const; +#endif + + using INHERITED = SkSVGFe; +}; +} // namespace tgfx diff --git a/include/tgfx/svg/node/SVGFeGaussianBlur.h b/include/tgfx/svg/node/SVGFeGaussianBlur.h new file mode 100644 index 00000000..cbe00e38 --- /dev/null +++ b/include/tgfx/svg/node/SVGFeGaussianBlur.h @@ -0,0 +1,64 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGFe.h" +#include "tgfx/svg/node/SVGNode.h" + +namespace tgfx { + +class SkSVGFilterContext; +class SVGRenderContext; + +class SkSVGFeGaussianBlur : public SkSVGFe { + public: + struct StdDeviation { + SVGNumberType fX; + SVGNumberType fY; + }; + + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGFeGaussianBlur()); + } + + SVG_ATTR(StdDeviation, StdDeviation, StdDeviation({0, 0})) + + protected: +#ifndef RENDER_SVG + std::shared_ptr onMakeImageFilter(const SVGRenderContext&, + const SkSVGFilterContext&) const override; +#endif + + std::vector getInputs() const override { + return {this->getIn()}; + } + + bool parseAndSetAttribute(const char*, const char*) override; + + private: + SkSVGFeGaussianBlur() : INHERITED(SVGTag::kFeGaussianBlur) { + } + + using INHERITED = SkSVGFe; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGFeImage.h b/include/tgfx/svg/node/SVGFeImage.h new file mode 100644 index 00000000..fca0b944 --- /dev/null +++ b/include/tgfx/svg/node/SVGFeImage.h @@ -0,0 +1,60 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGFe.h" +#include "tgfx/svg/node/SVGNode.h" + +class SkSVGFilterContext; +class SVGRenderContext; + +namespace tgfx { + +class SkSVGFeImage : public SkSVGFe { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGFeImage()); + } + + SVG_ATTR(Href, SVGIRI, SVGIRI()) + SVG_ATTR(PreserveAspectRatio, SVGPreserveAspectRatio, SVGPreserveAspectRatio()) + + protected: + bool parseAndSetAttribute(const char*, const char*) override; + +#ifndef RENDER_SVG + std::shared_ptr onMakeImageFilter(const SVGRenderContext&, + const SkSVGFilterContext&) const override; +#endif + + std::vector getInputs() const override { + return {}; + } + + private: + SkSVGFeImage() : INHERITED(SVGTag::kFeImage) { + } + + using INHERITED = SkSVGFe; +}; + +} // namespace tgfx diff --git a/include/tgfx/svg/node/SVGFeLightSource.h b/include/tgfx/svg/node/SVGFeLightSource.h new file mode 100644 index 00000000..dc3bcf7c --- /dev/null +++ b/include/tgfx/svg/node/SVGFeLightSource.h @@ -0,0 +1,105 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGHiddenContainer.h" +#include "tgfx/svg/node/SVGNode.h" + +namespace tgfx { + +class SkSVGFeLightSource : public SVGHiddenContainer { + public: + void appendChild(std::shared_ptr) final { + } + + protected: + explicit SkSVGFeLightSource(SVGTag tag) : INHERITED(tag) { + } + + private: + using INHERITED = SVGHiddenContainer; +}; + +class SkSVGFeDistantLight final : public SkSVGFeLightSource { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGFeDistantLight()); + } + + // pk::SkPoint3 computeDirection() const; + + SVG_ATTR(Azimuth, SVGNumberType, 0) + SVG_ATTR(Elevation, SVGNumberType, 0) + + private: + SkSVGFeDistantLight() : INHERITED(SVGTag::kFeDistantLight) { + } + + bool parseAndSetAttribute(const char*, const char*) override; + + using INHERITED = SkSVGFeLightSource; +}; + +class SkSVGFePointLight final : public SkSVGFeLightSource { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGFePointLight()); + } + + SVG_ATTR(X, SVGNumberType, 0) + SVG_ATTR(Y, SVGNumberType, 0) + SVG_ATTR(Z, SVGNumberType, 0) + + private: + SkSVGFePointLight() : INHERITED(SVGTag::kFePointLight) { + } + + bool parseAndSetAttribute(const char*, const char*) override; + + using INHERITED = SkSVGFeLightSource; +}; + +class SkSVGFeSpotLight final : public SkSVGFeLightSource { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGFeSpotLight()); + } + + SVG_ATTR(X, SVGNumberType, 0) + SVG_ATTR(Y, SVGNumberType, 0) + SVG_ATTR(Z, SVGNumberType, 0) + SVG_ATTR(PointsAtX, SVGNumberType, 0) + SVG_ATTR(PointsAtY, SVGNumberType, 0) + SVG_ATTR(PointsAtZ, SVGNumberType, 0) + SVG_ATTR(SpecularExponent, SVGNumberType, 1) + + SVG_OPTIONAL_ATTR(LimitingConeAngle, SVGNumberType) + + private: + SkSVGFeSpotLight() : INHERITED(SVGTag::kFeSpotLight) { + } + + bool parseAndSetAttribute(const char*, const char*) override; + + using INHERITED = SkSVGFeLightSource; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGFeLighting.h b/include/tgfx/svg/node/SVGFeLighting.h new file mode 100644 index 00000000..cbb2e42b --- /dev/null +++ b/include/tgfx/svg/node/SVGFeLighting.h @@ -0,0 +1,141 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include "tgfx/core/ImageFilter.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGFe.h" +#include "tgfx/svg/node/SVGNode.h" + +namespace tgfx { + +class SkSVGFeDistantLight; +class SkSVGFePointLight; +class SkSVGFeSpotLight; +// class SkSVGFilterContext; +// class SVGRenderContext; + +class SkSVGFeLighting : public SkSVGFe { + public: + struct KernelUnitLength { + SVGNumberType fDx; + SVGNumberType fDy; + }; + + SVG_ATTR(SurfaceScale, SVGNumberType, 1) + SVG_OPTIONAL_ATTR(KernelUnitLength, KernelUnitLength) + + protected: + explicit SkSVGFeLighting(SVGTag t) : INHERITED(t) { + } + + std::vector getInputs() const final { + return {this->getIn()}; + } + + bool parseAndSetAttribute(const char*, const char*) override; + + std::shared_ptr onMakeImageFilter(const SVGRenderContext&, + const SkSVGFilterContext&) const final { + return nullptr; + }; +#ifdef RENDER_SVG + sk_sp onMakeImageFilter(const SVGRenderContext&, + const SkSVGFilterContext&) const final; + + virtual sk_sp makeDistantLight(const SVGRenderContext&, const SkSVGFilterContext&, + const SkSVGFeDistantLight*) const = 0; + + virtual sk_sp makePointLight(const SVGRenderContext&, const SkSVGFilterContext&, + const SkSVGFePointLight*) const = 0; + + virtual sk_sp makeSpotLight(const SVGRenderContext&, const SkSVGFilterContext&, + const SkSVGFeSpotLight*) const = 0; + + SkColor resolveLightingColor(const SVGRenderContext&) const; + + SkPoint3 resolveXYZ(const SVGRenderContext&, const SkSVGFilterContext&, SkSVGNumberType, + SkSVGNumberType, SkSVGNumberType) const; +#endif + + private: + using INHERITED = SkSVGFe; +}; + +class SkSVGFeSpecularLighting final : public SkSVGFeLighting { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGFeSpecularLighting()); + } + + SVG_ATTR(SpecularConstant, SVGNumberType, 1) + SVG_ATTR(SpecularExponent, SVGNumberType, 1) + + protected: + bool parseAndSetAttribute(const char*, const char*) override; + +#ifdef RENDER_SVG + sk_sp makeDistantLight(const SVGRenderContext&, const SkSVGFilterContext&, + const SkSVGFeDistantLight*) const final; + + sk_sp makePointLight(const SVGRenderContext&, const SkSVGFilterContext&, + const SkSVGFePointLight*) const final; + + sk_sp makeSpotLight(const SVGRenderContext&, const SkSVGFilterContext&, + const SkSVGFeSpotLight*) const final; +#endif + + private: + SkSVGFeSpecularLighting() : INHERITED(SVGTag::kFeSpecularLighting) { + } + + using INHERITED = SkSVGFeLighting; +}; + +class SkSVGFeDiffuseLighting final : public SkSVGFeLighting { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGFeDiffuseLighting()); + } + + SVG_ATTR(DiffuseConstant, SVGNumberType, 1) + + protected: + bool parseAndSetAttribute(const char*, const char*) override; + +#ifdef RENDER_SVG + sk_sp makeDistantLight(const SVGRenderContext&, const SkSVGFilterContext&, + const SkSVGFeDistantLight*) const final; + + sk_sp makePointLight(const SVGRenderContext&, const SkSVGFilterContext&, + const SkSVGFePointLight*) const final; + + sk_sp makeSpotLight(const SVGRenderContext&, const SkSVGFilterContext&, + const SkSVGFeSpotLight*) const final; +#endif + private: + SkSVGFeDiffuseLighting() : INHERITED(SVGTag::kFeDiffuseLighting) { + } + + using INHERITED = SkSVGFeLighting; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGFeMerge.h b/include/tgfx/svg/node/SVGFeMerge.h new file mode 100644 index 00000000..f9ac49d4 --- /dev/null +++ b/include/tgfx/svg/node/SVGFeMerge.h @@ -0,0 +1,76 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGFe.h" +#include "tgfx/svg/node/SVGHiddenContainer.h" +#include "tgfx/svg/node/SVGNode.h" + +// class SkSVGFilterContext; +// class SVGRenderContext; + +namespace tgfx { + +// https://www.w3.org/TR/SVG11/filters.html#feMergeNodeElement +class SkSVGFeMergeNode : public SVGHiddenContainer { + public: + static constexpr SVGTag tag = SVGTag::kFeMergeNode; + + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGFeMergeNode()); + } + + SVG_ATTR(In, SVGFeInputType, SVGFeInputType()) + + protected: + bool parseAndSetAttribute(const char*, const char*) override; + + private: + SkSVGFeMergeNode() : INHERITED(tag) { + } + + using INHERITED = SVGHiddenContainer; +}; + +// https://www.w3.org/TR/SVG11/filters.html#feMergeElement +class SkSVGFeMerge : public SkSVGFe { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGFeMerge()); + } + + protected: +#ifndef RENDER_SVG + std::shared_ptr onMakeImageFilter(const SVGRenderContext&, + const SkSVGFilterContext&) const override; +#endif + + std::vector getInputs() const override; + + private: + SkSVGFeMerge() : INHERITED(SVGTag::kFeMerge) { + } + + using INHERITED = SkSVGFe; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGFeMorphology.h b/include/tgfx/svg/node/SVGFeMorphology.h new file mode 100644 index 00000000..7e170bc5 --- /dev/null +++ b/include/tgfx/svg/node/SVGFeMorphology.h @@ -0,0 +1,70 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGFe.h" +#include "tgfx/svg/node/SVGNode.h" + +// class SkSVGFilterContext; +// class SVGRenderContext; + +namespace tgfx { + +class SkSVGFeMorphology : public SkSVGFe { + public: + struct Radius { + SVGNumberType fX; + SVGNumberType fY; + }; + + enum class Operator { + kErode, + kDilate, + }; + + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGFeMorphology()); + } + + SVG_ATTR(Operator, Operator, Operator::kErode) + SVG_ATTR(Radius, Radius, Radius({0, 0})) + + protected: +#ifndef RENDER_SVG + std::shared_ptr onMakeImageFilter(const SVGRenderContext&, + const SkSVGFilterContext&) const override; +#endif + + std::vector getInputs() const override { + return {this->getIn()}; + } + + bool parseAndSetAttribute(const char*, const char*) override; + + private: + SkSVGFeMorphology() : INHERITED(SVGTag::kFeMorphology) { + } + + using INHERITED = SkSVGFe; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGFeOffset.h b/include/tgfx/svg/node/SVGFeOffset.h new file mode 100644 index 00000000..621d0938 --- /dev/null +++ b/include/tgfx/svg/node/SVGFeOffset.h @@ -0,0 +1,60 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGFe.h" +#include "tgfx/svg/node/SVGNode.h" + +namespace tgfx { + +class SkImageFilter; +class SkSVGFilterContext; +class SVGRenderContext; + +class SkSVGFeOffset : public SkSVGFe { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGFeOffset()); + } + + SVG_ATTR(Dx, SVGNumberType, SVGNumberType(0)) + SVG_ATTR(Dy, SVGNumberType, SVGNumberType(0)) + + protected: +#ifndef RENDER_SVG + std::shared_ptr onMakeImageFilter(const SVGRenderContext&, + const SkSVGFilterContext&) const override; +#endif + + std::vector getInputs() const override { + return {this->getIn()}; + } + + bool parseAndSetAttribute(const char*, const char*) override; + + private: + SkSVGFeOffset() : INHERITED(SVGTag::kFeOffset) { + } + + using INHERITED = SkSVGFe; +}; +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGFeTurbulence.h b/include/tgfx/svg/node/SVGFeTurbulence.h new file mode 100644 index 00000000..c085c765 --- /dev/null +++ b/include/tgfx/svg/node/SVGFeTurbulence.h @@ -0,0 +1,60 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGFe.h" +#include "tgfx/svg/node/SVGNode.h" + +namespace tgfx { + +class SkSVGFeTurbulence : public SkSVGFe { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGFeTurbulence()); + } + + SVG_ATTR(BaseFrequency, SVGFeTurbulenceBaseFrequency, SVGFeTurbulenceBaseFrequency({})) + SVG_ATTR(NumOctaves, SVGIntegerType, SVGIntegerType(1)) + SVG_ATTR(Seed, SVGNumberType, SVGNumberType(0)) + SVG_ATTR(TurbulenceType, SVGFeTurbulenceType, + SVGFeTurbulenceType(SVGFeTurbulenceType::Type::kTurbulence)) + + protected: +#ifndef RENDER_SVG + std::shared_ptr onMakeImageFilter(const SVGRenderContext&, + const SkSVGFilterContext&) const override; +#endif + + std::vector getInputs() const override { + return {}; + } + + bool parseAndSetAttribute(const char*, const char*) override; + + private: + SkSVGFeTurbulence() : INHERITED(SVGTag::kFeTurbulence) { + } + + using INHERITED = SkSVGFe; +}; + +} // namespace tgfx diff --git a/include/tgfx/svg/node/SVGFilter.h b/include/tgfx/svg/node/SVGFilter.h new file mode 100644 index 00000000..f366296b --- /dev/null +++ b/include/tgfx/svg/node/SVGFilter.h @@ -0,0 +1,57 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/core/ImageFilter.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGHiddenContainer.h" +#include "tgfx/svg/node/SVGNode.h" + +namespace tgfx { + +class SkSVGFilter final : public SVGHiddenContainer { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGFilter()); + } + + /** Propagates any inherited presentation attributes in the given context. */ + void applyProperties(SVGRenderContext*) const; + + std::shared_ptr buildFilterDAG(const SVGRenderContext&) const; + + SVG_ATTR(X, SVGLength, SVGLength(-10, SVGLength::Unit::kPercentage)) + SVG_ATTR(Y, SVGLength, SVGLength(-10, SVGLength::Unit::kPercentage)) + SVG_ATTR(Width, SVGLength, SVGLength(120, SVGLength::Unit::kPercentage)) + SVG_ATTR(Height, SVGLength, SVGLength(120, SVGLength::Unit::kPercentage)) + SVG_ATTR(FilterUnits, SVGObjectBoundingBoxUnits, + SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox)) + SVG_ATTR(PrimitiveUnits, SVGObjectBoundingBoxUnits, + SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse)) + + private: + SkSVGFilter() : INHERITED(SVGTag::kFilter) { + } + + bool parseAndSetAttribute(const char*, const char*) override; + + using INHERITED = SVGHiddenContainer; +}; +} // namespace tgfx diff --git a/include/tgfx/svg/node/SVGFilterContext.h b/include/tgfx/svg/node/SVGFilterContext.h new file mode 100644 index 00000000..3a9357fd --- /dev/null +++ b/include/tgfx/svg/node/SVGFilterContext.h @@ -0,0 +1,87 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include "tgfx/core/ImageFilter.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGRenderContext.h" +#include "tgfx/svg/SVGTypes.h" + +namespace tgfx { + +class SkSVGFilterContext { + public: + SkSVGFilterContext(const Rect& filterEffectsRegion, + const SVGObjectBoundingBoxUnits& primitiveUnits) + : fFilterEffectsRegion(filterEffectsRegion), fPrimitiveUnits(primitiveUnits), + fPreviousResult({nullptr, filterEffectsRegion, SVGColorspace::kSRGB}) { + } + + const Rect& filterEffectsRegion() const { + return fFilterEffectsRegion; + } + + const Rect& filterPrimitiveSubregion(const SVGFeInputType&) const; + + const SVGObjectBoundingBoxUnits& primitiveUnits() const { + return fPrimitiveUnits; + } + + void registerResult(const SVGStringType&, const std::shared_ptr&, const Rect&, + SVGColorspace); + + void setPreviousResult(const std::shared_ptr&, const Rect&, SVGColorspace); + + bool previousResultIsSourceGraphic() const; + +#ifndef RENDER_SVG + SVGColorspace resolveInputColorspace(const SVGRenderContext&, const SVGFeInputType&) const; + + std::shared_ptr resolveInput(const SVGRenderContext&, const SVGFeInputType&) const; + + std::shared_ptr resolveInput(const SVGRenderContext&, const SVGFeInputType&, + SVGColorspace) const; +#endif + + private: + struct Result { + std::shared_ptr fImageFilter; + Rect fFilterSubregion; + SVGColorspace fColorspace; + }; + + const Result* findResultById(const SVGStringType&) const; + +#ifndef RENDER_SVG + std::tuple, SVGColorspace> getInput(const SVGRenderContext&, + const SVGFeInputType&) const; +#endif + + Rect fFilterEffectsRegion; + + SVGObjectBoundingBoxUnits fPrimitiveUnits; + + std::unordered_map fResults; + + Result fPreviousResult; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGG.h b/include/tgfx/svg/node/SVGG.h new file mode 100644 index 00000000..0ced7f36 --- /dev/null +++ b/include/tgfx/svg/node/SVGG.h @@ -0,0 +1,37 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/svg/node/SVGContainer.h" + +namespace tgfx { +class SkSVGG : public SkSVGContainer { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGG()); + } + + private: + SkSVGG() : INHERITED(SVGTag::kG) { + } + + using INHERITED = SkSVGContainer; +}; +} // namespace tgfx diff --git a/include/tgfx/svg/node/SVGGradient.h b/include/tgfx/svg/node/SVGGradient.h new file mode 100644 index 00000000..e9ef7d8e --- /dev/null +++ b/include/tgfx/svg/node/SVGGradient.h @@ -0,0 +1,59 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/core/Color.h" +#include "tgfx/core/Matrix.h" +#include "tgfx/core/Paint.h" +#include "tgfx/core/TileMode.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGHiddenContainer.h" +#include "tgfx/svg/node/SVGNode.h" +#include "tgfx/svg/node/SVGStop.h" + +namespace tgfx { + +class SkSVGGradient : public SVGHiddenContainer { + public: + SVG_ATTR(Href, SVGIRI, SVGIRI()) + SVG_ATTR(GradientTransform, SVGTransformType, SVGTransformType(Matrix::I())) + SVG_ATTR(SpreadMethod, SVGSpreadMethod, SVGSpreadMethod(SVGSpreadMethod::Type::kPad)) + SVG_ATTR(GradientUnits, SVGObjectBoundingBoxUnits, + SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox)) + + protected: + explicit SkSVGGradient(SVGTag t) : INHERITED(t) { + } + + bool parseAndSetAttribute(const char*, const char*) override; + + bool onAsPaint(const SVGRenderContext&, Paint*) const final; + + virtual std::shared_ptr onMakeShader(const SVGRenderContext&, const std::vector&, + const std::vector&, TileMode, + const Matrix& localMatrix) const = 0; + + private: + void collectColorStops(const SVGRenderContext&, std::vector&, std::vector&) const; + Color resolveStopColor(const SVGRenderContext&, const SkSVGStop&) const; + + using INHERITED = SVGHiddenContainer; +}; +} // namespace tgfx diff --git a/include/tgfx/svg/node/SVGHiddenContainer.h b/include/tgfx/svg/node/SVGHiddenContainer.h new file mode 100644 index 00000000..b1fb718e --- /dev/null +++ b/include/tgfx/svg/node/SVGHiddenContainer.h @@ -0,0 +1,39 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "tgfx/svg/node/SVGContainer.h" + +namespace tgfx { + +class SVGHiddenContainer : public SkSVGContainer { + protected: + explicit SVGHiddenContainer(SVGTag t) : INHERITED(t) { + } + +#ifndef RENDER_SVG + void onRender(const SVGRenderContext&) const final { + //abort rendering children nodes + } +#endif + + private: + using INHERITED = SkSVGContainer; +}; +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGImage.h b/include/tgfx/svg/node/SVGImage.h new file mode 100644 index 00000000..3be2141d --- /dev/null +++ b/include/tgfx/svg/node/SVGImage.h @@ -0,0 +1,84 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +// #include "include/core/SkImage.h" +// #include "include/core/SkRect.h" +// #include "include/core/SkRefCnt.h" +// #include "include/private/base/SkAPI.h" +// #include "include/private/base/SkDebug.h" +#include "tgfx/core/Image.h" +#include "tgfx/core/Path.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGNode.h" +#include "tgfx/svg/node/SVGTransformableNode.h" + +namespace tgfx { + +class SVGRenderContext; + +namespace skresources { +class ResourceProvider; +} + +class SkSVGImage final : public SkSVGTransformableNode { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGImage()); + } + + void appendChild(std::shared_ptr) override { + } + + struct ImageInfo { + std::shared_ptr fImage; + Rect fDst; + }; + +#ifndef RENDER_SVG + bool onPrepareToRender(SVGRenderContext*) const override; + void onRender(const SVGRenderContext&) const override; + Path onAsPath(const SVGRenderContext&) const override; + Rect onObjectBoundingBox(const SVGRenderContext&) const override; + + // TODO (YG) + static ImageInfo LoadImage(const std::shared_ptr& rp, const SVGIRI&, + const Rect&, SVGPreserveAspectRatio); +#endif + + SVG_ATTR(X, SVGLength, SVGLength(0)) + SVG_ATTR(Y, SVGLength, SVGLength(0)) + SVG_ATTR(Width, SVGLength, SVGLength(0)) + SVG_ATTR(Height, SVGLength, SVGLength(0)) + SVG_ATTR(Href, SVGIRI, SVGIRI()) + SVG_ATTR(PreserveAspectRatio, SVGPreserveAspectRatio, SVGPreserveAspectRatio()) + + protected: + bool parseAndSetAttribute(const char*, const char*) override; + + private: + SkSVGImage() : INHERITED(SVGTag::kImage) { + } + + using INHERITED = SkSVGTransformableNode; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGLine.h b/include/tgfx/svg/node/SVGLine.h new file mode 100644 index 00000000..5828db1a --- /dev/null +++ b/include/tgfx/svg/node/SVGLine.h @@ -0,0 +1,63 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/core/Canvas.h" +#include "tgfx/core/Paint.h" +#include "tgfx/core/Path.h" +#include "tgfx/core/Point.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGNode.h" +#include "tgfx/svg/node/SVGShape.h" + +namespace tgfx { + +class SkSVGLine final : public SVGShape { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGLine()); + } + + SVG_ATTR(X1, SVGLength, SVGLength(0)) + SVG_ATTR(Y1, SVGLength, SVGLength(0)) + SVG_ATTR(X2, SVGLength, SVGLength(0)) + SVG_ATTR(Y2, SVGLength, SVGLength(0)) + + protected: + bool parseAndSetAttribute(const char*, const char*) override; + +#ifndef RENDER_SVG + void onDraw(Canvas*, const SVGLengthContext&, const Paint&, PathFillType) const override; + + Path onAsPath(const SVGRenderContext&) const override; +#endif + + private: + SkSVGLine(); + +#ifndef RENDER_SVG + // resolve and return the two endpoints + std::tuple resolve(const SVGLengthContext&) const; +#endif + + using INHERITED = SVGShape; +}; + +} // namespace tgfx diff --git a/include/tgfx/svg/node/SVGLinearGradient.h b/include/tgfx/svg/node/SVGLinearGradient.h new file mode 100644 index 00000000..70b3e20e --- /dev/null +++ b/include/tgfx/svg/node/SVGLinearGradient.h @@ -0,0 +1,54 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/core/Shader.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGGradient.h" +#include "tgfx/svg/node/SVGNode.h" + +namespace tgfx { + +class SkSVGLinearGradient final : public SkSVGGradient { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGLinearGradient()); + } + + SVG_ATTR(X1, SVGLength, SVGLength(0, SVGLength::Unit::kPercentage)) + SVG_ATTR(Y1, SVGLength, SVGLength(0, SVGLength::Unit::kPercentage)) + SVG_ATTR(X2, SVGLength, SVGLength(100, SVGLength::Unit::kPercentage)) + SVG_ATTR(Y2, SVGLength, SVGLength(0, SVGLength::Unit::kPercentage)) + + protected: + bool parseAndSetAttribute(const char*, const char*) override; + +#ifndef RENDER_SVG + std::shared_ptr onMakeShader(const SVGRenderContext&, const std::vector&, + const std::vector&, TileMode, + const Matrix& localMatrix) const override; +#endif + + private: + SkSVGLinearGradient(); + + using INHERITED = SkSVGGradient; +}; +} // namespace tgfx diff --git a/include/tgfx/svg/node/SVGMask.h b/include/tgfx/svg/node/SVGMask.h new file mode 100644 index 00000000..7b43d1fc --- /dev/null +++ b/include/tgfx/svg/node/SVGMask.h @@ -0,0 +1,61 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGHiddenContainer.h" +#include "tgfx/svg/node/SVGNode.h" + +namespace tgfx { + +class SkSVGMask final : public SVGHiddenContainer { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGMask()); + } + + SVG_ATTR(X, SVGLength, SVGLength(-10, SVGLength::Unit::kPercentage)) + SVG_ATTR(Y, SVGLength, SVGLength(-10, SVGLength::Unit::kPercentage)) + SVG_ATTR(Width, SVGLength, SVGLength(120, SVGLength::Unit::kPercentage)) + SVG_ATTR(Height, SVGLength, SVGLength(120, SVGLength::Unit::kPercentage)) + + SVG_ATTR(MaskUnits, SVGObjectBoundingBoxUnits, + SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox)) + SVG_ATTR(MaskContentUnits, SVGObjectBoundingBoxUnits, + SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse)) + + private: + friend class SVGRenderContext; + + SkSVGMask() : INHERITED(SVGTag::kMask) { + } + + bool parseAndSetAttribute(const char*, const char*) override; + +#ifndef RENDER_SVG + Rect bounds(const SVGRenderContext&) const; + + void renderMask(const SVGRenderContext&) const; +#endif + + using INHERITED = SVGHiddenContainer; +}; + +} // namespace tgfx diff --git a/include/tgfx/svg/node/SVGNode.h b/include/tgfx/svg/node/SVGNode.h new file mode 100644 index 00000000..64600dd6 --- /dev/null +++ b/include/tgfx/svg/node/SVGNode.h @@ -0,0 +1,264 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include "tgfx/core/Matrix.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg//SVGTypes.h" +#include "tgfx/svg/SVGAttribute.h" +#include "tgfx/svg/SVGAttributeParser.h" +#include "tgfx/svg/SVGRenderContext.h" + +namespace tgfx { + +class SVGValue; + +enum class SVGTag { + kCircle, + kClipPath, + kDefs, + kEllipse, + kFeBlend, + kFeColorMatrix, + kFeComponentTransfer, + kFeComposite, + kFeDiffuseLighting, + kFeDisplacementMap, + kFeDistantLight, + kFeFlood, + kFeFuncA, + kFeFuncR, + kFeFuncG, + kFeFuncB, + kFeGaussianBlur, + kFeImage, + kFeMerge, + kFeMergeNode, + kFeMorphology, + kFeOffset, + kFePointLight, + kFeSpecularLighting, + kFeSpotLight, + kFeTurbulence, + kFilter, + kG, + kImage, + kLine, + kLinearGradient, + kMask, + kPath, + kPattern, + kPolygon, + kPolyline, + kRadialGradient, + kRect, + kStop, + kSvg, + kText, + kTextLiteral, + kTextPath, + kTSpan, + kUse +}; + +#define SVG_PRES_ATTR(attr_name, attr_type, attr_inherited) \ + private: \ + bool set##attr_name( \ + SVGAttributeParser::ParseResult>&& pr) { \ + if (pr.has_value()) { \ + this->set##attr_name(std::move(*pr)); \ + } \ + return pr.has_value(); \ + } \ + \ + public: \ + const SVGProperty& get##attr_name() const { \ + return fPresentationAttributes.f##attr_name; \ + } \ + void set##attr_name(const SVGProperty& v) { \ + auto* dest = &fPresentationAttributes.f##attr_name; \ + if (!dest->isInheritable() || v.isValue()) { \ + /* TODO: If dest is not inheritable, handle v == "inherit" */ \ + *dest = v; \ + } else { \ + dest->set(SVGPropertyState::kInherit); \ + } \ + } \ + void set##attr_name(SVGProperty&& v) { \ + auto* dest = &fPresentationAttributes.f##attr_name; \ + if (!dest->isInheritable() || v.isValue()) { \ + /* TODO: If dest is not inheritable, handle v == "inherit" */ \ + *dest = std::move(v); \ + } else { \ + dest->set(SVGPropertyState::kInherit); \ + } \ + } + +class SVGNode { + public: + virtual ~SVGNode(); + + SVGTag tag() const { + return fTag; + } + + virtual void appendChild(std::shared_ptr) = 0; + + virtual bool hasChildren() const { + return false; + } + +#ifndef RENDER_SVG + + void render(const SVGRenderContext&) const; + bool asPaint(const SVGRenderContext&, Paint*) const; + Path asPath(const SVGRenderContext&) const; + Rect objectBoundingBox(const SVGRenderContext&) const; + +#endif + + void setAttribute(SVGAttribute, const SVGValue&); + bool setAttribute(const std::string& attributeName, const std::string& attributeValue); + virtual bool parseAndSetAttribute(const char* name, const char* value); + + // inherited + SVG_PRES_ATTR(ClipRule, SVGFillRule, true) + SVG_PRES_ATTR(Color, SVGColorType, true) + SVG_PRES_ATTR(ColorInterpolation, SVGColorspace, true) + SVG_PRES_ATTR(ColorInterpolationFilters, SVGColorspace, true) + SVG_PRES_ATTR(FillRule, SVGFillRule, true) + SVG_PRES_ATTR(Fill, SVGPaint, true) + SVG_PRES_ATTR(FillOpacity, SVGNumberType, true) + SVG_PRES_ATTR(FontFamily, SVGFontFamily, true) + SVG_PRES_ATTR(FontSize, SVGFontSize, true) + SVG_PRES_ATTR(FontStyle, SVGFontStyle, true) + SVG_PRES_ATTR(FontWeight, SVGFontWeight, true) + SVG_PRES_ATTR(Stroke, SVGPaint, true) + SVG_PRES_ATTR(StrokeDashArray, SVGDashArray, true) + SVG_PRES_ATTR(StrokeDashOffset, SVGLength, true) + SVG_PRES_ATTR(StrokeLineCap, SVGLineCap, true) + SVG_PRES_ATTR(StrokeLineJoin, SVGLineJoin, true) + SVG_PRES_ATTR(StrokeMiterLimit, SVGNumberType, true) + SVG_PRES_ATTR(StrokeOpacity, SVGNumberType, true) + SVG_PRES_ATTR(StrokeWidth, SVGLength, true) + SVG_PRES_ATTR(TextAnchor, SVGTextAnchor, true) + SVG_PRES_ATTR(Visibility, SVGVisibility, true) + + // not inherited + SVG_PRES_ATTR(ClipPath, SVGFuncIRI, false) + SVG_PRES_ATTR(Display, SVGDisplay, false) + SVG_PRES_ATTR(Mask, SVGFuncIRI, false) + SVG_PRES_ATTR(Filter, SVGFuncIRI, false) + SVG_PRES_ATTR(Opacity, SVGNumberType, false) + SVG_PRES_ATTR(StopColor, SVGColor, false) + SVG_PRES_ATTR(StopOpacity, SVGNumberType, false) + SVG_PRES_ATTR(FloodColor, SVGColor, false) + SVG_PRES_ATTR(FloodOpacity, SVGNumberType, false) + SVG_PRES_ATTR(LightingColor, SVGColor, false) + + protected: + explicit SVGNode(SVGTag); + + static Matrix ComputeViewboxMatrix(const Rect&, const Rect&, SVGPreserveAspectRatio); + + virtual void onSetAttribute(SVGAttribute, const SVGValue&) { + } +#ifndef RENDER_SVG + // Called before onRender(), to apply local attributes to the context. Unlike onRender(), + // onPrepareToRender() bubbles up the inheritance chain: overriders should always call + // INHERITED::onPrepareToRender(), unless they intend to short-circuit rendering + // (return false). + // Implementations are expected to return true if rendering is to continue, or false if + // the node/subtree rendering is disabled. + virtual bool onPrepareToRender(SVGRenderContext*) const; + + virtual void onRender(const SVGRenderContext&) const = 0; + + virtual bool onAsPaint(const SVGRenderContext&, Paint*) const { + return false; + } + + virtual Path onAsPath(const SVGRenderContext&) const = 0; + + virtual Rect onObjectBoundingBox(const SVGRenderContext&) const { + return Rect::MakeEmpty(); + } +#endif + + private: + SVGTag fTag; + + // FIXME: this should be sparse + SkSVGPresentationAttributes fPresentationAttributes; +}; + +//NOLINTBEGIN +#undef SVG_PRES_ATTR // presentation attributes are only defined for the base class + +#define SVG_ATTR_SETTERS(attr_name, attr_type, attr_default, set_cp, set_mv) \ + private: \ + bool set##attr_name(const SVGAttributeParser::ParseResult& pr) { \ + if (pr.has_value()) { \ + this->set##attr_name(*pr); \ + } \ + return pr.has_value(); \ + } \ + bool set##attr_name(SVGAttributeParser::ParseResult&& pr) { \ + if (pr.has_value()) { \ + this->set##attr_name(std::move(*pr)); \ + } \ + return pr.has_value(); \ + } \ + \ + public: \ + void set##attr_name(const attr_type& a) { \ + set_cp(a); \ + } \ + void set##attr_name(attr_type&& a) { \ + set_mv(std::move(a)); \ + } + +#define SVG_ATTR(attr_name, attr_type, attr_default) \ + private: \ + attr_type f##attr_name = attr_default; \ + \ + public: \ + const attr_type& get##attr_name() const { \ + return f##attr_name; \ + } \ + SVG_ATTR_SETTERS( \ + attr_name, attr_type, attr_default, [this](const attr_type& a) { this->f##attr_name = a; }, \ + [this](attr_type&& a) { this->f##attr_name = std::move(a); }) + +#define SVG_OPTIONAL_ATTR(attr_name, attr_type) \ + private: \ + std::optional f##attr_name; \ + \ + public: \ + const std::optional& get##attr_name() const { \ + return f##attr_name; \ + } \ + SVG_ATTR_SETTERS( \ + attr_name, attr_type, attr_default, [this](const attr_type& a) { this->f##attr_name = a; }, \ + [this](attr_type&& a) { this->f##attr_name = a; }) +//NOLINTEND + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGPath.h b/include/tgfx/svg/node/SVGPath.h new file mode 100644 index 00000000..b63406cd --- /dev/null +++ b/include/tgfx/svg/node/SVGPath.h @@ -0,0 +1,54 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/core/Canvas.h" +#include "tgfx/core/Paint.h" +#include "tgfx/core/Path.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/node/SVGShape.h" + +namespace tgfx { + +class SkSVGPath final : public SVGShape { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGPath()); + } + + SVG_ATTR(Path, Path, Path()) + + protected: + bool parseAndSetAttribute(const char*, const char*) override; + +#ifndef RENDER_SVG + void onDraw(Canvas*, const SVGLengthContext&, const Paint&, PathFillType) const override; + + Path onAsPath(const SVGRenderContext&) const override; + + Rect onObjectBoundingBox(const SVGRenderContext&) const override; +#endif + + private: + SkSVGPath(); + + using INHERITED = SVGShape; +}; +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGPattern.h b/include/tgfx/svg/node/SVGPattern.h new file mode 100644 index 00000000..927ced8f --- /dev/null +++ b/include/tgfx/svg/node/SVGPattern.h @@ -0,0 +1,68 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include "tgfx/core/Paint.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGHiddenContainer.h" +#include "tgfx/svg/node/SVGNode.h" + +namespace tgfx { + +class SkSVGPattern final : public SVGHiddenContainer { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGPattern()); + } + + SVG_ATTR(Href, SVGIRI, SVGIRI()) + SVG_OPTIONAL_ATTR(X, SVGLength) + SVG_OPTIONAL_ATTR(Y, SVGLength) + SVG_OPTIONAL_ATTR(Width, SVGLength) + SVG_OPTIONAL_ATTR(Height, SVGLength) + SVG_OPTIONAL_ATTR(PatternTransform, SVGTransformType) + SVG_ATTR(PatternUnits, SVGPatternUnits, SVGPatternUnits::ObjectBoundingBox) + SVG_ATTR(ContentUnits, SVGPatternUnits, SVGPatternUnits::UserSpaceOnUse) + + protected: + SkSVGPattern(); + + bool parseAndSetAttribute(const char*, const char*) override; + +#ifndef RENDER_SVG + bool onAsPaint(const SVGRenderContext&, Paint*) const override; +#endif + + private: + struct PatternAttributes { + std::optional fX, fY, fWidth, fHeight; + std::optional fPatternTransform; + }; + +#ifndef RENDER_SVG + const SkSVGPattern* resolveHref(const SVGRenderContext&, PatternAttributes*) const; + const SkSVGPattern* hrefTarget(const SVGRenderContext&) const; +#endif + + using INHERITED = SVGHiddenContainer; +}; + +} // namespace tgfx diff --git a/include/tgfx/svg/node/SVGPoly.h b/include/tgfx/svg/node/SVGPoly.h new file mode 100644 index 00000000..2b33f492 --- /dev/null +++ b/include/tgfx/svg/node/SVGPoly.h @@ -0,0 +1,62 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/core/Canvas.h" +#include "tgfx/core/Paint.h" +#include "tgfx/core/Path.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGNode.h" +#include "tgfx/svg/node/SVGShape.h" + +namespace tgfx { + +// Handles and elements. +class SkSVGPoly final : public SVGShape { + public: + static std::shared_ptr MakePolygon() { + return std::shared_ptr(new SkSVGPoly(SVGTag::kPolygon)); + } + + static std::shared_ptr MakePolyline() { + return std::shared_ptr(new SkSVGPoly(SVGTag::kPolyline)); + } + + SVG_ATTR(Points, SVGPointsType, SVGPointsType()) + + protected: + bool parseAndSetAttribute(const char*, const char*) override; + +#ifndef RENDER_SVG + void onDraw(Canvas*, const SVGLengthContext&, const Paint&, PathFillType) const override; + + Path onAsPath(const SVGRenderContext&) const override; + + Rect onObjectBoundingBox(const SVGRenderContext&) const override; +#endif + + private: + SkSVGPoly(SVGTag); + + mutable Path fPath; // mutated in onDraw(), to apply inherited fill types. + + using INHERITED = SVGShape; +}; +} // namespace tgfx diff --git a/include/tgfx/svg/node/SVGRadialGradient.h b/include/tgfx/svg/node/SVGRadialGradient.h new file mode 100644 index 00000000..56a19c90 --- /dev/null +++ b/include/tgfx/svg/node/SVGRadialGradient.h @@ -0,0 +1,56 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/core/Matrix.h" +#include "tgfx/core/TileMode.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGGradient.h" +#include "tgfx/svg/node/SVGNode.h" + +namespace tgfx { + +class SkSVGRadialGradient final : public SkSVGGradient { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGRadialGradient()); + } + + SVG_ATTR(Cx, SVGLength, SVGLength(50, SVGLength::Unit::kPercentage)) + SVG_ATTR(Cy, SVGLength, SVGLength(50, SVGLength::Unit::kPercentage)) + SVG_ATTR(R, SVGLength, SVGLength(50, SVGLength::Unit::kPercentage)) + SVG_OPTIONAL_ATTR(Fx, SVGLength) + SVG_OPTIONAL_ATTR(Fy, SVGLength) + + protected: + bool parseAndSetAttribute(const char*, const char*) override; + +#ifndef RENDER_SVG + std::shared_ptr onMakeShader(const SVGRenderContext&, const std::vector&, + const std::vector&, TileMode, + const Matrix&) const override; +#endif + + private: + SkSVGRadialGradient(); + + using INHERITED = SkSVGGradient; +}; +} // namespace tgfx diff --git a/include/tgfx/svg/node/SVGRect.h b/include/tgfx/svg/node/SVGRect.h new file mode 100644 index 00000000..18a1ab90 --- /dev/null +++ b/include/tgfx/svg/node/SVGRect.h @@ -0,0 +1,68 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/core/Canvas.h" +#include "tgfx/core/Paint.h" +#include "tgfx/core/Path.h" +#include "tgfx/core/RRect.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGNode.h" +#include "tgfx/svg/node/SVGShape.h" + +namespace tgfx { + +class SkSVGRect final : public SVGShape { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGRect()); + } + + SVG_ATTR(X, SVGLength, SVGLength(0)) + SVG_ATTR(Y, SVGLength, SVGLength(0)) + SVG_ATTR(Width, SVGLength, SVGLength(0)) + SVG_ATTR(Height, SVGLength, SVGLength(0)) + + SVG_OPTIONAL_ATTR(Rx, SVGLength) + SVG_OPTIONAL_ATTR(Ry, SVGLength) + + protected: + bool parseAndSetAttribute(const char*, const char*) override; + +#ifndef RENDER_SVG + + // void onRender(const SVGRenderContext&) const override; + + void onDraw(Canvas*, const SVGLengthContext&, const Paint&, PathFillType) const override; + + Path onAsPath(const SVGRenderContext&) const override; + + Rect onObjectBoundingBox(const SVGRenderContext&) const override; +#endif + + private: + SkSVGRect(); + + RRect resolve(const SVGLengthContext&) const; + + using INHERITED = SVGShape; +}; +} // namespace tgfx diff --git a/include/tgfx/svg/node/SVGSVG.h b/include/tgfx/svg/node/SVGSVG.h new file mode 100644 index 00000000..8a529194 --- /dev/null +++ b/include/tgfx/svg/node/SVGSVG.h @@ -0,0 +1,71 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/core/Size.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/SVGValue.h" +#include "tgfx/svg/node/SVGContainer.h" +#include "tgfx/svg/node/SVGNode.h" + +namespace tgfx { + +class SVGSVG : public SkSVGContainer { + public: + enum class Type { + kRoot, + kInner, + }; + static std::shared_ptr Make(Type t = Type::kInner) { + return std::shared_ptr(new SVGSVG(t)); + } + + SVG_ATTR(X, SVGLength, SVGLength(0)) + SVG_ATTR(Y, SVGLength, SVGLength(0)) + SVG_ATTR(Width, SVGLength, SVGLength(100, SVGLength::Unit::kPercentage)) + SVG_ATTR(Height, SVGLength, SVGLength(100, SVGLength::Unit::kPercentage)) + SVG_ATTR(PreserveAspectRatio, SVGPreserveAspectRatio, SVGPreserveAspectRatio()) + + SVG_OPTIONAL_ATTR(ViewBox, SVGViewBoxType) + +#ifndef RENDER_SVG + Size intrinsicSize(const SVGLengthContext&) const; + + void renderNode(const SVGRenderContext&, const SVGIRI& iri) const; +#endif + + protected: +#ifndef RENDER_SVG + bool onPrepareToRender(SVGRenderContext*) const override; +#endif + + void onSetAttribute(SVGAttribute, const SVGValue&) override; + + private: + explicit SVGSVG(Type t) : INHERITED(SVGTag::kSvg), fType(t) { + } + + // Some attributes behave differently for the outermost svg element. + const Type fType; + + using INHERITED = SkSVGContainer; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGShape.h b/include/tgfx/svg/node/SVGShape.h new file mode 100644 index 00000000..a8b83ae5 --- /dev/null +++ b/include/tgfx/svg/node/SVGShape.h @@ -0,0 +1,50 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/core/Canvas.h" +#include "tgfx/core/Paint.h" +#include "tgfx/svg/node/SVGTransformableNode.h" + +namespace tgfx { + +class SVGLengthContext; +class SVGNode; +class SVGRenderContext; +enum class SVGTag; + +class SVGShape : public SkSVGTransformableNode { + public: + void appendChild(std::shared_ptr) override; + + protected: + explicit SVGShape(SVGTag); + +#ifndef RENDER_SVG + void onRender(const SVGRenderContext&) const override; + + virtual void onDraw(Canvas*, const SVGLengthContext&, const Paint&, PathFillType) const = 0; +#endif + + private: + using INHERITED = SkSVGTransformableNode; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGStop.h b/include/tgfx/svg/node/SVGStop.h new file mode 100644 index 00000000..64e493a4 --- /dev/null +++ b/include/tgfx/svg/node/SVGStop.h @@ -0,0 +1,47 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGHiddenContainer.h" +#include "tgfx/svg/node/SVGNode.h" + +namespace tgfx { + +class SkSVGStop : public SVGHiddenContainer { + public: + static constexpr SVGTag tag = SVGTag::kStop; + + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGStop()); + } + + SVG_ATTR(Offset, SVGLength, SVGLength(0, SVGLength::Unit::kPercentage)) + + protected: + bool parseAndSetAttribute(const char*, const char*) override; + + private: + SkSVGStop(); + + using INHERITED = SVGHiddenContainer; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGText.h b/include/tgfx/svg/node/SVGText.h new file mode 100644 index 00000000..3db33e20 --- /dev/null +++ b/include/tgfx/svg/node/SVGText.h @@ -0,0 +1,152 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include "tgfx/core/Path.h" +#include "tgfx/core/Rect.h" +#include "tgfx/core/TextBlob.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGNode.h" +#include "tgfx/svg/node/SVGTransformableNode.h" + +namespace tgfx { + +class SVGRenderContext; + +using ShapedTextCallback = + std::function&)>; + +// Base class for text-rendering nodes. +class SkSVGTextFragment : public SkSVGTransformableNode { + public: + void renderText(const SVGRenderContext&, const ShapedTextCallback&) const; + + protected: + explicit SkSVGTextFragment(SVGTag t) : INHERITED(t) { + } + + virtual void onShapeText(const SVGRenderContext&, const ShapedTextCallback&) const = 0; + + // Text nodes other than the root element are not rendered directly. + void onRender(const SVGRenderContext&) const override { + } + + private: + Path onAsPath(const SVGRenderContext&) const override; + + using INHERITED = SkSVGTransformableNode; +}; + +// Base class for nestable text containers (, , etc). +class SkSVGTextContainer : public SkSVGTextFragment { + public: + SVG_ATTR(X, std::vector, {}) + SVG_ATTR(Y, std::vector, {}) + SVG_ATTR(Dx, std::vector, {}) + SVG_ATTR(Dy, std::vector, {}) + SVG_ATTR(Rotate, std::vector, {}) + + void appendChild(std::shared_ptr) final; + + protected: + explicit SkSVGTextContainer(SVGTag t) : INHERITED(t) { + } + + void onShapeText(const SVGRenderContext&, const ShapedTextCallback&) const override; + + bool parseAndSetAttribute(const char*, const char*) override; + + private: + std::vector> fChildren; + + using INHERITED = SkSVGTextFragment; +}; + +class SkSVGText final : public SkSVGTextContainer { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGText()); + } + + private: + SkSVGText() : INHERITED(SVGTag::kText) { + } + + void onRender(const SVGRenderContext&) const override; + Rect onObjectBoundingBox(const SVGRenderContext&) const override; + Path onAsPath(const SVGRenderContext&) const override; + + using INHERITED = SkSVGTextContainer; +}; + +class SkSVGTSpan final : public SkSVGTextContainer { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGTSpan()); + } + + private: + SkSVGTSpan() : INHERITED(SVGTag::kTSpan) { + } + + using INHERITED = SkSVGTextContainer; +}; + +class SkSVGTextLiteral final : public SkSVGTextFragment { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGTextLiteral()); + } + + SVG_ATTR(Text, SVGStringType, SVGStringType()) + + private: + SkSVGTextLiteral() : INHERITED(SVGTag::kTextLiteral) { + } + + void onShapeText(const SVGRenderContext&, const ShapedTextCallback&) const override; + + void appendChild(std::shared_ptr) override { + } + + using INHERITED = SkSVGTextFragment; +}; + +class SkSVGTextPath final : public SkSVGTextContainer { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGTextPath()); + } + + SVG_ATTR(Href, SVGIRI, {}) + SVG_ATTR(StartOffset, SVGLength, SVGLength(0)) + + private: + SkSVGTextPath() : INHERITED(SVGTag::kTextPath) { + } + + void onShapeText(const SVGRenderContext&, const ShapedTextCallback&) const override; + + bool parseAndSetAttribute(const char*, const char*) override; + + using INHERITED = SkSVGTextContainer; +}; +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGTransformableNode.h b/include/tgfx/svg/node/SVGTransformableNode.h new file mode 100644 index 00000000..50ae7f30 --- /dev/null +++ b/include/tgfx/svg/node/SVGTransformableNode.h @@ -0,0 +1,58 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "tgfx/core/Path.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/SVGValue.h" +#include "tgfx/svg/node/SVGNode.h" + +namespace tgfx { + +class SVGRenderContext; +enum class SVGAttribute; + +class SkSVGTransformableNode : public SVGNode { + public: + void setTransform(const SVGTransformType& t) { + fTransform = t; + } + + protected: + SkSVGTransformableNode(SVGTag); + +#ifndef RENDER_SVG + bool onPrepareToRender(SVGRenderContext*) const override; +#endif + void onSetAttribute(SVGAttribute, const SVGValue&) override; + +#ifndef RENDER_SVG + void mapToParent(Path*) const; + + void mapToParent(Rect*) const; +#endif + + private: + // FIXME: should be sparse + SVGTransformType fTransform; + + using INHERITED = SVGNode; +}; +} // namespace tgfx diff --git a/include/tgfx/svg/node/SVGUse.h b/include/tgfx/svg/node/SVGUse.h new file mode 100644 index 00000000..8ccf11c2 --- /dev/null +++ b/include/tgfx/svg/node/SVGUse.h @@ -0,0 +1,63 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/core/Path.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGNode.h" +#include "tgfx/svg/node/SVGTransformableNode.h" + +namespace tgfx { + +class SVGRenderContext; + +/** + * Implements support for (reference) elements. + * (https://www.w3.org/TR/SVG11/struct.html#UseElement) + */ +class SkSVGUse final : public SkSVGTransformableNode { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SkSVGUse()); + } + + void appendChild(std::shared_ptr) override{}; + + SVG_ATTR(X, SVGLength, SVGLength(0)) + SVG_ATTR(Y, SVGLength, SVGLength(0)) + SVG_ATTR(Href, SVGIRI, SVGIRI()) + + protected: +#ifndef RENDER_SVG + bool onPrepareToRender(SVGRenderContext*) const override; + void onRender(const SVGRenderContext&) const override; + Path onAsPath(const SVGRenderContext&) const override; + Rect onObjectBoundingBox(const SVGRenderContext&) const override; +#endif + + private: + SkSVGUse(); + + bool parseAndSetAttribute(const char*, const char*) override; + + using INHERITED = SkSVGTransformableNode; +}; +} // namespace tgfx \ No newline at end of file diff --git a/qt/src/TGFXView.cpp b/qt/src/TGFXView.cpp index 15ce809f..58a946e0 100644 --- a/qt/src/TGFXView.cpp +++ b/qt/src/TGFXView.cpp @@ -91,9 +91,11 @@ void TGFXView::createAppHost() { std::string DefaultFont = "Microsoft YaHei"; std::string DefualtEmojiFont = "Segoe UI Emoji"; #endif - auto typeface = tgfx::Typeface::MakeFromName(DefaultFont, ""); + auto typeface = tgfx::Typeface::MakeFromPath( + "/Users/yg/code/c++_space/tgfx/resources/font/NotoSansSC-Regular.otf"); appHost->addTypeface("default", typeface); - typeface = tgfx::Typeface::MakeFromName(DefualtEmojiFont, ""); + typeface = tgfx::Typeface::MakeFromPath( + "/Users/yg/code/c++_space/tgfx/resources/font/NotoColorEmoji.ttf"); appHost->addTypeface("emoji", typeface); } diff --git a/qt/src/main.cpp b/qt/src/main.cpp index 2d2dc7de..005faf62 100644 --- a/qt/src/main.cpp +++ b/qt/src/main.cpp @@ -23,11 +23,13 @@ #include #include #include +#include #include "TGFXView.h" #include "qobject.h" +#include "tgfx/core/Data.h" +#include "tgfx/svg/SVGDOM.h" int main(int argc, char* argv[]) { - QApplication::setApplicationName("Hello2D"); QApplication::setOrganizationName("org.tgfx"); QSurfaceFormat defaultFormat = QSurfaceFormat(); diff --git a/src/gpu/RenderContext.cpp b/src/gpu/RenderContext.cpp index b4e828be..114a9108 100644 --- a/src/gpu/RenderContext.cpp +++ b/src/gpu/RenderContext.cpp @@ -215,7 +215,7 @@ void RenderContext::drawGlyphRunList(std::shared_ptr glyphRunList, auto aaType = getAAType(style); auto rasterizer = Rasterizer::MakeFrom(width, height, std::move(glyphRunList), aaType == AAType::Coverage, rasterizeMatrix, stroke); - auto proxyProvider = getContext()->proxyProvider(); + auto* proxyProvider = getContext()->proxyProvider(); auto textureProxy = proxyProvider->createTextureProxy({}, rasterizer, false, renderFlags); auto processor = TextureEffect::Make(std::move(textureProxy), {}, &rasterizeMatrix, true); if (processor == nullptr) { diff --git a/src/svg/SVGAttribute.cpp b/src/svg/SVGAttribute.cpp new file mode 100644 index 00000000..f9daced9 --- /dev/null +++ b/src/svg/SVGAttribute.cpp @@ -0,0 +1,63 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/SVGAttribute.h" +#include "tgfx/core/Color.h" + +namespace tgfx { +SkSVGPresentationAttributes SkSVGPresentationAttributes::MakeInitial() { + SkSVGPresentationAttributes result; + + result.fFill.set(SVGPaint(SVGColor(Color::Black()))); + result.fFillOpacity.set(static_cast(1)); + result.fFillRule.set(SVGFillRule(SVGFillRule::Type::kNonZero)); + result.fClipRule.set(SVGFillRule(SVGFillRule::Type::kNonZero)); + + result.fStroke.set(SVGPaint(SVGPaint::Type::kNone)); + result.fStrokeDashArray.set(SVGDashArray(SVGDashArray::Type::kNone)); + result.fStrokeDashOffset.set(SVGLength(0)); + result.fStrokeLineCap.set(SVGLineCap::kButt); + result.fStrokeLineJoin.set(SVGLineJoin(SVGLineJoin::Type::kMiter)); + result.fStrokeMiterLimit.set(static_cast(4)); + result.fStrokeOpacity.set(static_cast(1)); + result.fStrokeWidth.set(SVGLength(1)); + + result.fVisibility.set(SVGVisibility(SVGVisibility::Type::kVisible)); + + result.fColor.set(SVGColorType(Color::Black())); + result.fColorInterpolation.set(SVGColorspace::kSRGB); + result.fColorInterpolationFilters.set(SVGColorspace::kLinearRGB); + + result.fFontFamily.init("default"); + result.fFontStyle.init(SVGFontStyle::Type::kNormal); + result.fFontSize.init(SVGLength(24)); + result.fFontWeight.init(SVGFontWeight::Type::kNormal); + result.fTextAnchor.init(SVGTextAnchor::Type::kStart); + + result.fDisplay.init(SVGDisplay::kInline); + + result.fStopColor.set(SVGColor(Color::Black())); + result.fStopOpacity.set(static_cast(1)); + result.fFloodColor.set(SVGColor(Color::Black())); + result.fFloodOpacity.set(static_cast(1)); + result.fLightingColor.set(SVGColor(Color::White())); + + return result; +} + +} // namespace tgfx diff --git a/src/svg/SVGAttributeParser.cpp b/src/svg/SVGAttributeParser.cpp new file mode 100644 index 00000000..d8f67038 --- /dev/null +++ b/src/svg/SVGAttributeParser.cpp @@ -0,0 +1,1200 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/SVGAttributeParser.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "core/utils/Log.h" +#include "core/utils/MathExtra.h" +#include "tgfx/core/Color.h" +#include "tgfx/core/Matrix.h" +#include "tgfx/core/Point.h" +#include "tgfx/core/Rect.h" +#include "tgfx/core/Typeface.h" +#include "tgfx/core/UTF.h" +#include "tgfx/svg/SVGParse.h" +#include "tgfx/svg/SVGTypes.h" + +namespace { + +// TODO: these should be shared with SkParse.cpp + +inline bool is_between(char c, char min, char max) { + ASSERT(min <= max); + return (unsigned)(c - min) <= (unsigned)(max - min); +} + +inline bool is_ws(char c) { + return is_between(c, 1, 32); +} + +inline bool is_sep(char c) { + return is_ws(c) || c == ',' || c == ';'; +} + +inline bool is_nl(char c) { + return c == '\n' || c == '\r' || c == '\f'; +} + +inline bool is_hex(char c) { + return is_between(c, 'a', 'f') || is_between(c, 'A', 'F') || is_between(c, '0', '9'); +} + +} // namespace + +namespace tgfx { + +SVGAttributeParser::SVGAttributeParser(const char attributeString[]) + // TODO: need actual UTF-8 with length. + : fCurPos(attributeString), fEndPos(fCurPos + strlen(attributeString)) { +} + +template +inline bool SVGAttributeParser::advanceWhile(F f) { + auto initial = fCurPos; + while (fCurPos < fEndPos && f(*fCurPos)) { + fCurPos++; + } + return fCurPos != initial; +} + +bool SVGAttributeParser::matchStringToken(const char* token, const char** newPos) const { + const char* c = fCurPos; + + while (c < fEndPos && *token && *c == *token) { + c++; + token++; + } + + if (*token) { + return false; + } + + if (newPos) { + *newPos = c; + } + + return true; +} + +bool SVGAttributeParser::parseEOSToken() { + return fCurPos == fEndPos; +} + +bool SVGAttributeParser::parseSepToken() { + return this->advanceWhile(is_sep); +} + +bool SVGAttributeParser::parseWSToken() { + return this->advanceWhile(is_ws); +} + +bool SVGAttributeParser::parseCommaWspToken() { + // comma-wsp: + // (wsp+ comma? wsp*) | (comma wsp*) + return this->parseWSToken() || this->parseExpectedStringToken(","); +} + +bool SVGAttributeParser::parseExpectedStringToken(const char* expected) { + const char* newPos; + if (!matchStringToken(expected, &newPos)) { + return false; + } + + fCurPos = newPos; + return true; +} + +bool SVGAttributeParser::parseScalarToken(float* res) { + if (const char* next = SVGParse::FindScalar(fCurPos, res)) { + fCurPos = next; + return true; + } + return false; +} + +bool SVGAttributeParser::parseInt32Token(int32_t* res) { + if (const char* next = SVGParse::FindS32(fCurPos, res)) { + fCurPos = next; + return true; + } + return false; +} + +bool SVGAttributeParser::matchHexToken(const char** newPos) const { + *newPos = fCurPos; + while (*newPos < fEndPos && is_hex(**newPos)) { + ++*newPos; + } + return *newPos != fCurPos; +} + +bool SVGAttributeParser::parseEscape(Unichar* c) { + // \(hexDigit{1,6}whitespace?|[^newline|hexDigit]) + RestoreCurPos restoreCurPos(this); + + if (!this->parseExpectedStringToken("\\")) { + return false; + } + const char* hexEnd; + if (this->matchHexToken(&hexEnd)) { + if (hexEnd - fCurPos > 6) { + hexEnd = fCurPos + 6; + } + char hexString[7]; + auto hexSize = static_cast(hexEnd - fCurPos); + memcpy(hexString, fCurPos, hexSize); + hexString[hexSize] = '\0'; + uint32_t cp; + const char* hexFound = SVGParse::FindHex(hexString, &cp); + if (!hexFound || cp < 1 || (0xD800 <= cp && cp <= 0xDFFF) || 0x10FFFF < cp) { + cp = 0xFFFD; + } + *c = static_cast(cp); + fCurPos = hexEnd; + this->parseWSToken(); + } else if (this->parseEOSToken() || is_nl(*fCurPos)) { + *c = 0xFFFD; + return false; + } else { + if (*c = UTF::NextUTF8(&fCurPos, fEndPos); *c < 0) { + return false; + } + } + + restoreCurPos.clear(); + return true; +} + +bool SVGAttributeParser::parseIdentToken(std::string* ident) { + // + // (--|-?([a-z|A-Z|_|non-ASCII]|escape))([a-z|A-Z|0-9|_|-|non-ASCII]|escape)? + RestoreCurPos restoreCurPos(this); + + Unichar c; + if (this->parseExpectedStringToken("--")) { + ident->append("--"); + } else { + if (this->parseExpectedStringToken("-")) { + ident->append("-"); + } + if (this->parseEscape(&c)) { + // ident->appendUnichar(c); + } else { + if (c = UTF::NextUTF8(&fCurPos, fEndPos); c < 0) { + return false; + } + if ((c < 'a' || 'z' < c) && (c < 'A' || 'Z' < c) && (c != '_') && + (c < 0x80 || 0x10FFFF < c)) { + return false; + } + ident->append(UTF::ToUTF8(c)); + } + } + while (fCurPos < fEndPos) { + if (this->parseEscape(&c)) { + ident->append(UTF::ToUTF8(c)); + continue; + } + const char* next = fCurPos; + if (c = UTF::NextUTF8(&next, fEndPos); c < 0) { + break; + } + if ((c < 'a' || 'z' < c) && (c < 'A' || 'Z' < c) && (c < '0' || '9' < c) && (c != '_') && + (c != '-') && (c < 0x80 || 0x10FFFF < c)) { + break; + } + ident->append(UTF::ToUTF8(c)); + fCurPos = next; + } + + restoreCurPos.clear(); + return true; +} + +bool SVGAttributeParser::parseLengthUnitToken(SVGLength::Unit& unit) { + struct Unit { + Unit(std::string name, SVGLength::Unit u) : unitName(std::move(name)), unit(u) {}; + std::string unitName; + SVGLength::Unit unit; + }; + + static const std::array unitInfo = { + Unit("%", SVGLength::Unit::kPercentage), Unit("em", SVGLength::Unit::kEMS), + Unit("ex", SVGLength::Unit::kEXS), Unit("px", SVGLength::Unit::kPX), + Unit("cm", SVGLength::Unit::kCM), Unit("mm", SVGLength::Unit::kMM), + Unit("in", SVGLength::Unit::kIN), Unit("pt", SVGLength::Unit::kPT), + Unit("pc", SVGLength::Unit::kPC), + }; + + return std::any_of(unitInfo.begin(), unitInfo.end(), [&](const Unit& item) { + if (this->parseExpectedStringToken(item.unitName.data())) { + unit = item.unit; + return true; + } + return false; + }); +} + +// https://www.w3.org/TR/SVG11/types.html#DataTypeColor +bool SVGAttributeParser::parseNamedColorToken(Color* c) { + RestoreCurPos restoreCurPos(this); + + std::string ident; + if (!this->parseIdentToken(&ident)) { + return false; + } + if (!SVGParse::FindNamedColor(ident.c_str(), c)) { + return false; + } + + restoreCurPos.clear(); + return true; +} + +bool SVGAttributeParser::parseHexColorToken(Color* c) { + RestoreCurPos restoreCurPos(this); + + const char* hexEnd; + if (!this->parseExpectedStringToken("#") || !this->matchHexToken(&hexEnd)) { + return false; + } + + uint32_t v; + std::string hexString(fCurPos, static_cast(hexEnd - fCurPos)); + SVGParse::FindHex(hexString.c_str(), &v); + + switch (hexString.size()) { + case 6: + // matched #xxxxxxx + break; + case 3: + // matched '#xxx; + v = ((v << 12) & 0x00f00000) | ((v << 8) & 0x000ff000) | ((v << 4) & 0x00000ff0) | + ((v << 0) & 0x0000000f); + break; + default: + return false; + } + + *c = SVGParse::Uint32ToColor(v | 0xff000000); + fCurPos = hexEnd; + + restoreCurPos.clear(); + return true; +} + +bool SVGAttributeParser::parseColorComponentIntegralToken(int32_t* c) { + const char* p = SVGParse::FindS32(fCurPos, c); + if (!p || *p == '.') { + // No value parsed, or fractional value. + return false; + } + + if (*p == '%') { + *c = static_cast(std::round(static_cast(*c) * 255.0f / 100)); + *c = std::clamp(*c, 0, 255); + p++; + } + + fCurPos = p; + return true; +} + +bool SVGAttributeParser::parseColorComponentFractionalToken(int32_t* c) { + float s; + const char* p = SVGParse::FindScalar(fCurPos, &s); + if (!p || *p != '%') { + // Floating point must be a percentage (CSS2 rgb-percent syntax). + return false; + } + p++; // Skip '%' + + *c = static_cast(std::round(s * 255.0f / 100)); + *c = std::clamp(*c, 0, 255); + fCurPos = p; + return true; +} + +bool SVGAttributeParser::parseColorComponentScalarToken(int32_t* c) { + float s; + if (const char* p = SVGParse::FindScalar(fCurPos, &s)) { + *c = static_cast(std::round(s * 255.0f)); + *c = std::clamp(*c, 0, 255); + fCurPos = p; + return true; + } + return false; +} + +bool SVGAttributeParser::parseColorComponentToken(int32_t* c) { + return parseColorComponentIntegralToken(c) || parseColorComponentFractionalToken(c); +} + +bool SVGAttributeParser::parseRGBColorToken(Color* c) { + return this->parseParenthesized( + "rgb", + [this](Color* c) -> bool { + int32_t r, g, b; + if (this->parseColorComponentToken(&r) && this->parseSepToken() && + this->parseColorComponentToken(&g) && this->parseSepToken() && + this->parseColorComponentToken(&b)) { + + *c = Color::FromRGBA(static_cast(r), static_cast(g), + static_cast(b)); + return true; + } + return false; + }, + c); +} + +bool SVGAttributeParser::parseRGBAColorToken(Color* c) { + return this->parseParenthesized( + "rgba", + [this](Color* c) -> bool { + int32_t r, g, b, a; + if (this->parseColorComponentToken(&r) && this->parseSepToken() && + this->parseColorComponentToken(&g) && this->parseSepToken() && + this->parseColorComponentToken(&b) && this->parseSepToken() && + this->parseColorComponentScalarToken(&a)) { + + *c = Color::FromRGBA(static_cast(r), static_cast(g), + static_cast(b), static_cast(a)); + return true; + } + return false; + }, + c); +} + +bool SVGAttributeParser::parseColorToken(Color* c) { + return this->parseHexColorToken(c) || this->parseNamedColorToken(c) || + this->parseRGBAColorToken(c) || this->parseRGBColorToken(c); +} + +bool SVGAttributeParser::parseSVGColorType(SVGColorType* color) { + Color c; + if (!this->parseColorToken(&c)) { + return false; + } + *color = SVGColorType(c); + return true; +} + +// https://www.w3.org/TR/SVG11/types.html#DataTypeColor +// And https://www.w3.org/TR/CSS2/syndata.html#color-units for the alternative +// forms supported by SVG (e.g. RGB percentages). +template <> +bool SVGAttributeParser::parse(SVGColorType* color) { + this->parseWSToken(); + if (!this->parseSVGColorType(color)) { + return false; + } + this->parseWSToken(); + return this->parseEOSToken(); +} + +bool SVGAttributeParser::parseSVGColor(SVGColor* color, SVGColor::Vars&& vars) { + static const constexpr int kVarsLimit = 32; + + if (SVGColorType c; this->parseSVGColorType(&c)) { + *color = SVGColor(c, std::move(vars)); + return true; + } + if (this->parseExpectedStringToken("currentColor")) { + *color = SVGColor(SVGColor::Type::kCurrentColor, std::move(vars)); + return true; + } + // https://drafts.csswg.org/css-variables/#using-variables + if (this->parseParenthesized( + "var", + [this, &vars](SVGColor* colorResult) -> bool { + std::string ident; + if (!this->parseIdentToken(&ident) || ident.size() < 2 || + ident.compare(0, 2, "--") != 0) { + return false; + } + ident.erase(0, 2); + vars.push_back(std::move(ident)); + this->parseWSToken(); + if (!this->parseExpectedStringToken(",")) { + *colorResult = SVGColor(Color::Black(), std::move(vars)); + return true; + } + this->parseWSToken(); + if (this->matchStringToken(")")) { + *colorResult = SVGColor(Color::Black(), std::move(vars)); + return true; + } + return vars.size() < kVarsLimit && this->parseSVGColor(colorResult, std::move(vars)); + }, + color)) { + return true; + } + return false; +} + +// https://www.w3.org/TR/SVG11/types.html#InterfaceSVGColor +template <> +bool SVGAttributeParser::parse(SVGColor* color) { + this->parseWSToken(); + if (!this->parseSVGColor(color, SVGColor::Vars())) { + return false; + } + this->parseWSToken(); + return this->parseEOSToken(); +} + +// https://www.w3.org/TR/SVG11/linking.html#IRIReference +template <> +bool SVGAttributeParser::parse(SVGIRI* iri) { + // consume preceding whitespace + this->parseWSToken(); + + SVGIRI::Type iriType; + if (this->parseExpectedStringToken("#")) { + iriType = SVGIRI::Type::kLocal; + } else if (this->matchStringToken("data:")) { + iriType = SVGIRI::Type::kDataURI; + } else { + iriType = SVGIRI::Type::kNonlocal; + } + + const auto* start = fCurPos; + if (!this->advanceWhile([](char c) -> bool { return c != ')'; })) { + return false; + } + *iri = SVGIRI(iriType, std::string(start, static_cast(fCurPos - start))); + return true; +} + +// https://www.w3.org/TR/SVG11/types.html#DataTypeFuncIRI +bool SVGAttributeParser::parseFuncIRI(SVGFuncIRI* iri) { + return this->parseParenthesized( + "url", + [this](SVGFuncIRI* iriResult) -> bool { + SVGIRI iri; + if (this->parse(&iri)) { + *iriResult = SVGFuncIRI(std::move(iri)); + return true; + } + return false; + }, + iri); +} + +template <> +bool SVGAttributeParser::parse(SVGStringType* result) { + if (this->parseEOSToken()) { + return false; + } + *result = SVGStringType(fCurPos); + fCurPos += result->size(); + return this->parseEOSToken(); +} + +// https://www.w3.org/TR/SVG11/types.html#DataTypeNumber +template <> +bool SVGAttributeParser::parse(SVGNumberType* number) { + // consume WS + this->parseWSToken(); + + float s; + if (this->parseScalarToken(&s)) { + *number = SVGNumberType(s); + // consume trailing separators + this->parseSepToken(); + return true; + } + + return false; +} + +// https://www.w3.org/TR/SVG11/types.html#DataTypeInteger +bool SVGAttributeParser::parseInteger(SVGIntegerType* number) { + // consume WS + this->parseWSToken(); + + // consume optional '+' + this->parseExpectedStringToken("+"); + + SVGIntegerType i; + if (this->parseInt32Token(&i)) { + *number = i; + // consume trailing separators + this->parseSepToken(); + return true; + } + + return false; +} + +// https://www.w3.org/TR/SVG11/types.html#DataTypeLength +template <> +bool SVGAttributeParser::parse(SVGLength* length) { + float s; + SVGLength::Unit u = SVGLength::Unit::kNumber; + + if (this->parseScalarToken(&s) && + (this->parseLengthUnitToken(u) || this->parseSepToken() || this->parseEOSToken())) { + *length = SVGLength(s, u); + // consume trailing separators + this->parseSepToken(); + return true; + } + + return false; +} + +// https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute +bool SVGAttributeParser::parseViewBox(SVGViewBoxType* vb) { + float x, y, w, h; + this->parseWSToken(); + + bool parsedValue = false; + if (this->parseScalarToken(&x) && this->parseSepToken() && this->parseScalarToken(&y) && + this->parseSepToken() && this->parseScalarToken(&w) && this->parseSepToken() && + this->parseScalarToken(&h)) { + + *vb = SVGViewBoxType(Rect::MakeXYWH(x, y, w, h)); + parsedValue = true; + // consume trailing whitespace + this->parseWSToken(); + } + return parsedValue && this->parseEOSToken(); +} + +template +bool SVGAttributeParser::parseParenthesized(const char* prefix, Func f, T* result) { + RestoreCurPos restoreCurPos(this); + + this->parseWSToken(); + if (prefix && !this->parseExpectedStringToken(prefix)) { + return false; + } + this->parseWSToken(); + if (!this->parseExpectedStringToken("(")) { + return false; + } + this->parseWSToken(); + + if (!f(result)) { + return false; + } + + this->parseWSToken(); + if (!this->parseExpectedStringToken(")")) { + return false; + } + + restoreCurPos.clear(); + return true; +} + +bool SVGAttributeParser::parseMatrixToken(Matrix* matrix) { + return this->parseParenthesized( + "matrix", + [this](Matrix* m) -> bool { + float scalars[6]; + for (int i = 0; i < 6; ++i) { + if (!(this->parseScalarToken(scalars + i) && (i > 4 || this->parseSepToken()))) { + return false; + } + } + + m->setAll(scalars[0], scalars[2], scalars[4], scalars[1], scalars[3], scalars[5]); + return true; + }, + matrix); +} + +bool SVGAttributeParser::parseTranslateToken(Matrix* matrix) { + return this->parseParenthesized( + "translate", + [this](Matrix* m) -> bool { + float tx = 0.0, ty = 0.0; + this->parseWSToken(); + if (!this->parseScalarToken(&tx)) { + return false; + } + + if (!this->parseSepToken() || !this->parseScalarToken(&ty)) { + ty = 0.0; + } + + m->setTranslate(tx, ty); + return true; + }, + matrix); +} + +bool SVGAttributeParser::parseScaleToken(Matrix* matrix) { + return this->parseParenthesized( + "scale", + [this](Matrix* m) -> bool { + float sx = 0.0, sy = 0.0; + if (!this->parseScalarToken(&sx)) { + return false; + } + + if (!(this->parseSepToken() && this->parseScalarToken(&sy))) { + sy = sx; + } + + m->setScale(sx, sy); + return true; + }, + matrix); +} + +bool SVGAttributeParser::parseRotateToken(Matrix* matrix) { + return this->parseParenthesized( + "rotate", + [this](Matrix* m) -> bool { + float angle; + if (!this->parseScalarToken(&angle)) { + return false; + } + + float cx = 0; + float cy = 0; + // optional [ ] + if (this->parseSepToken() && this->parseScalarToken(&cx)) { + if (!(this->parseSepToken() && this->parseScalarToken(&cy))) { + return false; + } + } + + m->setRotate(angle, cx, cy); + return true; + }, + matrix); +} + +bool SVGAttributeParser::parseSkewXToken(Matrix* matrix) { + return this->parseParenthesized( + "skewX", + [this](Matrix* m) -> bool { + float angle; + if (!this->parseScalarToken(&angle)) { + return false; + } + m->setSkewX(tanf(DegreesToRadians(angle))); + return true; + }, + matrix); +} + +bool SVGAttributeParser::parseSkewYToken(Matrix* matrix) { + return this->parseParenthesized( + "skewY", + [this](Matrix* m) -> bool { + float angle; + if (!this->parseScalarToken(&angle)) { + return false; + } + m->setSkewY(tanf(DegreesToRadians(angle))); + return true; + }, + matrix); +} + +// https://www.w3.org/TR/SVG11/coords.html#TransformAttribute +template <> +bool SVGAttributeParser::parse(SVGTransformType* t) { + Matrix matrix = Matrix::I(); + + bool parsed = false; + while (true) { + Matrix m; + + if (!(this->parseMatrixToken(&m) || this->parseTranslateToken(&m) || + this->parseScaleToken(&m) || this->parseRotateToken(&m) || this->parseSkewXToken(&m) || + this->parseSkewYToken(&m))) { + break; + } + + matrix.preConcat(m); + parsed = true; + + this->parseCommaWspToken(); + } + + this->parseWSToken(); + if (!parsed || !this->parseEOSToken()) { + return false; + } + + *t = SVGTransformType(matrix); + return true; +} + +// https://www.w3.org/TR/SVG11/painting.html#SpecifyingPaint +template <> +bool SVGAttributeParser::parse(SVGPaint* paint) { + SVGColor c; + SVGFuncIRI iri; + bool parsedValue = false; + + this->parseWSToken(); + if (this->parseSVGColor(&c, SVGColor::Vars())) { + *paint = SVGPaint(std::move(c)); + parsedValue = true; + } else if (this->parseExpectedStringToken("none")) { + *paint = SVGPaint(SVGPaint::Type::kNone); + parsedValue = true; + } else if (this->parseFuncIRI(&iri)) { + // optional fallback color + this->parseWSToken(); + this->parseSVGColor(&c, SVGColor::Vars()); + *paint = SVGPaint(iri.iri(), std::move(c)); + parsedValue = true; + } + this->parseWSToken(); + return parsedValue && this->parseEOSToken(); +} + +// https://www.w3.org/TR/SVG11/masking.html#ClipPathProperty +// https://www.w3.org/TR/SVG11/masking.html#MaskProperty +// https://www.w3.org/TR/SVG11/filters.html#FilterProperty +template <> +bool SVGAttributeParser::parse(SVGFuncIRI* firi) { + SVGStringType iri; + bool parsedValue = false; + + if (this->parseExpectedStringToken("none")) { + *firi = SVGFuncIRI(); + parsedValue = true; + } else if (this->parseFuncIRI(firi)) { + parsedValue = true; + } + + return parsedValue && this->parseEOSToken(); +} + +// https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty +template <> +bool SVGAttributeParser::parse(SVGLineCap* cap) { + static const struct { + SVGLineCap fType; + const char* fName; + } gCapInfo[] = { + {SVGLineCap::kButt, "butt"}, + {SVGLineCap::kRound, "round"}, + {SVGLineCap::kSquare, "square"}, + }; + + bool parsedValue = false; + for (size_t i = 0; i < std::size(gCapInfo); ++i) { + if (this->parseExpectedStringToken(gCapInfo[i].fName)) { + *cap = SVGLineCap(gCapInfo[i].fType); + parsedValue = true; + break; + } + } + + return parsedValue && this->parseEOSToken(); +} + +// https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty +template <> +bool SVGAttributeParser::parse(SVGLineJoin* join) { + static const struct { + SVGLineJoin::Type fType; + const char* fName; + } gJoinInfo[] = { + {SVGLineJoin::Type::kMiter, "miter"}, + {SVGLineJoin::Type::kRound, "round"}, + {SVGLineJoin::Type::kBevel, "bevel"}, + {SVGLineJoin::Type::kInherit, "inherit"}, + }; + + bool parsedValue = false; + for (size_t i = 0; i < std::size(gJoinInfo); ++i) { + if (this->parseExpectedStringToken(gJoinInfo[i].fName)) { + *join = SVGLineJoin(gJoinInfo[i].fType); + parsedValue = true; + break; + } + } + + return parsedValue && this->parseEOSToken(); +} + +// https://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBoxUnits +template <> +bool SVGAttributeParser::parse(SVGObjectBoundingBoxUnits* objectBoundingBoxUnits) { + bool parsedValue = false; + if (this->parseExpectedStringToken("userSpaceOnUse")) { + *objectBoundingBoxUnits = + SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse); + parsedValue = true; + } else if (this->parseExpectedStringToken("objectBoundingBox")) { + *objectBoundingBoxUnits = + SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox); + parsedValue = true; + } + return parsedValue && this->parseEOSToken(); +} + +// https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute +template <> +bool SVGAttributeParser::parse(SVGPointsType* points) { + SVGPointsType pts; + + // Skip initial wsp. + // list-of-points: + // wsp* coordinate-pairs? wsp* + this->advanceWhile(is_ws); + + bool parsedValue = false; + for (;;) { + // Adjacent coordinate-pairs separated by comma-wsp. + // coordinate-pairs: + // coordinate-pair + // | coordinate-pair comma-wsp coordinate-pairs + if (parsedValue && !this->parseCommaWspToken()) { + break; + } + + float x, y; + if (!this->parseScalarToken(&x)) { + break; + } + + // Coordinate values separated by comma-wsp or '-'. + // coordinate-pair: + // coordinate comma-wsp coordinate + // | coordinate negative-coordinate + if (!this->parseCommaWspToken() && !this->parseEOSToken() && *fCurPos != '-') { + break; + } + + if (!this->parseScalarToken(&y)) { + break; + } + + pts.push_back(Point::Make(x, y)); + parsedValue = true; + } + + if (parsedValue && this->parseEOSToken()) { + *points = std::move(pts); + return true; + } + + return false; +} + +// https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty +template <> +bool SVGAttributeParser::parse(SVGFillRule* fillRule) { + static const struct { + SVGFillRule::Type fType; + const char* fName; + } gFillRuleInfo[] = { + {SVGFillRule::Type::kNonZero, "nonzero"}, + {SVGFillRule::Type::kEvenOdd, "evenodd"}, + {SVGFillRule::Type::kInherit, "inherit"}, + }; + + bool parsedValue = false; + for (size_t i = 0; i < std::size(gFillRuleInfo); ++i) { + if (this->parseExpectedStringToken(gFillRuleInfo[i].fName)) { + *fillRule = SVGFillRule(gFillRuleInfo[i].fType); + parsedValue = true; + break; + } + } + + return parsedValue && this->parseEOSToken(); +} + +// https://www.w3.org/TR/SVG11/painting.html#VisibilityProperty +template <> +bool SVGAttributeParser::parse(SVGVisibility* visibility) { + static const struct { + SVGVisibility::Type fType; + const char* fName; + } gVisibilityInfo[] = { + {SVGVisibility::Type::kVisible, "visible"}, + {SVGVisibility::Type::kHidden, "hidden"}, + {SVGVisibility::Type::kCollapse, "collapse"}, + {SVGVisibility::Type::kInherit, "inherit"}, + }; + + bool parsedValue = false; + for (const auto& parseInfo : gVisibilityInfo) { + if (this->parseExpectedStringToken(parseInfo.fName)) { + *visibility = SVGVisibility(parseInfo.fType); + parsedValue = true; + break; + } + } + + return parsedValue && this->parseEOSToken(); +} + +// https://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty +template <> +bool SVGAttributeParser::parse(SVGDashArray* dashArray) { + bool parsedValue = false; + if (this->parseExpectedStringToken("none")) { + *dashArray = SVGDashArray(SVGDashArray::Type::kNone); + parsedValue = true; + } else if (this->parseExpectedStringToken("inherit")) { + *dashArray = SVGDashArray(SVGDashArray::Type::kInherit); + parsedValue = true; + } else { + std::vector dashes; + for (;;) { + SVGLength dash; + // parseLength() also consumes trailing separators. + if (!this->parse(&dash)) { + break; + } + + dashes.push_back(dash); + parsedValue = true; + } + + if (parsedValue) { + *dashArray = SVGDashArray(std::move(dashes)); + } + } + + return parsedValue && this->parseEOSToken(); +} + +// https://www.w3.org/TR/SVG11/text.html#FontFamilyProperty +template <> +bool SVGAttributeParser::parse(SVGFontFamily* family) { + bool parsedValue = false; + if (this->parseExpectedStringToken("inherit")) { + *family = SVGFontFamily(); + parsedValue = true; + } else { + // The spec allows specifying a comma-separated list for explicit fallback order. + // For now, we only use the first entry and rely on the font manager to handle fallback. + const auto* comma = strchr(fCurPos, ','); + auto family_name = + comma ? std::string(fCurPos, static_cast(comma - fCurPos)) : std::string(fCurPos); + *family = SVGFontFamily(family_name.c_str()); + fCurPos += strlen(fCurPos); + parsedValue = true; + } + + return parsedValue && this->parseEOSToken(); +} + +// https://www.w3.org/TR/SVG11/text.html#FontSizeProperty +template <> +bool SVGAttributeParser::parse(SVGFontSize* size) { + bool parsedValue = false; + if (this->parseExpectedStringToken("inherit")) { + *size = SVGFontSize(); + parsedValue = true; + } else { + SVGLength length; + if (this->parse(&length)) { + *size = SVGFontSize(length); + parsedValue = true; + } + } + + return parsedValue && this->parseEOSToken(); +} + +// https://www.w3.org/TR/SVG11/text.html#FontStyleProperty +template <> +bool SVGAttributeParser::parse(SVGFontStyle* style) { + static constexpr std::tuple gStyleMap[] = { + {"normal", SVGFontStyle::Type::kNormal}, + {"italic", SVGFontStyle::Type::kItalic}, + {"oblique", SVGFontStyle::Type::kOblique}, + {"inherit", SVGFontStyle::Type::kInherit}, + }; + + bool parsedValue = false; + SVGFontStyle::Type type; + + if (this->parseEnumMap(gStyleMap, &type)) { + *style = SVGFontStyle(type); + parsedValue = true; + } + + return parsedValue && this->parseEOSToken(); +} + +// https://www.w3.org/TR/SVG11/text.html#FontWeightProperty +template <> +bool SVGAttributeParser::parse(SVGFontWeight* weight) { + static constexpr std::tuple gWeightMap[] = { + {"normal", SVGFontWeight::Type::kNormal}, {"bold", SVGFontWeight::Type::kBold}, + {"bolder", SVGFontWeight::Type::kBolder}, {"lighter", SVGFontWeight::Type::kLighter}, + {"100", SVGFontWeight::Type::k100}, {"200", SVGFontWeight::Type::k200}, + {"300", SVGFontWeight::Type::k300}, {"400", SVGFontWeight::Type::k400}, + {"500", SVGFontWeight::Type::k500}, {"600", SVGFontWeight::Type::k600}, + {"700", SVGFontWeight::Type::k700}, {"800", SVGFontWeight::Type::k800}, + {"900", SVGFontWeight::Type::k900}, {"inherit", SVGFontWeight::Type::kInherit}, + }; + + bool parsedValue = false; + SVGFontWeight::Type type; + + if (this->parseEnumMap(gWeightMap, &type)) { + *weight = SVGFontWeight(type); + parsedValue = true; + } + + return parsedValue && this->parseEOSToken(); +} + +// https://www.w3.org/TR/SVG11/text.html#TextAnchorProperty +template <> +bool SVGAttributeParser::parse(SVGTextAnchor* anchor) { + static constexpr std::tuple gAnchorMap[] = { + {"start", SVGTextAnchor::Type::kStart}, + {"middle", SVGTextAnchor::Type::kMiddle}, + {"end", SVGTextAnchor::Type::kEnd}, + {"inherit", SVGTextAnchor::Type::kInherit}, + }; + + bool parsedValue = false; + SVGTextAnchor::Type type; + + if (this->parseEnumMap(gAnchorMap, &type)) { + *anchor = SVGTextAnchor(type); + parsedValue = true; + } + + return parsedValue && this->parseEOSToken(); +} + +// https://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute +bool SVGAttributeParser::parsePreserveAspectRatio(SVGPreserveAspectRatio* par) { + static constexpr std::tuple gAlignMap[] = { + {"none", SVGPreserveAspectRatio::kNone}, + {"xMinYMin", SVGPreserveAspectRatio::kXMinYMin}, + {"xMidYMin", SVGPreserveAspectRatio::kXMidYMin}, + {"xMaxYMin", SVGPreserveAspectRatio::kXMaxYMin}, + {"xMinYMid", SVGPreserveAspectRatio::kXMinYMid}, + {"xMidYMid", SVGPreserveAspectRatio::kXMidYMid}, + {"xMaxYMid", SVGPreserveAspectRatio::kXMaxYMid}, + {"xMinYMax", SVGPreserveAspectRatio::kXMinYMax}, + {"xMidYMax", SVGPreserveAspectRatio::kXMidYMax}, + {"xMaxYMax", SVGPreserveAspectRatio::kXMaxYMax}, + }; + + static constexpr std::tuple gScaleMap[] = { + {"meet", SVGPreserveAspectRatio::kMeet}, + {"slice", SVGPreserveAspectRatio::kSlice}, + }; + + bool parsedValue = false; + + // ignoring optional 'defer' + this->parseExpectedStringToken("defer"); + this->parseWSToken(); + + if (this->parseEnumMap(gAlignMap, &par->fAlign)) { + parsedValue = true; + + // optional scaling selector + this->parseWSToken(); + this->parseEnumMap(gScaleMap, &par->fScale); + } + + return parsedValue && this->parseEOSToken(); +} + +template <> +bool SVGAttributeParser::parse(SVGPreserveAspectRatio* par) { + return this->parsePreserveAspectRatio(par); +} + +// https://www.w3.org/TR/SVG11/types.html#DataTypeCoordinates +template +bool SVGAttributeParser::parseList(std::vector* vals) { + ASSERT(vals->empty()); + + T v; + for (;;) { + if (!this->parse(&v)) { + break; + } + + vals->push_back(v); + + this->parseCommaWspToken(); + } + + return !vals->empty() && this->parseEOSToken(); +} + +template <> +bool SVGAttributeParser::parse(std::vector* lengths) { + return this->parseList(lengths); +} + +template <> +bool SVGAttributeParser::parse(std::vector* numbers) { + return this->parseList(numbers); +} + +template <> +bool SVGAttributeParser::parse(SVGColorspace* colorspace) { + static constexpr std::tuple gColorspaceMap[] = { + {"auto", SVGColorspace::kAuto}, + {"sRGB", SVGColorspace::kSRGB}, + {"linearRGB", SVGColorspace::kLinearRGB}, + }; + + return this->parseEnumMap(gColorspaceMap, colorspace) && this->parseEOSToken(); +} + +// https://www.w3.org/TR/SVG11/painting.html#DisplayProperty +template <> +bool SVGAttributeParser::parse(SVGDisplay* display) { + static const struct { + SVGDisplay fType; + const char* fName; + } gDisplayInfo[] = { + {SVGDisplay::kInline, "inline"}, + {SVGDisplay::kNone, "none"}, + }; + + bool parsedValue = false; + for (const auto& parseInfo : gDisplayInfo) { + if (this->parseExpectedStringToken(parseInfo.fName)) { + *display = SVGDisplay(parseInfo.fType); + parsedValue = true; + break; + } + } + + return parsedValue && this->parseEOSToken(); +} +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGDOM.cpp b/src/svg/SVGDOM.cpp new file mode 100644 index 00000000..d1861394 --- /dev/null +++ b/src/svg/SVGDOM.cpp @@ -0,0 +1,536 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/SVGDOM.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "core/utils/Log.h" +#include "tgfx/core/Canvas.h" +#include "tgfx/core/Data.h" +#include "tgfx/svg/SVGAttribute.h" +#include "tgfx/svg/SVGAttributeParser.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/SVGValue.h" +#include "tgfx/svg/node/SVGCircle.h" +#include "tgfx/svg/node/SVGClipPath.h" +#include "tgfx/svg/node/SVGDefs.h" +#include "tgfx/svg/node/SVGEllipse.h" +#include "tgfx/svg/node/SVGFeBlend.h" +#include "tgfx/svg/node/SVGFeColorMatrix.h" +#include "tgfx/svg/node/SVGFeComponentTransfer.h" +#include "tgfx/svg/node/SVGFeComposite.h" +#include "tgfx/svg/node/SVGFeDisplacementMap.h" +#include "tgfx/svg/node/SVGFeFlood.h" +#include "tgfx/svg/node/SVGFeGaussianBlur.h" +#include "tgfx/svg/node/SVGFeImage.h" +#include "tgfx/svg/node/SVGFeLightSource.h" +#include "tgfx/svg/node/SVGFeLighting.h" +#include "tgfx/svg/node/SVGFeMerge.h" +#include "tgfx/svg/node/SVGFeMorphology.h" +#include "tgfx/svg/node/SVGFeOffset.h" +#include "tgfx/svg/node/SVGFeTurbulence.h" +#include "tgfx/svg/node/SVGFilter.h" +#include "tgfx/svg/node/SVGG.h" +#include "tgfx/svg/node/SVGImage.h" +#include "tgfx/svg/node/SVGLine.h" +#include "tgfx/svg/node/SVGLinearGradient.h" +#include "tgfx/svg/node/SVGMask.h" +#include "tgfx/svg/node/SVGNode.h" +#include "tgfx/svg/node/SVGPath.h" +#include "tgfx/svg/node/SVGPattern.h" +#include "tgfx/svg/node/SVGPoly.h" +#include "tgfx/svg/node/SVGRadialGradient.h" +#include "tgfx/svg/node/SVGRect.h" +#include "tgfx/svg/node/SVGSVG.h" +#include "tgfx/svg/node/SVGStop.h" +#include "tgfx/svg/node/SVGText.h" +#include "tgfx/svg/node/SVGUse.h" +#include "tgfx/svg/xml/XMLDOM.h" + +namespace tgfx { +namespace { + +bool SetIRIAttribute(const std::shared_ptr& node, SVGAttribute attr, + const char* stringValue) { + auto parseResult = SVGAttributeParser::parse(stringValue); + if (!parseResult.has_value()) { + return false; + } + + node->setAttribute(attr, SVGStringValue(parseResult->iri())); + return true; +} + +bool SetStringAttribute(const std::shared_ptr& node, SVGAttribute attr, + const char* stringValue) { + std::string str(stringValue, strlen(stringValue)); + SVGStringType strType = SVGStringType(str); + node->setAttribute(attr, SVGStringValue(strType)); + return true; +} + +bool SetTransformAttribute(const std::shared_ptr& node, SVGAttribute attr, + const char* stringValue) { + auto parseResult = SVGAttributeParser::parse(stringValue); + if (!parseResult.has_value()) { + return false; + } + + node->setAttribute(attr, SVGTransformValue(*parseResult)); + return true; +} + +bool SetLengthAttribute(const std::shared_ptr& node, SVGAttribute attr, + const char* stringValue) { + auto parseResult = SVGAttributeParser::parse(stringValue); + if (!parseResult.has_value()) { + return false; + } + + node->setAttribute(attr, SVGLengthValue(*parseResult)); + return true; +} + +bool SetViewBoxAttribute(const std::shared_ptr& node, SVGAttribute attr, + const char* stringValue) { + SVGViewBoxType viewBox; + SVGAttributeParser parser(stringValue); + if (!parser.parseViewBox(&viewBox)) { + return false; + } + + node->setAttribute(attr, SVGViewBoxValue(viewBox)); + return true; +} + +bool SetObjectBoundingBoxUnitsAttribute(const std::shared_ptr& node, SVGAttribute attr, + const char* stringValue) { + auto parseResult = SVGAttributeParser::parse(stringValue); + if (!parseResult.has_value()) { + return false; + } + + node->setAttribute(attr, SVGObjectBoundingBoxUnitsValue(*parseResult)); + return true; +} + +bool SetPreserveAspectRatioAttribute(const std::shared_ptr& node, SVGAttribute attr, + const char* stringValue) { + SVGPreserveAspectRatio par; + SVGAttributeParser parser(stringValue); + if (!parser.parsePreserveAspectRatio(&par)) { + return false; + } + + node->setAttribute(attr, SVGPreserveAspectRatioValue(par)); + return true; +} + +std::string TrimmedString(const char* first, const char* last) { + ASSERT(first); + ASSERT(last); + ASSERT(first <= last); + + while (first <= last && *first <= ' ') { + first++; + } + while (first <= last && *last <= ' ') { + last--; + } + + ASSERT(last - first + 1 >= 0); + return std::string(first, static_cast(last - first + 1)); +} + +// Breaks a "foo: bar; baz: ..." string into key:value pairs. +class StyleIterator { + public: + explicit StyleIterator(const char* str) : fPos(str) { + } + + std::tuple next() { + std::string name; + std::string value; + + if (fPos) { + const char* sep = this->nextSeparator(); + ASSERT(*sep == ';' || *sep == '\0'); + + const char* valueSep = strchr(fPos, ':'); + if (valueSep && valueSep < sep) { + name = TrimmedString(fPos, valueSep - 1); + value = TrimmedString(valueSep + 1, sep - 1); + } + + fPos = *sep ? sep + 1 : nullptr; + } + + return std::make_tuple(name, value); + } + + private: + const char* nextSeparator() const { + const char* sep = fPos; + while (*sep != ';' && *sep != '\0') { + sep++; + } + return sep; + } + + const char* fPos; +}; + +bool set_string_attribute(const std::shared_ptr& node, const std::string& name, + const std::string& value); + +bool SetStyleAttributes(const std::shared_ptr& node, SVGAttribute, + const char* stringValue) { + + std::string name; + std::string value; + StyleIterator iter(stringValue); + for (;;) { + std::tie(name, value) = iter.next(); + if (name.empty()) { + break; + } + set_string_attribute(node, name, value); + } + + return true; +} + +inline const char* index_into_base(const char* const* base, int index, size_t elemSize) { + return *reinterpret_cast(reinterpret_cast(base) + + static_cast(index) * elemSize); +} + +int StringSearch(const char* const* base, int count, const char target[], size_t elemSize) { + if (count <= 0) return ~0; + + ASSERT(base != nullptr); + + size_t target_len = strlen(target); + int lo = 0; + int hi = count - 1; + + while (lo < hi) { + int mid = (hi + lo) >> 1; + const char* elem = index_into_base(base, mid, elemSize); + + int cmp = strncmp(elem, target, target_len); + if (cmp < 0) lo = mid + 1; + else if (cmp > 0 || strlen(elem) > target_len) + hi = mid; + else + return mid; + } + + const char* elem = index_into_base(base, hi, elemSize); + int cmp = strncmp(elem, target, target_len); + if (cmp || strlen(elem) > target_len) { + if (cmp < 0) hi += 1; + hi = ~hi; + } + return hi; +} + +template +struct SortedDictionaryEntry { + const char* fKey; + const T fValue; +}; + +struct AttrParseInfo { + SVGAttribute fAttr; + bool (*fSetter)(const std::shared_ptr& node, SVGAttribute attr, const char* stringValue); +}; + +SortedDictionaryEntry gAttributeParseInfo[] = { + {"cx", {SVGAttribute::kCx, SetLengthAttribute}}, + {"cy", {SVGAttribute::kCy, SetLengthAttribute}}, + {"filterUnits", {SVGAttribute::kFilterUnits, SetObjectBoundingBoxUnitsAttribute}}, + // focal point x & y + {"fx", {SVGAttribute::kFx, SetLengthAttribute}}, + {"fy", {SVGAttribute::kFy, SetLengthAttribute}}, + {"height", {SVGAttribute::kHeight, SetLengthAttribute}}, + {"preserveAspectRatio", {SVGAttribute::kPreserveAspectRatio, SetPreserveAspectRatioAttribute}}, + {"r", {SVGAttribute::kR, SetLengthAttribute}}, + {"rx", {SVGAttribute::kRx, SetLengthAttribute}}, + {"ry", {SVGAttribute::kRy, SetLengthAttribute}}, + {"style", {SVGAttribute::kUnknown, SetStyleAttributes}}, + {"text", {SVGAttribute::kText, SetStringAttribute}}, + {"transform", {SVGAttribute::kTransform, SetTransformAttribute}}, + {"viewBox", {SVGAttribute::kViewBox, SetViewBoxAttribute}}, + {"width", {SVGAttribute::kWidth, SetLengthAttribute}}, + {"x", {SVGAttribute::kX, SetLengthAttribute}}, + {"x1", {SVGAttribute::kX1, SetLengthAttribute}}, + {"x2", {SVGAttribute::kX2, SetLengthAttribute}}, + {"xlink:href", {SVGAttribute::kHref, SetIRIAttribute}}, + {"y", {SVGAttribute::kY, SetLengthAttribute}}, + {"y1", {SVGAttribute::kY1, SetLengthAttribute}}, + {"y2", {SVGAttribute::kY2, SetLengthAttribute}}, +}; + +SortedDictionaryEntry (*)()> gTagFactories[] = { + {"a", []() -> std::shared_ptr { return SkSVGG::Make(); }}, + {"circle", []() -> std::shared_ptr { return SVGCircle::Make(); }}, + {"clipPath", []() -> std::shared_ptr { return SVGClipPath::Make(); }}, + {"defs", []() -> std::shared_ptr { return SkSVGDefs::Make(); }}, + {"ellipse", []() -> std::shared_ptr { return SkSVGEllipse::Make(); }}, + {"feBlend", []() -> std::shared_ptr { return SkSVGFeBlend::Make(); }}, + {"feColorMatrix", []() -> std::shared_ptr { return SkSVGFeColorMatrix::Make(); }}, + {"feComponentTransfer", + []() -> std::shared_ptr { return SkSVGFeComponentTransfer::Make(); }}, + {"feComposite", []() -> std::shared_ptr { return SkSVGFeComposite::Make(); }}, + {"feDiffuseLighting", + []() -> std::shared_ptr { return SkSVGFeDiffuseLighting::Make(); }}, + {"feDisplacementMap", + []() -> std::shared_ptr { return SkSVGFeDisplacementMap::Make(); }}, + {"feDistantLight", []() -> std::shared_ptr { return SkSVGFeDistantLight::Make(); }}, + {"feFlood", []() -> std::shared_ptr { return SkSVGFeFlood::Make(); }}, + {"feFuncA", []() -> std::shared_ptr { return SkSVGFeFunc::MakeFuncA(); }}, + {"feFuncB", []() -> std::shared_ptr { return SkSVGFeFunc::MakeFuncB(); }}, + {"feFuncG", []() -> std::shared_ptr { return SkSVGFeFunc::MakeFuncG(); }}, + {"feFuncR", []() -> std::shared_ptr { return SkSVGFeFunc::MakeFuncR(); }}, + {"feGaussianBlur", []() -> std::shared_ptr { return SkSVGFeGaussianBlur::Make(); }}, + {"feImage", []() -> std::shared_ptr { return SkSVGFeImage::Make(); }}, + {"feMerge", []() -> std::shared_ptr { return SkSVGFeMerge::Make(); }}, + {"feMergeNode", []() -> std::shared_ptr { return SkSVGFeMergeNode::Make(); }}, + {"feMorphology", []() -> std::shared_ptr { return SkSVGFeMorphology::Make(); }}, + {"feOffset", []() -> std::shared_ptr { return SkSVGFeOffset::Make(); }}, + {"fePointLight", []() -> std::shared_ptr { return SkSVGFePointLight::Make(); }}, + {"feSpecularLighting", + []() -> std::shared_ptr { return SkSVGFeSpecularLighting::Make(); }}, + {"feSpotLight", []() -> std::shared_ptr { return SkSVGFeSpotLight::Make(); }}, + {"feTurbulence", []() -> std::shared_ptr { return SkSVGFeTurbulence::Make(); }}, + {"filter", []() -> std::shared_ptr { return SkSVGFilter::Make(); }}, + {"g", []() -> std::shared_ptr { return SkSVGG::Make(); }}, + {"image", []() -> std::shared_ptr { return SkSVGImage::Make(); }}, + {"line", []() -> std::shared_ptr { return SkSVGLine::Make(); }}, + {"linearGradient", []() -> std::shared_ptr { return SkSVGLinearGradient::Make(); }}, + {"mask", []() -> std::shared_ptr { return SkSVGMask::Make(); }}, + {"path", []() -> std::shared_ptr { return SkSVGPath::Make(); }}, + {"pattern", []() -> std::shared_ptr { return SkSVGPattern::Make(); }}, + {"polygon", []() -> std::shared_ptr { return SkSVGPoly::MakePolygon(); }}, + {"polyline", []() -> std::shared_ptr { return SkSVGPoly::MakePolyline(); }}, + {"radialGradient", []() -> std::shared_ptr { return SkSVGRadialGradient::Make(); }}, + {"rect", []() -> std::shared_ptr { return SkSVGRect::Make(); }}, + {"stop", []() -> std::shared_ptr { return SkSVGStop::Make(); }}, + // "svg" handled explicitly + {"text", []() -> std::shared_ptr { return SkSVGText::Make(); }}, + {"textPath", []() -> std::shared_ptr { return SkSVGTextPath::Make(); }}, + {"tspan", []() -> std::shared_ptr { return SkSVGTSpan::Make(); }}, + {"use", []() -> std::shared_ptr { return SkSVGUse::Make(); }}, +}; + +struct ConstructionContext { + ConstructionContext(SVGIDMapper* mapper) : fParent(nullptr), fIDMapper(mapper) { + } + ConstructionContext(const ConstructionContext& other, const std::shared_ptr& newParent) + : fParent(newParent.get()), fIDMapper(other.fIDMapper) { + } + + SVGNode* fParent; + SVGIDMapper* fIDMapper; +}; + +bool set_string_attribute(const std::shared_ptr& node, const std::string& name, + const std::string& value) { + if (node->parseAndSetAttribute(name.c_str(), value.c_str())) { + // Handled by new code path + return true; + } + + const int attrIndex = + StringSearch(&gAttributeParseInfo[0].fKey, static_cast(std::size(gAttributeParseInfo)), + name.c_str(), sizeof(gAttributeParseInfo[0])); + if (attrIndex < 0) { + return false; + } + + ASSERT(static_cast(attrIndex) < std::size(gAttributeParseInfo)); + const auto& attrInfo = gAttributeParseInfo[attrIndex].fValue; + return attrInfo.fSetter(node, attrInfo.fAttr, value.c_str()); +} + +void parse_node_attributes(const DOMNode* xmlNode, const std::shared_ptr& svgNode, + SVGIDMapper* mapper) { + + for (const auto& attr : xmlNode->attributes) { + const char* name = attr.name.c_str(); + const char* value = attr.value.c_str(); + if (!strcmp(name, "id")) { + mapper->insert({value, svgNode}); + continue; + } + set_string_attribute(svgNode, name, value); + } +} + +std::shared_ptr construct_svg_node(const ConstructionContext& ctx, + const DOMNode* xmlNode) { + std::string elem = xmlNode->name; + const auto elemType = xmlNode->type; + + if (elemType == DOMNodeType::Text) { + // Text literals require special handling. + ASSERT(xmlNode->attributes.empty()); + auto txt = SkSVGTextLiteral::Make(); + txt->setText(xmlNode->name); + ctx.fParent->appendChild(std::move(txt)); + + return nullptr; + } + + ASSERT(elemType == DOMNodeType::Element); + + auto makeNode = [](const ConstructionContext& ctx, + const std::string& elem) -> std::shared_ptr { + if (elem == "svg") { + // Outermost SVG element must be tagged as such. + return SVGSVG::Make(ctx.fParent ? SVGSVG::Type::kInner : SVGSVG::Type::kRoot); + } + + const int tagIndex = + StringSearch(&gTagFactories[0].fKey, static_cast(std::size(gTagFactories)), + elem.c_str(), sizeof(gTagFactories[0])); + if (tagIndex < 0) { + return nullptr; + } + ASSERT(static_cast(tagIndex) < std::size(gTagFactories)); + return gTagFactories[tagIndex].fValue(); + }; + + auto node = makeNode(ctx, elem); + if (!node) { + return nullptr; + } + + parse_node_attributes(xmlNode, node, ctx.fIDMapper); + + ConstructionContext localCtx(ctx, node); + std::shared_ptr child = xmlNode->firstChild; + while (child) { + std::shared_ptr childNode = construct_svg_node(localCtx, child.get()); + if (childNode) { + node->appendChild(std::move(childNode)); + } + child = child->nextSibling; + } + return node; +} + +} // anonymous namespace + +// SVGDOM::Builder& SVGDOM::Builder::setFontManager(std::shared_ptr fmgr) { +// fFontMgr = std::move(fmgr); +// return *this; +// } + +// SVGDOM::Builder& SVGDOM::Builder::setResourceProvider(std::shared_ptr rp) { +// fResourceProvider = std::move(rp); +// return *this; +// } + +// SVGDOM::Builder& SVGDOM::Builder::setTextShapingFactory(std::shared_ptr f) { +// fTextShapingFactory = f; +// return *this; +// } + +std::shared_ptr SVGDOM::Builder::make(Data& data, + std::shared_ptr fontManager) const { + // TRACE_EVENT0("skia", TRACE_FUNC); + if (data.empty()) { + return nullptr; + } + auto xmlDom = DOM::MakeFromData(data); + if (!xmlDom) { + return nullptr; + } + + SVGIDMapper mapper; + ConstructionContext ctx(&mapper); + + auto root = construct_svg_node(ctx, xmlDom->getRootNode().get()); + if (!root || root->tag() != SVGTag::kSvg) { + return nullptr; + } + + // class NullResourceProvider final : public skresources::ResourceProvider { + // std::shared_ptr load(const char[], const char[]) const override { return nullptr; } + // }; + + // auto resource_provider = fResourceProvider ? fResourceProvider + // : sk_make_sp(); + + // auto factory = fTextShapingFactory ? fTextShapingFactory : SkShapers::Primitive::Factory(); + + return std::shared_ptr(new SVGDOM(std::static_pointer_cast(root), + std::move(mapper), std::move(fontManager))); +} + +SVGDOM::SVGDOM(std::shared_ptr root, SVGIDMapper&& mapper, + std::shared_ptr fontManager) + : fRoot(std::move(root)), fFontMgr(std::move(fontManager)), fIDMapper(std::move(mapper)) { +} + +void SVGDOM::render(Canvas* canvas) const { + if (fRoot) { + SVGLengthContext lctx(fContainerSize); + SkSVGPresentationContext pctx; + fRoot->render(SVGRenderContext(canvas, fFontMgr, fResourceProvider, fIDMapper, lctx, pctx, + {nullptr, nullptr})); + } +} + +void SVGDOM::renderNode(Canvas* canvas, SkSVGPresentationContext& pctx, const char* id) const { + if (fRoot) { + SVGLengthContext lctx(fContainerSize); + fRoot->renderNode(SVGRenderContext(canvas, fFontMgr, fResourceProvider, fIDMapper, lctx, pctx, + {nullptr, nullptr}), + SVGIRI(SVGIRI::Type::kLocal, SVGStringType(id))); + } +} + +const Size& SVGDOM::containerSize() const { + return fContainerSize; +} + +void SVGDOM::setContainerSize(const Size& containerSize) { + // TODO: inval + fContainerSize = containerSize; +} + +std::shared_ptr SVGDOM::findNodeById(const char* id) { + auto iter = fIDMapper.find(id); + return iter == this->fIDMapper.end() ? nullptr : iter->second; +} + +// TODO(fuego): move this to SkSVGNode or its own CU. +bool SVGNode::setAttribute(const std::string& attributeName, const std::string& attributeValue) { + //std::shared_ptr(this) 临时写法 有点危险 + return set_string_attribute(std::shared_ptr(this), attributeName, attributeValue); +} +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGFontManager.cpp b/src/svg/SVGFontManager.cpp new file mode 100644 index 00000000..f647e57f --- /dev/null +++ b/src/svg/SVGFontManager.cpp @@ -0,0 +1,89 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/SVGFontManager.h" +#include "tgfx/core/Typeface.h" + +namespace tgfx { + +void SVGFontManager::setDefaultTypeface(const std::shared_ptr& typeface) { + _defaultTypeface = typeface; +} + +void SVGFontManager::addFontStyle(const std::string& fontFamily, FontStyle style) { + if (_typefaces.at(fontFamily).find(style) == _typefaces.at(fontFamily).end()) { + _typefaces[fontFamily][style] = nullptr; + } +} + +void SVGFontManager::setTypeface(const std::string& fontFamily, FontStyle style, + const std::shared_ptr& typeface) { + _typefaces[fontFamily][style] = typeface; +} + +std::vector SVGFontManager::getFontFamilies() const { + std::vector families; + families.reserve(_typefaces.size()); + for (const auto& [family, _] : _typefaces) { + families.push_back(family); + } + return families; +} + +std::vector SVGFontManager::getFontStyles(const std::string& fontFamily) const { + if (auto iter = _typefaces.find(fontFamily); iter != _typefaces.end()) { + std::vector styles; + styles.reserve(iter->second.size()); + for (const auto& [style, _] : iter->second) { + styles.push_back(style); + } + return styles; + } else { + return {}; + } +} + +std::shared_ptr SVGFontManager::getTypefaceForConfig(const std::string& fontFamily, + FontStyle style) const { + auto familyIter = _typefaces.find(fontFamily); + if (familyIter == _typefaces.end()) { + return nullptr; + } + auto styleIter = familyIter->second.find(style); + if (styleIter == familyIter->second.end()) { + return nullptr; + } else { + return styleIter->second; + } +} + +std::shared_ptr SVGFontManager::getTypefaceForRender(const std::string& fontFamily, + FontStyle style) const { + auto familyIter = _typefaces.find(fontFamily); + if (familyIter == _typefaces.end()) { + return _defaultTypeface; + } + auto styleIter = familyIter->second.find(style); + if (styleIter == familyIter->second.end()) { + return _defaultTypeface; + } else { + return styleIter->second; + } +} + +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGParse.cpp b/src/svg/SVGParse.cpp new file mode 100644 index 00000000..25405669 --- /dev/null +++ b/src/svg/SVGParse.cpp @@ -0,0 +1,472 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/SVGParse.h" +#include +#include +#include +#include +#include "core/utils/Log.h" +#include "tgfx/core/Path.h" +#include "tgfx/core/Point.h" +#include "tgfx/core/Typeface.h" + +namespace tgfx { + +namespace { +inline bool is_between(int c, int min, int max) { + return static_cast(c - min) <= static_cast(max - min); +} + +inline bool is_ws(int c) { + return is_between(c, 1, 32); +} + +inline bool is_digit(int c) { + return is_between(c, '0', '9'); +} + +int to_hex(int c) { + if (is_digit(c)) { + return c - '0'; + } + + c |= 0x20; // make us lower-case + return is_between(c, 'a', 'f') ? c + 10 - 'a' : -1; +} + +inline bool is_hex(int c) { + return to_hex(c) >= 0; +} + +const char* skip_ws(const char str[]) { + ASSERT(str); + while (is_ws(*str)) { + str++; + } + return str; +} + +template +tgfx::Unichar next_fail(const T** ptr, const T* end) { + *ptr = end; + return -1; +} + +inline bool is_sep(int c) { + return is_ws(c) || c == ',' || c == ';'; +} + +const char* skip_sep(const char str[]) { + ASSERT(str); + while (is_sep(*str)) { + str++; + } + return str; +} + +bool lookup_str(const char str[], const char** table, int count) { + while (--count >= 0) { + if (!strcmp(str, table[count])) { + return true; + } + } + return false; +} + +inline bool is_lower(int c) { + return is_between(c, 'a', 'z'); +} + +inline int to_upper(int c) { + return c - 'a' + 'A'; +} + +// If unable to read count points from str into value, this will return nullptr +// to signal the failure. Otherwise, it will return the next offset to read from. +const char* find_points(const char str[], Point value[], int count, bool isRelative, + Point* relative) { + str = SVGParse::FindScalars(str, &value[0].x, count * 2); + if (isRelative) { + for (int index = 0; index < count; index++) { + value[index].x += relative->x; + value[index].y += relative->y; + } + } + return str; +} + +// If unable to read a scalar from str into value, this will return nullptr +// to signal the failure. Otherwise, it will return the next offset to read from. +const char* find_scalar(const char str[], float* value, bool isRelative, float relative) { + str = SVGParse::FindScalar(str, value); + if (!str) { + return nullptr; + } + if (isRelative) { + *value += relative; + } + str = skip_sep(str); + return str; +} + +// https://www.w3.org/TR/SVG11/paths.html#PathDataBNF +// flag: +// "0" | "1" +const char* find_flag(const char str[], bool* value) { + if (!str) { + return nullptr; + } + if (str[0] != '1' && str[0] != '0') { + return nullptr; + } + *value = str[0] != '0'; + str = skip_sep(str + 1); + return str; +} + +} // namespace + +const char* SVGParse::FindHex(const char str[], uint32_t* value) { + ASSERT(str); + str = skip_ws(str); + + if (!is_hex(*str)) { + return nullptr; + } + + uint32_t n = 0; + int max_digits = 8; + int digit; + while ((digit = to_hex(*str)) >= 0) { + if (--max_digits < 0) { + return nullptr; + } + n = (n << 4) | static_cast(digit); + str += 1; + } + + if (*str == 0 || is_ws(*str)) { + if (value) { + *value = n; + } + return str; + } + return nullptr; +} + +const char* SVGParse::FindS32(const char str[], int32_t* value) { + ASSERT(str); + str = skip_ws(str); + + int sign = 1; + int64_t maxAbsValue = std::numeric_limits::max(); + if (*str == '-') { + sign = -1; + maxAbsValue = -static_cast(std::numeric_limits::min()); + str += 1; + } + + if (!is_digit(*str)) { + return nullptr; + } + + int64_t n = 0; + while (is_digit(*str)) { + n = 10 * n + *str - '0'; + if (n > maxAbsValue) { + return nullptr; + } + + str += 1; + } + + if (value) { + *value = static_cast(sign * n); + } + return str; +} + +const char* SVGParse::FindScalar(const char str[], float* value) { + ASSERT(str); + str = skip_ws(str); + + char* stop; + auto v = static_cast(strtod(str, &stop)); + if (str == stop) { + return nullptr; + } + if (value) { + *value = v; + } + return stop; +} + +const char* SVGParse::FindScalars(const char str[], float value[], int count) { + ASSERT(count >= 0); + + if (count > 0) { + for (;;) { + str = SVGParse::FindScalar(str, value); + if (--count == 0 || str == nullptr) { + break; + } + + // keep going + str = skip_sep(str); + if (value) { + value += 1; + } + } + } + return str; +} + +bool SVGParse::FindBool(const char str[], bool* value) { + static const char* yesSet[] = {"yes", "1", "true"}; + static const char* noSet[] = {"no", "0", "false"}; + + if (lookup_str(str, yesSet, std::size(yesSet))) { + if (value) { + *value = true; + } + return true; + } else if (lookup_str(str, noSet, std::size(noSet))) { + if (value) { + *value = false; + } + return true; + } + return false; +} + +int SVGParse::FindList(const char target[], const char list[]) { + auto len = strlen(target); + int index = 0; + + for (;;) { + const char* end = strchr(list, ','); + size_t entryLen = end == nullptr ? strlen(list) : static_cast(end - list); + + if (entryLen == len && memcmp(target, list, len) == 0) { + return index; + } + if (end == nullptr) { + break; + } + + list = end + 1; // skip the ',' + index += 1; + } + return -1; +} + +std::tuple> PathParse::FromSVGString(const char data[]) { + // We will write all data to this local path and only write it + // to result if the whole parsing succeeds. + auto path = std::make_shared(); + auto first = Point::Zero(); + auto opOrigin = Point::Zero(); + auto lastOpOrigin = Point::Zero(); + + // We will use find_points and find_scalar to read into these. + // There might not be enough data to fill them, so to avoid + // MSAN warnings about using uninitialized bytes, we initialize + // them there. + Point points[3] = {}; + float scratch = 0; + char op = '\0'; + char previousOp = '\0'; + bool relative = false; + for (;;) { + if (!data) { + // Truncated data + return {false, nullptr}; + } + data = skip_ws(data); + if (data[0] == '\0') { + break; + } + char ch = data[0]; + if (is_digit(ch) || ch == '-' || ch == '+' || ch == '.') { + if (op == '\0' || op == 'Z') { + return {false, nullptr}; + } + } else if (is_sep(ch)) { + data = skip_sep(data); + } else { + op = ch; + relative = false; + if (is_lower(op)) { + op = static_cast(to_upper(op)); + relative = true; + } + data++; + data = skip_sep(data); + } + switch (op) { + case 'M': // Move + data = find_points(data, points, 1, relative, &opOrigin); + // find_points might have failed, so this might be the + // previous point. However, data will be set to nullptr + // if it failed, so we will check this at the top of the loop. + path->moveTo(points[0]); + previousOp = '\0'; + op = 'L'; + opOrigin = points[0]; + break; + case 'L': // Line + data = find_points(data, points, 1, relative, &opOrigin); + path->lineTo(points[0]); + opOrigin = points[0]; + break; + case 'H': // Horizontal Line + data = find_scalar(data, &scratch, relative, opOrigin.x); + // Similarly, if there wasn't a scalar to read, data will + // be set to nullptr and this lineTo is bogus but will + // be ultimately ignored when the next time through the loop + // detects that and bails out. + path->lineTo(scratch, opOrigin.y); + opOrigin.x = scratch; + break; + case 'V': // Vertical Line + data = find_scalar(data, &scratch, relative, opOrigin.y); + path->lineTo(opOrigin.x, scratch); + opOrigin.y = scratch; + break; + case 'C': // Cubic Bezier Curve + data = find_points(data, points, 3, relative, &opOrigin); + goto cubicCommon; + case 'S': // Continued "Smooth" Cubic Bezier Curve + data = find_points(data, &points[1], 2, relative, &opOrigin); + points[0] = opOrigin; + if (previousOp == 'C' || previousOp == 'S') { + points[0].x -= lastOpOrigin.x - opOrigin.x; + points[0].y -= lastOpOrigin.y - opOrigin.y; + } + cubicCommon: + path->cubicTo(points[0], points[1], points[2]); + lastOpOrigin = points[1]; + opOrigin = points[2]; + break; + case 'Q': // Quadratic Bezier Curve + data = find_points(data, points, 2, relative, &opOrigin); + goto quadraticCommon; + case 'T': // Continued Quadratic Bezier Curve + data = find_points(data, &points[1], 1, relative, &opOrigin); + points[0] = opOrigin; + if (previousOp == 'Q' || previousOp == 'T') { + points[0].x -= lastOpOrigin.x - opOrigin.x; + points[0].y -= lastOpOrigin.y - opOrigin.y; + } + quadraticCommon: + path->quadTo(points[0], points[1]); + lastOpOrigin = points[0]; + opOrigin = points[1]; + break; + case 'A': { // Arc (Elliptical) + auto xyRadii = Point::Zero(); + float angle = 0.0f; + bool largeArc = true; + bool sweep = true; + + if (data = find_points(data, &xyRadii, 1, false, nullptr); !data) break; + if (data = skip_sep(data); !data) break; + if (data = find_scalar(data, &angle, false, 0); !data) break; + if (data = skip_sep(data); !data) break; + if (data = find_flag(data, &largeArc); !data) break; + if (data = skip_sep(data); !data) break; + if (data = find_flag(data, &sweep); !data) break; + if (data = skip_sep(data); !data) break; + if (data = find_points(data, &points[0], 1, relative, &opOrigin); !data) break; + + auto arcSize = largeArc ? PathArcSize::Large : PathArcSize::Small; + path->arcTo(xyRadii.x, xyRadii.y, angle, arcSize, !sweep, points[0]); + path->getLastPoint(&opOrigin); + } break; + case 'Z': // Close Path + path->close(); + opOrigin = first; + break; + default: + return {false, nullptr}; + } + if (previousOp == 0) { + first = opOrigin; + } + previousOp = op; + } + return {true, path}; +} + +/////////////////////////////////////////////////////////////////////////////// + +std::string PathParse::ToSVGString(const std::shared_ptr& path, + PathParse::PathEncoding encoding) { + + // SkDynamicMemoryWStream stream; + + Point currentPoint = Point::Zero(); + const int relSelector = encoding == PathParse::PathEncoding::Relative ? 1 : 0; + + const auto appendCommand = [&](std::string& inputString, char commandChar, const Point points[], + size_t count) { + // Use lower case cmds for relative encoding. + commandChar = static_cast(commandChar + 32 * relSelector); + inputString.push_back(commandChar); + for (size_t i = 0; i < count; ++i) { + const auto pt = points[i] - currentPoint; + if (i > 0) { + inputString.push_back(' '); + } + inputString.append(std::to_string(pt.x) + " " + std::to_string(pt.y)); + } + + ASSERT(count > 0); + // For relative encoding, track the current point (otherwise == origin). + currentPoint = {points[count - 1].x * static_cast(relSelector), + points[count - 1].y * static_cast(relSelector)}; + }; + + auto pathIter = [&](PathVerb verb, const Point points[4], void* info) -> void { + auto* castedString = reinterpret_cast(info); + switch (verb) { + case PathVerb::Move: + appendCommand(*castedString, 'M', points, 1); + break; + case PathVerb::Line: + appendCommand(*castedString, 'L', points, 1); + break; + case PathVerb::Quad: + appendCommand(*castedString, 'Q', points, 2); + break; + case PathVerb::Cubic: + appendCommand(*castedString, 'C', points, 3); + break; + case PathVerb::Close: + castedString->push_back('Z'); + break; + } + }; + + std::string svgString; + path->decompose(pathIter, &svgString); + return svgString; +} + +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGParseColor.cpp b/src/svg/SVGParseColor.cpp new file mode 100644 index 00000000..4955af28 --- /dev/null +++ b/src/svg/SVGParseColor.cpp @@ -0,0 +1,334 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include "tgfx/core/Color.h" +#include "tgfx/svg/SVGParse.h" + +namespace { +static const char* colorNames[] = { + "aliceblue", + "antiquewhite", + "aqua", + "aquamarine", + "azure", + "beige", + "bisque", + "black", + "blanchedalmond", + "blue", + "blueviolet", + "brown", + "burlywood", + "cadetblue", + "chartreuse", + "chocolate", + "coral", + "cornflowerblue", + "cornsilk", + "crimson", + "cyan", + "darkblue", + "darkcyan", + "darkgoldenrod", + "darkgray", + "darkgreen", + "darkkhaki", + "darkmagenta", + "darkolivegreen", + "darkorange", + "darkorchid", + "darkred", + "darksalmon", + "darkseagreen", + "darkslateblue", + "darkslategray", + "darkturquoise", + "darkviolet", + "deeppink", + "deepskyblue", + "dimgray", + "dodgerblue", + "firebrick", + "floralwhite", + "forestgreen", + "fuchsia", + "gainsboro", + "ghostwhite", + "gold", + "goldenrod", + "gray", + "green", + "greenyellow", + "honeydew", + "hotpink", + "indianred", + "indigo", + "ivory", + "khaki", + "lavender", + "lavenderblush", + "lawngreen", + "lemonchiffon", + "lightblue", + "lightcoral", + "lightcyan", + "lightgoldenrodyellow", + "lightgreen", + "lightgrey", + "lightpink", + "lightsalmon", + "lightseagreen", + "lightskyblue", + "lightslategray", + "lightsteelblue", + "lightyellow", + "lime", + "limegreen", + "linen", + "magenta", + "maroon", + "mediumaquamarine", + "mediumblue", + "mediumorchid", + "mediumpurple", + "mediumseagreen", + "mediumslateblue", + "mediumspringgreen", + "mediumturquoise", + "mediumvioletred", + "midnightblue", + "mintcream", + "mistyrose", + "moccasin", + "navajowhite", + "navy", + "oldlace", + "olive", + "olivedrab", + "orange", + "orangered", + "orchid", + "palegoldenrod", + "palegreen", + "paleturquoise", + "palevioletred", + "papayawhip", + "peachpuff", + "peru", + "pink", + "plum", + "powderblue", + "purple", + "red", + "rosybrown", + "royalblue", + "saddlebrown", + "salmon", + "sandybrown", + "seagreen", + "seashell", + "sienna", + "silver", + "skyblue", + "slateblue", + "slategray", + "snow", + "springgreen", + "steelblue", + "tan", + "teal", + "thistle", + "tomato", + "turquoise", + "violet", + "wheat", + "white", + "whitesmoke", + "yellow", + "yellowgreen", +}; + +struct ColorRec { + uint8_t r, g, b; +}; + +static constexpr ColorRec colors[] = { + {0xf0, 0xf8, 0xff}, // aliceblue + {0xfa, 0xeb, 0xd7}, // antiquewhite + {0x00, 0xff, 0xff}, // aqua + {0x7f, 0xff, 0xd4}, // aquamarine + {0xf0, 0xff, 0xff}, // azure + {0xf5, 0xf5, 0xdc}, // beige + {0xff, 0xe4, 0xc4}, // bisque + {0x00, 0x00, 0x00}, // black + {0xff, 0xeb, 0xcd}, // blanchedalmond + {0x00, 0x00, 0xff}, // blue + {0x8a, 0x2b, 0xe2}, // blueviolet + {0xa5, 0x2a, 0x2a}, // brown + {0xde, 0xb8, 0x87}, // burlywood + {0x5f, 0x9e, 0xa0}, // cadetblue + {0x7f, 0xff, 0x00}, // chartreuse + {0xd2, 0x69, 0x1e}, // chocolate + {0xff, 0x7f, 0x50}, // coral + {0x64, 0x95, 0xed}, // cornflowerblue + {0xff, 0xf8, 0xdc}, // cornsilk + {0xdc, 0x14, 0x3c}, // crimson + {0x00, 0xff, 0xff}, // cyan + {0x00, 0x00, 0x8b}, // darkblue + {0x00, 0x8b, 0x8b}, // darkcyan + {0xb8, 0x86, 0x0b}, // darkgoldenrod + {0xa9, 0xa9, 0xa9}, // darkgray + {0x00, 0x64, 0x00}, // darkgreen + {0xbd, 0xb7, 0x6b}, // darkkhaki + {0x8b, 0x00, 0x8b}, // darkmagenta + {0x55, 0x6b, 0x2f}, // darkolivegreen + {0xff, 0x8c, 0x00}, // darkorange + {0x99, 0x32, 0xcc}, // darkorchid + {0x8b, 0x00, 0x00}, // darkred + {0xe9, 0x96, 0x7a}, // darksalmon + {0x8f, 0xbc, 0x8f}, // darkseagreen + {0x48, 0x3d, 0x8b}, // darkslateblue + {0x2f, 0x4f, 0x4f}, // darkslategray + {0x00, 0xce, 0xd1}, // darkturquoise + {0x94, 0x00, 0xd3}, // darkviolet + {0xff, 0x14, 0x93}, // deeppink + {0x00, 0xbf, 0xff}, // deepskyblue + {0x69, 0x69, 0x69}, // dimgray + {0x1e, 0x90, 0xff}, // dodgerblue + {0xb2, 0x22, 0x22}, // firebrick + {0xff, 0xfa, 0xf0}, // floralwhite + {0x22, 0x8b, 0x22}, // forestgreen + {0xff, 0x00, 0xff}, // fuchsia + {0xdc, 0xdc, 0xdc}, // gainsboro + {0xf8, 0xf8, 0xff}, // ghostwhite + {0xff, 0xd7, 0x00}, // gold + {0xda, 0xa5, 0x20}, // goldenrod + {0x80, 0x80, 0x80}, // gray + {0x00, 0x80, 0x00}, // green + {0xad, 0xff, 0x2f}, // greenyellow + {0xf0, 0xff, 0xf0}, // honeydew + {0xff, 0x69, 0xb4}, // hotpink + {0xcd, 0x5c, 0x5c}, // indianred + {0x4b, 0x00, 0x82}, // indigo + {0xff, 0xff, 0xf0}, // ivory + {0xf0, 0xe6, 0x8c}, // khaki + {0xe6, 0xe6, 0xfa}, // lavender + {0xff, 0xf0, 0xf5}, // lavenderblush + {0x7c, 0xfc, 0x00}, // lawngreen + {0xff, 0xfa, 0xcd}, // lemonchiffon + {0xad, 0xd8, 0xe6}, // lightblue + {0xf0, 0x80, 0x80}, // lightcoral + {0xe0, 0xff, 0xff}, // lightcyan + {0xfa, 0xfa, 0xd2}, // lightgoldenrodyellow + {0x90, 0xee, 0x90}, // lightgreen + {0xd3, 0xd3, 0xd3}, // lightgrey + {0xff, 0xb6, 0xc1}, // lightpink + {0xff, 0xa0, 0x7a}, // lightsalmon + {0x20, 0xb2, 0xaa}, // lightseagreen + {0x87, 0xce, 0xfa}, // lightskyblue + {0x77, 0x88, 0x99}, // lightslategray + {0xb0, 0xc4, 0xde}, // lightsteelblue + {0xff, 0xff, 0xe0}, // lightyellow + {0x00, 0xff, 0x00}, // lime + {0x32, 0xcd, 0x32}, // limegreen + {0xfa, 0xf0, 0xe6}, // linen + {0xff, 0x00, 0xff}, // magenta + {0x80, 0x00, 0x00}, // maroon + {0x66, 0xcd, 0xaa}, // mediumaquamarine + {0x00, 0x00, 0xcd}, // mediumblue + {0xba, 0x55, 0xd3}, // mediumorchid + {0x93, 0x70, 0xdb}, // mediumpurple + {0x3c, 0xb3, 0x71}, // mediumseagreen + {0x7b, 0x68, 0xee}, // mediumslateblue + {0x00, 0xfa, 0x9a}, // mediumspringgreen + {0x48, 0xd1, 0xcc}, // mediumturquoise + {0xc7, 0x15, 0x85}, // mediumvioletred + {0x19, 0x19, 0x70}, // midnightblue + {0xf5, 0xff, 0xfa}, // mintcream + {0xff, 0xe4, 0xe1}, // mistyrose + {0xff, 0xe4, 0xb5}, // moccasin + {0xff, 0xde, 0xad}, // navajowhite + {0x00, 0x00, 0x80}, // navy + {0xfd, 0xf5, 0xe6}, // oldlace + {0x80, 0x80, 0x00}, // olive + {0x6b, 0x8e, 0x23}, // olivedrab + {0xff, 0xa5, 0x00}, // orange + {0xff, 0x45, 0x00}, // orangered + {0xda, 0x70, 0xd6}, // orchid + {0xee, 0xe8, 0xaa}, // palegoldenrod + {0x98, 0xfb, 0x98}, // palegreen + {0xaf, 0xee, 0xee}, // paleturquoise + {0xdb, 0x70, 0x93}, // palevioletred + {0xff, 0xef, 0xd5}, // papayawhip + {0xff, 0xda, 0xb9}, // peachpuff + {0xcd, 0x85, 0x3f}, // peru + {0xff, 0xc0, 0xcb}, // pink + {0xdd, 0xa0, 0xdd}, // plum + {0xb0, 0xe0, 0xe6}, // powderblue + {0x80, 0x00, 0x80}, // purple + {0xff, 0x00, 0x00}, // red + {0xbc, 0x8f, 0x8f}, // rosybrown + {0x41, 0x69, 0xe1}, // royalblue + {0x8b, 0x45, 0x13}, // saddlebrown + {0xfa, 0x80, 0x72}, // salmon + {0xf4, 0xa4, 0x60}, // sandybrown + {0x2e, 0x8b, 0x57}, // seagreen + {0xff, 0xf5, 0xee}, // seashell + {0xa0, 0x52, 0x2d}, // sienna + {0xc0, 0xc0, 0xc0}, // silver + {0x87, 0xce, 0xeb}, // skyblue + {0x6a, 0x5a, 0xcd}, // slateblue + {0x70, 0x80, 0x90}, // slategray + {0xff, 0xfa, 0xfa}, // snow + {0x00, 0xff, 0x7f}, // springgreen + {0x46, 0x82, 0xb4}, // steelblue + {0xd2, 0xb4, 0x8c}, // tan + {0x00, 0x80, 0x80}, // teal + {0xd8, 0xbf, 0xd8}, // thistle + {0xff, 0x63, 0x47}, // tomato + {0x40, 0xe0, 0xd0}, // turquoise + {0xee, 0x82, 0xee}, // violet + {0xf5, 0xde, 0xb3}, // wheat + {0xff, 0xff, 0xff}, // white + {0xf5, 0xf5, 0xf5}, // whitesmoke + {0xff, 0xff, 0x00}, // yellow + {0x9a, 0xcd, 0x32}, // yellowgreen +}; +} // namespace + +namespace tgfx { +const char* SVGParse::FindNamedColor(const char* name, Color* color) { + const auto rec = + std::lower_bound(std::begin(colorNames), std::end(colorNames), + name, // key + [](const char* name, const char* key) { return strcmp(name, key) < 0; }); + + if (rec == std::end(colorNames) || 0 != strcmp(name, *rec)) { + return nullptr; + } + + if (color) { + int64_t index = rec - colorNames; + *color = Color::FromRGBA(colors[index].r, colors[index].g, colors[index].b); + } + + return name + strlen(*rec); +} +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGRenderContext.cpp b/src/svg/SVGRenderContext.cpp new file mode 100644 index 00000000..c0a168a4 --- /dev/null +++ b/src/svg/SVGRenderContext.cpp @@ -0,0 +1,539 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/SVGRenderContext.h" +#include +#include +#include +#include +#include "core/utils/Log.h" +#include "tgfx/core/BlendMode.h" +#include "tgfx/core/Canvas.h" +#include "tgfx/core/ImageFilter.h" +#include "tgfx/core/Paint.h" +#include "tgfx/core/PathEffect.h" +#include "tgfx/core/Rect.h" +#include "tgfx/core/Stroke.h" +#include "tgfx/svg/node/SVGClipPath.h" +#include "tgfx/svg/node/SVGFilter.h" +#include "tgfx/svg/node/SVGMask.h" +#include "tgfx/svg/node/SVGNode.h" + +#ifndef RENDER_SVG + +namespace tgfx { + +namespace { + +float length_size_for_type(const Size& viewport, SVGLengthContext::LengthType t) { + switch (t) { + case SVGLengthContext::LengthType::kHorizontal: + return viewport.width; + case SVGLengthContext::LengthType::kVertical: + return viewport.height; + case SVGLengthContext::LengthType::kOther: { + // https://www.w3.org/TR/SVG11/coords.html#Units_viewport_percentage + const float rsqrt2 = 1.0f / std::sqrt(2.0f); + const float w = viewport.width; + const float h = viewport.height; + return rsqrt2 * std::sqrt(w * w + h * h); + } + } + ASSERT(false); // Not reached. + return 0; +} + +// Multipliers for DPI-relative units. +constexpr float kINMultiplier = 1.00f; +constexpr float kPTMultiplier = kINMultiplier / 72.272f; +constexpr float kPCMultiplier = kPTMultiplier * 12; +constexpr float kMMMultiplier = kINMultiplier / 25.4f; +constexpr float kCMMultiplier = kMMMultiplier * 10; + +} // namespace + +float SVGLengthContext::resolve(const SVGLength& l, LengthType t) const { + switch (l.unit()) { + case SVGLength::Unit::kNumber: { + if (fPatternUnit.has_value()) { + if (fPatternUnit.value() == SVGPatternUnits::ObjectBoundingBox) { + return l.value() * length_size_for_type(fViewport, t); + } else { + return l.value(); + } + } else { + return l.value(); + } + } + case SVGLength::Unit::kPX: + return l.value(); + case SVGLength::Unit::kPercentage: { + if (fPatternUnit.has_value()) { + if (fPatternUnit.value() == SVGPatternUnits::ObjectBoundingBox) { + return l.value() * length_size_for_type(fViewport, t) / 100.f; + } else { + return l.value() / 100.f; + } + } else { + return l.value() * length_size_for_type(fViewport, t) / 100; + } + } + case SVGLength::Unit::kCM: + return l.value() * fDPI * kCMMultiplier; + case SVGLength::Unit::kMM: + return l.value() * fDPI * kMMMultiplier; + case SVGLength::Unit::kIN: + return l.value() * fDPI * kINMultiplier; + case SVGLength::Unit::kPT: + return l.value() * fDPI * kPTMultiplier; + case SVGLength::Unit::kPC: + return l.value() * fDPI * kPCMultiplier; + default: + //unsupported unit type + ASSERT(false); + return 0; + } +} + +Rect SVGLengthContext::resolveRect(const SVGLength& x, const SVGLength& y, const SVGLength& width, + const SVGLength& height) const { + return Rect::MakeXYWH(this->resolve(x, SVGLengthContext::LengthType::kHorizontal), + this->resolve(y, SVGLengthContext::LengthType::kVertical), + this->resolve(width, SVGLengthContext::LengthType::kHorizontal), + this->resolve(height, SVGLengthContext::LengthType::kVertical)); +} + +namespace { + +LineCap toSkCap(const SVGLineCap& cap) { + switch (cap) { + case SVGLineCap::kButt: + return LineCap::Butt; + case SVGLineCap::kRound: + return LineCap::Round; + case SVGLineCap::kSquare: + return LineCap::Square; + } +} + +LineJoin toSkJoin(const SVGLineJoin& join) { + switch (join.type()) { + case SVGLineJoin::Type::kMiter: + return LineJoin::Miter; + case SVGLineJoin::Type::kRound: + return LineJoin::Round; + case SVGLineJoin::Type::kBevel: + return LineJoin::Bevel; + default: + ASSERT(false); + return LineJoin::Miter; + } +} + +std::unique_ptr dash_effect(const SkSVGPresentationAttributes& props, + const SVGLengthContext& lctx) { + if (props.fStrokeDashArray->type() != SVGDashArray::Type::kDashArray) { + return nullptr; + } + + const auto& da = *props.fStrokeDashArray; + const auto count = da.dashArray().size(); + std::vector intervals; + intervals.reserve(count); + for (const auto& dash : da.dashArray()) { + intervals.push_back(lctx.resolve(dash, SVGLengthContext::LengthType::kOther)); + } + + if (count & 1) { + // If an odd number of values is provided, then the list of values + // is repeated to yield an even number of values. + intervals.resize(count * 2); + memcpy(intervals.data() + count, intervals.data(), count * sizeof(float)); + } + + ASSERT((intervals.size() & 1) == 0); + + const auto phase = lctx.resolve(*props.fStrokeDashOffset, SVGLengthContext::LengthType::kOther); + + return PathEffect::MakeDash(intervals.data(), static_cast(intervals.size()), phase); +} + +} // namespace + +SkSVGPresentationContext::SkSVGPresentationContext() + : fInherited(SkSVGPresentationAttributes::MakeInitial()) { +} + +SVGRenderContext::SVGRenderContext(Canvas* canvas, + const std::shared_ptr& fontManager, + const std::shared_ptr& resource, + const SVGIDMapper& mapper, const SVGLengthContext& lctx, + const SkSVGPresentationContext& pctx, const OBBScope& obbs) + : fFontMgr(fontManager), fResourceProvider(resource), fIDMapper(mapper), fLengthContext(lctx), + fPresentationContext(pctx), fCanvas(canvas), fCanvasSaveCount(canvas->getSaveCount()), + fOBBScope(obbs) { +} + +SVGRenderContext::SVGRenderContext(const SVGRenderContext& other) + : SVGRenderContext(other.fCanvas, other.fFontMgr, other.fResourceProvider, other.fIDMapper, + *other.fLengthContext, *other.fPresentationContext, other.fOBBScope) { +} + +SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, Canvas* canvas) + : SVGRenderContext(canvas, other.fFontMgr, other.fResourceProvider, other.fIDMapper, + *other.fLengthContext, *other.fPresentationContext, other.fOBBScope) { +} + +SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, const SVGLengthContext& lengthCtx) + : SVGRenderContext(other.fCanvas, other.fFontMgr, other.fResourceProvider, other.fIDMapper, + lengthCtx, *other.fPresentationContext, other.fOBBScope) { +} + +SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, Canvas* canvas, + const SVGLengthContext& lengthCtx) + : SVGRenderContext(canvas, other.fFontMgr, other.fResourceProvider, other.fIDMapper, lengthCtx, + *other.fPresentationContext, other.fOBBScope) { +} + +SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, const SVGNode* node) + : SVGRenderContext(other.fCanvas, other.fFontMgr, other.fResourceProvider, other.fIDMapper, + *other.fLengthContext, *other.fPresentationContext, OBBScope{node, this}) { +} + +SVGRenderContext::~SVGRenderContext() { + fCanvas->restoreToCount(fCanvasSaveCount); +} + +std::shared_ptr SVGRenderContext::findNodeById(const SVGIRI& iri) const { + if (iri.type() != SVGIRI::Type::kLocal) { + return nullptr; + } + auto p = fIDMapper.find(iri.iri())->second; + return p; +} + +void SVGRenderContext::applyPresentationAttributes(const SkSVGPresentationAttributes& attrs, + uint32_t flags) { + +#define ApplyLazyInheritedAttribute(ATTR) \ + do { \ + /* All attributes should be defined on the inherited context. */ \ + ASSERT(fPresentationContext->fInherited.f##ATTR.isValue()); \ + const auto& attr = attrs.f##ATTR; \ + if (attr.isValue() && *attr != *fPresentationContext->fInherited.f##ATTR) { \ + /* Update the local attribute value */ \ + fPresentationContext.writable()->fInherited.f##ATTR.set(*attr); \ + } \ + } while (false) + + ApplyLazyInheritedAttribute(Fill); + ApplyLazyInheritedAttribute(FillOpacity); + ApplyLazyInheritedAttribute(FillRule); + ApplyLazyInheritedAttribute(FontFamily); + ApplyLazyInheritedAttribute(FontSize); + ApplyLazyInheritedAttribute(FontStyle); + ApplyLazyInheritedAttribute(FontWeight); + ApplyLazyInheritedAttribute(ClipRule); + ApplyLazyInheritedAttribute(Stroke); + ApplyLazyInheritedAttribute(StrokeDashOffset); + ApplyLazyInheritedAttribute(StrokeDashArray); + ApplyLazyInheritedAttribute(StrokeLineCap); + ApplyLazyInheritedAttribute(StrokeLineJoin); + ApplyLazyInheritedAttribute(StrokeMiterLimit); + ApplyLazyInheritedAttribute(StrokeOpacity); + ApplyLazyInheritedAttribute(StrokeWidth); + ApplyLazyInheritedAttribute(TextAnchor); + ApplyLazyInheritedAttribute(Visibility); + ApplyLazyInheritedAttribute(Color); + ApplyLazyInheritedAttribute(ColorInterpolation); + ApplyLazyInheritedAttribute(ColorInterpolationFilters); + +#undef ApplyLazyInheritedAttribute + + // Uninherited attributes. Only apply to the current context. + + const bool hasFilter = attrs.fFilter.isValue(); + if (attrs.fOpacity.isValue()) { + this->applyOpacity(*attrs.fOpacity, flags, hasFilter); + } + + if (attrs.fClipPath.isValue()) { + this->applyClip(*attrs.fClipPath); + } + + if (attrs.fMask.isValue()) { + this->applyMask(*attrs.fMask); + } + + // TODO: when both a filter and opacity are present, we can apply both with a single layer + if (hasFilter) { + this->applyFilter(*attrs.fFilter); + } + + // Remaining uninherited presentation attributes are accessed as SkSVGNode fields, not via + // the render context. + // TODO: resolve these in a pre-render styling pass and assert here that they are values. + // - stop-color + // - stop-opacity + // - flood-color + // - flood-opacity + // - lighting-color +} + +void SVGRenderContext::applyOpacity(float opacity, uint32_t flags, bool hasFilter) { + if (opacity >= 1) { + return; + } + + const auto& props = fPresentationContext->fInherited; + const bool hasFill = props.fFill->type() != SVGPaint::Type::kNone; + const bool hasStroke = props.fStroke->type() != SVGPaint::Type::kNone; + + // We can apply the opacity as paint alpha if it only affects one atomic draw. + // For now, this means all of the following must be true: + // - the target node doesn't have any descendants; + // - it only has a stroke or a fill (but not both); + // - it does not have a filter. + // Going forward, we may needto refine this heuristic (e.g. to accommodate markers). + if ((flags & kLeaf) && (hasFill ^ hasStroke) && !hasFilter) { + fDeferredPaintOpacity *= opacity; + } else { + // Expensive, layer-based fall back. + Paint opacityPaint; + opacityPaint.setAlpha(std::clamp(opacity, 0.0f, 1.0f)); + // Balanced in the destructor, via restoreToCount(). + + //TODO (YG) + // fCanvas.saveLayer(nullptr, &opacityPaint); + } +} + +void SVGRenderContext::applyFilter(const SVGFuncIRI& filter) { + if (filter.type() != SVGFuncIRI::Type::kIRI) { + return; + } + + const auto node = this->findNodeById(filter.iri()); + if (!node || node->tag() != SVGTag::kFilter) { + return; + } + + const auto* filterNode = reinterpret_cast(node.get()); + auto imageFilter = filterNode->buildFilterDAG(*this); + if (imageFilter) { + Paint filterPaint; + filterPaint.setImageFilter(imageFilter); + // Balanced in the destructor, via restoreToCount(). + //TODO (YG) + // fCanvas->saveLayer(nullptr, &filterPaint); + } +} + +void SVGRenderContext::saveOnce() { + // The canvas only needs to be saved once, per local SVGRenderContext. + if (fCanvas->getSaveCount() == fCanvasSaveCount) { + fCanvas->save(); + } + ASSERT(fCanvas->getSaveCount() > fCanvasSaveCount); +} + +void SVGRenderContext::applyClip(const SVGFuncIRI& clip) { + if (clip.type() != SVGFuncIRI::Type::kIRI) { + return; + } + + const auto clipNode = this->findNodeById(clip.iri()); + if (!clipNode || clipNode->tag() != SVGTag::kClipPath) { + return; + } + + const Path clipPath = static_cast(clipNode.get())->resolveClip(*this); + + // We use the computed clip path in two ways: + // + // - apply to the current canvas, for drawing + // - track in the presentation context, for asPath() composition + // + // TODO: the two uses are exclusive, avoid canvas churn when non needed. + + this->saveOnce(); + + fCanvas->clipPath(clipPath); + fClipPath = clipPath; +} + +void SVGRenderContext::applyMask(const SVGFuncIRI& mask) { + if (mask.type() != SVGFuncIRI::Type::kIRI) { + return; + } + + const auto node = this->findNodeById(mask.iri()); + if (!node || node->tag() != SVGTag::kMask) { + return; + } + + const auto* mask_node = static_cast(node.get()); + const auto mask_bounds = mask_node->bounds(*this); + + // Isolation/mask layer. + // TODO (YG) + // fCanvas->saveLayer(mask_bounds, nullptr); + + // Render and filter mask content. + mask_node->renderMask(*this); + + // Content layer + Paint masking_paint; + masking_paint.setBlendMode(BlendMode::SrcIn); + + // TODO (YG) + // fCanvas->saveLayer(mask_bounds, &masking_paint); + + // Content is also clipped to the specified mask bounds. + fCanvas->clipRect(mask_bounds); + + // At this point we're set up for content rendering. + // The pending layers are restored in the destructor (render context scope exit). + // Restoring triggers srcIn-compositing the content against the mask. +} + +std::optional SVGRenderContext::commonPaint(const SVGPaint& paint_selector, + float paint_opacity) const { + if (paint_selector.type() == SVGPaint::Type::kNone) { + return std::optional(); + } + + std::optional p = Paint(); + p->getAlpha(); + switch (paint_selector.type()) { + case SVGPaint::Type::kColor: + p->setColor(this->resolveSvgColor(paint_selector.color())); + break; + case SVGPaint::Type::kIRI: { + // Our property inheritance is borked as it follows the render path and not the tree + // hierarchy. To avoid gross transgressions like leaf node presentation attributes + // leaking into the paint server context, use a pristine presentation context when + // following hrefs. + // + // Preserve the OBB scope because some paints use object bounding box coords + // (e.g. gradient control points), which requires access to the render context + // and node being rendered. + SkSVGPresentationContext pctx; + pctx.fNamedColors = fPresentationContext->fNamedColors; + SVGRenderContext local_ctx(fCanvas, fFontMgr, fResourceProvider, fIDMapper, *fLengthContext, + pctx, fOBBScope); + + const auto node = this->findNodeById(paint_selector.iri()); + if (!node || !node->asPaint(local_ctx, &(p.value()))) { + // Use the fallback color. + p->setColor(this->resolveSvgColor(paint_selector.color())); + } + } break; + default: + break; + } + p->setAntiAlias(true); // TODO: shape-rendering support + + // We observe 3 opacity components: + // - initial paint server opacity (e.g. color stop opacity) + // - paint-specific opacity (e.g. 'fill-opacity', 'stroke-opacity') + // - deferred opacity override (optimization for leaf nodes 'opacity') + p->setAlpha(std::clamp(p->getAlpha() * paint_opacity * fDeferredPaintOpacity, 0.0f, 1.0f)); + return p; +} + +std::optional SVGRenderContext::fillPaint() const { + const auto& props = fPresentationContext->fInherited; + auto p = this->commonPaint(*props.fFill, *props.fFillOpacity); + + if (p.has_value()) { + p->setStyle(PaintStyle::Fill); + } + + return p; +} + +std::optional SVGRenderContext::strokePaint() const { + const auto& props = fPresentationContext->fInherited; + auto p = this->commonPaint(*props.fStroke, *props.fStrokeOpacity); + + if (p.has_value()) { + p->setStyle(PaintStyle::Stroke); + p->setStrokeWidth( + fLengthContext->resolve(*props.fStrokeWidth, SVGLengthContext::LengthType::kOther)); + Stroke stroke; + stroke.cap = toSkCap(*props.fStrokeLineCap); + stroke.join = toSkJoin(*props.fStrokeLineJoin); + stroke.miterLimit = *props.fStrokeMiterLimit; + p->setStroke(stroke); + + //TODO (YG) + // p->setPathEffect(dash_effect(props, *fLengthContext)); + dash_effect(props, *fLengthContext); + } + + return p; +} + +SVGColorType SVGRenderContext::resolveSvgColor(const SVGColor& color) const { + if (fPresentationContext->fNamedColors) { + for (auto&& ident : *color.vars()) { + auto iter = fPresentationContext->fNamedColors->find(ident); + if (iter != fPresentationContext->fNamedColors->end()) { + return iter->second; + } + } + } + switch (color.type()) { + case SVGColor::Type::kColor: + return color.color(); + case SVGColor::Type::kCurrentColor: + return *fPresentationContext->fInherited.fColor; + case SVGColor::Type::kICCColor: + return Color::Black(); + } +} + +SVGRenderContext::OBBTransform SVGRenderContext::transformForCurrentOBB( + SVGObjectBoundingBoxUnits u) const { + if (!fOBBScope.fNode || u.type() == SVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse) { + return {{0, 0}, {1, 1}}; + } + ASSERT(fOBBScope.fCtx); + + const auto obb = fOBBScope.fNode->objectBoundingBox(*fOBBScope.fCtx); + return {{obb.x(), obb.y()}, {obb.width(), obb.height()}}; +} + +Rect SVGRenderContext::resolveOBBRect(const SVGLength& x, const SVGLength& y, const SVGLength& w, + const SVGLength& h, SVGObjectBoundingBoxUnits obbu) const { + CopyOnWrite lctx(fLengthContext); + + if (obbu.type() == SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox) { + *lctx.writable() = SVGLengthContext({1, 1}); + } + + auto r = lctx->resolveRect(x, y, w, h); + const auto obbt = this->transformForCurrentOBB(obbu); + + return Rect::MakeXYWH(obbt.scale.x * r.x() + obbt.offset.x, obbt.scale.y * r.y() + obbt.offset.y, + obbt.scale.x * r.width(), obbt.scale.y * r.height()); +} +} // namespace tgfx + +#endif // RENDER_SVG \ No newline at end of file diff --git a/src/svg/node/SVGCircle.cpp b/src/svg/node/SVGCircle.cpp new file mode 100644 index 00000000..3172182c --- /dev/null +++ b/src/svg/node/SVGCircle.cpp @@ -0,0 +1,70 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGCircle.h" +#include "tgfx/core/Paint.h" +#include "tgfx/core/Path.h" +#include "tgfx/core/Point.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGAttributeParser.h" + +namespace tgfx { + +SVGCircle::SVGCircle() : INHERITED(SVGTag::kCircle) { +} + +bool SVGCircle::parseAndSetAttribute(const char* n, const char* v) { + return INHERITED::parseAndSetAttribute(n, v) || + this->setCx(SVGAttributeParser::parse("cx", n, v)) || + this->setCy(SVGAttributeParser::parse("cy", n, v)) || + this->setR(SVGAttributeParser::parse("r", n, v)); +} + +#ifndef RENDER_SVG +std::tuple SVGCircle::resolve(const SVGLengthContext& lctx) const { + const auto cx = lctx.resolve(fCx, SVGLengthContext::LengthType::kHorizontal); + const auto cy = lctx.resolve(fCy, SVGLengthContext::LengthType::kVertical); + const auto r = lctx.resolve(fR, SVGLengthContext::LengthType::kOther); + + return std::make_tuple(Point::Make(cx, cy), r); +} + +void SVGCircle::onDraw(Canvas* canvas, const SVGLengthContext& lctx, const Paint& paint, + PathFillType) const { + auto [pos, r] = this->resolve(lctx); + + if (r > 0) { + canvas->drawCircle(pos.x, pos.y, r, paint); + } +} + +Path SVGCircle::onAsPath(const SVGRenderContext& ctx) const { + auto [pos, r] = this->resolve(ctx.lengthContext()); + + Path path; + path.addOval(Rect::MakeXYWH(pos.x - r, pos.y - r, 2 * r, 2 * r)); + this->mapToParent(&path); + return path; +} + +Rect SVGCircle::onObjectBoundingBox(const SVGRenderContext& ctx) const { + const auto [pos, r] = this->resolve(ctx.lengthContext()); + return Rect::MakeXYWH(pos.x - r, pos.y - r, 2 * r, 2 * r); +} +#endif +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGClipPath.cpp b/src/svg/node/SVGClipPath.cpp new file mode 100644 index 00000000..4c0dc3e8 --- /dev/null +++ b/src/svg/node/SVGClipPath.cpp @@ -0,0 +1,47 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGClipPath.h" +#include "tgfx/core/Matrix.h" +#include "tgfx/core/Path.h" +#include "tgfx/svg/SVGAttributeParser.h" + +namespace tgfx { + +SVGClipPath::SVGClipPath() : INHERITED(SVGTag::kClipPath) { +} + +bool SVGClipPath::parseAndSetAttribute(const char* n, const char* v) { + return INHERITED::parseAndSetAttribute(n, v) || + this->setClipPathUnits( + SVGAttributeParser::parse("clipPathUnits", n, v)); +} + +#ifndef RENDER_SVG +Path SVGClipPath::resolveClip(const SVGRenderContext& ctx) const { + auto clip = this->asPath(ctx); + + const auto obbt = ctx.transformForCurrentOBB(fClipPathUnits); + const auto m = Matrix::MakeTrans(obbt.offset.x, obbt.offset.y) * + Matrix::MakeScale(obbt.scale.x, obbt.scale.y); + clip.transform(m); + + return clip; +} +#endif +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGContainer.cpp b/src/svg/node/SVGContainer.cpp new file mode 100644 index 00000000..8755e185 --- /dev/null +++ b/src/svg/node/SVGContainer.cpp @@ -0,0 +1,76 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGContainer.h" +#include "core/utils/Log.h" +#include "tgfx/core/Path.h" +#include "tgfx/core/PathTypes.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/node/SVGNode.h" + +namespace tgfx { + +class SVGRenderContext; + +SkSVGContainer::SkSVGContainer(SVGTag t) : INHERITED(t) { +} + +void SkSVGContainer::appendChild(std::shared_ptr node) { + ASSERT(node); + fChildren.push_back(std::move(node)); +} + +const std::vector>& SkSVGContainer::getChildren() const { + return fChildren; +} + +bool SkSVGContainer::hasChildren() const { + return !fChildren.empty(); +} + +#ifndef RENDER_SVG +void SkSVGContainer::onRender(const SVGRenderContext& ctx) const { + for (const auto& i : fChildren) { + i->render(ctx); + } +} + +Path SkSVGContainer::onAsPath(const SVGRenderContext& ctx) const { + Path path; + + for (const auto& i : fChildren) { + const Path childPath = i->asPath(ctx); + path.addPath(childPath, PathOp::Union); + } + + this->mapToParent(&path); + return path; +} + +Rect SkSVGContainer::onObjectBoundingBox(const SVGRenderContext& ctx) const { + Rect bounds = Rect::MakeEmpty(); + + for (const auto& i : fChildren) { + const Rect childBounds = i->objectBoundingBox(ctx); + bounds.join(childBounds); + } + + return bounds; +} +#endif +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGEllipse.cpp b/src/svg/node/SVGEllipse.cpp new file mode 100644 index 00000000..ff145a16 --- /dev/null +++ b/src/svg/node/SVGEllipse.cpp @@ -0,0 +1,68 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// +#include "tgfx/svg/node/SVGEllipse.h" +#include "SVGRectPriv.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGAttributeParser.h" +#include "tgfx/svg/SVGTypes.h" + +namespace tgfx { + +SkSVGEllipse::SkSVGEllipse() : INHERITED(SVGTag::kEllipse) { +} + +bool SkSVGEllipse::parseAndSetAttribute(const char* n, const char* v) { + return INHERITED::parseAndSetAttribute(n, v) || + this->setCx(SVGAttributeParser::parse("cx", n, v)) || + this->setCy(SVGAttributeParser::parse("cy", n, v)) || + this->setRx(SVGAttributeParser::parse("rx", n, v)) || + this->setRy(SVGAttributeParser::parse("ry", n, v)); + std::optional s; + s = 10; +} + +#ifndef RENDER_SVG +Rect SkSVGEllipse::resolve(const SVGLengthContext& lctx) const { + const auto cx = lctx.resolve(fCx, SVGLengthContext::LengthType::kHorizontal); + const auto cy = lctx.resolve(fCy, SVGLengthContext::LengthType::kVertical); + + // https://www.w3.org/TR/SVG2/shapes.html#EllipseElement + // + // An auto value for either rx or ry is converted to a used value, following the rules given + // above for rectangles (but without any clamping based on width or height). + const auto [rx, ry] = ResolveOptionalRadii(fRx, fRy, lctx); + + // A computed value of zero for either dimension, or a computed value of auto for both + // dimensions, disables rendering of the element. + return (rx > 0 && ry > 0) ? Rect::MakeXYWH(cx - rx, cy - ry, rx * 2, ry * 2) : Rect::MakeEmpty(); +} + +void SkSVGEllipse::onDraw(Canvas* canvas, const SVGLengthContext& lctx, const Paint& paint, + PathFillType) const { + canvas->drawOval(this->resolve(lctx), paint); +} + +Path SkSVGEllipse::onAsPath(const SVGRenderContext& ctx) const { + Path path; + path.addOval(this->resolve(ctx.lengthContext())); + this->mapToParent(&path); + + return path; +} +#endif +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFe.cpp b/src/svg/node/SVGFe.cpp new file mode 100644 index 00000000..75ddff12 --- /dev/null +++ b/src/svg/node/SVGFe.cpp @@ -0,0 +1,147 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGFe.h" +#include +#include +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGAttributeParser.h" +#include "tgfx/svg/SVGRenderContext.h" +#include "tgfx/svg/node/SVGFilterContext.h" + +namespace tgfx { + +#ifndef RENDER_SVG + +std::shared_ptr SkSVGFe::makeImageFilter( + const SVGRenderContext& ctx, const SkSVGFilterContext& filterContext) const { + return this->onMakeImageFilter(ctx, filterContext); +} + +Rect SkSVGFe::resolveBoundaries(const SVGRenderContext& ctx, + const SkSVGFilterContext& filterContext) const { + const auto x = fX.has_value() ? *fX : SVGLength(0, SVGLength::Unit::kPercentage); + const auto y = fY.has_value() ? *fY : SVGLength(0, SVGLength::Unit::kPercentage); + const auto w = fWidth.has_value() ? *fWidth : SVGLength(100, SVGLength::Unit::kPercentage); + const auto h = fHeight.has_value() ? *fHeight : SVGLength(100, SVGLength::Unit::kPercentage); + + return ctx.resolveOBBRect(x, y, w, h, filterContext.primitiveUnits()); +} + +static bool AnyIsStandardInput(const SkSVGFilterContext& fctx, + const std::vector& inputs) { + for (const auto& in : inputs) { + switch (in.type()) { + case SVGFeInputType::Type::kFilterPrimitiveReference: + break; + case SVGFeInputType::Type::kSourceGraphic: + case SVGFeInputType::Type::kSourceAlpha: + case SVGFeInputType::Type::kBackgroundImage: + case SVGFeInputType::Type::kBackgroundAlpha: + case SVGFeInputType::Type::kFillPaint: + case SVGFeInputType::Type::kStrokePaint: + return true; + case SVGFeInputType::Type::kUnspecified: + // Unspecified means previous result (which may be SourceGraphic). + if (fctx.previousResultIsSourceGraphic()) { + return true; + } + break; + } + } + + return false; +} + +Rect SkSVGFe::resolveFilterSubregion(const SVGRenderContext& ctx, + const SkSVGFilterContext& fctx) const { + // From https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveSubRegion, + // the default filter effect subregion is equal to the union of the subregions defined + // for all "referenced nodes" (filter effect inputs). If there are no inputs, the + // default subregion is equal to the filter effects region + // (https://www.w3.org/TR/SVG11/filters.html#FilterEffectsRegion). + const std::vector inputs = this->getInputs(); + Rect defaultSubregion; + if (inputs.empty() || AnyIsStandardInput(fctx, inputs)) { + defaultSubregion = fctx.filterEffectsRegion(); + } else { + defaultSubregion = fctx.filterPrimitiveSubregion(inputs[0]); + for (size_t i = 1; i < inputs.size(); i++) { + defaultSubregion.join(fctx.filterPrimitiveSubregion(inputs[i])); + } + } + + // Next resolve the rect specified by the x, y, width, height attributes on this filter effect. + // If those attributes were given, they override the corresponding attribute of the default + // filter effect subregion calculated above. + const Rect boundaries = this->resolveBoundaries(ctx, fctx); + + // Compute and return the fully resolved subregion. + return Rect::MakeXYWH(fX.has_value() ? boundaries.left : defaultSubregion.left, + fY.has_value() ? boundaries.top : defaultSubregion.top, + fWidth.has_value() ? boundaries.width() : defaultSubregion.width(), + fHeight.has_value() ? boundaries.height() : defaultSubregion.height()); +} + +SVGColorspace SkSVGFe::resolveColorspace(const SVGRenderContext& ctx, + const SkSVGFilterContext&) const { + constexpr SVGColorspace kDefaultCS = SVGColorspace::kSRGB; + const SVGColorspace cs = *ctx.presentationContext().fInherited.fColorInterpolationFilters; + return cs == SVGColorspace::kAuto ? kDefaultCS : cs; +} + +void SkSVGFe::applyProperties(SVGRenderContext* ctx) const { + this->onPrepareToRender(ctx); +} +#endif + +bool SkSVGFe::parseAndSetAttribute(const char* name, const char* value) { + return INHERITED::parseAndSetAttribute(name, value) || + this->setIn(SVGAttributeParser::parse("in", name, value)) || + this->setResult(SVGAttributeParser::parse("result", name, value)) || + this->setX(SVGAttributeParser::parse("x", name, value)) || + this->setY(SVGAttributeParser::parse("y", name, value)) || + this->setWidth(SVGAttributeParser::parse("width", name, value)) || + this->setHeight(SVGAttributeParser::parse("height", name, value)); +} + +template <> +bool SVGAttributeParser::parse(SVGFeInputType* type) { + static constexpr std::tuple gTypeMap[] = { + {"SourceGraphic", SVGFeInputType::Type::kSourceGraphic}, + {"SourceAlpha", SVGFeInputType::Type::kSourceAlpha}, + {"BackgroundImage", SVGFeInputType::Type::kBackgroundImage}, + {"BackgroundAlpha", SVGFeInputType::Type::kBackgroundAlpha}, + {"FillPaint", SVGFeInputType::Type::kFillPaint}, + {"StrokePaint", SVGFeInputType::Type::kStrokePaint}, + }; + + SVGStringType resultId; + SVGFeInputType::Type t; + bool parsedValue = false; + if (this->parseEnumMap(gTypeMap, &t)) { + *type = SVGFeInputType(t); + parsedValue = true; + } else if (parse(&resultId)) { + *type = SVGFeInputType(resultId); + parsedValue = true; + } + + return parsedValue && this->parseEOSToken(); +} +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeBlend.cpp b/src/svg/node/SVGFeBlend.cpp new file mode 100644 index 00000000..5605f090 --- /dev/null +++ b/src/svg/node/SVGFeBlend.cpp @@ -0,0 +1,76 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGFeBlend.h" +#include +#include "tgfx/core/BlendMode.h" +#include "tgfx/core/ImageFilter.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGAttributeParser.h" + +namespace tgfx { + +class SVGRenderContext; + +bool SkSVGFeBlend::parseAndSetAttribute(const char* name, const char* value) { + return INHERITED::parseAndSetAttribute(name, value) || + this->setIn2(SVGAttributeParser::parse("in2", name, value)) || + this->setMode(SVGAttributeParser::parse("mode", name, value)); +} + +#ifndef RENDER_SVG + +// static BlendMode GetBlendMode(SkSVGFeBlend::Mode mode) { +// switch (mode) { +// case SkSVGFeBlend::Mode::kNormal: +// return BlendMode::SrcOver; +// case SkSVGFeBlend::Mode::kMultiply: +// return BlendMode::Multiply; +// case SkSVGFeBlend::Mode::kScreen: +// return BlendMode::Screen; +// case SkSVGFeBlend::Mode::kDarken: +// return BlendMode::Darken; +// case SkSVGFeBlend::Mode::kLighten: +// return BlendMode::Lighten; +// } +// } + +std::shared_ptr SkSVGFeBlend::onMakeImageFilter(const SVGRenderContext& ctx, + const SkSVGFilterContext& fctx) const { + // const Rect cropRect = this->resolveFilterSubregion(ctx, fctx); + // const BlendMode blendMode = GetBlendMode(this->getMode()); + const SVGColorspace colorspace = this->resolveColorspace(ctx, fctx); + const std::shared_ptr background = fctx.resolveInput(ctx, fIn2, colorspace); + const std::shared_ptr foreground = fctx.resolveInput(ctx, this->getIn(), colorspace); + return ImageFilter::Compose(background, foreground); + // TODO (YG),relay on ImageFilters::Blend to implement this + // return SkImageFilters::Blend(blendMode, background, foreground, cropRect); +} +#endif + +template <> +bool SVGAttributeParser::parse(SkSVGFeBlend::Mode* mode) { + static constexpr std::tuple gMap[] = { + {"normal", SkSVGFeBlend::Mode::kNormal}, {"multiply", SkSVGFeBlend::Mode::kMultiply}, + {"screen", SkSVGFeBlend::Mode::kScreen}, {"darken", SkSVGFeBlend::Mode::kDarken}, + {"lighten", SkSVGFeBlend::Mode::kLighten}, + }; + + return this->parseEnumMap(gMap, mode) && this->parseEOSToken(); +} +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeColorMatrix.cpp b/src/svg/node/SVGFeColorMatrix.cpp new file mode 100644 index 00000000..f939d32c --- /dev/null +++ b/src/svg/node/SVGFeColorMatrix.cpp @@ -0,0 +1,158 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGFeColorMatrix.h" +#include +#include "core/utils/MathExtra.h" +#include "tgfx/core/ColorFilter.h" +#include "tgfx/core/ImageFilter.h" +#include "tgfx/svg/SVGAttributeParser.h" + +class SVGRenderContext; + +namespace tgfx { + +bool SkSVGFeColorMatrix::parseAndSetAttribute(const char* name, const char* value) { + return INHERITED::parseAndSetAttribute(name, value) || + this->setType(SVGAttributeParser::parse("type", name, value)) || + this->setValues(SVGAttributeParser::parse("values", name, value)); +} + +template <> +bool SVGAttributeParser::parse(SVGFeColorMatrixType* type) { + static constexpr std::tuple gTypeMap[] = { + {"matrix", SVGFeColorMatrixType::kMatrix}, + {"saturate", SVGFeColorMatrixType::kSaturate}, + {"hueRotate", SVGFeColorMatrixType::kHueRotate}, + {"luminanceToAlpha", SVGFeColorMatrixType::kLuminanceToAlpha}, + }; + + return this->parseEnumMap(gTypeMap, type) && this->parseEOSToken(); +} + +#ifndef RENDER_SVG +ColorMatrix SkSVGFeColorMatrix::makeMatrixForType() const { + if (fValues.empty() && fType != SVGFeColorMatrixType::kLuminanceToAlpha) { + return ColorMatrix(); + } + + switch (fType) { + case SVGFeColorMatrixType::kMatrix: { + if (fValues.size() < 20) { + return ColorMatrix(); + } + ColorMatrix m; + // m.setRowMajor(fValues.data()); + std::copy_n(fValues.data(), 20, m.begin()); + return m; + } + case SVGFeColorMatrixType::kSaturate: + return MakeSaturate(!fValues.empty() ? fValues[0] : 1); + case SVGFeColorMatrixType::kHueRotate: + return MakeHueRotate(!fValues.empty() ? fValues[0] : 0); + case SVGFeColorMatrixType::kLuminanceToAlpha: + return MakeLuminanceToAlpha(); + } +} + +ColorMatrix SkSVGFeColorMatrix::MakeSaturate(SVGNumberType sat) { + enum { + kR_Scale = 0, + kG_Scale = 6, + kB_Scale = 12, + kA_Scale = 18, + + kR_Trans = 4, + kG_Trans = 9, + kB_Trans = 14, + kA_Trans = 19, + }; + + static const float kHueR = 0.213f; + static const float kHueG = 0.715f; + static const float kHueB = 0.072f; + + auto setrow = [](float row[], float r, float g, float b) { + row[0] = r; + row[1] = g; + row[2] = b; + }; + + ColorMatrix matrix; + // m.setSaturation(s); + + matrix.fill(0.0f); + const float R = kHueR * (1 - sat); + const float G = kHueG * (1 - sat); + const float B = kHueB * (1 - sat); + setrow(matrix.data() + 0, R + sat, G, B); + setrow(matrix.data() + 5, R, G + sat, B); + setrow(matrix.data() + 10, R, G, B + sat); + matrix[kA_Scale] = 1; + return matrix; +} + +ColorMatrix SkSVGFeColorMatrix::MakeHueRotate(SVGNumberType degrees) { + const float theta = DegreesToRadians(degrees); + const SVGNumberType c = std::cos(theta); + const SVGNumberType s = std::sin(theta); + return ColorMatrix{0.213f + c * 0.787f + s * -0.213f, + 0.715f + c * -0.715f + s * -0.715f, + 0.072f + c * -0.072f + s * 0.928f, + 0, + 0, + + 0.213f + c * -0.213f + s * 0.143f, + 0.715f + c * 0.285f + s * 0.140f, + 0.072f + c * -0.072f + s * -0.283f, + 0, + 0, + + 0.213f + c * -0.213f + s * -0.787f, + 0.715f + c * -0.715f + s * 0.715f, + 0.072f + c * 0.928f + s * 0.072f, + 0, + 0, + + 0, + 0, + 0, + 1, + 0}; +} + +/** See ITU-R Recommendation BT.709 at http://www.itu.int/rec/R-REC-BT.709/ .*/ +constexpr float LUM_COEFF_R = 0.2126f; +constexpr float LUM_COEFF_G = 0.7152f; +constexpr float LUM_COEFF_B = 0.0722f; + +ColorMatrix SkSVGFeColorMatrix::MakeLuminanceToAlpha() { + return ColorMatrix{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, LUM_COEFF_R, LUM_COEFF_G, LUM_COEFF_B, 0, 0}; +} + +std::shared_ptr SkSVGFeColorMatrix::onMakeImageFilter( + const SVGRenderContext& /*ctx*/, const SkSVGFilterContext& /*fctx*/) const { + return ImageFilter::ColorFilter(ColorFilter::Matrix(makeMatrixForType())); + // return ImageFilter::ColorFilter( + // SkColorFilters::Matrix(makeMatrixForType()), + // fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)), + // this->resolveFilterSubregion(ctx, fctx)); +} +#endif +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeComponentTransfer.cpp b/src/svg/node/SVGFeComponentTransfer.cpp new file mode 100644 index 00000000..bf52a904 --- /dev/null +++ b/src/svg/node/SVGFeComponentTransfer.cpp @@ -0,0 +1,178 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGFeComponentTransfer.h" +#include +#include +#include +#include +#include +#include "core/utils/Log.h" +#include "tgfx/svg/SVGAttributeParser.h" +#include "tgfx/svg/SVGTypes.h" + +namespace tgfx { + +class SVGRenderContext; + +#ifdef RENDER_SVG +sk_sp SkSVGFeComponentTransfer::onMakeImageFilter( + const SVGRenderContext& ctx, const SkSVGFilterContext& fctx) const { + std::vector a_tbl, b_tbl, g_tbl, r_tbl; + + for (const auto& child : fChildren) { + switch (child->tag()) { + case SkSVGTag::kFeFuncA: + a_tbl = static_cast(child.get())->getTable(); + break; + case SkSVGTag::kFeFuncB: + b_tbl = static_cast(child.get())->getTable(); + break; + case SkSVGTag::kFeFuncG: + g_tbl = static_cast(child.get())->getTable(); + break; + case SkSVGTag::kFeFuncR: + r_tbl = static_cast(child.get())->getTable(); + break; + default: + break; + } + } + SkASSERT(a_tbl.empty() || a_tbl.size() == 256); + SkASSERT(b_tbl.empty() || b_tbl.size() == 256); + SkASSERT(g_tbl.empty() || g_tbl.size() == 256); + SkASSERT(r_tbl.empty() || r_tbl.size() == 256); + + const SkRect cropRect = this->resolveFilterSubregion(ctx, fctx); + const sk_sp input = + fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)); + + const auto cf = SkColorFilters::TableARGB( + a_tbl.empty() ? nullptr : a_tbl.data(), r_tbl.empty() ? nullptr : r_tbl.data(), + g_tbl.empty() ? nullptr : g_tbl.data(), b_tbl.empty() ? nullptr : b_tbl.data()); + + return SkImageFilters::ColorFilter(std::move(cf), std::move(input), cropRect); +} +#endif + +std::vector SkSVGFeFunc::getTable() const { + // https://www.w3.org/TR/SVG11/filters.html#feComponentTransferTypeAttribute + const auto make_linear = [this]() -> std::vector { + std::vector tbl(256); + const float slope = this->getSlope(), intercept255 = this->getIntercept() * 255; + + for (size_t i = 0; i < 256; ++i) { + tbl[i] = static_cast( + std::clamp(std::round(intercept255 + static_cast(i) * slope), 0.0f, 255.0f)); + } + + return tbl; + }; + + const auto make_gamma = [this]() -> std::vector { + std::vector tbl(256); + const float exponent = this->getExponent(), offset = this->getOffset(); + + for (size_t i = 0; i < 256; ++i) { + const float component = offset + std::pow(static_cast(i) * (1 / 255.f), exponent); + tbl[i] = static_cast(std::clamp(std::round(component * 255), 0.0f, 255.0f)); + } + + return tbl; + }; + + const auto lerp_from_table_values = [this](auto lerp_func) -> std::vector { + const auto& vals = this->getTableValues(); + if (vals.size() < 2 || vals.size() > 255) { + return {}; + } + + // number of interpolation intervals + const size_t n = vals.size() - 1; + + std::vector tbl(256); + for (size_t k = 0; k < n; ++k) { + // interpolation values + const SVGNumberType v0 = std::clamp(vals[k + 0], 0.f, 1.f); + const SVGNumberType v1 = std::clamp(vals[k + 1], 0.f, 1.f); + + // start/end component table indices + const size_t c_start = k * 255 / n, c_end = (k + 1) * 255 / n; + ASSERT(c_end <= 255); + + for (size_t ci = c_start; ci < c_end; ++ci) { + const float lerp_t = static_cast(ci - c_start) / static_cast(c_end - c_start); + const float component = lerp_func(v0, v1, lerp_t); + ASSERT(component >= 0 && component <= 1); + + tbl[ci] = static_cast(std::round(component * 255)); + } + } + + tbl.back() = static_cast(std::round(255 * std::clamp(vals.back(), 0.f, 1.f))); + + return tbl; + }; + + const auto make_table = [&]() -> std::vector { + return lerp_from_table_values([](float v0, float v1, float t) { return v0 + (v1 - v0) * t; }); + }; + + const auto make_discrete = [&]() -> std::vector { + return lerp_from_table_values([](float v0, float /*v1*/, float /*t*/) { return v0; }); + }; + + switch (this->getType()) { + case SVGFeFuncType::kIdentity: + return {}; + case SVGFeFuncType::kTable: + return make_table(); + case SVGFeFuncType::kDiscrete: + return make_discrete(); + case SVGFeFuncType::kLinear: + return make_linear(); + case SVGFeFuncType::kGamma: + return make_gamma(); + } + + // SkUNREACHABLE; +} + +bool SkSVGFeFunc::parseAndSetAttribute(const char* name, const char* val) { + return INHERITED::parseAndSetAttribute(name, val) || + this->setAmplitude(SVGAttributeParser::parse("amplitude", name, val)) || + this->setExponent(SVGAttributeParser::parse("exponent", name, val)) || + this->setIntercept(SVGAttributeParser::parse("intercept", name, val)) || + this->setOffset(SVGAttributeParser::parse("offset", name, val)) || + this->setSlope(SVGAttributeParser::parse("slope", name, val)) || + this->setTableValues( + SVGAttributeParser::parse>("tableValues", name, val)) || + this->setType(SVGAttributeParser::parse("type", name, val)); +} + +template <> +bool SVGAttributeParser::parse(SVGFeFuncType* type) { + static constexpr std::tuple gTypeMap[] = { + {"identity", SVGFeFuncType::kIdentity}, {"table", SVGFeFuncType::kTable}, + {"discrete", SVGFeFuncType::kDiscrete}, {"linear", SVGFeFuncType::kLinear}, + {"gamma", SVGFeFuncType::kGamma}, + }; + + return this->parseEnumMap(gTypeMap, type) && this->parseEOSToken(); +} +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeComposite.cpp b/src/svg/node/SVGFeComposite.cpp new file mode 100644 index 00000000..f895cc52 --- /dev/null +++ b/src/svg/node/SVGFeComposite.cpp @@ -0,0 +1,90 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGFeComposite.h" +#include +#include "core/utils/Log.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGAttributeParser.h" + +namespace tgfx { + +class SVGRenderContext; + +bool SkSVGFeComposite::parseAndSetAttribute(const char* name, const char* value) { + return INHERITED::parseAndSetAttribute(name, value) || + // SkSVGFeInputType parsing defined in SkSVGFe.cpp: + this->setIn2(SVGAttributeParser::parse("in2", name, value)) || + this->setK1(SVGAttributeParser::parse("k1", name, value)) || + this->setK2(SVGAttributeParser::parse("k2", name, value)) || + this->setK3(SVGAttributeParser::parse("k3", name, value)) || + this->setK4(SVGAttributeParser::parse("k4", name, value)) || + this->setOperator( + SVGAttributeParser::parse("operator", name, value)); +} + +#ifndef RENDER_SVG +BlendMode SkSVGFeComposite::BlendModeForOperator(SVGFeCompositeOperator op) { + switch (op) { + case SVGFeCompositeOperator::kOver: + return BlendMode::SrcOver; + case SVGFeCompositeOperator::kIn: + return BlendMode::SrcIn; + case SVGFeCompositeOperator::kOut: + return BlendMode::SrcOut; + case SVGFeCompositeOperator::kAtop: + return BlendMode::SrcATop; + case SVGFeCompositeOperator::kXor: + return BlendMode::Xor; + case SVGFeCompositeOperator::kArithmetic: + // Arithmetic is not handled with a blend + ASSERT(false); + return BlendMode::SrcOver; + } +} + +std::shared_ptr SkSVGFeComposite::onMakeImageFilter( + const SVGRenderContext& /*ctx*/, const SkSVGFilterContext& /*fctx*/) const { + // const Rect cropRect = this->resolveFilterSubregion(ctx, fctx); + // const SVGColorspace colorspace = this->resolveColorspace(ctx, fctx); + // const std::shared_ptr background = fctx.resolveInput(ctx, fIn2, colorspace); + // const std::shared_ptr foreground = fctx.resolveInput(ctx, this->getIn(), colorspace); + // if (fOperator == SVGFeCompositeOperator::kArithmetic) { + // constexpr bool enforcePMColor = true; + // return ImageFilter::Arithmetic(fK1, fK2, fK3, fK4, enforcePMColor, background, foreground, + // cropRect); + // } else { + // return ImageFilter::Blend(BlendModeForOperator(fOperator), background, foreground, cropRect); + // } + + //TODO (YG) + return nullptr; +} +#endif // RENDER_SVG + +template <> +bool SVGAttributeParser::parse(SVGFeCompositeOperator* op) { + static constexpr std::tuple gOpMap[] = { + {"over", SVGFeCompositeOperator::kOver}, {"in", SVGFeCompositeOperator::kIn}, + {"out", SVGFeCompositeOperator::kOut}, {"atop", SVGFeCompositeOperator::kAtop}, + {"xor", SVGFeCompositeOperator::kXor}, {"arithmetic", SVGFeCompositeOperator::kArithmetic}, + }; + + return this->parseEnumMap(gOpMap, op) && this->parseEOSToken(); +} +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeDisplacementMap.cpp b/src/svg/node/SVGFeDisplacementMap.cpp new file mode 100644 index 00000000..bc44895d --- /dev/null +++ b/src/svg/node/SVGFeDisplacementMap.cpp @@ -0,0 +1,85 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGFeDisplacementMap.h" +#include +#include "tgfx/core/Color.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGAttributeParser.h" + +namespace tgfx { + +bool SkSVGFeDisplacementMap::parseAndSetAttribute(const char* name, const char* value) { + return INHERITED::parseAndSetAttribute(name, value) || + this->setIn2(SVGAttributeParser::parse("in2", name, value)) || + this->setXChannelSelector( + SVGAttributeParser::parse("xChannelSelector", + name, value)) || + this->setYChannelSelector( + SVGAttributeParser::parse("yChannelSelector", + name, value)) || + this->setScale(SVGAttributeParser::parse("scale", name, value)); +} + +#ifndef RENDER_SVG +std::shared_ptr SkSVGFeDisplacementMap::onMakeImageFilter( + const SVGRenderContext& ctx, const SkSVGFilterContext& fctx) const { + const Rect cropRect = this->resolveFilterSubregion(ctx, fctx); + cropRect.centerX(); + const SVGColorspace colorspace = this->resolveColorspace(ctx, fctx); + + // According to spec https://www.w3.org/TR/SVG11/filters.html#feDisplacementMapElement, + // the 'in' source image must remain in its current colorspace. + std::shared_ptr in = fctx.resolveInput(ctx, this->getIn()); + std::shared_ptr in2 = fctx.resolveInput(ctx, this->getIn2(), colorspace); + + float scale = fScale; + if (fctx.primitiveUnits().type() == SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox) { + const auto obbt = ctx.transformForCurrentOBB(fctx.primitiveUnits()); + scale = SVGLengthContext({obbt.scale.x, obbt.scale.y}) + .resolve(SVGLength(scale, SVGLength::Unit::kPercentage), + SVGLengthContext::LengthType::kOther); + } + + return nullptr; + // return SkImageFilters::DisplacementMap(fXChannelSelector, fYChannelSelector, scale, in2, in, + // cropRect); +} + +SVGColorspace SkSVGFeDisplacementMap::resolveColorspace(const SVGRenderContext& ctx, + const SkSVGFilterContext& fctx) const { + // According to spec https://www.w3.org/TR/SVG11/filters.html#feDisplacementMapElement, + // the 'in' source image must remain in its current colorspace, which means the colorspace of + // this FE node is the same as the input. + return fctx.resolveInputColorspace(ctx, this->getIn()); +} +#endif + +template <> +bool SVGAttributeParser::parse( + SkSVGFeDisplacementMap::ChannelSelector* channel) { + static constexpr std::tuple gMap[] = { + {"R", SkSVGFeDisplacementMap::ChannelSelector::R}, + {"G", SkSVGFeDisplacementMap::ChannelSelector::G}, + {"B", SkSVGFeDisplacementMap::ChannelSelector::B}, + {"A", SkSVGFeDisplacementMap::ChannelSelector::A}, + }; + + return this->parseEnumMap(gMap, channel) && this->parseEOSToken(); +} +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeFlood.cpp b/src/svg/node/SVGFeFlood.cpp new file mode 100644 index 00000000..603b5a73 --- /dev/null +++ b/src/svg/node/SVGFeFlood.cpp @@ -0,0 +1,42 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +// #include "tgfx/svg/SVGFeFlood.h" + +// class SkSVGFilterContext; + +#ifdef RENDER_SVG +SkColor SkSVGFeFlood::resolveFloodColor(const SVGRenderContext& ctx) const { + const auto floodColor = this->getFloodColor(); + const auto floodOpacity = this->getFloodOpacity(); + // Uninherited presentation attributes should have a concrete value by now. + if (!floodColor.isValue() || !floodOpacity.isValue()) { + SkDebugf("unhandled: flood-color or flood-opacity has no value\n"); + return SK_ColorBLACK; + } + + const SkColor color = ctx.resolveSvgColor(*floodColor); + return SkColorSetA(color, SkScalarRoundToInt(*floodOpacity * 255)); +} + +sk_sp SkSVGFeFlood::onMakeImageFilter(const SVGRenderContext& ctx, + const SkSVGFilterContext& fctx) const { + return SkImageFilters::Shader(SkShaders::Color(resolveFloodColor(ctx)), + this->resolveFilterSubregion(ctx, fctx)); +} +#endif \ No newline at end of file diff --git a/src/svg/node/SVGFeGaussianBlur.cpp b/src/svg/node/SVGFeGaussianBlur.cpp new file mode 100644 index 00000000..a2c97b68 --- /dev/null +++ b/src/svg/node/SVGFeGaussianBlur.cpp @@ -0,0 +1,59 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGFeGaussianBlur.h" +#include "tgfx/core/ImageFilter.h" +#include "tgfx/core/Point.h" +#include "tgfx/svg/SVGAttributeParser.h" + +namespace tgfx { + +bool SkSVGFeGaussianBlur::parseAndSetAttribute(const char* name, const char* value) { + return INHERITED::parseAndSetAttribute(name, value) || + this->setStdDeviation(SVGAttributeParser::parse( + "stdDeviation", name, value)); +} + +#ifndef RENDER_SVG +std::shared_ptr SkSVGFeGaussianBlur::onMakeImageFilter( + const SVGRenderContext& ctx, const SkSVGFilterContext& fctx) const { + auto scale = ctx.transformForCurrentOBB(fctx.primitiveUnits()).scale; + const auto sigmaX = fStdDeviation.fX * scale.x; + const auto sigmaY = fStdDeviation.fY * scale.y; + return ImageFilter::Blur(sigmaX, sigmaY); + + //TODO (YG): Implement this + // return SkImageFilters::Blur( + // sigma.x, sigma.y, fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)), + // this->resolveFilterSubregion(ctx, fctx)); +} +#endif + +template <> +bool SVGAttributeParser::parse( + SkSVGFeGaussianBlur::StdDeviation* stdDeviation) { + std::vector values; + if (!this->parse(&values)) { + return false; + } + + stdDeviation->fX = values[0]; + stdDeviation->fY = values.size() > 1 ? values[1] : values[0]; + return true; +} +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeImage.cpp b/src/svg/node/SVGFeImage.cpp new file mode 100644 index 00000000..8efe5349 --- /dev/null +++ b/src/svg/node/SVGFeImage.cpp @@ -0,0 +1,57 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGFeImage.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGAttributeParser.h" + +namespace tgfx { + +bool SkSVGFeImage::parseAndSetAttribute(const char* n, const char* v) { + return INHERITED::parseAndSetAttribute(n, v) || + this->setHref(SVGAttributeParser::parse("xlink:href", n, v)) || + this->setPreserveAspectRatio( + SVGAttributeParser::parse("preserveAspectRatio", n, v)); +} + +#ifndef RENDER_SVG +std::shared_ptr SkSVGFeImage::onMakeImageFilter( + const SVGRenderContext& /*ctx*/, const SkSVGFilterContext& /*fctx*/) const { + // // Load image and map viewbox (image bounds) to viewport (filter effects subregion). + // const Rect viewport = this->resolveFilterSubregion(ctx, fctx); + // const auto imgInfo = + // SVGImage::LoadImage(ctx.resourceProvider(), fHref, viewport, fPreserveAspectRatio); + // if (!imgInfo.fImage) { + // return nullptr; + // } + + // // Create the image filter mapped according to aspect ratio + // const Rect srcRect = Rect::Make(imgInfo.fImage->bounds()); + // const Rect& dstRect = imgInfo.fDst; + // // TODO: image-rendering property + // auto imgfilt = + // ImageFilter:: ::Image(imgInfo.fImage, srcRect, dstRect, + // SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNearest)); + + // // Aspect ratio mapping may end up drawing content outside of the filter effects region, + // // so perform an explicit crop. + // return SkImageFilters::Merge(&imgfilt, 1, fctx.filterEffectsRegion()); + return nullptr; +} +#endif +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeLightSource.cpp b/src/svg/node/SVGFeLightSource.cpp new file mode 100644 index 00000000..b697879c --- /dev/null +++ b/src/svg/node/SVGFeLightSource.cpp @@ -0,0 +1,63 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGFeLightSource.h" +#include +#include "tgfx/svg/SVGAttributeParser.h" + +namespace tgfx { + +// SkPoint3 SkSVGFeDistantLight::computeDirection() const { +// // Computing direction from azimuth+elevation is two 3D rotations: +// // - Rotate [1,0,0] about y axis first (elevation) +// // - Rotate result about z axis (azimuth) +// // Which is just the first column vector in the 3x3 matrix Rz*Ry. +// const float azimuthRad = SkDegreesToRadians(fAzimuth); +// const float elevationRad = SkDegreesToRadians(fElevation); +// const float sinAzimuth = sinf(azimuthRad), cosAzimuth = cosf(azimuthRad); +// const float sinElevation = sinf(elevationRad), cosElevation = cosf(elevationRad); +// return SkPoint3::Make(cosAzimuth * cosElevation, sinAzimuth * cosElevation, sinElevation); +// } + +bool SkSVGFeDistantLight::parseAndSetAttribute(const char* n, const char* v) { + return INHERITED::parseAndSetAttribute(n, v) || + this->setAzimuth(SVGAttributeParser::parse("azimuth", n, v)) || + this->setElevation(SVGAttributeParser::parse("elevation", n, v)); +} + +bool SkSVGFePointLight::parseAndSetAttribute(const char* n, const char* v) { + return INHERITED::parseAndSetAttribute(n, v) || + this->setX(SVGAttributeParser::parse("x", n, v)) || + this->setY(SVGAttributeParser::parse("y", n, v)) || + this->setZ(SVGAttributeParser::parse("z", n, v)); +} + +bool SkSVGFeSpotLight::parseAndSetAttribute(const char* n, const char* v) { + return INHERITED::parseAndSetAttribute(n, v) || + this->setX(SVGAttributeParser::parse("x", n, v)) || + this->setY(SVGAttributeParser::parse("y", n, v)) || + this->setZ(SVGAttributeParser::parse("z", n, v)) || + this->setPointsAtX(SVGAttributeParser::parse("pointsAtX", n, v)) || + this->setPointsAtY(SVGAttributeParser::parse("pointsAtY", n, v)) || + this->setPointsAtZ(SVGAttributeParser::parse("pointsAtZ", n, v)) || + this->setSpecularExponent( + SVGAttributeParser::parse("specularExponent", n, v)) || + this->setLimitingConeAngle( + SVGAttributeParser::parse("limitingConeAngle", n, v)); +} +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeLighting.cpp b/src/svg/node/SVGFeLighting.cpp new file mode 100644 index 00000000..bd415e98 --- /dev/null +++ b/src/svg/node/SVGFeLighting.cpp @@ -0,0 +1,180 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGFeLighting.h" +#include "tgfx/svg/SVGAttributeParser.h" + +namespace tgfx { + +bool SkSVGFeLighting::parseAndSetAttribute(const char* n, const char* v) { + return INHERITED::parseAndSetAttribute(n, v) || + this->setSurfaceScale(SVGAttributeParser::parse("surfaceScale", n, v)) || + this->setKernelUnitLength(SVGAttributeParser::parse( + "kernelUnitLength", n, v)); +} + +template <> +bool SVGAttributeParser::parse( + SkSVGFeLighting::KernelUnitLength* kernelUnitLength) { + std::vector values; + if (!this->parse(&values)) { + return false; + } + + kernelUnitLength->fDx = values[0]; + kernelUnitLength->fDy = values.size() > 1 ? values[1] : values[0]; + return true; +} + +#ifdef RENDER_SVG +sk_sp SkSVGFeLighting::onMakeImageFilter(const SVGRenderContext& ctx, + const SkSVGFilterContext& fctx) const { + for (const auto& child : fChildren) { + switch (child->tag()) { + case SkSVGTag::kFeDistantLight: + return this->makeDistantLight(ctx, fctx, + static_cast(child.get())); + case SkSVGTag::kFePointLight: + return this->makePointLight(ctx, fctx, static_cast(child.get())); + case SkSVGTag::kFeSpotLight: + return this->makeSpotLight(ctx, fctx, static_cast(child.get())); + default: + // Ignore unknown children, such as elements + break; + } + } + + SkDebugf("lighting filter effect needs exactly one light source\n"); + return nullptr; +} + +SkColor SkSVGFeLighting::resolveLightingColor(const SVGRenderContext& ctx) const { + const auto color = this->getLightingColor(); + if (!color.isValue()) { + // Uninherited presentation attributes should have a concrete value by now. + SkDebugf("unhandled: lighting-color has no value\n"); + return SK_ColorWHITE; + } + + return ctx.resolveSvgColor(*color); +} + +SkPoint3 SkSVGFeLighting::resolveXYZ(const SVGRenderContext& ctx, const SkSVGFilterContext& fctx, + SkSVGNumberType x, SkSVGNumberType y, + SkSVGNumberType z) const { + const auto obbt = ctx.transformForCurrentOBB(fctx.primitiveUnits()); + const auto xy = SkV2{x, y} * obbt.scale + obbt.offset; + z = SkSVGLengthContext({obbt.scale.x, obbt.scale.y}) + .resolve(SkSVGLength(z * 100.f, SkSVGLength::Unit::kPercentage), + SkSVGLengthContext::LengthType::kOther); + return SkPoint3::Make(xy.x, xy.y, z); +} +#endif + +bool SkSVGFeSpecularLighting::parseAndSetAttribute(const char* n, const char* v) { + return INHERITED::parseAndSetAttribute(n, v) || + this->setSpecularConstant( + SVGAttributeParser::parse("specularConstant", n, v)) || + this->setSpecularExponent( + SVGAttributeParser::parse("specularExponent", n, v)); +} + +#ifdef RENDER_SVG +sk_sp SkSVGFeSpecularLighting::makeDistantLight( + const SVGRenderContext& ctx, const SkSVGFilterContext& fctx, + const SkSVGFeDistantLight* light) const { + const SkPoint3 dir = light->computeDirection(); + return SkImageFilters::DistantLitSpecular( + this->resolveXYZ(ctx, fctx, dir.fX, dir.fY, dir.fZ), this->resolveLightingColor(ctx), + this->getSurfaceScale(), fSpecularConstant, fSpecularExponent, + fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)), + this->resolveFilterSubregion(ctx, fctx)); +} + +sk_sp SkSVGFeSpecularLighting::makePointLight(const SVGRenderContext& ctx, + const SkSVGFilterContext& fctx, + const SkSVGFePointLight* light) const { + return SkImageFilters::PointLitSpecular( + this->resolveXYZ(ctx, fctx, light->getX(), light->getY(), light->getZ()), + this->resolveLightingColor(ctx), this->getSurfaceScale(), fSpecularConstant, + fSpecularExponent, fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)), + this->resolveFilterSubregion(ctx, fctx)); +} + +sk_sp SkSVGFeSpecularLighting::makeSpotLight(const SVGRenderContext& ctx, + const SkSVGFilterContext& fctx, + const SkSVGFeSpotLight* light) const { + const auto& limitingConeAngle = light->getLimitingConeAngle(); + const float cutoffAngle = limitingConeAngle.isValid() ? *limitingConeAngle : 180.f; + + return SkImageFilters::SpotLitSpecular( + this->resolveXYZ(ctx, fctx, light->getX(), light->getY(), light->getZ()), + this->resolveXYZ(ctx, fctx, light->getPointsAtX(), light->getPointsAtY(), + light->getPointsAtZ()), + light->getSpecularExponent(), cutoffAngle, this->resolveLightingColor(ctx), + this->getSurfaceScale(), fSpecularConstant, fSpecularExponent, + fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)), + this->resolveFilterSubregion(ctx, fctx)); +} +#endif + +bool SkSVGFeDiffuseLighting::parseAndSetAttribute(const char* n, const char* v) { + return INHERITED::parseAndSetAttribute(n, v) || + this->setDiffuseConstant( + SVGAttributeParser::parse("diffuseConstant", n, v)); +} + +#ifdef RENDER_SVG +sk_sp SkSVGFeDiffuseLighting::makeDistantLight( + const SVGRenderContext& ctx, const SkSVGFilterContext& fctx, + const SkSVGFeDistantLight* light) const { + const SkPoint3 dir = light->computeDirection(); + return SkImageFilters::DistantLitDiffuse( + this->resolveXYZ(ctx, fctx, dir.fX, dir.fY, dir.fZ), this->resolveLightingColor(ctx), + this->getSurfaceScale(), this->getDiffuseConstant(), + fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)), + this->resolveFilterSubregion(ctx, fctx)); +} + +sk_sp SkSVGFeDiffuseLighting::makePointLight(const SVGRenderContext& ctx, + const SkSVGFilterContext& fctx, + const SkSVGFePointLight* light) const { + return SkImageFilters::PointLitDiffuse( + this->resolveXYZ(ctx, fctx, light->getX(), light->getY(), light->getZ()), + this->resolveLightingColor(ctx), this->getSurfaceScale(), this->getDiffuseConstant(), + fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)), + this->resolveFilterSubregion(ctx, fctx)); +} + +sk_sp SkSVGFeDiffuseLighting::makeSpotLight(const SVGRenderContext& ctx, + const SkSVGFilterContext& fctx, + const SkSVGFeSpotLight* light) const { + const auto& limitingConeAngle = light->getLimitingConeAngle(); + const float cutoffAngle = limitingConeAngle.isValid() ? *limitingConeAngle : 180.f; + + return SkImageFilters::SpotLitDiffuse( + this->resolveXYZ(ctx, fctx, light->getX(), light->getY(), light->getZ()), + this->resolveXYZ(ctx, fctx, light->getPointsAtX(), light->getPointsAtY(), + light->getPointsAtZ()), + light->getSpecularExponent(), cutoffAngle, this->resolveLightingColor(ctx), + this->getSurfaceScale(), this->getDiffuseConstant(), + fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)), + this->resolveFilterSubregion(ctx, fctx)); +} +#endif +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeMerge.cpp b/src/svg/node/SVGFeMerge.cpp new file mode 100644 index 00000000..94952a64 --- /dev/null +++ b/src/svg/node/SVGFeMerge.cpp @@ -0,0 +1,59 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGFeMerge.h" +#include "tgfx/core/ImageFilter.h" +#include "tgfx/svg/SVGAttributeParser.h" + +namespace tgfx { + +// class SVGRenderContext; + +bool SkSVGFeMergeNode::parseAndSetAttribute(const char* name, const char* value) { + return INHERITED::parseAndSetAttribute(name, value) || + this->setIn(SVGAttributeParser::parse("in", name, value)); +} + +#ifndef RENDER_SVG +std::shared_ptr SkSVGFeMerge::onMakeImageFilter( + const SVGRenderContext& /*ctx*/, const SkSVGFilterContext& /*fctx*/) const { + // const SkSVGColorspace colorspace = this->resolveColorspace(ctx, fctx); + + // skia_private::STArray<8, sk_sp> merge_node_filters; + // merge_node_filters.reserve(fChildren.size()); + + // this->forEachChild([&](const SkSVGFeMergeNode* child) { + // merge_node_filters.push_back(fctx.resolveInput(ctx, child->getIn(), colorspace)); + // }); + + // return SkImageFilters::Merge(merge_node_filters.data(), merge_node_filters.size(), + // this->resolveFilterSubregion(ctx, fctx)); + return nullptr; +} +#endif + +std::vector SkSVGFeMerge::getInputs() const { + std::vector inputs; + inputs.reserve(fChildren.size()); + + this->forEachChild( + [&](const SkSVGFeMergeNode* child) { inputs.push_back(child->getIn()); }); + + return inputs; +} +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeMorphology.cpp b/src/svg/node/SVGFeMorphology.cpp new file mode 100644 index 00000000..e6309435 --- /dev/null +++ b/src/svg/node/SVGFeMorphology.cpp @@ -0,0 +1,73 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGFeMorphology.h" +#include +#include "tgfx/svg/SVGAttributeParser.h" + +namespace tgfx { + +bool SkSVGFeMorphology::parseAndSetAttribute(const char* name, const char* value) { + return INHERITED::parseAndSetAttribute(name, value) || + this->setOperator( + SVGAttributeParser::parse("operator", name, value)) || + this->setRadius( + SVGAttributeParser::parse("radius", name, value)); +} + +#ifndef RENDER_SVG +std::shared_ptr SkSVGFeMorphology::onMakeImageFilter( + const SVGRenderContext& /*ctx*/, const SkSVGFilterContext& /*fctx*/) const { + // const SkRect cropRect = this->resolveFilterSubregion(ctx, fctx); + // const SkSVGColorspace colorspace = this->resolveColorspace(ctx, fctx); + // sk_sp input = fctx.resolveInput(ctx, this->getIn(), colorspace); + + // const auto r = + // SkV2{fRadius.fX, fRadius.fY} * ctx.transformForCurrentOBB(fctx.primitiveUnits()).scale; + // switch (fOperator) { + // case Operator::kErode: + // return SkImageFilters::Erode(r.x, r.y, input, cropRect); + // case Operator::kDilate: + // return SkImageFilters::Dilate(r.x, r.y, input, cropRect); + // } + return nullptr; +} +#endif + +template <> +bool SVGAttributeParser::parse(SkSVGFeMorphology::Operator* op) { + static constexpr std::tuple gMap[] = { + {"dilate", SkSVGFeMorphology::Operator::kDilate}, + {"erode", SkSVGFeMorphology::Operator::kErode}, + }; + + return this->parseEnumMap(gMap, op) && this->parseEOSToken(); +} + +template <> +bool SVGAttributeParser::parse(SkSVGFeMorphology::Radius* radius) { + std::vector values; + if (!this->parse(&values)) { + return false; + } + + radius->fX = values[0]; + radius->fY = values.size() > 1 ? values[1] : values[0]; + return true; +} +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeOffset.cpp b/src/svg/node/SVGFeOffset.cpp new file mode 100644 index 00000000..baecffbe --- /dev/null +++ b/src/svg/node/SVGFeOffset.cpp @@ -0,0 +1,45 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGFeOffset.h" +#include "tgfx/core/ImageFilter.h" +#include "tgfx/svg/SVGAttributeParser.h" + +namespace tgfx { + +bool SkSVGFeOffset::parseAndSetAttribute(const char* name, const char* value) { + return INHERITED::parseAndSetAttribute(name, value) || + this->setDx(SVGAttributeParser::parse("dx", name, value)) || + this->setDy(SVGAttributeParser::parse("dy", name, value)); +} + +#ifndef RENDER_SVG +std::shared_ptr SkSVGFeOffset::onMakeImageFilter( + const SVGRenderContext& /*ctx*/, const SkSVGFilterContext& /*fctx*/) const { + // const auto d = + // SkV2{this->getDx(), this->getDy()} * ctx.transformForCurrentOBB(fctx.primitiveUnits()).scale; + + // sk_sp in = + // fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)); + // return SkImageFilters::Offset(d.x, d.y, std::move(in), this->resolveFilterSubregion(ctx, fctx)); + + //TODO (YG) + return nullptr; +} +#endif +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeTurbulence.cpp b/src/svg/node/SVGFeTurbulence.cpp new file mode 100644 index 00000000..0eacc41d --- /dev/null +++ b/src/svg/node/SVGFeTurbulence.cpp @@ -0,0 +1,81 @@ +/* + * Copyright 2020 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "tgfx/svg/node/SVGFeTurbulence.h" +#include "tgfx/core/ImageFilter.h" +#include "tgfx/svg/SVGAttributeParser.h" + +namespace tgfx { + +bool SkSVGFeTurbulence::parseAndSetAttribute(const char* name, const char* value) { + return INHERITED::parseAndSetAttribute(name, value) || + this->setNumOctaves( + SVGAttributeParser::parse("numOctaves", name, value)) || + this->setSeed(SVGAttributeParser::parse("seed", name, value)) || + this->setBaseFrequency(SVGAttributeParser::parse( + "baseFrequency", name, value)) || + this->setTurbulenceType( + SVGAttributeParser::parse("type", name, value)); +} + +template <> +bool SVGAttributeParser::parse(SVGFeTurbulenceBaseFrequency* freq) { + SVGNumberType freqX; + if (!this->parse(&freqX)) { + return false; + } + + SVGNumberType freqY; + this->parseCommaWspToken(); + if (this->parse(&freqY)) { + *freq = SVGFeTurbulenceBaseFrequency(freqX, freqY); + } else { + *freq = SVGFeTurbulenceBaseFrequency(freqX, freqX); + } + + return this->parseEOSToken(); +} + +template <> +bool SVGAttributeParser::parse(SVGFeTurbulenceType* type) { + bool parsedValue = false; + + if (this->parseExpectedStringToken("fractalNoise")) { + *type = SVGFeTurbulenceType(SVGFeTurbulenceType::kFractalNoise); + parsedValue = true; + } else if (this->parseExpectedStringToken("turbulence")) { + *type = SVGFeTurbulenceType(SVGFeTurbulenceType::kTurbulence); + parsedValue = true; + } + + return parsedValue && this->parseEOSToken(); +} + +#ifndef RENDER_SVG +std::shared_ptr SkSVGFeTurbulence::onMakeImageFilter( + const SVGRenderContext& /*ctx*/, const SkSVGFilterContext& /*fctx*/) const { + // const SkISize* tileSize = nullptr; // TODO: needs filter element subregion properties + + // sk_sp shader; + // switch (fTurbulenceType.fType) { + // case SkSVGFeTurbulenceType::Type::kTurbulence: + // shader = SkShaders::MakeTurbulence(fBaseFrequency.freqX(), fBaseFrequency.freqY(), + // fNumOctaves, fSeed, tileSize); + // break; + // case SkSVGFeTurbulenceType::Type::kFractalNoise: + // shader = SkShaders::MakeFractalNoise(fBaseFrequency.freqX(), fBaseFrequency.freqY(), + // fNumOctaves, fSeed, tileSize); + // break; + // } + + // return SkImageFilters::Shader(shader, this->resolveFilterSubregion(ctx, fctx)); + + //TODO (YG) + return nullptr; +} +#endif +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFilter.cpp b/src/svg/node/SVGFilter.cpp new file mode 100644 index 00000000..fcea1cdd --- /dev/null +++ b/src/svg/node/SVGFilter.cpp @@ -0,0 +1,91 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGFilter.h" +#include "tgfx/core/ColorFilter.h" +#include "tgfx/core/ImageFilter.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGAttributeParser.h" +#include "tgfx/svg/SVGRenderContext.h" +#include "tgfx/svg/node/SVGFe.h" +#include "tgfx/svg/node/SVGFilterContext.h" + +namespace tgfx { + +bool SkSVGFilter::parseAndSetAttribute(const char* name, const char* value) { + return INHERITED::parseAndSetAttribute(name, value) || + this->setX(SVGAttributeParser::parse("x", name, value)) || + this->setY(SVGAttributeParser::parse("y", name, value)) || + this->setWidth(SVGAttributeParser::parse("width", name, value)) || + this->setHeight(SVGAttributeParser::parse("height", name, value)) || + this->setFilterUnits( + SVGAttributeParser::parse("filterUnits", name, value)) || + this->setPrimitiveUnits( + SVGAttributeParser::parse("primitiveUnits", name, value)); +} + +void SkSVGFilter::applyProperties(SVGRenderContext* ctx) const { + this->onPrepareToRender(ctx); +} + +std::shared_ptr SkSVGFilter::buildFilterDAG(const SVGRenderContext& ctx) const { + std::shared_ptr filter; + SkSVGFilterContext fctx(ctx.resolveOBBRect(fX, fY, fWidth, fHeight, fFilterUnits), + fPrimitiveUnits); + SVGRenderContext localCtx(ctx); + this->applyProperties(&localCtx); + SVGColorspace cs = SVGColorspace::kSRGB; + for (const auto& child : fChildren) { + if (!SkSVGFe::IsFilterEffect(child)) { + continue; + } + + const auto& feNode = static_cast(*child); + const auto& feResultType = feNode.getResult(); + + // Propagate any inherited properties that may impact filter effect behavior (e.g. + // color-interpolation-filters). We call this explicitly here because the SkSVGFe + // nodes do not participate in the normal onRender path, which is when property + // propagation currently occurs. + SVGRenderContext localChildCtx(localCtx); + feNode.applyProperties(&localChildCtx); + + const Rect filterSubregion = feNode.resolveFilterSubregion(localChildCtx, fctx); + cs = feNode.resolveColorspace(localChildCtx, fctx); + filter = feNode.makeImageFilter(localChildCtx, fctx); + + if (!feResultType.empty()) { + fctx.registerResult(feResultType, filter, filterSubregion, cs); + } + + // Unspecified 'in' and 'in2' inputs implicitly resolve to the previous filter's result. + fctx.setPreviousResult(filter, filterSubregion, cs); + } + + // Convert to final destination colorspace + if (cs != SVGColorspace::kSRGB) { + // filter = SkImageFilters::ColorFilter(SkColorFilters::LinearToSRGBGamma(), filter); + + //TODO (YG) + filter = nullptr; + } + + return filter; +} + +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFilterContext.cpp b/src/svg/node/SVGFilterContext.cpp new file mode 100644 index 00000000..6de1326f --- /dev/null +++ b/src/svg/node/SVGFilterContext.cpp @@ -0,0 +1,174 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGFilterContext.h" +#include +#include "core/utils/Log.h" +#include "tgfx/core/ColorFilter.h" +#include "tgfx/core/ImageFilter.h" +#include "tgfx/core/Rect.h" +#include "tgfx/core/Shader.h" +#include "tgfx/svg/SVGTypes.h" + +namespace tgfx { +namespace { +#ifndef RENDER_SVG +std::shared_ptr ConvertFilterColorspace(std::shared_ptr&& input, + SVGColorspace src, SVGColorspace dst) { + if (src == dst) { + return std::move(input); + } + // TODO (YG) - Implement this + // else if (src == SVGColorspace::kSRGB && dst == SVGColorspace::kLinearRGB) { + // return ImageFilter::ColorFilter(SkColorFilters::SRGBToLinearGamma(), input); + // } else { + // ASSERT(src == SVGColorspace::kLinearRGB && dst == SVGColorspace::kSRGB); + // return ImageFilter::ColorFilter(ColorFilters::LinearToSRGBGamma(), input); + // } + return std::move(input); +} + +// std::shared_ptr paint_as_shader(const Paint& paint) { +// std::shared_ptr shader = paint.getShader(); +// auto color = paint.getColor(); +// if (shader && color.alpha < 1.f) { +// // Multiply by paint alpha +// shader = shader->makeWithColorFilter(ColorFilter::Blend(color, BlendMode::DstIn)); +// } else if (!shader) { +// shader = Shader::MakeColorShader(color); +// } +// if (paint.getColorFilter()) { +// shader = shader->makeWithColorFilter(paint.getColorFilter()); +// } +// return shader; +// } + +#endif +} // namespace + +const SkSVGFilterContext::Result* SkSVGFilterContext::findResultById( + const SVGStringType& id) const { + auto iter = fResults.find(id); + return iter != fResults.end() ? &iter->second : nullptr; +} + +const Rect& SkSVGFilterContext::filterPrimitiveSubregion(const SVGFeInputType& input) const { + const Result* res = nullptr; + if (input.type() == SVGFeInputType::Type::kFilterPrimitiveReference) { + auto iter = fResults.find(input.id()); + res = iter != fResults.end() ? &iter->second : nullptr; + } else if (input.type() == SVGFeInputType::Type::kUnspecified) { + res = &fPreviousResult; + } + return res ? res->fFilterSubregion : fFilterEffectsRegion; +} + +void SkSVGFilterContext::registerResult(const SVGStringType& id, + const std::shared_ptr& result, + const Rect& subregion, SVGColorspace resultColorspace) { + ASSERT(!id.empty()); + fResults[id] = {result, subregion, resultColorspace}; +} + +void SkSVGFilterContext::setPreviousResult(const std::shared_ptr& result, + const Rect& subregion, SVGColorspace resultColorspace) { + fPreviousResult = {result, subregion, resultColorspace}; +} + +bool SkSVGFilterContext::previousResultIsSourceGraphic() const { + return fPreviousResult.fImageFilter == nullptr; +} + +#ifndef RENDER_SVG +// https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveInAttribute +std::tuple, SVGColorspace> SkSVGFilterContext::getInput( + const SVGRenderContext& ctx, const SVGFeInputType& inputType) const { + SVGColorspace inputCS = SVGColorspace::kSRGB; + std::shared_ptr result; + switch (inputType.type()) { + case SVGFeInputType::Type::kSourceAlpha: { + + //TODO (YG) - Implement this with class ColorMatrix + std::array colorMatrix{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}; + auto colorFilter = ColorFilter::Matrix(colorMatrix); + result = ImageFilter::ColorFilter(colorFilter); + break; + } + case SVGFeInputType::Type::kSourceGraphic: + // Do nothing. + break; + case SVGFeInputType::Type::kFillPaint: { + const auto& fillPaint = ctx.fillPaint(); + if (fillPaint.has_value()) { + //TODO (YG) - Implement this by dither and shader image filter + // auto dither = + // fillPaint->isDither() ? SkImageFilters::Dither::kYes : SkImageFilters::Dither::kNo; + // result = SkImageFilters::Shader(paint_as_shader(*fillPaint), dither); + } + break; + } + case SVGFeInputType::Type::kStrokePaint: { + // The paint filter doesn't apply fill/stroke styling, but use the paint settings + // defined for strokes. + const auto& strokePaint = ctx.strokePaint(); + if (strokePaint.has_value()) { + //TODO (YG) - Implement this by dither and shader image filter + // auto dither = + // strokePaint->isDither() ? SkImageFilters::Dither::kYes : SkImageFilters::Dither::kNo; + // result = SkImageFilters::Shader(paint_as_shader(*strokePaint), dither); + } + break; + } + case SVGFeInputType::Type::kFilterPrimitiveReference: { + const Result* res = findResultById(inputType.id()); + if (res) { + result = res->fImageFilter; + inputCS = res->fColorspace; + } + break; + } + case SVGFeInputType::Type::kUnspecified: { + result = fPreviousResult.fImageFilter; + inputCS = fPreviousResult.fColorspace; + break; + } + default: + break; + } + + return {result, inputCS}; +} + +SVGColorspace SkSVGFilterContext::resolveInputColorspace(const SVGRenderContext& ctx, + const SVGFeInputType& inputType) const { + return std::get<1>(this->getInput(ctx, inputType)); +} + +std::shared_ptr SkSVGFilterContext::resolveInput( + const SVGRenderContext& ctx, const SVGFeInputType& inputType) const { + return std::get<0>(this->getInput(ctx, inputType)); +} + +std::shared_ptr SkSVGFilterContext::resolveInput(const SVGRenderContext& ctx, + const SVGFeInputType& inputType, + SVGColorspace colorspace) const { + auto [result, inputCS] = this->getInput(ctx, inputType); + return ConvertFilterColorspace(std::move(result), inputCS, colorspace); +} +#endif +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGGradient.cpp b/src/svg/node/SVGGradient.cpp new file mode 100644 index 00000000..8c22587c --- /dev/null +++ b/src/svg/node/SVGGradient.cpp @@ -0,0 +1,130 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGGradient.h" +#include +#include +#include +#include "core/utils/Log.h" +#include "tgfx/core/Color.h" +#include "tgfx/core/Matrix.h" +#include "tgfx/svg/SVGAttributeParser.h" + +namespace tgfx { + +bool SkSVGGradient::parseAndSetAttribute(const char* name, const char* value) { + return INHERITED::parseAndSetAttribute(name, value) || + this->setGradientTransform( + SVGAttributeParser::parse("gradientTransform", name, value)) || + this->setHref(SVGAttributeParser::parse("xlink:href", name, value)) || + this->setSpreadMethod( + SVGAttributeParser::parse("spreadMethod", name, value)) || + this->setGradientUnits( + SVGAttributeParser::parse("gradientUnits", name, value)); +} + +#ifndef RENDER_SVG +// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementHrefAttribute +void SkSVGGradient::collectColorStops(const SVGRenderContext& ctx, std::vector& colors, + std::vector& positions) const { + // Used to resolve percentage offsets. + const SVGLengthContext ltx(Size::Make(1, 1)); + + this->forEachChild([&](const SkSVGStop* stop) { + colors.push_back(this->resolveStopColor(ctx, *stop)); + positions.push_back( + std::clamp(ltx.resolve(stop->getOffset(), SVGLengthContext::LengthType::kOther), 0.f, 1.f)); + }); + + ASSERT(colors.size() == positions.size()); + + if (positions.empty() && !fHref.iri().empty()) { + const auto ref = ctx.findNodeById(fHref); + if (ref && (ref->tag() == SVGTag::kLinearGradient || ref->tag() == SVGTag::kRadialGradient)) { + static_cast(ref.get())->collectColorStops(ctx, colors, positions); + } + } +} + +Color SkSVGGradient::resolveStopColor(const SVGRenderContext& ctx, const SkSVGStop& stop) const { + const auto& stopColor = stop.getStopColor(); + const auto& stopOpacity = stop.getStopOpacity(); + // Uninherited presentation attrs should have a concrete value at this point. + if (!stopColor.isValue() || !stopOpacity.isValue()) { + return Color::Black(); + } + + const auto color = ctx.resolveSvgColor(*stopColor); + + return {color.red, color.green, color.blue, *stopOpacity * color.alpha}; +} + +bool SkSVGGradient::onAsPaint(const SVGRenderContext& ctx, Paint* paint) const { + std::vector colors; + std::vector positions; + + this->collectColorStops(ctx, colors, positions); + + // TODO: + // * stop (lazy?) sorting + // * href loop detection + // * href attribute inheritance (not just color stops) + // * objectBoundingBox units support + + static_assert(static_cast(SVGSpreadMethod::Type::kPad) == TileMode::Clamp, + "SkSVGSpreadMethod::Type is out of sync"); + static_assert(static_cast(SVGSpreadMethod::Type::kRepeat) == TileMode::Repeat, + "SkSVGSpreadMethod::Type is out of sync"); + static_assert(static_cast(SVGSpreadMethod::Type::kReflect) == TileMode::Mirror, + "SkSVGSpreadMethod::Type is out of sync"); + const auto tileMode = static_cast(fSpreadMethod.type()); + + const auto obbt = ctx.transformForCurrentOBB(fGradientUnits); + const auto localMatrix = Matrix::MakeTrans(obbt.offset.x, obbt.offset.y) * + Matrix::MakeScale(obbt.scale.x, obbt.scale.y) * fGradientTransform; + + paint->setShader(this->onMakeShader(ctx, colors, positions, tileMode, localMatrix)); + return true; +} +#endif + +// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementSpreadMethodAttribute +template <> +bool SVGAttributeParser::parse(SVGSpreadMethod* spread) { + struct TileInfo { + SVGSpreadMethod::Type fType; + const char* fName; + }; + static const TileInfo spreadInfoSet[] = { + {SVGSpreadMethod::Type::kPad, "pad"}, + {SVGSpreadMethod::Type::kReflect, "reflect"}, + {SVGSpreadMethod::Type::kRepeat, "repeat"}, + }; + + bool parsedValue = false; + for (auto info : spreadInfoSet) { + if (this->parseExpectedStringToken(info.fName)) { + *spread = SVGSpreadMethod(info.fType); + parsedValue = true; + break; + } + } + + return parsedValue && this->parseEOSToken(); +} +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGImage.cpp b/src/svg/node/SVGImage.cpp new file mode 100644 index 00000000..c12baef3 --- /dev/null +++ b/src/svg/node/SVGImage.cpp @@ -0,0 +1,184 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGImage.h" +#include +#include "core/ImageDecoder.h" +#include "core/utils/Log.h" +#include "fstream" +#include "gpu/ResourceProvider.h" +#include "tgfx/core/Data.h" +#include "tgfx/core/Image.h" +#include "tgfx/core/Matrix.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGAttributeParser.h" + +namespace tgfx { + +bool SkSVGImage::parseAndSetAttribute(const char* n, const char* v) { + return INHERITED::parseAndSetAttribute(n, v) || + this->setX(SVGAttributeParser::parse("x", n, v)) || + this->setY(SVGAttributeParser::parse("y", n, v)) || + this->setWidth(SVGAttributeParser::parse("width", n, v)) || + this->setHeight(SVGAttributeParser::parse("height", n, v)) || + this->setHref(SVGAttributeParser::parse("xlink:href", n, v)) || + this->setPreserveAspectRatio( + SVGAttributeParser::parse("preserveAspectRatio", n, v)); +} + +#ifndef RENDER_SVG +bool SkSVGImage::onPrepareToRender(SVGRenderContext* ctx) const { + // Width or height of 0 disables rendering per spec: + // https://www.w3.org/TR/SVG11/struct.html#ImageElement + return !fHref.iri().empty() && fWidth.value() > 0 && fHeight.value() > 0 && + INHERITED::onPrepareToRender(ctx); +} + +std::vector base64_decode(const std::string& encoded_string) { + static constexpr unsigned char kDecodingTable[] = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, + 64, 64, 64, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, 64, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 64, 64, 64, 64, 64, 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64}; + + size_t in_len = encoded_string.size(); + if (in_len % 4 != 0) throw std::runtime_error("Invalid base64 length"); + + size_t out_len = in_len / 4 * 3; + if (encoded_string[in_len - 1] == '=') out_len--; + if (encoded_string[in_len - 2] == '=') out_len--; + + std::vector out(out_len); + for (size_t i = 0, j = 0; i < in_len;) { + uint32_t a = encoded_string[i] == '=' + ? 0 & i++ + : kDecodingTable[static_cast(encoded_string[i++])]; + uint32_t b = encoded_string[i] == '=' + ? 0 & i++ + : kDecodingTable[static_cast(encoded_string[i++])]; + uint32_t c = encoded_string[i] == '=' + ? 0 & i++ + : kDecodingTable[static_cast(encoded_string[i++])]; + uint32_t d = encoded_string[i] == '=' + ? 0 & i++ + : kDecodingTable[static_cast(encoded_string[i++])]; + + uint32_t triple = (a << 18) + (b << 12) + (c << 6) + d; + + if (j < out_len) out[j++] = (triple >> 16) & 0xFF; + if (j < out_len) out[j++] = (triple >> 8) & 0xFF; + if (j < out_len) out[j++] = triple & 0xFF; + } + + return out; +} + +std::shared_ptr LoadImage(const std::shared_ptr&, const SVGIRI& href) { + const auto& base64URL = href.iri(); + auto pos = base64URL.find("base64,"); + if (pos == std::string::npos) { + return nullptr; + } + std::string base64Data = base64URL.substr(pos + 7); + std::vector imageData = base64_decode(base64Data); + auto data = Data::MakeWithCopy(imageData.data(), imageData.size()); + // { + // std::ofstream out("/Users/yg/Downloads/yg2.png", std::ios::binary); + // if (!out) { + // return nullptr; + // } + // out.write(reinterpret_cast(data->data()), + // static_cast(data->size())); + // } + return Image::MakeFromEncoded(data); + + // TODO: It may be better to use the SVG 'id' attribute as the asset id, to allow + // clients to perform asset substitution based on element id. + // sk_sp imageAsset; + // switch (href.type()) { + // case SVGIRI::Type::kDataURI: + // imageAsset = rp.loadImageAsset("", href.iri().c_str(), ""); + // break; + // case SVGIRI::Type::kNonlocal: { + // const auto path = SkOSPath::Dirname(href.iri().c_str()); + // const auto name = SkOSPath::Basename(href.iri().c_str()); + // imageAsset = rp->loadImageAsset(path.c_str(), name.c_str(), /* id */ name.c_str()); + // break; + // } + // default: + // return nullptr; + // } + + // return imageAsset ? imageAsset->getFrameData(0).image : nullptr; +} + +SkSVGImage::ImageInfo SkSVGImage::LoadImage(const std::shared_ptr& rp, + const SVGIRI& iri, const Rect& viewPort, + SVGPreserveAspectRatio /*par*/) { + // TODO: svg sources + std::shared_ptr image = ::tgfx::LoadImage(rp, iri); + if (!image) { + return {}; + } + + // Per spec: raster content has implicit viewbox of '0 0 width height'. + const Rect viewBox = Rect::MakeWH(image->width(), image->height()); + + // Map and place at x, y specified by viewport + // const Matrix m = ComputeViewboxMatrix(viewBox, viewPort, par); + // const Rect dst = m.mapRect(viewBox).makeOffset(viewPort.left, viewPort.top); + Rect dst = viewBox.makeOffset(viewPort.left, viewPort.top); + + return {std::move(image), dst}; +} + +void SkSVGImage::onRender(const SVGRenderContext& ctx) const { + // Per spec: x, w, width, height attributes establish the new viewport. + const SVGLengthContext& lctx = ctx.lengthContext(); + const Rect viewPort = lctx.resolveRect(fX, fY, fWidth, fHeight); + + //TODO (YG) + ImageInfo image; + const auto imgInfo = LoadImage(ctx.resourceProvider(), fHref, viewPort, fPreserveAspectRatio); + if (!imgInfo.fImage) { + LOGE("can't render image: load image failed\n"); + return; + } + + // TODO: image-rendering property + auto matrix = Matrix::MakeScale(viewPort.width() / imgInfo.fDst.width(), + viewPort.height() / imgInfo.fDst.height()); + matrix.preTranslate(imgInfo.fDst.x(), imgInfo.fDst.y()); + + ctx.canvas()->drawImage(imgInfo.fImage, matrix); + + // drawImageRect(imgInfo.fImage, imgInfo.fDst, SkSamplingOptions(SkFilterMode::kLinear)); +} + +Path SkSVGImage::onAsPath(const SVGRenderContext&) const { + return {}; +} + +Rect SkSVGImage::onObjectBoundingBox(const SVGRenderContext& ctx) const { + const SVGLengthContext& lctx = ctx.lengthContext(); + return lctx.resolveRect(fX, fY, fWidth, fHeight); +} +#endif +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGLine.cpp b/src/svg/node/SVGLine.cpp new file mode 100644 index 00000000..bc508afa --- /dev/null +++ b/src/svg/node/SVGLine.cpp @@ -0,0 +1,64 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGLine.h" +#include "tgfx/core/Canvas.h" +#include "tgfx/core/Path.h" +#include "tgfx/core/Point.h" +#include "tgfx/svg/SVGAttributeParser.h" + +namespace tgfx { + +SkSVGLine::SkSVGLine() : INHERITED(SVGTag::kLine) { +} + +bool SkSVGLine::parseAndSetAttribute(const char* n, const char* v) { + return INHERITED::parseAndSetAttribute(n, v) || + this->setX1(SVGAttributeParser::parse("x1", n, v)) || + this->setY1(SVGAttributeParser::parse("y1", n, v)) || + this->setX2(SVGAttributeParser::parse("x2", n, v)) || + this->setY2(SVGAttributeParser::parse("y2", n, v)); +} + +#ifndef RENDER_SVG +std::tuple SkSVGLine::resolve(const SVGLengthContext& lctx) const { + return std::make_tuple(Point::Make(lctx.resolve(fX1, SVGLengthContext::LengthType::kHorizontal), + lctx.resolve(fY1, SVGLengthContext::LengthType::kVertical)), + Point::Make(lctx.resolve(fX2, SVGLengthContext::LengthType::kHorizontal), + lctx.resolve(fY2, SVGLengthContext::LengthType::kVertical))); +} + +void SkSVGLine::onDraw(Canvas* canvas, const SVGLengthContext& lctx, const Paint& paint, + PathFillType) const { + auto [p0, p1] = this->resolve(lctx); + canvas->drawLine(p0, p1, paint); +} + +Path SkSVGLine::onAsPath(const SVGRenderContext& ctx) const { + auto [p0, p1] = this->resolve(ctx.lengthContext()); + + //TODO (YG) path add methods to support line + Path path; + path.moveTo(p0); + path.lineTo(p1); + this->mapToParent(&path); + + return path; +} +#endif +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGLinearGradient.cpp b/src/svg/node/SVGLinearGradient.cpp new file mode 100644 index 00000000..9083bf5e --- /dev/null +++ b/src/svg/node/SVGLinearGradient.cpp @@ -0,0 +1,57 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGLinearGradient.h" +#include "tgfx/core/Matrix.h" +#include "tgfx/core/Point.h" +#include "tgfx/core/Shader.h" +#include "tgfx/core/TileMode.h" +#include "tgfx/svg/SVGAttributeParser.h" + +namespace tgfx { + +SkSVGLinearGradient::SkSVGLinearGradient() : INHERITED(SVGTag::kLinearGradient) { +} + +bool SkSVGLinearGradient::parseAndSetAttribute(const char* name, const char* value) { + return INHERITED::parseAndSetAttribute(name, value) || + this->setX1(SVGAttributeParser::parse("x1", name, value)) || + this->setY1(SVGAttributeParser::parse("y1", name, value)) || + this->setX2(SVGAttributeParser::parse("x2", name, value)) || + this->setY2(SVGAttributeParser::parse("y2", name, value)); +} + +#ifndef RENDER_SVG +std::shared_ptr SkSVGLinearGradient::onMakeShader(const SVGRenderContext& ctx, + const std::vector& colors, + const std::vector& positions, + TileMode, const Matrix&) const { + const SVGLengthContext lctx = + this->getGradientUnits().type() == SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox + ? SVGLengthContext({1, 1}) + : ctx.lengthContext(); + + auto startPoint = Point::Make(lctx.resolve(fX1, SVGLengthContext::LengthType::kHorizontal), + lctx.resolve(fY1, SVGLengthContext::LengthType::kVertical)); + auto endPoint = Point::Make(lctx.resolve(fX2, SVGLengthContext::LengthType::kHorizontal), + lctx.resolve(fY2, SVGLengthContext::LengthType::kVertical)); + + return Shader::MakeLinearGradient(startPoint, endPoint, colors, positions); +} +#endif +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGMask.cpp b/src/svg/node/SVGMask.cpp new file mode 100644 index 00000000..b29853e6 --- /dev/null +++ b/src/svg/node/SVGMask.cpp @@ -0,0 +1,82 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGMask.h" +#include "tgfx/core/MaskFilter.h" +#include "tgfx/core/Paint.h" +#include "tgfx/svg/SVGAttributeParser.h" + +namespace tgfx { + +bool SkSVGMask::parseAndSetAttribute(const char* n, const char* v) { + return INHERITED::parseAndSetAttribute(n, v) || + this->setX(SVGAttributeParser::parse("x", n, v)) || + this->setY(SVGAttributeParser::parse("y", n, v)) || + this->setWidth(SVGAttributeParser::parse("width", n, v)) || + this->setHeight(SVGAttributeParser::parse("height", n, v)) || + this->setMaskUnits( + SVGAttributeParser::parse("maskUnits", n, v)) || + this->setMaskContentUnits( + SVGAttributeParser::parse("maskContentUnits", n, v)); +} + +#ifndef RENDER_SVG +Rect SkSVGMask::bounds(const SVGRenderContext& ctx) const { + return ctx.resolveOBBRect(fX, fY, fWidth, fHeight, fMaskUnits); +} + +void SkSVGMask::renderMask(const SVGRenderContext& /*ctx*/) const { + //TODO (YG) + + // // https://www.w3.org/TR/SVG11/masking.html#Masking + + // // Propagate any inherited properties that may impact mask effect behavior (e.g. + // // color-interpolation). We call this explicitly here because the SkSVGMask + // // nodes do not participate in the normal onRender path, which is when property + // // propagation currently occurs. + // // The local context also restores the filter layer created below on scope exit. + // SVGRenderContext lctx(ctx); + // this->onPrepareToRender(&lctx); + + // const auto ci = *lctx.presentationContext().fInherited.fColorInterpolation; + // auto ci_filter = (ci == SVGColorspace::kLinearRGB) ? ColorFilters::SRGBToLinearGamma() : nullptr; + + // Paint mask_filter; + // MaskFilter::Compose(std::shared_ptr inner, std::shared_ptr outer) + // mask_filter.setMaskFilter() + // mask_filter.setColorFilter( + // SkColorFilters::Compose(SkLumaColorFilter::Make(), std::move(ci_filter))); + + // // Mask color filter layer. + // // Note: We could avoid this extra layer if we invert the stacking order + // // (mask/content -> content/mask, kSrcIn -> kDstIn) and apply the filter + // // via the top (mask) layer paint. That requires deferring mask rendering + // // until after node content, which introduces extra state/complexity. + // // Something to consider if masking performance ever becomes an issue. + // lctx.canvas()->saveLayer(nullptr, &mask_filter); + + // const auto obbt = ctx.transformForCurrentOBB(fMaskContentUnits); + // lctx.canvas()->translate(obbt.offset.x, obbt.offset.y); + // lctx.canvas()->scale(obbt.scale.x, obbt.scale.y); + + // for (const auto& child : fChildren) { + // child->render(lctx); + // } +} +#endif +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGNode.cpp b/src/svg/node/SVGNode.cpp new file mode 100644 index 00000000..23eea5af --- /dev/null +++ b/src/svg/node/SVGNode.cpp @@ -0,0 +1,189 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGNode.h" +#include +#include +#include +#include +#include "core/utils/Log.h" +#include "tgfx/core/Color.h" +#include "tgfx/core/Matrix.h" +#include "tgfx/core/Paint.h" +#include "tgfx/core/Path.h" +#include "tgfx/core/PathTypes.h" +#include "tgfx/core/Point.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGRenderContext.h" +#include "tgfx/svg/SVGTypes.h" + +namespace tgfx { + +SVGNode::SVGNode(SVGTag t) : fTag(t) { + // Uninherited presentation attributes need a non-null default value. + fPresentationAttributes.fStopColor.set(SVGColor(Color::Black())); + fPresentationAttributes.fStopOpacity.set(static_cast(1.0f)); + fPresentationAttributes.fFloodColor.set(SVGColor(Color::Black())); + fPresentationAttributes.fFloodOpacity.set(static_cast(1.0f)); + fPresentationAttributes.fLightingColor.set(SVGColor(Color::White())); +} + +SVGNode::~SVGNode() { +} + +#ifndef RENDER_SVG +void SVGNode::render(const SVGRenderContext& ctx) const { + SVGRenderContext localContext(ctx, this); + + if (this->onPrepareToRender(&localContext)) { + this->onRender(localContext); + } +} + +bool SVGNode::asPaint(const SVGRenderContext& ctx, Paint* paint) const { + SVGRenderContext localContext(ctx); + + return this->onPrepareToRender(&localContext) && this->onAsPaint(localContext, paint); +} + +Path SVGNode::asPath(const SVGRenderContext& ctx) const { + SVGRenderContext localContext(ctx); + if (!this->onPrepareToRender(&localContext)) { + return Path(); + } + + Path path = this->onAsPath(localContext); + + if (auto clipPath = localContext.clipPath(); !clipPath.isEmpty()) { + // There is a clip-path present on the current node. + path.addPath(clipPath, PathOp::Intersect); + } + + return path; +} + +Rect SVGNode::objectBoundingBox(const SVGRenderContext& ctx) const { + return this->onObjectBoundingBox(ctx); +} + +bool SVGNode::onPrepareToRender(SVGRenderContext* ctx) const { + ctx->applyPresentationAttributes(fPresentationAttributes, + this->hasChildren() ? 0 : SVGRenderContext::kLeaf); + + // visibility:hidden and display:none disable rendering. + // TODO: if display is not a value (true when display="inherit"), we currently + // ignore it. Eventually we should be able to add SkASSERT(display.isValue()). + const auto visibility = ctx->presentationContext().fInherited.fVisibility->type(); + const auto display = fPresentationAttributes.fDisplay; // display is uninherited + return visibility != SVGVisibility::Type::kHidden && + (!display.isValue() || *display != SVGDisplay::kNone); +} +#endif + +void SVGNode::setAttribute(SVGAttribute attr, const SVGValue& v) { + this->onSetAttribute(attr, v); +} + +template +void SetInheritedByDefault(std::optional& presentation_attribute, const T& value) { + if (value.type() != T::Type::kInherit) { + presentation_attribute.set(value); + } else { + // kInherited values are semantically equivalent to + // the absence of a local presentation attribute. + presentation_attribute.reset(); + } +} + +bool SVGNode::parseAndSetAttribute(const char* n, const char* v) { +#define PARSE_AND_SET(svgName, attrName) \ + this->set##attrName( \ + SVGAttributeParser::parseProperty(svgName, n, \ + v)) + + return PARSE_AND_SET("clip-path", ClipPath) || PARSE_AND_SET("clip-rule", ClipRule) || + PARSE_AND_SET("color", Color) || + PARSE_AND_SET("color-interpolation", ColorInterpolation) || + PARSE_AND_SET("color-interpolation-filters", ColorInterpolationFilters) || + PARSE_AND_SET("display", Display) || PARSE_AND_SET("fill", Fill) || + PARSE_AND_SET("fill-opacity", FillOpacity) || PARSE_AND_SET("fill-rule", FillRule) || + PARSE_AND_SET("filter", Filter) || PARSE_AND_SET("flood-color", FloodColor) || + PARSE_AND_SET("flood-opacity", FloodOpacity) || PARSE_AND_SET("font-family", FontFamily) || + PARSE_AND_SET("font-size", FontSize) || PARSE_AND_SET("font-style", FontStyle) || + PARSE_AND_SET("font-weight", FontWeight) || + PARSE_AND_SET("lighting-color", LightingColor) || PARSE_AND_SET("mask", Mask) || + PARSE_AND_SET("opacity", Opacity) || PARSE_AND_SET("stop-color", StopColor) || + PARSE_AND_SET("stop-opacity", StopOpacity) || PARSE_AND_SET("stroke", Stroke) || + PARSE_AND_SET("stroke-dasharray", StrokeDashArray) || + PARSE_AND_SET("stroke-dashoffset", StrokeDashOffset) || + PARSE_AND_SET("stroke-linecap", StrokeLineCap) || + PARSE_AND_SET("stroke-linejoin", StrokeLineJoin) || + PARSE_AND_SET("stroke-miterlimit", StrokeMiterLimit) || + PARSE_AND_SET("stroke-opacity", StrokeOpacity) || + PARSE_AND_SET("stroke-width", StrokeWidth) || PARSE_AND_SET("text-anchor", TextAnchor) || + PARSE_AND_SET("visibility", Visibility); + +#undef PARSE_AND_SET +} + +// https://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute +Matrix SVGNode::ComputeViewboxMatrix(const Rect& viewBox, const Rect& viewPort, + SVGPreserveAspectRatio par) { + if (viewBox.isEmpty() || viewPort.isEmpty()) { + return Matrix::MakeScale(0, 0); + } + + auto compute_scale = [&]() -> Point { + const auto sx = viewPort.width() / viewBox.width(); + const auto sy = viewPort.height() / viewBox.height(); + + if (par.fAlign == SVGPreserveAspectRatio::kNone) { + // none -> anisotropic scaling, regardless of fScale + return {sx, sy}; + } + + // isotropic scaling + const auto s = + par.fScale == SVGPreserveAspectRatio::kMeet ? std::min(sx, sy) : std::max(sx, sy); + return {s, s}; + }; + + auto compute_trans = [&](const Point& scale) -> Point { + static constexpr float gAlignCoeffs[] = { + 0.0f, // Min + 0.5f, // Mid + 1.0f // Max + }; + + const size_t x_coeff = par.fAlign >> 0 & 0x03; + const size_t y_coeff = par.fAlign >> 2 & 0x03; + + const auto tx = -viewBox.x() * scale.x; + const auto ty = -viewBox.y() * scale.y; + const auto dx = viewPort.width() - viewBox.width() * scale.x; + const auto dy = viewPort.height() - viewBox.height() * scale.y; + + return {tx + dx * gAlignCoeffs[x_coeff], ty + dy * gAlignCoeffs[y_coeff]}; + }; + + const auto s = compute_scale(); + const auto t = compute_trans(s); + + return Matrix::MakeTrans(t.x, t.y) * Matrix::MakeScale(s.x, s.y); +} +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGPath.cpp b/src/svg/node/SVGPath.cpp new file mode 100644 index 00000000..fe9cc916 --- /dev/null +++ b/src/svg/node/SVGPath.cpp @@ -0,0 +1,69 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGPath.h" +#include +#include "tgfx/core/Canvas.h" +#include "tgfx/core/Path.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGAttributeParser.h" +#include "tgfx/svg/SVGParse.h" + +namespace tgfx { + +SkSVGPath::SkSVGPath() : INHERITED(SVGTag::kPath) { +} + +bool SkSVGPath::parseAndSetAttribute(const char* n, const char* v) { + return INHERITED::parseAndSetAttribute(n, v) || + this->setPath(SVGAttributeParser::parse("d", n, v)); +} + +template <> +bool SVGAttributeParser::parse(Path* path) { + auto [success, parsePath] = PathParse::FromSVGString(fCurPos); + if (success) { + *path = *parsePath; + } + return success; +} + +#ifndef RENDER_SVG +void SkSVGPath::onDraw(Canvas* canvas, const SVGLengthContext&, const Paint& paint, + PathFillType fillType) const { + // the passed fillType follows inheritance rules and needs to be applied at draw time. + Path path = fPath; // Note: point and verb data are CoW + path.setFillType(fillType); + canvas->drawPath(path, paint); +} + +Path SkSVGPath::onAsPath(const SVGRenderContext& ctx) const { + Path path = fPath; + // clip-rule can be inherited and needs to be applied at clip time. + path.setFillType(ctx.presentationContext().fInherited.fClipRule->asFillType()); + this->mapToParent(&path); + return path; +} + +Rect SkSVGPath::onObjectBoundingBox(const SVGRenderContext&) const { + return fPath.getBounds(); + //TODO (YG): Implement this + // return fPath.computeTightBounds(); +} +#endif +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGPattern.cpp b/src/svg/node/SVGPattern.cpp new file mode 100644 index 00000000..38761c24 --- /dev/null +++ b/src/svg/node/SVGPattern.cpp @@ -0,0 +1,200 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGPattern.h" +#include +#include +#include +#include "tgfx/core/Matrix.h" +#include "tgfx/core/Paint.h" +#include "tgfx/core/Rect.h" +#include "tgfx/core/Shader.h" +#include "tgfx/core/Surface.h" +#include "tgfx/core/TileMode.h" +#include "tgfx/svg/SVGAttributeParser.h" +#include "tgfx/svg/SVGTypes.h" + +namespace tgfx { + +SkSVGPattern::SkSVGPattern() : INHERITED(SVGTag::kPattern) { +} + +bool SkSVGPattern::parseAndSetAttribute(const char* name, const char* value) { + return INHERITED::parseAndSetAttribute(name, value) || + this->setX(SVGAttributeParser::parse("x", name, value)) || + this->setY(SVGAttributeParser::parse("y", name, value)) || + this->setWidth(SVGAttributeParser::parse("width", name, value)) || + this->setHeight(SVGAttributeParser::parse("height", name, value)) || + this->setPatternTransform( + SVGAttributeParser::parse("patternTransform", name, value)) || + this->setHref(SVGAttributeParser::parse("xlink:href", name, value)) || + this->setPatternUnits( + SVGAttributeParser::parse("patternUnits", name, value)) || + this->setContentUnits( + SVGAttributeParser::parse("patternContentUnits", name, value)); +} + +template <> +bool SVGAttributeParser::parse(SVGPatternUnits* type) { + static constexpr std::tuple gTypeMap[] = { + {"userSpaceOnUse", SVGPatternUnits::UserSpaceOnUse}, + {"objectBoundingBox", SVGPatternUnits::ObjectBoundingBox}, + }; + return this->parseEnumMap(gTypeMap, type) && this->parseEOSToken(); +} + +#ifndef RENDER_SVG +const SkSVGPattern* SkSVGPattern::hrefTarget(const SVGRenderContext& ctx) const { + if (fHref.iri().empty()) { + return nullptr; + } + + const auto href = ctx.findNodeById(fHref); + if (!href || href->tag() != SVGTag::kPattern) { + return nullptr; + } + + return static_cast(href.get()); +} + +template +int inherit_if_needed(const std::optional& src, std::optional& dst) { + if (!dst.has_value()) { + dst = src; + return 1; + } + + return 0; +} + +/* https://www.w3.org/TR/SVG11/pservers.html#PatternElementHrefAttribute + * + * Any attributes which are defined on the referenced element which are not defined on this element + * are inherited by this element. If this element has no children, and the referenced element does + * (possibly due to its own ‘xlink:href’ attribute), then this element inherits the children from + * the referenced element. Inheritance can be indirect to an arbitrary level; thus, if the + * referenced element inherits attributes or children due to its own ‘xlink:href’ attribute, then + * the current element can inherit those attributes or children. + */ +const SkSVGPattern* SkSVGPattern::resolveHref(const SVGRenderContext& ctx, + PatternAttributes* attrs) const { + const SkSVGPattern* currentNode = this; + const SkSVGPattern* contentNode = this; + + do { + // Bitwise OR to avoid short-circuiting. + const bool didInherit = + inherit_if_needed(currentNode->fX, attrs->fX) | + inherit_if_needed(currentNode->fY, attrs->fY) | + inherit_if_needed(currentNode->fWidth, attrs->fWidth) | + inherit_if_needed(currentNode->fHeight, attrs->fHeight) | + inherit_if_needed(currentNode->fPatternTransform, attrs->fPatternTransform); + + if (!contentNode->hasChildren()) { + contentNode = currentNode; + } + + if (contentNode->hasChildren() && !didInherit) { + // All attributes have been resolved, and a valid content node has been found. + // We can terminate the href chain early. + break; + } + + // TODO: reference loop mitigation. + currentNode = currentNode->hrefTarget(ctx); + } while (currentNode); + + // To unify with Chrome and macOS preview, the width and height attributes here need to be + // converted to percentages, direct numbers are not supported. + if (fPatternUnits == SVGPatternUnits::UserSpaceOnUse) { + if (attrs->fWidth.has_value() && attrs->fWidth->unit() == SVGLength::Unit::kPercentage) { + attrs->fWidth = SVGLength(attrs->fWidth->value() / 100.f, SVGLength::Unit::kNumber); + } + if (attrs->fHeight.has_value() && attrs->fHeight->unit() == SVGLength::Unit::kPercentage) { + attrs->fHeight = SVGLength(attrs->fHeight->value() / 100.f, SVGLength::Unit::kNumber); + } + } else if (fPatternUnits == SVGPatternUnits::ObjectBoundingBox) { + if (attrs->fWidth.has_value() && attrs->fWidth->unit() == SVGLength::Unit::kNumber) { + attrs->fWidth = SVGLength(attrs->fWidth->value() * 100.f, SVGLength::Unit::kPercentage); + } + if (attrs->fHeight.has_value() && attrs->fHeight->unit() == SVGLength::Unit::kNumber) { + attrs->fHeight = SVGLength(attrs->fHeight->value() * 100.f, SVGLength::Unit::kPercentage); + } + } + + return contentNode; +} + +bool SkSVGPattern::onAsPaint(const SVGRenderContext& ctx, Paint* paint) const { + PatternAttributes attrs; + const auto* contentNode = this->resolveHref(ctx, &attrs); + auto lengthContext = ctx.lengthContext(); + lengthContext.setPatternUnits(fPatternUnits); + Rect tile = lengthContext.resolveRect(attrs.fX.has_value() ? *attrs.fX : SVGLength(0), + attrs.fY.has_value() ? *attrs.fY : SVGLength(0), + attrs.fWidth.has_value() ? *attrs.fWidth : SVGLength(0), + attrs.fHeight.has_value() ? *attrs.fHeight : SVGLength(0)); + + if (tile.isEmpty()) { + return false; + } + + auto* context = ctx.canvas()->getSurface()->getContext(); + if (!context) { + return false; + } + auto tempSurface = + Surface::Make(context, static_cast(tile.width()), static_cast(tile.height())); + if (!tempSurface) { + return false; + } + auto* tempCanvas = tempSurface->getCanvas(); + auto patternMatrix = attrs.fPatternTransform.value_or(Matrix::I()); + tempCanvas->concat(patternMatrix); + + lengthContext.setPatternUnits(fContentUnits); + SVGRenderContext recordingContext(ctx, tempCanvas, lengthContext); + contentNode->SkSVGContainer::onRender(recordingContext); + context->flushAndSubmit(); + // { + // tgfx::Bitmap bitmap = {}; + // bitmap.allocPixels(tempSurface->width(), tempSurface->height()); + // auto* pixels = bitmap.lockPixels(); + // auto success = tempSurface->readPixels(bitmap.info(), pixels); + // bitmap.unlockPixels(); + // if (!success) { + // return false; + // } + // auto imageData = bitmap.encode(); + // imageData->bytes(); + + // std::ofstream out("/Users/yg/Downloads/yg.png", std::ios::binary); + // if (!out) { + // return false; + // } + // out.write(reinterpret_cast(imageData->data()), + // static_cast(imageData->size())); + // } + auto shaderImage = tempSurface->makeImageSnapshot(); + shaderImage = shaderImage->makeTextureImage(context); + auto shader = Shader::MakeImageShader(shaderImage, TileMode::Repeat, TileMode::Repeat); + paint->setShader(shader); + return true; +} +#endif +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGPoly.cpp b/src/svg/node/SVGPoly.cpp new file mode 100644 index 00000000..9e9fa5fc --- /dev/null +++ b/src/svg/node/SVGPoly.cpp @@ -0,0 +1,66 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGPoly.h" +#include "tgfx/core/Canvas.h" +#include "tgfx/svg/SVGAttributeParser.h" + +namespace tgfx { + +SkSVGPoly::SkSVGPoly(SVGTag t) : INHERITED(t) { +} + +bool SkSVGPoly::parseAndSetAttribute(const char* n, const char* v) { + if (INHERITED::parseAndSetAttribute(n, v)) { + return true; + } + + if (this->setPoints(SVGAttributeParser::parse("points", n, v))) { + // TODO: we can likely just keep the points array and create the SkPath when needed. + // fPath = SkPath::Polygon( + // fPoints.data(), fPoints.size(), + // this->tag() == SkSVGTag::kPolygon); // only polygons are auto-closed + } + + // No other attributes on this node + return false; +} + +#ifndef RENDER_SVG +void SkSVGPoly::onDraw(Canvas* canvas, const SVGLengthContext&, const Paint& paint, + PathFillType fillType) const { + // the passed fillType follows inheritance rules and needs to be applied at draw time. + fPath.setFillType(fillType); + canvas->drawPath(fPath, paint); +} + +Path SkSVGPoly::onAsPath(const SVGRenderContext& ctx) const { + Path path = fPath; + + // clip-rule can be inherited and needs to be applied at clip time. + path.setFillType(ctx.presentationContext().fInherited.fClipRule->asFillType()); + + this->mapToParent(&path); + return path; +} + +Rect SkSVGPoly::onObjectBoundingBox(const SVGRenderContext&) const { + return fPath.getBounds(); +} +#endif +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGRadialGradient.cpp b/src/svg/node/SVGRadialGradient.cpp new file mode 100644 index 00000000..a79503e0 --- /dev/null +++ b/src/svg/node/SVGRadialGradient.cpp @@ -0,0 +1,68 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGRadialGradient.h" +#include +#include "tgfx/core/Color.h" +#include "tgfx/core/Matrix.h" +#include "tgfx/core/Shader.h" +#include "tgfx/core/TileMode.h" +#include "tgfx/svg/SVGAttributeParser.h" + +namespace tgfx { + +SkSVGRadialGradient::SkSVGRadialGradient() : INHERITED(SVGTag::kRadialGradient) { +} + +bool SkSVGRadialGradient::parseAndSetAttribute(const char* name, const char* value) { + return INHERITED::parseAndSetAttribute(name, value) || + this->setCx(SVGAttributeParser::parse("cx", name, value)) || + this->setCy(SVGAttributeParser::parse("cy", name, value)) || + this->setR(SVGAttributeParser::parse("r", name, value)) || + this->setFx(SVGAttributeParser::parse("fx", name, value)) || + this->setFy(SVGAttributeParser::parse("fy", name, value)); +} + +#ifndef RENDER_SVG +std::shared_ptr SkSVGRadialGradient::onMakeShader(const SVGRenderContext& ctx, + const std::vector& colors, + const std::vector& position, + TileMode, const Matrix&) const { + SVGLengthContext lctx = + this->getGradientUnits().type() == SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox + ? SVGLengthContext({1, 1}) + : ctx.lengthContext(); + + auto radius = lctx.resolve(fR, SVGLengthContext::LengthType::kOther); + auto center = Point::Make(lctx.resolve(fCx, SVGLengthContext::LengthType::kHorizontal), + lctx.resolve(fCy, SVGLengthContext::LengthType::kVertical)); + + // TODO(YGAurora): MakeTwoPointConical are unimplemented in tgfx + // const auto focal = Point::Make( + // fFx.has_value() ? lctx.resolve(*fFx, SkSVGLengthContext::LengthType::kHorizontal) : center.x, + // fFy.has_value() ? lctx.resolve(*fFy, SkSVGLengthContext::LengthType::kVertical) : center.y); + + if (radius == 0) { + const auto lastColor = colors.size() > 0 ? *colors.end() : Color::Black(); + return Shader::MakeColorShader(lastColor); + } + + return Shader::MakeRadialGradient(center, radius, colors, position); +} +#endif +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGRect.cpp b/src/svg/node/SVGRect.cpp new file mode 100644 index 00000000..eadf16f0 --- /dev/null +++ b/src/svg/node/SVGRect.cpp @@ -0,0 +1,138 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGRect.h" +#include +#include "SVGRectPriv.h" +#include "tgfx/core/Canvas.h" +#include "tgfx/core/Point.h" +#include "tgfx/core/RRect.h" +#include "tgfx/core/Rect.h" +#include "tgfx/core/Size.h" +#include "tgfx/svg/SVGAttributeParser.h" +#include "tgfx/svg/SVGRenderContext.h" + +namespace tgfx { + +SkSVGRect::SkSVGRect() : INHERITED(SVGTag::kRect) { +} + +bool SkSVGRect::parseAndSetAttribute(const char* n, const char* v) { + return INHERITED::parseAndSetAttribute(n, v) || + this->setX(SVGAttributeParser::parse("x", n, v)) || + this->setY(SVGAttributeParser::parse("y", n, v)) || + this->setWidth(SVGAttributeParser::parse("width", n, v)) || + this->setHeight(SVGAttributeParser::parse("height", n, v)) || + this->setRx(SVGAttributeParser::parse("rx", n, v)) || + this->setRy(SVGAttributeParser::parse("ry", n, v)); +} + +#ifndef RENDER_SVG +std::tuple ResolveOptionalRadii(const std::optional& opt_rx, + const std::optional& opt_ry, + const SVGLengthContext& lctx) { + // https://www.w3.org/TR/SVG2/shapes.html#RectElement + // + // The used values for rx and ry are determined from the computed values by following these + // steps in order: + // + // 1. If both rx and ry have a computed value of auto (since auto is the initial value for both + // properties, this will also occur if neither are specified by the author or if all + // author-supplied values are invalid), then the used value of both rx and ry is 0. + // (This will result in square corners.) + // 2. Otherwise, convert specified values to absolute values as follows: + // 1. If rx is set to a length value or a percentage, but ry is auto, calculate an absolute + // length equivalent for rx, resolving percentages against the used width of the + // rectangle; the absolute value for ry is the same. + // 2. If ry is set to a length value or a percentage, but rx is auto, calculate the absolute + // length equivalent for ry, resolving percentages against the used height of the + // rectangle; the absolute value for rx is the same. + // 3. If both rx and ry were set to lengths or percentages, absolute values are generated + // individually, resolving rx percentages against the used width, and resolving ry + // percentages against the used height. + const float rx = + opt_rx.has_value() ? lctx.resolve(*opt_rx, SVGLengthContext::LengthType::kHorizontal) : 0; + const float ry = + opt_ry.has_value() ? lctx.resolve(*opt_ry, SVGLengthContext::LengthType::kVertical) : 0; + + return {opt_rx.has_value() ? rx : ry, opt_ry.has_value() ? ry : rx}; +} + +RRect SkSVGRect::resolve(const SVGLengthContext& lctx) const { + const auto rect = lctx.resolveRect(fX, fY, fWidth, fHeight); + const auto [rx, ry] = ResolveOptionalRadii(fRx, fRy, lctx); + + // https://www.w3.org/TR/SVG2/shapes.html#RectElement + // ... + // 3. Finally, apply clamping to generate the used values: + // 1. If the absolute rx (after the above steps) is greater than half of the used width, + // then the used value of rx is half of the used width. + // 2. If the absolute ry (after the above steps) is greater than half of the used height, + // then the used value of ry is half of the used height. + // 3. Otherwise, the used values of rx and ry are the absolute values computed previously. + + RRect rrect; + rrect.setRectXY(rect, std::min(rx, rect.width() / 2), std::min(ry, rect.height() / 2)); + return rrect; +} + +// void SkSVGRect::onRender(const SVGRenderContext& ctx) const { +// const auto fillType = ctx.presentationContext().fInherited.fFillRule->asFillType(); + +// auto lengthCtx = ctx.lengthContext(); +// Size rectSize = Size::Make(lengthCtx.resolve(fWidth, SkSVGLengthContext::LengthType::kHorizontal), +// lengthCtx.resolve(fHeight, SkSVGLengthContext::LengthType::kVertical)); +// lengthCtx.setViewPort(rectSize); +// SVGRenderContext paintCtx(ctx, ctx.canvas(), lengthCtx); +// const auto fillPaint = paintCtx.fillPaint(); +// const auto strokePaint = paintCtx.strokePaint(); + +// // TODO: this approach forces duplicate geometry resolution in onDraw(); refactor to avoid. +// if (fillPaint.has_value()) { +// this->onDraw(ctx.canvas(), ctx.lengthContext(), fillPaint.value(), fillType); +// } + +// if (strokePaint.has_value()) { +// this->onDraw(ctx.canvas(), ctx.lengthContext(), strokePaint.value(), fillType); +// } +// } + +void SkSVGRect::onDraw(Canvas* canvas, const SVGLengthContext& lctx, const Paint& paint, + PathFillType) const { + auto rect = this->resolve(lctx); + auto offset = Point::Make(rect.rect.left, rect.rect.top); + rect.rect = rect.rect.makeOffset(-offset.x, -offset.y); + canvas->save(); + canvas->translate(offset.x, offset.y); + canvas->drawRRect(rect, paint); + canvas->restore(); +} + +Path SkSVGRect::onAsPath(const SVGRenderContext& ctx) const { + Path path; + path.addRRect(this->resolve(ctx.lengthContext())); + this->mapToParent(&path); + + return path; +} + +Rect SkSVGRect::onObjectBoundingBox(const SVGRenderContext& ctx) const { + return ctx.lengthContext().resolveRect(fX, fY, fWidth, fHeight); +} +#endif +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGRectPriv.h b/src/svg/node/SVGRectPriv.h new file mode 100644 index 00000000..00083fb9 --- /dev/null +++ b/src/svg/node/SVGRectPriv.h @@ -0,0 +1,30 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +class SkSVGLength; +class SkSVGLengthContext; + +namespace tgfx { + +std::tuple ResolveOptionalRadii(const std::optional& rx, + const std::optional& ry, + const SVGLengthContext&); +} diff --git a/src/svg/node/SVGSVG.cpp b/src/svg/node/SVGSVG.cpp new file mode 100644 index 00000000..97cc2b31 --- /dev/null +++ b/src/svg/node/SVGSVG.cpp @@ -0,0 +1,135 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGSVG.h" +#include "tgfx/core/Size.h" +#include "tgfx/svg/SVGAttribute.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/SVGValue.h" + +namespace tgfx { + +#ifndef RENDER_SVG +void SVGSVG::renderNode(const SVGRenderContext& ctx, const SVGIRI& iri) const { + SVGRenderContext localContext(ctx, this); + auto node = localContext.findNodeById(iri); + if (!node) { + return; + } + + if (this->onPrepareToRender(&localContext)) { + if (this == node.get()) { + this->onRender(ctx); + } else { + node->render(localContext); + } + } +} + +bool SVGSVG::onPrepareToRender(SVGRenderContext* ctx) const { + // x/y are ignored for outermost svg elements + const auto x = fType == Type::kInner ? fX : SVGLength(0); + const auto y = fType == Type::kInner ? fY : SVGLength(0); + + auto viewPortRect = ctx->lengthContext().resolveRect(x, y, fWidth, fHeight); + auto contentMatrix = Matrix::MakeTrans(viewPortRect.x(), viewPortRect.y()); + auto viewPort = Size::Make(viewPortRect.width(), viewPortRect.height()); + + if (fViewBox.has_value()) { + const Rect& viewBox = *fViewBox; + + // An empty viewbox disables rendering. + if (viewBox.isEmpty()) { + return false; + } + + // A viewBox overrides the intrinsic viewport. + viewPort = Size::Make(viewBox.width(), viewBox.height()); + + contentMatrix.preConcat(ComputeViewboxMatrix(viewBox, viewPortRect, fPreserveAspectRatio)); + } + + if (!contentMatrix.isIdentity()) { + ctx->saveOnce(); + ctx->canvas()->concat(contentMatrix); + } + + if (viewPort != ctx->lengthContext().viewPort()) { + ctx->writableLengthContext()->setViewPort(viewPort); + } + + return this->INHERITED::onPrepareToRender(ctx); +} + +// https://www.w3.org/TR/SVG11/coords.html#IntrinsicSizing +Size SVGSVG::intrinsicSize(const SVGLengthContext& lctx) const { + // Percentage values do not provide an intrinsic size. + if (fWidth.unit() == SVGLength::Unit::kPercentage || + fHeight.unit() == SVGLength::Unit::kPercentage) { + return Size::Make(0, 0); + } + + return Size::Make(lctx.resolve(fWidth, SVGLengthContext::LengthType::kHorizontal), + lctx.resolve(fHeight, SVGLengthContext::LengthType::kVertical)); +} +#endif + +void SVGSVG::onSetAttribute(SVGAttribute attr, const SVGValue& v) { + if (fType != Type::kInner && fType != Type::kRoot) return; + switch (attr) { + case SVGAttribute::kX: + if (const auto* x = v.as()) { + SVGLength xValue = *x; + this->setX(xValue); + } + break; + case SVGAttribute::kY: + if (const auto* y = v.as()) { + SVGLength yValue = *y; + this->setY(yValue); + } + break; + case SVGAttribute::kWidth: + if (const auto* w = v.as()) { + SVGLength wValue = *w; + this->setWidth(wValue); + } + break; + case SVGAttribute::kHeight: + if (const auto* h = v.as()) { + SVGLength hValue = *h; + this->setHeight(hValue); + } + break; + case SVGAttribute::kViewBox: + if (const auto* vb = v.as()) { + SVGViewBoxType vbValue = *vb; + this->setViewBox(vbValue); + } + break; + case SVGAttribute::kPreserveAspectRatio: + if (const auto* par = v.as()) { + SVGPreserveAspectRatio parValue = *par; + this->setPreserveAspectRatio(parValue); + } + break; + default: + this->INHERITED::onSetAttribute(attr, v); + } +} +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGShape.cpp b/src/svg/node/SVGShape.cpp new file mode 100644 index 00000000..fe942889 --- /dev/null +++ b/src/svg/node/SVGShape.cpp @@ -0,0 +1,59 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGShape.h" +#include +#include "core/utils/Log.h" +#include "tgfx/core/Size.h" +#include "tgfx/svg/SVGRenderContext.h" + +class SkSVGNode; +enum class SkSVGTag; + +namespace tgfx { + +SVGShape::SVGShape(SVGTag t) : INHERITED(t) { +} + +#ifndef RENDER_SVG +void SVGShape::onRender(const SVGRenderContext& ctx) const { + const auto fillType = ctx.presentationContext().fInherited.fFillRule->asFillType(); + + auto selfRect = onObjectBoundingBox(ctx); + auto lengthCtx = ctx.lengthContext(); + lengthCtx.setViewPort(Size::Make(selfRect.width(), selfRect.height())); + SVGRenderContext paintCtx(ctx, ctx.canvas(), lengthCtx); + + const auto fillPaint = paintCtx.fillPaint(); + const auto strokePaint = paintCtx.strokePaint(); + + // TODO: this approach forces duplicate geometry resolution in onDraw(); refactor to avoid. + if (fillPaint.has_value()) { + this->onDraw(ctx.canvas(), ctx.lengthContext(), fillPaint.value(), fillType); + } + + if (strokePaint.has_value()) { + this->onDraw(ctx.canvas(), ctx.lengthContext(), strokePaint.value(), fillType); + } +} +#endif + +void SVGShape::appendChild(std::shared_ptr) { + LOGE("cannot append child nodes to an SVG shape.\n"); +} +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGStop.cpp b/src/svg/node/SVGStop.cpp new file mode 100644 index 00000000..57eaa88d --- /dev/null +++ b/src/svg/node/SVGStop.cpp @@ -0,0 +1,31 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGStop.h" +#include "tgfx/svg/SVGAttributeParser.h" + +namespace tgfx { + +SkSVGStop::SkSVGStop() : INHERITED(SVGTag::kStop) { +} + +bool SkSVGStop::parseAndSetAttribute(const char* n, const char* v) { + return INHERITED::parseAndSetAttribute(n, v) || + this->setOffset(SVGAttributeParser::parse("offset", n, v)); +} +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGText.cpp b/src/svg/node/SVGText.cpp new file mode 100644 index 00000000..bf208c2f --- /dev/null +++ b/src/svg/node/SVGText.cpp @@ -0,0 +1,262 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGText.h" +#include +#include +#include +#include +#include "core/utils/Log.h" +#include "tgfx/core/Font.h" +#include "tgfx/core/Path.h" +#include "tgfx/core/TextBlob.h" +#include "tgfx/svg/SVGAttributeParser.h" +#include "tgfx/svg/SVGFontManager.h" + +namespace tgfx { +namespace { +std::tuple ResolveFont(const SVGRenderContext& context) { + auto weight = [](const SVGFontWeight& w) { + switch (w.type()) { + case SVGFontWeight::Type::k100: + return FontStyle::Weight::kThin_Weight; + case SVGFontWeight::Type::k200: + return FontStyle::Weight::kExtraLight_Weight; + case SVGFontWeight::Type::k300: + return FontStyle::Weight::kLight_Weight; + case SVGFontWeight::Type::k400: + return FontStyle::Weight::kNormal_Weight; + case SVGFontWeight::Type::k500: + return FontStyle::Weight::kMedium_Weight; + case SVGFontWeight::Type::k600: + return FontStyle::Weight::kSemiBold_Weight; + case SVGFontWeight::Type::k700: + return FontStyle::Weight::kBold_Weight; + case SVGFontWeight::Type::k800: + return FontStyle::Weight::kExtraBold_Weight; + case SVGFontWeight::Type::k900: + return FontStyle::Weight::kBlack_Weight; + case SVGFontWeight::Type::kNormal: + return FontStyle::Weight::kNormal_Weight; + case SVGFontWeight::Type::kBold: + return FontStyle::Weight::kBold_Weight; + case SVGFontWeight::Type::kBolder: + return FontStyle::Weight::kExtraBold_Weight; + case SVGFontWeight::Type::kLighter: + return FontStyle::Weight::kLight_Weight; + case SVGFontWeight::Type::kInherit: { + ASSERT(false); + return FontStyle::Weight::kNormal_Weight; + } + } + }; + + auto slant = [](const SVGFontStyle& style) { + switch (style.type()) { + case SVGFontStyle::Type::kNormal: + return FontStyle::Slant::kUpright_Slant; + case SVGFontStyle::Type::kItalic: + return FontStyle::Slant::kItalic_Slant; + case SVGFontStyle::Type::kOblique: + return FontStyle::Slant::kOblique_Slant; + case SVGFontStyle::Type::kInherit: { + ASSERT(false); + return FontStyle::Slant::kUpright_Slant; + } + } + }; + + const std::string& family = context.presentationContext().fInherited.fFontFamily->family(); + + auto fontWeight = weight(*context.presentationContext().fInherited.fFontWeight); + auto fontWidth = FontStyle::Width::kNormal_Width; + auto fontSlant = slant(*context.presentationContext().fInherited.fFontStyle); + FontStyle style(fontWeight, fontWidth, fontSlant); + + auto typeface = context.fontMgr()->getTypefaceForRender(family, style); + ASSERT(typeface); + if (!typeface) { + return {false, Font()}; + } + + float size = + context.lengthContext().resolve(context.presentationContext().fInherited.fFontSize->size(), + SVGLengthContext::LengthType::kVertical); + return {true, Font(typeface, size)}; +} + +std::vector ResolveLengths(const SVGLengthContext& lengthCtx, + const std::vector& lengths, + SVGLengthContext::LengthType lengthType) { + std::vector resolved; + resolved.reserve(lengths.size()); + + for (const auto& length : lengths) { + resolved.push_back(lengthCtx.resolve(length, lengthType)); + } + + return resolved; +} + +float ComputeAlignmentFactor(const SkSVGPresentationContext& context) { + switch (context.fInherited.fTextAnchor->type()) { + case SVGTextAnchor::Type::kStart: + return 0.0f; + case SVGTextAnchor::Type::kMiddle: + return -0.5f; + case SVGTextAnchor::Type::kEnd: + return -1.0f; + case SVGTextAnchor::Type::kInherit: + ASSERT(false); + return 0.0f; + } +} +} // namespace + +void SkSVGTextContainer::appendChild(std::shared_ptr child) { + // Only allow text content child nodes. + switch (child->tag()) { + case SVGTag::kTextLiteral: + case SVGTag::kTextPath: + case SVGTag::kTSpan: + fChildren.push_back(std::static_pointer_cast(child)); + break; + default: + break; + } +} + +void SkSVGTextFragment::renderText(const SVGRenderContext& context, + const ShapedTextCallback& function) const { + // N.B.: unlike regular elements, text fragments do not establish a new OBB scope -- they + // always defer to the root element for OBB resolution. + SVGRenderContext localContext(context); + + if (this->onPrepareToRender(&localContext)) { + this->onShapeText(localContext, function); + } +} + +Path SkSVGTextFragment::onAsPath(const SVGRenderContext&) const { + // TODO (YGAurora) + return Path(); +} + +void SkSVGTextContainer::onShapeText(const SVGRenderContext& context, + const ShapedTextCallback& function) const { + + auto x = ResolveLengths(context.lengthContext(), fX, SVGLengthContext::LengthType::kHorizontal); + auto y = ResolveLengths(context.lengthContext(), fY, SVGLengthContext::LengthType::kVertical); + auto dx = ResolveLengths(context.lengthContext(), fDx, SVGLengthContext::LengthType::kHorizontal); + auto dy = ResolveLengths(context.lengthContext(), fDy, SVGLengthContext::LengthType::kVertical); + + // TODO (YGAurora) : Handle rotate + for (uint32_t i = 0; i < fChildren.size(); i++) { + auto child = fChildren[i]; + context.canvas()->save(); + float offsetX = x[i] + (i < dx.size() ? dx[i] : 0); + float offsetY = y[i] + (i < dy.size() ? dy[i] : 0); + context.canvas()->translate(offsetX, offsetY); + child->renderText(context, function); + context.canvas()->restore(); + } +} + +bool SkSVGTextContainer::parseAndSetAttribute(const char* name, const char* value) { + return INHERITED::parseAndSetAttribute(name, value) || + this->setX(SVGAttributeParser::parse>("x", name, value)) || + this->setY(SVGAttributeParser::parse>("y", name, value)) || + this->setDx(SVGAttributeParser::parse>("dx", name, value)) || + this->setDy(SVGAttributeParser::parse>("dy", name, value)) || + this->setRotate( + SVGAttributeParser::parse>("rotate", name, value)); +} + +void SkSVGTextLiteral::onShapeText(const SVGRenderContext& context, + const ShapedTextCallback& function) const { + + auto [success, font] = ResolveFont(context); + if (!success) { + return; + } + auto textBlob = TextBlob::MakeFrom(fText, font); + function(context, textBlob); +} + +void SkSVGText::onRender(const SVGRenderContext& context) const { + + auto renderer = [](const SVGRenderContext& renderCtx, + const std::shared_ptr& textBlob) -> void { + if (!textBlob) { + return; + } + + auto bound = textBlob->getBounds(); + float x = ComputeAlignmentFactor(renderCtx.presentationContext()) * bound.width(); + float y = 0; + + auto lengthCtx = renderCtx.lengthContext(); + lengthCtx.setViewPort(Size::Make(bound.width(), bound.height())); + SVGRenderContext paintCtx(renderCtx, renderCtx.canvas(), lengthCtx); + const auto fillPaint = paintCtx.fillPaint(); + const auto strokePaint = paintCtx.strokePaint(); + + if (fillPaint.has_value()) { + renderCtx.canvas()->drawTextBlob(textBlob, x, y, fillPaint.value()); + } + if (strokePaint.has_value()) { + renderCtx.canvas()->drawTextBlob(textBlob, x, y, strokePaint.value()); + } + }; + + this->onShapeText(context, renderer); +} + +Rect SkSVGText::onObjectBoundingBox(const SVGRenderContext& ctx) const { + Rect bounds = Rect::MakeEmpty(); + + auto boundCollector = [&bounds](const SVGRenderContext&, + const std::shared_ptr& textBlob) -> void { + if (!textBlob) { + return; + } + auto textBound = textBlob->getBounds(); + bounds.join(textBound); + }; + + this->onShapeText(ctx, boundCollector); + return bounds; +} + +Path SkSVGText::onAsPath(const SVGRenderContext&) const { + // TODO (YGAurora) + return Path(); +} + +void SkSVGTextPath::onShapeText(const SVGRenderContext& ctx, + const ShapedTextCallback& function) const { + this->INHERITED::onShapeText(ctx, function); +} + +bool SkSVGTextPath::parseAndSetAttribute(const char* name, const char* value) { + return INHERITED::parseAndSetAttribute(name, value) || + this->setHref(SVGAttributeParser::parse("xlink:href", name, value)) || + this->setStartOffset(SVGAttributeParser::parse("startOffset", name, value)); +} + +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGTransformableNode.cpp b/src/svg/node/SVGTransformableNode.cpp new file mode 100644 index 00000000..97d8359d --- /dev/null +++ b/src/svg/node/SVGTransformableNode.cpp @@ -0,0 +1,73 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGTransformableNode.h" +#include "tgfx/core/Matrix.h" +#include "tgfx/core/Path.h" +#include "tgfx/core/Point.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGAttribute.h" +#include "tgfx/svg/SVGValue.h" + +namespace tgfx { + +SkSVGTransformableNode::SkSVGTransformableNode(SVGTag tag) + : INHERITED(tag), fTransform(Matrix::I()) { +} + +#ifndef RENDER_SVG +bool SkSVGTransformableNode::onPrepareToRender(SVGRenderContext* ctx) const { + if (!fTransform.isIdentity()) { + auto transform = fTransform; + if (auto unit = ctx->lengthContext().getPatternUnits(); + unit.has_value() && unit.value() == SVGPatternUnits::ObjectBoundingBox) { + transform.postScale(ctx->lengthContext().viewPort().width, + ctx->lengthContext().viewPort().height); + } + ctx->saveOnce(); + ctx->canvas()->concat(transform); + } + + return this->INHERITED::onPrepareToRender(ctx); +} +#endif + +void SkSVGTransformableNode::onSetAttribute(SVGAttribute attr, const SVGValue& v) { + switch (attr) { + case SVGAttribute::kTransform: + if (const auto* transform = v.as()) { + this->setTransform(*transform); + } + break; + default: + this->INHERITED::onSetAttribute(attr, v); + break; + } +} + +#ifndef RENDER_SVG +void SkSVGTransformableNode::mapToParent(Path* path) const { + // transforms the path to parent node coordinates. + path->transform(fTransform); +} + +void SkSVGTransformableNode::mapToParent(Rect* rect) const { + *rect = fTransform.mapRect(*rect); +} +#endif +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGUse.cpp b/src/svg/node/SVGUse.cpp new file mode 100644 index 00000000..7ffd680a --- /dev/null +++ b/src/svg/node/SVGUse.cpp @@ -0,0 +1,97 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGUse.h" +#include +#include "core/utils/MathExtra.h" +#include "tgfx/core/Path.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGAttributeParser.h" +#include "tgfx/svg/SVGRenderContext.h" + +namespace tgfx { + +SkSVGUse::SkSVGUse() : INHERITED(SVGTag::kUse) { +} + +bool SkSVGUse::parseAndSetAttribute(const char* n, const char* v) { + return INHERITED::parseAndSetAttribute(n, v) || + this->setX(SVGAttributeParser::parse("x", n, v)) || + this->setY(SVGAttributeParser::parse("y", n, v)) || + this->setHref(SVGAttributeParser::parse("xlink:href", n, v)); +} + +#ifndef RENDER_SVG +bool SkSVGUse::onPrepareToRender(SVGRenderContext* ctx) const { + if (fHref.iri().empty() || !INHERITED::onPrepareToRender(ctx)) { + return false; + } + + if (!FloatNearlyZero(fX.value()) || !FloatNearlyZero(fY.value())) { + // Restored when the local SVGRenderContext leaves scope. + ctx->saveOnce(); + ctx->canvas()->translate(fX.value(), fY.value()); + } + + // TODO: width/height override for targets. + + return true; +} + +void SkSVGUse::onRender(const SVGRenderContext& ctx) const { + const auto ref = ctx.findNodeById(fHref); + if (!ref) { + return; + } + + auto lengthContext = ctx.lengthContext(); + lengthContext.clearPatternUnits(); + SVGRenderContext localContext(ctx, lengthContext); + ref->render(localContext); +} + +Path SkSVGUse::onAsPath(const SVGRenderContext& ctx) const { + const auto ref = ctx.findNodeById(fHref); + if (!ref) { + return Path(); + } + + auto lengthContext = ctx.lengthContext(); + lengthContext.clearPatternUnits(); + SVGRenderContext localContext(ctx, lengthContext); + return ref->asPath(localContext); +} + +Rect SkSVGUse::onObjectBoundingBox(const SVGRenderContext& ctx) const { + const auto ref = ctx.findNodeById(fHref); + if (!ref) { + return Rect::MakeEmpty(); + } + + auto lengthContext = ctx.lengthContext(); + lengthContext.clearPatternUnits(); + float x = lengthContext.resolve(fX, SVGLengthContext::LengthType::kHorizontal); + float y = lengthContext.resolve(fY, SVGLengthContext::LengthType::kVertical); + + Rect bounds = ref->objectBoundingBox(ctx); + bounds.offset(x, y); + + return bounds; +} +#endif +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/xml/XMLWriter.cpp b/src/svg/xml/XMLWriter.cpp index 9331949a..5f7f32e5 100644 --- a/src/svg/xml/XMLWriter.cpp +++ b/src/svg/xml/XMLWriter.cpp @@ -265,7 +265,7 @@ XMLParserWriter::~XMLParserWriter() { void XMLParserWriter::onAddAttribute(const std::string& name, const std::string& value) { ASSERT(_elementsStack.empty() || (!_elementsStack.top().hasChildren && !_elementsStack.top().hasText)); - _parser.addAttribute(name.c_str(), value.c_str()); + _parser.addAttribute(name, value); } void XMLParserWriter::onAddText(const std::string& text) { @@ -274,13 +274,13 @@ void XMLParserWriter::onAddText(const std::string& text) { void XMLParserWriter::onEndElement() { Elem elem = this->getEnd(); - _parser.endElement(elem.name.c_str()); + _parser.endElement(elem.name); this->doEnd(); } void XMLParserWriter::onStartElement(const std::string& element) { this->doStart(element); - _parser.startElement(element.c_str()); + _parser.startElement(element); } } // namespace tgfx \ No newline at end of file From b1db87250a72b566e2e252afd3e5189bc49a0237 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Fri, 15 Nov 2024 15:06:00 +0800 Subject: [PATCH 02/31] commit before context structure refactor --- include/tgfx/svg/SVGDOM.h | 14 +- include/tgfx/svg/SVGRenderContext.h | 201 +++++++------------------- include/tgfx/svg/node/SVGImage.h | 5 +- src/svg/SVGDOM.cpp | 165 +++++++++------------ src/svg/SVGRenderContext.cpp | 128 ++++++++-------- src/svg/node/SVGCircle.cpp | 6 +- src/svg/node/SVGEllipse.cpp | 4 +- src/svg/node/SVGFe.cpp | 2 +- src/svg/node/SVGFeDisplacementMap.cpp | 2 +- src/svg/node/SVGGradient.cpp | 2 +- src/svg/node/SVGImage.cpp | 37 +---- src/svg/node/SVGLine.cpp | 8 +- src/svg/node/SVGLinearGradient.cpp | 8 +- src/svg/node/SVGMask.cpp | 27 ++-- src/svg/node/SVGNode.cpp | 4 +- src/svg/node/SVGPath.cpp | 2 +- src/svg/node/SVGPoly.cpp | 2 +- src/svg/node/SVGRadialGradient.cpp | 6 +- src/svg/node/SVGRect.cpp | 4 +- src/svg/node/SVGSVG.cpp | 4 +- src/svg/node/SVGShape.cpp | 2 +- src/svg/node/SVGText.cpp | 20 +-- src/svg/node/SVGUse.cpp | 4 +- 23 files changed, 249 insertions(+), 408 deletions(-) diff --git a/include/tgfx/svg/SVGDOM.h b/include/tgfx/svg/SVGDOM.h index cc8f2cb0..90cfedb9 100644 --- a/include/tgfx/svg/SVGDOM.h +++ b/include/tgfx/svg/SVGDOM.h @@ -67,7 +67,7 @@ class SVGDOM { * Returns the root (outermost) SVG element. */ const std::shared_ptr& getRoot() const { - return fRoot; + return _root; } /** @@ -96,7 +96,7 @@ class SVGDOM { const Size& containerSize() const; // Returns the node with the given id, or nullptr if not found. - std::shared_ptr findNodeById(const char* id); + std::shared_ptr findNodeById(const std::string& id); void render(Canvas*) const; @@ -106,11 +106,9 @@ class SVGDOM { private: SVGDOM(std::shared_ptr, SVGIDMapper&&, std::shared_ptr fontManager); - const std::shared_ptr fRoot; - const std::shared_ptr fFontMgr; - // const sk_sp fTextShapingFactory; - const std::shared_ptr fResourceProvider; - const SVGIDMapper fIDMapper; - Size fContainerSize; + const std::shared_ptr _root; + const std::shared_ptr _fontMgr; + const SVGIDMapper _nodeIDMapper; + Size _containerSize; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGRenderContext.h b/include/tgfx/svg/SVGRenderContext.h index f3e28d07..0b4c2917 100644 --- a/include/tgfx/svg/SVGRenderContext.h +++ b/include/tgfx/svg/SVGRenderContext.h @@ -36,26 +36,19 @@ namespace tgfx { -// class SkCanvas; -// class SkPaint; -// class SkString; -// namespace skresources { -// class ResourceProvider; -// } - class SVGNode; template class CopyOnWrite { public: - explicit CopyOnWrite(const T& initial) : fObj(&initial) { + explicit CopyOnWrite(const T& initial) : _obj(&initial) { } - explicit CopyOnWrite(const T* initial) : fObj(initial) { + explicit CopyOnWrite(const T* initial) : _obj(initial) { } // Constructor for delayed initialization. - CopyOnWrite() : fObj(nullptr) { + CopyOnWrite() : _obj(nullptr) { } CopyOnWrite(const CopyOnWrite& that) { @@ -67,46 +60,30 @@ class CopyOnWrite { } CopyOnWrite& operator=(const CopyOnWrite& that) { - fLazy = that.fLazy; - fObj = fLazy.has_value() ? &fLazy.value() : that.fObj; + _optional = that._optional; + _obj = _optional.has_value() ? &_optional.value() : that._obj; return *this; } CopyOnWrite& operator=(CopyOnWrite&& that) { - fLazy = std::move(that.fLazy); - fObj = fLazy.has_value() ? &fLazy.value() : that.fObj; + _optional = std::move(that._optional); + _obj = _optional.has_value() ? &_optional.value() : that._obj; return *this; } - // Should only be called once, and only if the default constructor was used. - void init(const T& initial) { - SkASSERT(!fObj); - SkASSERT(!fLazy.has_value()); - fObj = &initial; - } - - // If not already initialized, in-place instantiates the writable object - template - void initIfNeeded(Args&&... args) { - if (!fObj) { - SkASSERT(!fLazy.has_value()); - fObj = &fLazy.emplace(std::forward(args)...); - } - } - /** * Returns a writable T*. The first time this is called the initial object is cloned. */ T* writable() { - if (!fLazy.has_value()) { - fLazy = *fObj; - fObj = &fLazy.value(); + if (!_optional.has_value()) { + _optional = *_obj; + _obj = &_optional.value(); } - return &fLazy.value(); + return &_optional.value(); } const T* get() const { - return fObj; + return _obj; } /** @@ -114,38 +91,34 @@ class CopyOnWrite { */ const T* operator->() const { - return fObj; - } - - operator const T*() const { - return fObj; + return _obj; } const T& operator*() const { - return *fObj; + return *_obj; } private: - const T* fObj; - std::optional fLazy; + const T* _obj; + std::optional _optional; }; class SVGLengthContext { public: - explicit SVGLengthContext(const Size& viewport, float dpi = 90) : fViewport(viewport), fDPI(dpi) { + explicit SVGLengthContext(const Size& viewport, float dpi = 90) : _viewport(viewport), _dpi(dpi) { } enum class LengthType { - kHorizontal, - kVertical, - kOther, + Horizontal, + Vertical, + Other, }; const Size& viewPort() const { - return fViewport; + return _viewport; } void setViewPort(const Size& viewport) { - fViewport = viewport; + _viewport = viewport; } float resolve(const SVGLength&, LengthType) const; @@ -153,21 +126,21 @@ class SVGLengthContext { const SVGLength& h) const; void setPatternUnits(SVGPatternUnits unit) { - fPatternUnit = unit; + _patternUnit = unit; } void clearPatternUnits() { - fPatternUnit.reset(); + _patternUnit.reset(); } std::optional getPatternUnits() const { - return fPatternUnit; + return _patternUnit; } private: - Size fViewport; - float fDPI; - std::optional fPatternUnit; + Size _viewport; + float _dpi; + std::optional _patternUnit; }; struct SkSVGPresentationContext { @@ -175,22 +148,20 @@ struct SkSVGPresentationContext { SkSVGPresentationContext(const SkSVGPresentationContext&) = default; SkSVGPresentationContext& operator=(const SkSVGPresentationContext&) = default; - const std::unordered_map* fNamedColors = nullptr; - + const std::unordered_map* _namedColors = nullptr; // Inherited presentation attributes, computed for the current node. - SkSVGPresentationAttributes fInherited; + SkSVGPresentationAttributes _inherited; }; class SVGRenderContext { public: // Captures data required for object bounding box resolution. struct OBBScope { - const SVGNode* fNode; - const SVGRenderContext* fCtx; + const SVGNode* node; + const SVGRenderContext* context; }; - SVGRenderContext(Canvas*, const std::shared_ptr&, - const std::shared_ptr&, const SVGIDMapper&, + SVGRenderContext(Canvas*, const std::shared_ptr&, const SVGIDMapper&, const SVGLengthContext&, const SkSVGPresentationContext&, const OBBScope&); SVGRenderContext(const SVGRenderContext&); SVGRenderContext(const SVGRenderContext&, Canvas*); @@ -201,18 +172,18 @@ class SVGRenderContext { ~SVGRenderContext(); const SVGLengthContext& lengthContext() const { - return *fLengthContext; + return *_lengthContext; } SVGLengthContext* writableLengthContext() { - return fLengthContext.writable(); + return _lengthContext.writable(); } const SkSVGPresentationContext& presentationContext() const { - return *fPresentationContext; + return *_presentationContext; } Canvas* canvas() const { - return fCanvas; + return _canvas; } void saveOnce(); @@ -221,45 +192,6 @@ class SVGRenderContext { }; void applyPresentationAttributes(const SkSVGPresentationAttributes&, uint32_t flags); - // Scoped wrapper that temporarily clears the original node reference. - // class BorrowedNode { - // public: - // explicit BorrowedNode(std::shared_ptr* node) : fOwner(node) { - // if (fOwner) { - // fBorrowed = std::move(*fOwner); - // *fOwner = nullptr; - // } - // } - - // ~BorrowedNode() { - // if (fOwner) { - // *fOwner = std::move(fBorrowed); - // } - // } - - // const SVGNode* get() const { - // return fBorrowed.get(); - // } - // const SVGNode* operator->() const { - // return fBorrowed.get(); - // } - // const SVGNode& operator*() const { - // return *fBorrowed; - // } - - // explicit operator bool() const { - // return !!fBorrowed; - // } - - // // noncopyable - // BorrowedNode(const BorrowedNode&) = delete; - // BorrowedNode& operator=(BorrowedNode&) = delete; - - // private: - // std::shared_ptr* fOwner; - // std::shared_ptr fBorrowed; - // }; - // Note: the id->node association is cleared for the lifetime of the returned value // (effectively breaks reference cycles, assuming appropriate return value scoping). std::shared_ptr findNodeById(const SVGIRI&) const; @@ -271,21 +203,17 @@ class SVGRenderContext { // The local computed clip path (not inherited). Path clipPath() const { - return fClipPath.value_or(Path()); - } - - const std::shared_ptr& resourceProvider() const { - return fResourceProvider; + return _clipPath.value_or(Path()); } const std::shared_ptr& fontMgr() const { - return fFontMgr; + return _fontMgr; } std::shared_ptr& fontMgr() { - // It is probably an oversight to try to render without having set the SkFontMgr. - // We will assert this in debug mode, but fallback to an empty fontmgr in release builds. - return fFontMgr; + // It is probably an oversight to try to render without having set the SVGFontManager. + // We will assert this in debug mode, but fallback to an empty _fontMgr in release builds. + return _fontMgr; } // Returns the translate/scale transformation required to map into the current OBB scope, @@ -297,32 +225,14 @@ class SVGRenderContext { OBBTransform transformForCurrentOBB(SVGObjectBoundingBoxUnits) const; Rect resolveOBBRect(const SVGLength& x, const SVGLength& y, const SVGLength& w, - const SVGLength& h, SVGObjectBoundingBoxUnits) const; - - // std::unique_ptr makeShaper() const { - // SkASSERT(fTextShapingFactory); - // return fTextShapingFactory->makeShaper(this->fontMgr()); - // } - - // std::unique_ptr makeBidiRunIterator(const char* utf8, size_t utf8Bytes, - // uint8_t bidiLevel) const { - // SkASSERT(fTextShapingFactory); - // return fTextShapingFactory->makeBidiRunIterator(utf8, utf8Bytes, bidiLevel); - // } - - // std::unique_ptr makeScriptRunIterator(const char* utf8, - // size_t utf8Bytes) const { - // SkASSERT(fTextShapingFactory); - // constexpr SkFourByteTag unknownScript = SkSetFourByteTag('Z', 'z', 'z', 'z'); - // return fTextShapingFactory->makeScriptRunIterator(utf8, utf8Bytes, unknownScript); - // } + const SVGLength& h, SVGObjectBoundingBoxUnits unit) const; - private: // Stack-only void* operator new(size_t) = delete; void* operator new(size_t, void*) = delete; SVGRenderContext& operator=(const SVGRenderContext&) = delete; + private: void applyOpacity(float opacity, uint32_t flags, bool hasFilter); void applyFilter(const SVGFuncIRI&); void applyClip(const SVGFuncIRI&); @@ -330,25 +240,22 @@ class SVGRenderContext { std::optional commonPaint(const SVGPaint&, float opacity) const; - std::shared_ptr fFontMgr; - // const sk_sp& fTextShapingFactory; - const std::shared_ptr& fResourceProvider; - - const SVGIDMapper& fIDMapper; - CopyOnWrite fLengthContext; - CopyOnWrite fPresentationContext; - Canvas* fCanvas; - // The save count on 'fCanvas' at construction time. + std::shared_ptr _fontMgr; + const SVGIDMapper& _nodeIDMapper; + CopyOnWrite _lengthContext; + CopyOnWrite _presentationContext; + Canvas* _canvas; + // The save count on '_canvas' at construction time. // A restoreToCount() will be issued on destruction. - size_t fCanvasSaveCount; + size_t _canvasSaveCount; // clipPath, if present for the current context (not inherited). - std::optional fClipPath; + std::optional _clipPath; // Deferred opacity optimization for leaf nodes. - float fDeferredPaintOpacity = 1; + float _deferredPaintOpacity = 1; // Current object bounding box scope. - const OBBScope fOBBScope; + const OBBScope _scope; }; } // namespace tgfx diff --git a/include/tgfx/svg/node/SVGImage.h b/include/tgfx/svg/node/SVGImage.h index 3be2141d..5033900e 100644 --- a/include/tgfx/svg/node/SVGImage.h +++ b/include/tgfx/svg/node/SVGImage.h @@ -58,10 +58,7 @@ class SkSVGImage final : public SkSVGTransformableNode { void onRender(const SVGRenderContext&) const override; Path onAsPath(const SVGRenderContext&) const override; Rect onObjectBoundingBox(const SVGRenderContext&) const override; - - // TODO (YG) - static ImageInfo LoadImage(const std::shared_ptr& rp, const SVGIRI&, - const Rect&, SVGPreserveAspectRatio); + static ImageInfo LoadImage(const SVGIRI&, const Rect&, SVGPreserveAspectRatio); #endif SVG_ATTR(X, SVGLength, SVGLength(0)) diff --git a/src/svg/SVGDOM.cpp b/src/svg/SVGDOM.cpp index d1861394..a39e107c 100644 --- a/src/svg/SVGDOM.cpp +++ b/src/svg/SVGDOM.cpp @@ -19,7 +19,9 @@ #include "tgfx/svg/SVGDOM.h" #include #include +#include #include +#include #include #include #include @@ -71,79 +73,72 @@ namespace tgfx { namespace { -bool SetIRIAttribute(const std::shared_ptr& node, SVGAttribute attr, - const char* stringValue) { +bool SetIRIAttribute(SVGNode& node, SVGAttribute attr, const char* stringValue) { auto parseResult = SVGAttributeParser::parse(stringValue); if (!parseResult.has_value()) { return false; } - node->setAttribute(attr, SVGStringValue(parseResult->iri())); + node.setAttribute(attr, SVGStringValue(parseResult->iri())); return true; } -bool SetStringAttribute(const std::shared_ptr& node, SVGAttribute attr, - const char* stringValue) { +bool SetStringAttribute(SVGNode& node, SVGAttribute attr, const char* stringValue) { std::string str(stringValue, strlen(stringValue)); SVGStringType strType = SVGStringType(str); - node->setAttribute(attr, SVGStringValue(strType)); + node.setAttribute(attr, SVGStringValue(strType)); return true; } -bool SetTransformAttribute(const std::shared_ptr& node, SVGAttribute attr, - const char* stringValue) { +bool SetTransformAttribute(SVGNode& node, SVGAttribute attr, const char* stringValue) { auto parseResult = SVGAttributeParser::parse(stringValue); if (!parseResult.has_value()) { return false; } - node->setAttribute(attr, SVGTransformValue(*parseResult)); + node.setAttribute(attr, SVGTransformValue(*parseResult)); return true; } -bool SetLengthAttribute(const std::shared_ptr& node, SVGAttribute attr, - const char* stringValue) { +bool SetLengthAttribute(SVGNode& node, SVGAttribute attr, const char* stringValue) { auto parseResult = SVGAttributeParser::parse(stringValue); if (!parseResult.has_value()) { return false; } - node->setAttribute(attr, SVGLengthValue(*parseResult)); + node.setAttribute(attr, SVGLengthValue(*parseResult)); return true; } -bool SetViewBoxAttribute(const std::shared_ptr& node, SVGAttribute attr, - const char* stringValue) { +bool SetViewBoxAttribute(SVGNode& node, SVGAttribute attr, const char* stringValue) { SVGViewBoxType viewBox; SVGAttributeParser parser(stringValue); if (!parser.parseViewBox(&viewBox)) { return false; } - node->setAttribute(attr, SVGViewBoxValue(viewBox)); + node.setAttribute(attr, SVGViewBoxValue(viewBox)); return true; } -bool SetObjectBoundingBoxUnitsAttribute(const std::shared_ptr& node, SVGAttribute attr, - const char* stringValue) { +bool SetObjectBoundingBoxUnitsAttribute(SVGNode& node, SVGAttribute attr, const char* stringValue) { auto parseResult = SVGAttributeParser::parse(stringValue); if (!parseResult.has_value()) { return false; } - node->setAttribute(attr, SVGObjectBoundingBoxUnitsValue(*parseResult)); + node.setAttribute(attr, SVGObjectBoundingBoxUnitsValue(*parseResult)); return true; } -bool SetPreserveAspectRatioAttribute(const std::shared_ptr& node, SVGAttribute attr, - const char* stringValue) { +bool SetPreserveAspectRatioAttribute(SVGNode& node, SVGAttribute attr, const char* stringValue) { SVGPreserveAspectRatio par; SVGAttributeParser parser(stringValue); if (!parser.parsePreserveAspectRatio(&par)) { return false; } - node->setAttribute(attr, SVGPreserveAspectRatioValue(par)); + node.setAttribute(attr, SVGPreserveAspectRatioValue(par)); return true; } @@ -201,11 +196,9 @@ class StyleIterator { const char* fPos; }; -bool set_string_attribute(const std::shared_ptr& node, const std::string& name, - const std::string& value); +bool set_string_attribute(SVGNode& node, const std::string& name, const std::string& value); -bool SetStyleAttributes(const std::shared_ptr& node, SVGAttribute, - const char* stringValue) { +bool SetStyleAttributes(SVGNode& node, SVGAttribute, const char* stringValue) { std::string name; std::string value; @@ -221,53 +214,20 @@ bool SetStyleAttributes(const std::shared_ptr& node, SVGAttribute, return true; } -inline const char* index_into_base(const char* const* base, int index, size_t elemSize) { - return *reinterpret_cast(reinterpret_cast(base) + - static_cast(index) * elemSize); -} - -int StringSearch(const char* const* base, int count, const char target[], size_t elemSize) { - if (count <= 0) return ~0; - - ASSERT(base != nullptr); - - size_t target_len = strlen(target); - int lo = 0; - int hi = count - 1; - - while (lo < hi) { - int mid = (hi + lo) >> 1; - const char* elem = index_into_base(base, mid, elemSize); - - int cmp = strncmp(elem, target, target_len); - if (cmp < 0) lo = mid + 1; - else if (cmp > 0 || strlen(elem) > target_len) - hi = mid; - else - return mid; - } - - const char* elem = index_into_base(base, hi, elemSize); - int cmp = strncmp(elem, target, target_len); - if (cmp || strlen(elem) > target_len) { - if (cmp < 0) hi += 1; - hi = ~hi; - } - return hi; -} - template struct SortedDictionaryEntry { - const char* fKey; + const std::string fKey; const T fValue; }; +using AttributeSetter = std::function; + struct AttrParseInfo { SVGAttribute fAttr; - bool (*fSetter)(const std::shared_ptr& node, SVGAttribute attr, const char* stringValue); + AttributeSetter fSetter; }; -SortedDictionaryEntry gAttributeParseInfo[] = { +std::vector> gAttributeParseInfo = { {"cx", {SVGAttribute::kCx, SetLengthAttribute}}, {"cy", {SVGAttribute::kCy, SetLengthAttribute}}, {"filterUnits", {SVGAttribute::kFilterUnits, SetObjectBoundingBoxUnitsAttribute}}, @@ -293,7 +253,7 @@ SortedDictionaryEntry gAttributeParseInfo[] = { {"y2", {SVGAttribute::kY2, SetLengthAttribute}}, }; -SortedDictionaryEntry (*)()> gTagFactories[] = { +std::vector (*)()>> gTagFactories = { {"a", []() -> std::shared_ptr { return SkSVGG::Make(); }}, {"circle", []() -> std::shared_ptr { return SVGCircle::Make(); }}, {"clipPath", []() -> std::shared_ptr { return SVGClipPath::Make(); }}, @@ -345,8 +305,22 @@ SortedDictionaryEntry (*)()> gTagFactories[] = { {"use", []() -> std::shared_ptr { return SkSVGUse::Make(); }}, }; +template +int StringSearch(const std::vector>& base, const std::string& target) { + if (base.empty()) { + return -1; + } + + for (uint32_t i = 0; i < base.size(); i++) { + if (base[i].fKey == target) { + return static_cast(i); + } + } + return -1; +} + struct ConstructionContext { - ConstructionContext(SVGIDMapper* mapper) : fParent(nullptr), fIDMapper(mapper) { + explicit ConstructionContext(SVGIDMapper* mapper) : fParent(nullptr), fIDMapper(mapper) { } ConstructionContext(const ConstructionContext& other, const std::shared_ptr& newParent) : fParent(newParent.get()), fIDMapper(other.fIDMapper) { @@ -356,22 +330,19 @@ struct ConstructionContext { SVGIDMapper* fIDMapper; }; -bool set_string_attribute(const std::shared_ptr& node, const std::string& name, - const std::string& value) { - if (node->parseAndSetAttribute(name.c_str(), value.c_str())) { +bool set_string_attribute(SVGNode& node, const std::string& name, const std::string& value) { + if (node.parseAndSetAttribute(name.c_str(), value.c_str())) { // Handled by new code path return true; } - const int attrIndex = - StringSearch(&gAttributeParseInfo[0].fKey, static_cast(std::size(gAttributeParseInfo)), - name.c_str(), sizeof(gAttributeParseInfo[0])); + const int attrIndex = StringSearch(gAttributeParseInfo, name); if (attrIndex < 0) { return false; } ASSERT(static_cast(attrIndex) < std::size(gAttributeParseInfo)); - const auto& attrInfo = gAttributeParseInfo[attrIndex].fValue; + const auto& attrInfo = gAttributeParseInfo[static_cast(attrIndex)].fValue; return attrInfo.fSetter(node, attrInfo.fAttr, value.c_str()); } @@ -379,13 +350,13 @@ void parse_node_attributes(const DOMNode* xmlNode, const std::shared_ptrattributes) { - const char* name = attr.name.c_str(); - const char* value = attr.value.c_str(); - if (!strcmp(name, "id")) { + auto name = attr.name; + auto value = attr.value; + if (name == "id") { mapper->insert({value, svgNode}); continue; } - set_string_attribute(svgNode, name, value); + set_string_attribute(*svgNode, name, value); } } @@ -413,14 +384,15 @@ std::shared_ptr construct_svg_node(const ConstructionContext& ctx, return SVGSVG::Make(ctx.fParent ? SVGSVG::Type::kInner : SVGSVG::Type::kRoot); } - const int tagIndex = - StringSearch(&gTagFactories[0].fKey, static_cast(std::size(gTagFactories)), - elem.c_str(), sizeof(gTagFactories[0])); + // const int tagIndex = + // StringSearch(&gTagFactories[0].fKey, static_cast(std::size(gTagFactories)), + // elem.c_str(), sizeof(gTagFactories[0])); + int tagIndex = StringSearch(gTagFactories, elem); if (tagIndex < 0) { return nullptr; } ASSERT(static_cast(tagIndex) < std::size(gTagFactories)); - return gTagFactories[tagIndex].fValue(); + return gTagFactories[static_cast(tagIndex)].fValue(); }; auto node = makeNode(ctx, elem); @@ -493,44 +465,43 @@ std::shared_ptr SVGDOM::Builder::make(Data& data, SVGDOM::SVGDOM(std::shared_ptr root, SVGIDMapper&& mapper, std::shared_ptr fontManager) - : fRoot(std::move(root)), fFontMgr(std::move(fontManager)), fIDMapper(std::move(mapper)) { + : _root(std::move(root)), _fontMgr(std::move(fontManager)), _nodeIDMapper(std::move(mapper)) { } void SVGDOM::render(Canvas* canvas) const { - if (fRoot) { - SVGLengthContext lctx(fContainerSize); + if (_root) { + SVGLengthContext lctx(_containerSize); SkSVGPresentationContext pctx; - fRoot->render(SVGRenderContext(canvas, fFontMgr, fResourceProvider, fIDMapper, lctx, pctx, - {nullptr, nullptr})); + _root->render( + SVGRenderContext(canvas, _fontMgr, _nodeIDMapper, lctx, pctx, {nullptr, nullptr})); } } void SVGDOM::renderNode(Canvas* canvas, SkSVGPresentationContext& pctx, const char* id) const { - if (fRoot) { - SVGLengthContext lctx(fContainerSize); - fRoot->renderNode(SVGRenderContext(canvas, fFontMgr, fResourceProvider, fIDMapper, lctx, pctx, - {nullptr, nullptr}), - SVGIRI(SVGIRI::Type::kLocal, SVGStringType(id))); + if (_root) { + SVGLengthContext lctx(_containerSize); + _root->renderNode( + SVGRenderContext(canvas, _fontMgr, _nodeIDMapper, lctx, pctx, {nullptr, nullptr}), + SVGIRI(SVGIRI::Type::kLocal, SVGStringType(id))); } } const Size& SVGDOM::containerSize() const { - return fContainerSize; + return _containerSize; } void SVGDOM::setContainerSize(const Size& containerSize) { // TODO: inval - fContainerSize = containerSize; + _containerSize = containerSize; } -std::shared_ptr SVGDOM::findNodeById(const char* id) { - auto iter = fIDMapper.find(id); - return iter == this->fIDMapper.end() ? nullptr : iter->second; +std::shared_ptr SVGDOM::findNodeById(const std::string& id) { + auto iter = _nodeIDMapper.find(id); + return iter == this->_nodeIDMapper.end() ? nullptr : iter->second; } // TODO(fuego): move this to SkSVGNode or its own CU. bool SVGNode::setAttribute(const std::string& attributeName, const std::string& attributeValue) { - //std::shared_ptr(this) 临时写法 有点危险 - return set_string_attribute(std::shared_ptr(this), attributeName, attributeValue); + return set_string_attribute(*this, attributeName, attributeValue); } } // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGRenderContext.cpp b/src/svg/SVGRenderContext.cpp index c0a168a4..4ae997c9 100644 --- a/src/svg/SVGRenderContext.cpp +++ b/src/svg/SVGRenderContext.cpp @@ -42,11 +42,11 @@ namespace { float length_size_for_type(const Size& viewport, SVGLengthContext::LengthType t) { switch (t) { - case SVGLengthContext::LengthType::kHorizontal: + case SVGLengthContext::LengthType::Horizontal: return viewport.width; - case SVGLengthContext::LengthType::kVertical: + case SVGLengthContext::LengthType::Vertical: return viewport.height; - case SVGLengthContext::LengthType::kOther: { + case SVGLengthContext::LengthType::Other: { // https://www.w3.org/TR/SVG11/coords.html#Units_viewport_percentage const float rsqrt2 = 1.0f / std::sqrt(2.0f); const float w = viewport.width; @@ -70,9 +70,9 @@ constexpr float kCMMultiplier = kMMMultiplier * 10; float SVGLengthContext::resolve(const SVGLength& l, LengthType t) const { switch (l.unit()) { case SVGLength::Unit::kNumber: { - if (fPatternUnit.has_value()) { - if (fPatternUnit.value() == SVGPatternUnits::ObjectBoundingBox) { - return l.value() * length_size_for_type(fViewport, t); + if (_patternUnit.has_value()) { + if (_patternUnit.value() == SVGPatternUnits::ObjectBoundingBox) { + return l.value() * length_size_for_type(_viewport, t); } else { return l.value(); } @@ -83,26 +83,26 @@ float SVGLengthContext::resolve(const SVGLength& l, LengthType t) const { case SVGLength::Unit::kPX: return l.value(); case SVGLength::Unit::kPercentage: { - if (fPatternUnit.has_value()) { - if (fPatternUnit.value() == SVGPatternUnits::ObjectBoundingBox) { - return l.value() * length_size_for_type(fViewport, t) / 100.f; + if (_patternUnit.has_value()) { + if (_patternUnit.value() == SVGPatternUnits::ObjectBoundingBox) { + return l.value() * length_size_for_type(_viewport, t) / 100.f; } else { return l.value() / 100.f; } } else { - return l.value() * length_size_for_type(fViewport, t) / 100; + return l.value() * length_size_for_type(_viewport, t) / 100; } } case SVGLength::Unit::kCM: - return l.value() * fDPI * kCMMultiplier; + return l.value() * _dpi * kCMMultiplier; case SVGLength::Unit::kMM: - return l.value() * fDPI * kMMMultiplier; + return l.value() * _dpi * kMMMultiplier; case SVGLength::Unit::kIN: - return l.value() * fDPI * kINMultiplier; + return l.value() * _dpi * kINMultiplier; case SVGLength::Unit::kPT: - return l.value() * fDPI * kPTMultiplier; + return l.value() * _dpi * kPTMultiplier; case SVGLength::Unit::kPC: - return l.value() * fDPI * kPCMultiplier; + return l.value() * _dpi * kPCMultiplier; default: //unsupported unit type ASSERT(false); @@ -112,10 +112,10 @@ float SVGLengthContext::resolve(const SVGLength& l, LengthType t) const { Rect SVGLengthContext::resolveRect(const SVGLength& x, const SVGLength& y, const SVGLength& width, const SVGLength& height) const { - return Rect::MakeXYWH(this->resolve(x, SVGLengthContext::LengthType::kHorizontal), - this->resolve(y, SVGLengthContext::LengthType::kVertical), - this->resolve(width, SVGLengthContext::LengthType::kHorizontal), - this->resolve(height, SVGLengthContext::LengthType::kVertical)); + return Rect::MakeXYWH(this->resolve(x, SVGLengthContext::LengthType::Horizontal), + this->resolve(y, SVGLengthContext::LengthType::Vertical), + this->resolve(width, SVGLengthContext::LengthType::Horizontal), + this->resolve(height, SVGLengthContext::LengthType::Vertical)); } namespace { @@ -156,7 +156,7 @@ std::unique_ptr dash_effect(const SkSVGPresentationAttributes& props std::vector intervals; intervals.reserve(count); for (const auto& dash : da.dashArray()) { - intervals.push_back(lctx.resolve(dash, SVGLengthContext::LengthType::kOther)); + intervals.push_back(lctx.resolve(dash, SVGLengthContext::LengthType::Other)); } if (count & 1) { @@ -168,7 +168,7 @@ std::unique_ptr dash_effect(const SkSVGPresentationAttributes& props ASSERT((intervals.size() & 1) == 0); - const auto phase = lctx.resolve(*props.fStrokeDashOffset, SVGLengthContext::LengthType::kOther); + const auto phase = lctx.resolve(*props.fStrokeDashOffset, SVGLengthContext::LengthType::Other); return PathEffect::MakeDash(intervals.data(), static_cast(intervals.size()), phase); } @@ -176,54 +176,53 @@ std::unique_ptr dash_effect(const SkSVGPresentationAttributes& props } // namespace SkSVGPresentationContext::SkSVGPresentationContext() - : fInherited(SkSVGPresentationAttributes::MakeInitial()) { + : _inherited(SkSVGPresentationAttributes::MakeInitial()) { } SVGRenderContext::SVGRenderContext(Canvas* canvas, const std::shared_ptr& fontManager, - const std::shared_ptr& resource, const SVGIDMapper& mapper, const SVGLengthContext& lctx, const SkSVGPresentationContext& pctx, const OBBScope& obbs) - : fFontMgr(fontManager), fResourceProvider(resource), fIDMapper(mapper), fLengthContext(lctx), - fPresentationContext(pctx), fCanvas(canvas), fCanvasSaveCount(canvas->getSaveCount()), - fOBBScope(obbs) { + : _fontMgr(fontManager), _nodeIDMapper(mapper), _lengthContext(lctx), + _presentationContext(pctx), _canvas(canvas), _canvasSaveCount(canvas->getSaveCount()), + _scope(obbs) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other) - : SVGRenderContext(other.fCanvas, other.fFontMgr, other.fResourceProvider, other.fIDMapper, - *other.fLengthContext, *other.fPresentationContext, other.fOBBScope) { + : SVGRenderContext(other._canvas, other._fontMgr, other._nodeIDMapper, *other._lengthContext, + *other._presentationContext, other._scope) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, Canvas* canvas) - : SVGRenderContext(canvas, other.fFontMgr, other.fResourceProvider, other.fIDMapper, - *other.fLengthContext, *other.fPresentationContext, other.fOBBScope) { + : SVGRenderContext(canvas, other._fontMgr, other._nodeIDMapper, *other._lengthContext, + *other._presentationContext, other._scope) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, const SVGLengthContext& lengthCtx) - : SVGRenderContext(other.fCanvas, other.fFontMgr, other.fResourceProvider, other.fIDMapper, - lengthCtx, *other.fPresentationContext, other.fOBBScope) { + : SVGRenderContext(other._canvas, other._fontMgr, other._nodeIDMapper, lengthCtx, + *other._presentationContext, other._scope) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, Canvas* canvas, const SVGLengthContext& lengthCtx) - : SVGRenderContext(canvas, other.fFontMgr, other.fResourceProvider, other.fIDMapper, lengthCtx, - *other.fPresentationContext, other.fOBBScope) { + : SVGRenderContext(canvas, other._fontMgr, other._nodeIDMapper, lengthCtx, + *other._presentationContext, other._scope) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, const SVGNode* node) - : SVGRenderContext(other.fCanvas, other.fFontMgr, other.fResourceProvider, other.fIDMapper, - *other.fLengthContext, *other.fPresentationContext, OBBScope{node, this}) { + : SVGRenderContext(other._canvas, other._fontMgr, other._nodeIDMapper, *other._lengthContext, + *other._presentationContext, OBBScope{node, this}) { } SVGRenderContext::~SVGRenderContext() { - fCanvas->restoreToCount(fCanvasSaveCount); + _canvas->restoreToCount(_canvasSaveCount); } std::shared_ptr SVGRenderContext::findNodeById(const SVGIRI& iri) const { if (iri.type() != SVGIRI::Type::kLocal) { return nullptr; } - auto p = fIDMapper.find(iri.iri())->second; + auto p = _nodeIDMapper.find(iri.iri())->second; return p; } @@ -233,11 +232,11 @@ void SVGRenderContext::applyPresentationAttributes(const SkSVGPresentationAttrib #define ApplyLazyInheritedAttribute(ATTR) \ do { \ /* All attributes should be defined on the inherited context. */ \ - ASSERT(fPresentationContext->fInherited.f##ATTR.isValue()); \ + ASSERT(_presentationContext->_inherited.f##ATTR.isValue()); \ const auto& attr = attrs.f##ATTR; \ - if (attr.isValue() && *attr != *fPresentationContext->fInherited.f##ATTR) { \ + if (attr.isValue() && *attr != *_presentationContext->_inherited.f##ATTR) { \ /* Update the local attribute value */ \ - fPresentationContext.writable()->fInherited.f##ATTR.set(*attr); \ + _presentationContext.writable()->_inherited.f##ATTR.set(*attr); \ } \ } while (false) @@ -300,7 +299,7 @@ void SVGRenderContext::applyOpacity(float opacity, uint32_t flags, bool hasFilte return; } - const auto& props = fPresentationContext->fInherited; + const auto& props = _presentationContext->_inherited; const bool hasFill = props.fFill->type() != SVGPaint::Type::kNone; const bool hasStroke = props.fStroke->type() != SVGPaint::Type::kNone; @@ -311,7 +310,7 @@ void SVGRenderContext::applyOpacity(float opacity, uint32_t flags, bool hasFilte // - it does not have a filter. // Going forward, we may needto refine this heuristic (e.g. to accommodate markers). if ((flags & kLeaf) && (hasFill ^ hasStroke) && !hasFilter) { - fDeferredPaintOpacity *= opacity; + _deferredPaintOpacity *= opacity; } else { // Expensive, layer-based fall back. Paint opacityPaint; @@ -346,10 +345,10 @@ void SVGRenderContext::applyFilter(const SVGFuncIRI& filter) { void SVGRenderContext::saveOnce() { // The canvas only needs to be saved once, per local SVGRenderContext. - if (fCanvas->getSaveCount() == fCanvasSaveCount) { - fCanvas->save(); + if (_canvas->getSaveCount() == _canvasSaveCount) { + _canvas->save(); } - ASSERT(fCanvas->getSaveCount() > fCanvasSaveCount); + ASSERT(_canvas->getSaveCount() > _canvasSaveCount); } void SVGRenderContext::applyClip(const SVGFuncIRI& clip) { @@ -373,8 +372,8 @@ void SVGRenderContext::applyClip(const SVGFuncIRI& clip) { this->saveOnce(); - fCanvas->clipPath(clipPath); - fClipPath = clipPath; + _canvas->clipPath(clipPath); + _clipPath = clipPath; } void SVGRenderContext::applyMask(const SVGFuncIRI& mask) { @@ -405,7 +404,7 @@ void SVGRenderContext::applyMask(const SVGFuncIRI& mask) { // fCanvas->saveLayer(mask_bounds, &masking_paint); // Content is also clipped to the specified mask bounds. - fCanvas->clipRect(mask_bounds); + _canvas->clipRect(mask_bounds); // At this point we're set up for content rendering. // The pending layers are restored in the destructor (render context scope exit). @@ -434,9 +433,8 @@ std::optional SVGRenderContext::commonPaint(const SVGPaint& paint_selecto // (e.g. gradient control points), which requires access to the render context // and node being rendered. SkSVGPresentationContext pctx; - pctx.fNamedColors = fPresentationContext->fNamedColors; - SVGRenderContext local_ctx(fCanvas, fFontMgr, fResourceProvider, fIDMapper, *fLengthContext, - pctx, fOBBScope); + pctx._namedColors = _presentationContext->_namedColors; + SVGRenderContext local_ctx(_canvas, _fontMgr, _nodeIDMapper, *_lengthContext, pctx, _scope); const auto node = this->findNodeById(paint_selector.iri()); if (!node || !node->asPaint(local_ctx, &(p.value()))) { @@ -453,12 +451,12 @@ std::optional SVGRenderContext::commonPaint(const SVGPaint& paint_selecto // - initial paint server opacity (e.g. color stop opacity) // - paint-specific opacity (e.g. 'fill-opacity', 'stroke-opacity') // - deferred opacity override (optimization for leaf nodes 'opacity') - p->setAlpha(std::clamp(p->getAlpha() * paint_opacity * fDeferredPaintOpacity, 0.0f, 1.0f)); + p->setAlpha(std::clamp(p->getAlpha() * paint_opacity * _deferredPaintOpacity, 0.0f, 1.0f)); return p; } std::optional SVGRenderContext::fillPaint() const { - const auto& props = fPresentationContext->fInherited; + const auto& props = _presentationContext->_inherited; auto p = this->commonPaint(*props.fFill, *props.fFillOpacity); if (p.has_value()) { @@ -469,13 +467,13 @@ std::optional SVGRenderContext::fillPaint() const { } std::optional SVGRenderContext::strokePaint() const { - const auto& props = fPresentationContext->fInherited; + const auto& props = _presentationContext->_inherited; auto p = this->commonPaint(*props.fStroke, *props.fStrokeOpacity); if (p.has_value()) { p->setStyle(PaintStyle::Stroke); p->setStrokeWidth( - fLengthContext->resolve(*props.fStrokeWidth, SVGLengthContext::LengthType::kOther)); + _lengthContext->resolve(*props.fStrokeWidth, SVGLengthContext::LengthType::Other)); Stroke stroke; stroke.cap = toSkCap(*props.fStrokeLineCap); stroke.join = toSkJoin(*props.fStrokeLineJoin); @@ -484,17 +482,17 @@ std::optional SVGRenderContext::strokePaint() const { //TODO (YG) // p->setPathEffect(dash_effect(props, *fLengthContext)); - dash_effect(props, *fLengthContext); + dash_effect(props, *_lengthContext); } return p; } SVGColorType SVGRenderContext::resolveSvgColor(const SVGColor& color) const { - if (fPresentationContext->fNamedColors) { + if (_presentationContext->_namedColors) { for (auto&& ident : *color.vars()) { - auto iter = fPresentationContext->fNamedColors->find(ident); - if (iter != fPresentationContext->fNamedColors->end()) { + auto iter = _presentationContext->_namedColors->find(ident); + if (iter != _presentationContext->_namedColors->end()) { return iter->second; } } @@ -503,7 +501,7 @@ SVGColorType SVGRenderContext::resolveSvgColor(const SVGColor& color) const { case SVGColor::Type::kColor: return color.color(); case SVGColor::Type::kCurrentColor: - return *fPresentationContext->fInherited.fColor; + return *_presentationContext->_inherited.fColor; case SVGColor::Type::kICCColor: return Color::Black(); } @@ -511,18 +509,18 @@ SVGColorType SVGRenderContext::resolveSvgColor(const SVGColor& color) const { SVGRenderContext::OBBTransform SVGRenderContext::transformForCurrentOBB( SVGObjectBoundingBoxUnits u) const { - if (!fOBBScope.fNode || u.type() == SVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse) { + if (!_scope.node || u.type() == SVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse) { return {{0, 0}, {1, 1}}; } - ASSERT(fOBBScope.fCtx); + ASSERT(_scope.context); - const auto obb = fOBBScope.fNode->objectBoundingBox(*fOBBScope.fCtx); + const auto obb = _scope.node->objectBoundingBox(*_scope.context); return {{obb.x(), obb.y()}, {obb.width(), obb.height()}}; } Rect SVGRenderContext::resolveOBBRect(const SVGLength& x, const SVGLength& y, const SVGLength& w, const SVGLength& h, SVGObjectBoundingBoxUnits obbu) const { - CopyOnWrite lctx(fLengthContext); + CopyOnWrite lctx(_lengthContext); if (obbu.type() == SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox) { *lctx.writable() = SVGLengthContext({1, 1}); diff --git a/src/svg/node/SVGCircle.cpp b/src/svg/node/SVGCircle.cpp index 3172182c..5e2777c1 100644 --- a/src/svg/node/SVGCircle.cpp +++ b/src/svg/node/SVGCircle.cpp @@ -37,9 +37,9 @@ bool SVGCircle::parseAndSetAttribute(const char* n, const char* v) { #ifndef RENDER_SVG std::tuple SVGCircle::resolve(const SVGLengthContext& lctx) const { - const auto cx = lctx.resolve(fCx, SVGLengthContext::LengthType::kHorizontal); - const auto cy = lctx.resolve(fCy, SVGLengthContext::LengthType::kVertical); - const auto r = lctx.resolve(fR, SVGLengthContext::LengthType::kOther); + const auto cx = lctx.resolve(fCx, SVGLengthContext::LengthType::Horizontal); + const auto cy = lctx.resolve(fCy, SVGLengthContext::LengthType::Vertical); + const auto r = lctx.resolve(fR, SVGLengthContext::LengthType::Other); return std::make_tuple(Point::Make(cx, cy), r); } diff --git a/src/svg/node/SVGEllipse.cpp b/src/svg/node/SVGEllipse.cpp index ff145a16..a95b8740 100644 --- a/src/svg/node/SVGEllipse.cpp +++ b/src/svg/node/SVGEllipse.cpp @@ -38,8 +38,8 @@ bool SkSVGEllipse::parseAndSetAttribute(const char* n, const char* v) { #ifndef RENDER_SVG Rect SkSVGEllipse::resolve(const SVGLengthContext& lctx) const { - const auto cx = lctx.resolve(fCx, SVGLengthContext::LengthType::kHorizontal); - const auto cy = lctx.resolve(fCy, SVGLengthContext::LengthType::kVertical); + const auto cx = lctx.resolve(fCx, SVGLengthContext::LengthType::Horizontal); + const auto cy = lctx.resolve(fCy, SVGLengthContext::LengthType::Vertical); // https://www.w3.org/TR/SVG2/shapes.html#EllipseElement // diff --git a/src/svg/node/SVGFe.cpp b/src/svg/node/SVGFe.cpp index 75ddff12..056d9aea 100644 --- a/src/svg/node/SVGFe.cpp +++ b/src/svg/node/SVGFe.cpp @@ -101,7 +101,7 @@ Rect SkSVGFe::resolveFilterSubregion(const SVGRenderContext& ctx, SVGColorspace SkSVGFe::resolveColorspace(const SVGRenderContext& ctx, const SkSVGFilterContext&) const { constexpr SVGColorspace kDefaultCS = SVGColorspace::kSRGB; - const SVGColorspace cs = *ctx.presentationContext().fInherited.fColorInterpolationFilters; + const SVGColorspace cs = *ctx.presentationContext()._inherited.fColorInterpolationFilters; return cs == SVGColorspace::kAuto ? kDefaultCS : cs; } diff --git a/src/svg/node/SVGFeDisplacementMap.cpp b/src/svg/node/SVGFeDisplacementMap.cpp index bc44895d..28302761 100644 --- a/src/svg/node/SVGFeDisplacementMap.cpp +++ b/src/svg/node/SVGFeDisplacementMap.cpp @@ -53,7 +53,7 @@ std::shared_ptr SkSVGFeDisplacementMap::onMakeImageFilter( const auto obbt = ctx.transformForCurrentOBB(fctx.primitiveUnits()); scale = SVGLengthContext({obbt.scale.x, obbt.scale.y}) .resolve(SVGLength(scale, SVGLength::Unit::kPercentage), - SVGLengthContext::LengthType::kOther); + SVGLengthContext::LengthType::Other); } return nullptr; diff --git a/src/svg/node/SVGGradient.cpp b/src/svg/node/SVGGradient.cpp index 8c22587c..ffd81753 100644 --- a/src/svg/node/SVGGradient.cpp +++ b/src/svg/node/SVGGradient.cpp @@ -48,7 +48,7 @@ void SkSVGGradient::collectColorStops(const SVGRenderContext& ctx, std::vectorforEachChild([&](const SkSVGStop* stop) { colors.push_back(this->resolveStopColor(ctx, *stop)); positions.push_back( - std::clamp(ltx.resolve(stop->getOffset(), SVGLengthContext::LengthType::kOther), 0.f, 1.f)); + std::clamp(ltx.resolve(stop->getOffset(), SVGLengthContext::LengthType::Other), 0.f, 1.f)); }); ASSERT(colors.size() == positions.size()); diff --git a/src/svg/node/SVGImage.cpp b/src/svg/node/SVGImage.cpp index c12baef3..4168912a 100644 --- a/src/svg/node/SVGImage.cpp +++ b/src/svg/node/SVGImage.cpp @@ -90,7 +90,7 @@ std::vector base64_decode(const std::string& encoded_string) { return out; } -std::shared_ptr LoadImage(const std::shared_ptr&, const SVGIRI& href) { +std::shared_ptr LoadImage(const SVGIRI& href) { const auto& base64URL = href.iri(); auto pos = base64URL.find("base64,"); if (pos == std::string::npos) { @@ -99,41 +99,14 @@ std::shared_ptr LoadImage(const std::shared_ptr&, const std::string base64Data = base64URL.substr(pos + 7); std::vector imageData = base64_decode(base64Data); auto data = Data::MakeWithCopy(imageData.data(), imageData.size()); - // { - // std::ofstream out("/Users/yg/Downloads/yg2.png", std::ios::binary); - // if (!out) { - // return nullptr; - // } - // out.write(reinterpret_cast(data->data()), - // static_cast(data->size())); - // } - return Image::MakeFromEncoded(data); - // TODO: It may be better to use the SVG 'id' attribute as the asset id, to allow - // clients to perform asset substitution based on element id. - // sk_sp imageAsset; - // switch (href.type()) { - // case SVGIRI::Type::kDataURI: - // imageAsset = rp.loadImageAsset("", href.iri().c_str(), ""); - // break; - // case SVGIRI::Type::kNonlocal: { - // const auto path = SkOSPath::Dirname(href.iri().c_str()); - // const auto name = SkOSPath::Basename(href.iri().c_str()); - // imageAsset = rp->loadImageAsset(path.c_str(), name.c_str(), /* id */ name.c_str()); - // break; - // } - // default: - // return nullptr; - // } - - // return imageAsset ? imageAsset->getFrameData(0).image : nullptr; + return Image::MakeFromEncoded(data); } -SkSVGImage::ImageInfo SkSVGImage::LoadImage(const std::shared_ptr& rp, - const SVGIRI& iri, const Rect& viewPort, +SkSVGImage::ImageInfo SkSVGImage::LoadImage(const SVGIRI& iri, const Rect& viewPort, SVGPreserveAspectRatio /*par*/) { // TODO: svg sources - std::shared_ptr image = ::tgfx::LoadImage(rp, iri); + std::shared_ptr image = ::tgfx::LoadImage(iri); if (!image) { return {}; } @@ -156,7 +129,7 @@ void SkSVGImage::onRender(const SVGRenderContext& ctx) const { //TODO (YG) ImageInfo image; - const auto imgInfo = LoadImage(ctx.resourceProvider(), fHref, viewPort, fPreserveAspectRatio); + const auto imgInfo = LoadImage(fHref, viewPort, fPreserveAspectRatio); if (!imgInfo.fImage) { LOGE("can't render image: load image failed\n"); return; diff --git a/src/svg/node/SVGLine.cpp b/src/svg/node/SVGLine.cpp index bc508afa..42e735df 100644 --- a/src/svg/node/SVGLine.cpp +++ b/src/svg/node/SVGLine.cpp @@ -37,10 +37,10 @@ bool SkSVGLine::parseAndSetAttribute(const char* n, const char* v) { #ifndef RENDER_SVG std::tuple SkSVGLine::resolve(const SVGLengthContext& lctx) const { - return std::make_tuple(Point::Make(lctx.resolve(fX1, SVGLengthContext::LengthType::kHorizontal), - lctx.resolve(fY1, SVGLengthContext::LengthType::kVertical)), - Point::Make(lctx.resolve(fX2, SVGLengthContext::LengthType::kHorizontal), - lctx.resolve(fY2, SVGLengthContext::LengthType::kVertical))); + return std::make_tuple(Point::Make(lctx.resolve(fX1, SVGLengthContext::LengthType::Horizontal), + lctx.resolve(fY1, SVGLengthContext::LengthType::Vertical)), + Point::Make(lctx.resolve(fX2, SVGLengthContext::LengthType::Horizontal), + lctx.resolve(fY2, SVGLengthContext::LengthType::Vertical))); } void SkSVGLine::onDraw(Canvas* canvas, const SVGLengthContext& lctx, const Paint& paint, diff --git a/src/svg/node/SVGLinearGradient.cpp b/src/svg/node/SVGLinearGradient.cpp index 9083bf5e..6336ff71 100644 --- a/src/svg/node/SVGLinearGradient.cpp +++ b/src/svg/node/SVGLinearGradient.cpp @@ -46,10 +46,10 @@ std::shared_ptr SkSVGLinearGradient::onMakeShader(const SVGRenderContext ? SVGLengthContext({1, 1}) : ctx.lengthContext(); - auto startPoint = Point::Make(lctx.resolve(fX1, SVGLengthContext::LengthType::kHorizontal), - lctx.resolve(fY1, SVGLengthContext::LengthType::kVertical)); - auto endPoint = Point::Make(lctx.resolve(fX2, SVGLengthContext::LengthType::kHorizontal), - lctx.resolve(fY2, SVGLengthContext::LengthType::kVertical)); + auto startPoint = Point::Make(lctx.resolve(fX1, SVGLengthContext::LengthType::Horizontal), + lctx.resolve(fY1, SVGLengthContext::LengthType::Vertical)); + auto endPoint = Point::Make(lctx.resolve(fX2, SVGLengthContext::LengthType::Horizontal), + lctx.resolve(fY2, SVGLengthContext::LengthType::Vertical)); return Shader::MakeLinearGradient(startPoint, endPoint, colors, positions); } diff --git a/src/svg/node/SVGMask.cpp b/src/svg/node/SVGMask.cpp index b29853e6..3040a6f3 100644 --- a/src/svg/node/SVGMask.cpp +++ b/src/svg/node/SVGMask.cpp @@ -40,27 +40,26 @@ Rect SkSVGMask::bounds(const SVGRenderContext& ctx) const { return ctx.resolveOBBRect(fX, fY, fWidth, fHeight, fMaskUnits); } -void SkSVGMask::renderMask(const SVGRenderContext& /*ctx*/) const { +void SkSVGMask::renderMask(const SVGRenderContext& ctx) const { //TODO (YG) - // // https://www.w3.org/TR/SVG11/masking.html#Masking + // https://www.w3.org/TR/SVG11/masking.html#Masking - // // Propagate any inherited properties that may impact mask effect behavior (e.g. - // // color-interpolation). We call this explicitly here because the SkSVGMask - // // nodes do not participate in the normal onRender path, which is when property - // // propagation currently occurs. - // // The local context also restores the filter layer created below on scope exit. - // SVGRenderContext lctx(ctx); - // this->onPrepareToRender(&lctx); + // Propagate any inherited properties that may impact mask effect behavior (e.g. + // color-interpolation). We call this explicitly here because the SkSVGMask + // nodes do not participate in the normal onRender path, which is when property + // propagation currently occurs. + // The local context also restores the filter layer created below on scope exit. + SVGRenderContext lctx(ctx); + this->onPrepareToRender(&lctx); - // const auto ci = *lctx.presentationContext().fInherited.fColorInterpolation; + // const auto ci = *lctx.presentationContext()._inherited.fColorInterpolation; // auto ci_filter = (ci == SVGColorspace::kLinearRGB) ? ColorFilters::SRGBToLinearGamma() : nullptr; // Paint mask_filter; - // MaskFilter::Compose(std::shared_ptr inner, std::shared_ptr outer) - // mask_filter.setMaskFilter() - // mask_filter.setColorFilter( - // SkColorFilters::Compose(SkLumaColorFilter::Make(), std::move(ci_filter))); + // MaskFilter::Compose(); + // mask_filter.setMaskFilter() mask_filter.setColorFilter( + // SkColorFilters::Compose(SkLumaColorFilter::Make(), std::move(ci_filter))); // // Mask color filter layer. // // Note: We could avoid this extra layer if we invert the stacking order diff --git a/src/svg/node/SVGNode.cpp b/src/svg/node/SVGNode.cpp index 23eea5af..aee3d2e3 100644 --- a/src/svg/node/SVGNode.cpp +++ b/src/svg/node/SVGNode.cpp @@ -18,10 +18,8 @@ #include "tgfx/svg/node/SVGNode.h" #include -#include #include #include -#include "core/utils/Log.h" #include "tgfx/core/Color.h" #include "tgfx/core/Matrix.h" #include "tgfx/core/Paint.h" @@ -88,7 +86,7 @@ bool SVGNode::onPrepareToRender(SVGRenderContext* ctx) const { // visibility:hidden and display:none disable rendering. // TODO: if display is not a value (true when display="inherit"), we currently // ignore it. Eventually we should be able to add SkASSERT(display.isValue()). - const auto visibility = ctx->presentationContext().fInherited.fVisibility->type(); + const auto visibility = ctx->presentationContext()._inherited.fVisibility->type(); const auto display = fPresentationAttributes.fDisplay; // display is uninherited return visibility != SVGVisibility::Type::kHidden && (!display.isValue() || *display != SVGDisplay::kNone); diff --git a/src/svg/node/SVGPath.cpp b/src/svg/node/SVGPath.cpp index fe9cc916..9469bb41 100644 --- a/src/svg/node/SVGPath.cpp +++ b/src/svg/node/SVGPath.cpp @@ -55,7 +55,7 @@ void SkSVGPath::onDraw(Canvas* canvas, const SVGLengthContext&, const Paint& pai Path SkSVGPath::onAsPath(const SVGRenderContext& ctx) const { Path path = fPath; // clip-rule can be inherited and needs to be applied at clip time. - path.setFillType(ctx.presentationContext().fInherited.fClipRule->asFillType()); + path.setFillType(ctx.presentationContext()._inherited.fClipRule->asFillType()); this->mapToParent(&path); return path; } diff --git a/src/svg/node/SVGPoly.cpp b/src/svg/node/SVGPoly.cpp index 9e9fa5fc..a0329155 100644 --- a/src/svg/node/SVGPoly.cpp +++ b/src/svg/node/SVGPoly.cpp @@ -53,7 +53,7 @@ Path SkSVGPoly::onAsPath(const SVGRenderContext& ctx) const { Path path = fPath; // clip-rule can be inherited and needs to be applied at clip time. - path.setFillType(ctx.presentationContext().fInherited.fClipRule->asFillType()); + path.setFillType(ctx.presentationContext()._inherited.fClipRule->asFillType()); this->mapToParent(&path); return path; diff --git a/src/svg/node/SVGRadialGradient.cpp b/src/svg/node/SVGRadialGradient.cpp index a79503e0..45491575 100644 --- a/src/svg/node/SVGRadialGradient.cpp +++ b/src/svg/node/SVGRadialGradient.cpp @@ -48,9 +48,9 @@ std::shared_ptr SkSVGRadialGradient::onMakeShader(const SVGRenderContext ? SVGLengthContext({1, 1}) : ctx.lengthContext(); - auto radius = lctx.resolve(fR, SVGLengthContext::LengthType::kOther); - auto center = Point::Make(lctx.resolve(fCx, SVGLengthContext::LengthType::kHorizontal), - lctx.resolve(fCy, SVGLengthContext::LengthType::kVertical)); + auto radius = lctx.resolve(fR, SVGLengthContext::LengthType::Other); + auto center = Point::Make(lctx.resolve(fCx, SVGLengthContext::LengthType::Horizontal), + lctx.resolve(fCy, SVGLengthContext::LengthType::Vertical)); // TODO(YGAurora): MakeTwoPointConical are unimplemented in tgfx // const auto focal = Point::Make( diff --git a/src/svg/node/SVGRect.cpp b/src/svg/node/SVGRect.cpp index eadf16f0..278b84d1 100644 --- a/src/svg/node/SVGRect.cpp +++ b/src/svg/node/SVGRect.cpp @@ -66,9 +66,9 @@ std::tuple ResolveOptionalRadii(const std::optional& op // individually, resolving rx percentages against the used width, and resolving ry // percentages against the used height. const float rx = - opt_rx.has_value() ? lctx.resolve(*opt_rx, SVGLengthContext::LengthType::kHorizontal) : 0; + opt_rx.has_value() ? lctx.resolve(*opt_rx, SVGLengthContext::LengthType::Horizontal) : 0; const float ry = - opt_ry.has_value() ? lctx.resolve(*opt_ry, SVGLengthContext::LengthType::kVertical) : 0; + opt_ry.has_value() ? lctx.resolve(*opt_ry, SVGLengthContext::LengthType::Vertical) : 0; return {opt_rx.has_value() ? rx : ry, opt_ry.has_value() ? ry : rx}; } diff --git a/src/svg/node/SVGSVG.cpp b/src/svg/node/SVGSVG.cpp index 97cc2b31..d4e4a546 100644 --- a/src/svg/node/SVGSVG.cpp +++ b/src/svg/node/SVGSVG.cpp @@ -84,8 +84,8 @@ Size SVGSVG::intrinsicSize(const SVGLengthContext& lctx) const { return Size::Make(0, 0); } - return Size::Make(lctx.resolve(fWidth, SVGLengthContext::LengthType::kHorizontal), - lctx.resolve(fHeight, SVGLengthContext::LengthType::kVertical)); + return Size::Make(lctx.resolve(fWidth, SVGLengthContext::LengthType::Horizontal), + lctx.resolve(fHeight, SVGLengthContext::LengthType::Vertical)); } #endif diff --git a/src/svg/node/SVGShape.cpp b/src/svg/node/SVGShape.cpp index fe942889..c3ed26d7 100644 --- a/src/svg/node/SVGShape.cpp +++ b/src/svg/node/SVGShape.cpp @@ -32,7 +32,7 @@ SVGShape::SVGShape(SVGTag t) : INHERITED(t) { #ifndef RENDER_SVG void SVGShape::onRender(const SVGRenderContext& ctx) const { - const auto fillType = ctx.presentationContext().fInherited.fFillRule->asFillType(); + const auto fillType = ctx.presentationContext()._inherited.fFillRule->asFillType(); auto selfRect = onObjectBoundingBox(ctx); auto lengthCtx = ctx.lengthContext(); diff --git a/src/svg/node/SVGText.cpp b/src/svg/node/SVGText.cpp index bf208c2f..4c321b60 100644 --- a/src/svg/node/SVGText.cpp +++ b/src/svg/node/SVGText.cpp @@ -81,11 +81,11 @@ std::tuple ResolveFont(const SVGRenderContext& context) { } }; - const std::string& family = context.presentationContext().fInherited.fFontFamily->family(); + const std::string& family = context.presentationContext()._inherited.fFontFamily->family(); - auto fontWeight = weight(*context.presentationContext().fInherited.fFontWeight); + auto fontWeight = weight(*context.presentationContext()._inherited.fFontWeight); auto fontWidth = FontStyle::Width::kNormal_Width; - auto fontSlant = slant(*context.presentationContext().fInherited.fFontStyle); + auto fontSlant = slant(*context.presentationContext()._inherited.fFontStyle); FontStyle style(fontWeight, fontWidth, fontSlant); auto typeface = context.fontMgr()->getTypefaceForRender(family, style); @@ -95,8 +95,8 @@ std::tuple ResolveFont(const SVGRenderContext& context) { } float size = - context.lengthContext().resolve(context.presentationContext().fInherited.fFontSize->size(), - SVGLengthContext::LengthType::kVertical); + context.lengthContext().resolve(context.presentationContext()._inherited.fFontSize->size(), + SVGLengthContext::LengthType::Vertical); return {true, Font(typeface, size)}; } @@ -114,7 +114,7 @@ std::vector ResolveLengths(const SVGLengthContext& lengthCtx, } float ComputeAlignmentFactor(const SkSVGPresentationContext& context) { - switch (context.fInherited.fTextAnchor->type()) { + switch (context._inherited.fTextAnchor->type()) { case SVGTextAnchor::Type::kStart: return 0.0f; case SVGTextAnchor::Type::kMiddle: @@ -160,10 +160,10 @@ Path SkSVGTextFragment::onAsPath(const SVGRenderContext&) const { void SkSVGTextContainer::onShapeText(const SVGRenderContext& context, const ShapedTextCallback& function) const { - auto x = ResolveLengths(context.lengthContext(), fX, SVGLengthContext::LengthType::kHorizontal); - auto y = ResolveLengths(context.lengthContext(), fY, SVGLengthContext::LengthType::kVertical); - auto dx = ResolveLengths(context.lengthContext(), fDx, SVGLengthContext::LengthType::kHorizontal); - auto dy = ResolveLengths(context.lengthContext(), fDy, SVGLengthContext::LengthType::kVertical); + auto x = ResolveLengths(context.lengthContext(), fX, SVGLengthContext::LengthType::Horizontal); + auto y = ResolveLengths(context.lengthContext(), fY, SVGLengthContext::LengthType::Vertical); + auto dx = ResolveLengths(context.lengthContext(), fDx, SVGLengthContext::LengthType::Horizontal); + auto dy = ResolveLengths(context.lengthContext(), fDy, SVGLengthContext::LengthType::Vertical); // TODO (YGAurora) : Handle rotate for (uint32_t i = 0; i < fChildren.size(); i++) { diff --git a/src/svg/node/SVGUse.cpp b/src/svg/node/SVGUse.cpp index 7ffd680a..9a7d8260 100644 --- a/src/svg/node/SVGUse.cpp +++ b/src/svg/node/SVGUse.cpp @@ -85,8 +85,8 @@ Rect SkSVGUse::onObjectBoundingBox(const SVGRenderContext& ctx) const { auto lengthContext = ctx.lengthContext(); lengthContext.clearPatternUnits(); - float x = lengthContext.resolve(fX, SVGLengthContext::LengthType::kHorizontal); - float y = lengthContext.resolve(fY, SVGLengthContext::LengthType::kVertical); + float x = lengthContext.resolve(fX, SVGLengthContext::LengthType::Horizontal); + float y = lengthContext.resolve(fY, SVGLengthContext::LengthType::Vertical); Rect bounds = ref->objectBoundingBox(ctx); bounds.offset(x, y); From f44ade387f7f494ca278821d8d184e64f24f8245 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Wed, 20 Nov 2024 14:13:01 +0800 Subject: [PATCH 03/31] debug and unit test --- include/tgfx/svg/SVGAttribute.h | 4 +- include/tgfx/svg/SVGRenderContext.h | 41 ++-- include/tgfx/svg/SVGTypes.h | 6 - include/tgfx/svg/node/SVGMask.h | 8 +- include/tgfx/svg/node/SVGNode.h | 6 +- include/tgfx/svg/node/SVGPattern.h | 6 +- resources/apitest/SVG/blur.svg | 12 ++ resources/apitest/SVG/jpg.svg | 9 + resources/apitest/SVG/mask.svg | 18 ++ resources/apitest/SVG/path.svg | 153 +++++++++++++ resources/apitest/SVG/png.svg | 9 + resources/apitest/SVG/radialGradient.svg | 10 + resources/apitest/SVG/text.svg | 8 + src/svg/SVGAttribute.cpp | 4 +- src/svg/SVGDOM.cpp | 12 +- src/svg/SVGRenderContext.cpp | 260 +++++++++++++---------- src/svg/node/SVGFeColorMatrix.cpp | 7 +- src/svg/node/SVGFeGaussianBlur.cpp | 9 +- src/svg/node/SVGImage.cpp | 1 - src/svg/node/SVGLinearGradient.cpp | 6 +- src/svg/node/SVGMask.cpp | 74 ++++--- src/svg/node/SVGPattern.cpp | 67 ++---- src/svg/node/SVGRadialGradient.cpp | 15 +- src/svg/node/SVGShape.cpp | 2 +- src/svg/node/SVGTransformableNode.cpp | 5 +- test/baseline/version.json | 9 + test/src/SVGTest.cpp | 188 ++++++++++++++++ vendor.json | 2 +- 28 files changed, 684 insertions(+), 267 deletions(-) create mode 100644 resources/apitest/SVG/blur.svg create mode 100644 resources/apitest/SVG/jpg.svg create mode 100644 resources/apitest/SVG/mask.svg create mode 100644 resources/apitest/SVG/path.svg create mode 100644 resources/apitest/SVG/png.svg create mode 100644 resources/apitest/SVG/radialGradient.svg create mode 100644 resources/apitest/SVG/text.svg create mode 100644 test/src/SVGTest.cpp diff --git a/include/tgfx/svg/SVGAttribute.h b/include/tgfx/svg/SVGAttribute.h index 84266bdc..d89f81c0 100644 --- a/include/tgfx/svg/SVGAttribute.h +++ b/include/tgfx/svg/SVGAttribute.h @@ -74,8 +74,8 @@ enum class SVGAttribute { kUnknown, }; -struct SkSVGPresentationAttributes { - static SkSVGPresentationAttributes MakeInitial(); +struct SVGPresentationAttributes { + static SVGPresentationAttributes MakeInitial(); // TODO: SVGProperty adds an extra ptr per attribute; refactor to reduce overhead. diff --git a/include/tgfx/svg/SVGRenderContext.h b/include/tgfx/svg/SVGRenderContext.h index 0b4c2917..cbadd4e2 100644 --- a/include/tgfx/svg/SVGRenderContext.h +++ b/include/tgfx/svg/SVGRenderContext.h @@ -22,13 +22,17 @@ #include #include #include +#include #include #include "tgfx/core/Canvas.h" +#include "tgfx/core/MaskFilter.h" #include "tgfx/core/Paint.h" #include "tgfx/core/Path.h" #include "tgfx/core/Point.h" +#include "tgfx/core/Recorder.h" #include "tgfx/core/Rect.h" #include "tgfx/core/Size.h" +#include "tgfx/gpu/Context.h" #include "tgfx/svg/SVGAttribute.h" #include "tgfx/svg/SVGFontManager.h" #include "tgfx/svg/SVGIDMapper.h" @@ -125,7 +129,7 @@ class SVGLengthContext { Rect resolveRect(const SVGLength& x, const SVGLength& y, const SVGLength& w, const SVGLength& h) const; - void setPatternUnits(SVGPatternUnits unit) { + void setPatternUnits(SVGObjectBoundingBoxUnits unit) { _patternUnit = unit; } @@ -133,14 +137,14 @@ class SVGLengthContext { _patternUnit.reset(); } - std::optional getPatternUnits() const { + std::optional getPatternUnits() const { return _patternUnit; } private: Size _viewport; float _dpi; - std::optional _patternUnit; + std::optional _patternUnit; }; struct SkSVGPresentationContext { @@ -150,7 +154,7 @@ struct SkSVGPresentationContext { const std::unordered_map* _namedColors = nullptr; // Inherited presentation attributes, computed for the current node. - SkSVGPresentationAttributes _inherited; + SVGPresentationAttributes _inherited; }; class SVGRenderContext { @@ -161,7 +165,7 @@ class SVGRenderContext { const SVGRenderContext* context; }; - SVGRenderContext(Canvas*, const std::shared_ptr&, const SVGIDMapper&, + SVGRenderContext(Context*, Canvas*, const std::shared_ptr&, const SVGIDMapper&, const SVGLengthContext&, const SkSVGPresentationContext&, const OBBScope&); SVGRenderContext(const SVGRenderContext&); SVGRenderContext(const SVGRenderContext&, Canvas*); @@ -171,6 +175,8 @@ class SVGRenderContext { SVGRenderContext(const SVGRenderContext&, const SVGNode*); ~SVGRenderContext(); + static SVGRenderContext CopyForPaint(const SVGRenderContext&, Canvas*, const SVGLengthContext&); + const SVGLengthContext& lengthContext() const { return *_lengthContext; } @@ -182,6 +188,10 @@ class SVGRenderContext { return *_presentationContext; } + Context* deviceContext() const { + return _deviceContext; + } + Canvas* canvas() const { return _canvas; } @@ -190,7 +200,7 @@ class SVGRenderContext { enum ApplyFlags { kLeaf = 1 << 0, // the target node doesn't have descendants }; - void applyPresentationAttributes(const SkSVGPresentationAttributes&, uint32_t flags); + void applyPresentationAttributes(const SVGPresentationAttributes& attrs, uint32_t flags); // Note: the id->node association is cleared for the lifetime of the returned value // (effectively breaks reference cycles, assuming appropriate return value scoping). @@ -204,7 +214,7 @@ class SVGRenderContext { // The local computed clip path (not inherited). Path clipPath() const { return _clipPath.value_or(Path()); - } + }; const std::shared_ptr& fontMgr() const { return _fontMgr; @@ -233,10 +243,10 @@ class SVGRenderContext { SVGRenderContext& operator=(const SVGRenderContext&) = delete; private: - void applyOpacity(float opacity, uint32_t flags, bool hasFilter); - void applyFilter(const SVGFuncIRI&); - void applyClip(const SVGFuncIRI&); - void applyMask(const SVGFuncIRI&); + float applyOpacity(float opacity, uint32_t flags, bool hasFilter); + std::shared_ptr applyFilter(const SVGFuncIRI&); + Path applyClip(const SVGFuncIRI&); + std::shared_ptr applyMask(const SVGFuncIRI&); std::optional commonPaint(const SVGPaint&, float opacity) const; @@ -244,10 +254,10 @@ class SVGRenderContext { const SVGIDMapper& _nodeIDMapper; CopyOnWrite _lengthContext; CopyOnWrite _presentationContext; + Canvas* _renderCanvas; + + Recorder _recorder; Canvas* _canvas; - // The save count on '_canvas' at construction time. - // A restoreToCount() will be issued on destruction. - size_t _canvasSaveCount; // clipPath, if present for the current context (not inherited). std::optional _clipPath; @@ -257,5 +267,8 @@ class SVGRenderContext { // Current object bounding box scope. const OBBScope _scope; + + Context* _deviceContext; + Paint _picturePaint; }; } // namespace tgfx diff --git a/include/tgfx/svg/SVGTypes.h b/include/tgfx/svg/SVGTypes.h index 8309feec..75ba2711 100644 --- a/include/tgfx/svg/SVGTypes.h +++ b/include/tgfx/svg/SVGTypes.h @@ -884,10 +884,4 @@ enum class SVGFeFuncType { kLinear, kGamma, }; - -//https://www.w3.org/TR/SVG2/pservers.html#PatternElementPatternUnitsAttribute -enum class SVGPatternUnits { - UserSpaceOnUse, - ObjectBoundingBox, -}; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGMask.h b/include/tgfx/svg/node/SVGMask.h index 7b43d1fc..d3ec7e54 100644 --- a/include/tgfx/svg/node/SVGMask.h +++ b/include/tgfx/svg/node/SVGMask.h @@ -31,10 +31,10 @@ class SkSVGMask final : public SVGHiddenContainer { return std::shared_ptr(new SkSVGMask()); } - SVG_ATTR(X, SVGLength, SVGLength(-10, SVGLength::Unit::kPercentage)) - SVG_ATTR(Y, SVGLength, SVGLength(-10, SVGLength::Unit::kPercentage)) - SVG_ATTR(Width, SVGLength, SVGLength(120, SVGLength::Unit::kPercentage)) - SVG_ATTR(Height, SVGLength, SVGLength(120, SVGLength::Unit::kPercentage)) + SVG_OPTIONAL_ATTR(X, SVGLength) + SVG_OPTIONAL_ATTR(Y, SVGLength) + SVG_OPTIONAL_ATTR(Width, SVGLength) + SVG_OPTIONAL_ATTR(Height, SVGLength) SVG_ATTR(MaskUnits, SVGObjectBoundingBoxUnits, SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox)) diff --git a/include/tgfx/svg/node/SVGNode.h b/include/tgfx/svg/node/SVGNode.h index 64600dd6..463ce2bd 100644 --- a/include/tgfx/svg/node/SVGNode.h +++ b/include/tgfx/svg/node/SVGNode.h @@ -120,6 +120,10 @@ class SVGNode { return fTag; } + const SVGPresentationAttributes* presentationAttributes() const { + return &fPresentationAttributes; + } + virtual void appendChild(std::shared_ptr) = 0; virtual bool hasChildren() const { @@ -207,7 +211,7 @@ class SVGNode { SVGTag fTag; // FIXME: this should be sparse - SkSVGPresentationAttributes fPresentationAttributes; + SVGPresentationAttributes fPresentationAttributes; }; //NOLINTBEGIN diff --git a/include/tgfx/svg/node/SVGPattern.h b/include/tgfx/svg/node/SVGPattern.h index 927ced8f..1dced3e8 100644 --- a/include/tgfx/svg/node/SVGPattern.h +++ b/include/tgfx/svg/node/SVGPattern.h @@ -39,8 +39,10 @@ class SkSVGPattern final : public SVGHiddenContainer { SVG_OPTIONAL_ATTR(Width, SVGLength) SVG_OPTIONAL_ATTR(Height, SVGLength) SVG_OPTIONAL_ATTR(PatternTransform, SVGTransformType) - SVG_ATTR(PatternUnits, SVGPatternUnits, SVGPatternUnits::ObjectBoundingBox) - SVG_ATTR(ContentUnits, SVGPatternUnits, SVGPatternUnits::UserSpaceOnUse) + SVG_ATTR(PatternUnits, SVGObjectBoundingBoxUnits, + SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox)) + SVG_ATTR(ContentUnits, SVGObjectBoundingBoxUnits, + SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse)) protected: SkSVGPattern(); diff --git a/resources/apitest/SVG/blur.svg b/resources/apitest/SVG/blur.svg new file mode 100644 index 00000000..809f1aec --- /dev/null +++ b/resources/apitest/SVG/blur.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/resources/apitest/SVG/jpg.svg b/resources/apitest/SVG/jpg.svg new file mode 100644 index 00000000..0771a79e --- /dev/null +++ b/resources/apitest/SVG/jpg.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/apitest/SVG/mask.svg b/resources/apitest/SVG/mask.svg new file mode 100644 index 00000000..1f982449 --- /dev/null +++ b/resources/apitest/SVG/mask.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/apitest/SVG/path.svg b/resources/apitest/SVG/path.svg new file mode 100644 index 00000000..b516f00a --- /dev/null +++ b/resources/apitest/SVG/path.svg @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/apitest/SVG/png.svg b/resources/apitest/SVG/png.svg new file mode 100644 index 00000000..921c2a13 --- /dev/null +++ b/resources/apitest/SVG/png.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/apitest/SVG/radialGradient.svg b/resources/apitest/SVG/radialGradient.svg new file mode 100644 index 00000000..4747a517 --- /dev/null +++ b/resources/apitest/SVG/radialGradient.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/resources/apitest/SVG/text.svg b/resources/apitest/SVG/text.svg new file mode 100644 index 00000000..d242aab0 --- /dev/null +++ b/resources/apitest/SVG/text.svg @@ -0,0 +1,8 @@ + + + + SVG GVS + diff --git a/src/svg/SVGAttribute.cpp b/src/svg/SVGAttribute.cpp index f9daced9..55a2d5fa 100644 --- a/src/svg/SVGAttribute.cpp +++ b/src/svg/SVGAttribute.cpp @@ -20,8 +20,8 @@ #include "tgfx/core/Color.h" namespace tgfx { -SkSVGPresentationAttributes SkSVGPresentationAttributes::MakeInitial() { - SkSVGPresentationAttributes result; +SVGPresentationAttributes SVGPresentationAttributes::MakeInitial() { + SVGPresentationAttributes result; result.fFill.set(SVGPaint(SVGColor(Color::Black()))); result.fFillOpacity.set(static_cast(1)); diff --git a/src/svg/SVGDOM.cpp b/src/svg/SVGDOM.cpp index a39e107c..ea01293b 100644 --- a/src/svg/SVGDOM.cpp +++ b/src/svg/SVGDOM.cpp @@ -30,6 +30,7 @@ #include "core/utils/Log.h" #include "tgfx/core/Canvas.h" #include "tgfx/core/Data.h" +#include "tgfx/core/Surface.h" #include "tgfx/svg/SVGAttribute.h" #include "tgfx/svg/SVGAttributeParser.h" #include "tgfx/svg/SVGTypes.h" @@ -472,17 +473,18 @@ void SVGDOM::render(Canvas* canvas) const { if (_root) { SVGLengthContext lctx(_containerSize); SkSVGPresentationContext pctx; - _root->render( - SVGRenderContext(canvas, _fontMgr, _nodeIDMapper, lctx, pctx, {nullptr, nullptr})); + SVGRenderContext renderCtx(canvas->getSurface()->getContext(), canvas, _fontMgr, _nodeIDMapper, + lctx, pctx, {nullptr, nullptr}); + _root->render(renderCtx); } } void SVGDOM::renderNode(Canvas* canvas, SkSVGPresentationContext& pctx, const char* id) const { if (_root) { SVGLengthContext lctx(_containerSize); - _root->renderNode( - SVGRenderContext(canvas, _fontMgr, _nodeIDMapper, lctx, pctx, {nullptr, nullptr}), - SVGIRI(SVGIRI::Type::kLocal, SVGStringType(id))); + SVGRenderContext renderCtx(canvas->getSurface()->getContext(), canvas, _fontMgr, _nodeIDMapper, + lctx, pctx, {nullptr, nullptr}); + _root->renderNode(renderCtx, SVGIRI(SVGIRI::Type::kLocal, SVGStringType(id))); } } diff --git a/src/svg/SVGRenderContext.cpp b/src/svg/SVGRenderContext.cpp index 4ae997c9..b99e215d 100644 --- a/src/svg/SVGRenderContext.cpp +++ b/src/svg/SVGRenderContext.cpp @@ -19,16 +19,23 @@ #include "tgfx/svg/SVGRenderContext.h" #include #include +#include #include #include #include "core/utils/Log.h" #include "tgfx/core/BlendMode.h" #include "tgfx/core/Canvas.h" +#include "tgfx/core/Color.h" #include "tgfx/core/ImageFilter.h" +#include "tgfx/core/MaskFilter.h" +#include "tgfx/core/Matrix.h" #include "tgfx/core/Paint.h" +#include "tgfx/core/Path.h" #include "tgfx/core/PathEffect.h" +#include "tgfx/core/Recorder.h" #include "tgfx/core/Rect.h" #include "tgfx/core/Stroke.h" +#include "tgfx/core/Surface.h" #include "tgfx/svg/node/SVGClipPath.h" #include "tgfx/svg/node/SVGFilter.h" #include "tgfx/svg/node/SVGMask.h" @@ -71,7 +78,7 @@ float SVGLengthContext::resolve(const SVGLength& l, LengthType t) const { switch (l.unit()) { case SVGLength::Unit::kNumber: { if (_patternUnit.has_value()) { - if (_patternUnit.value() == SVGPatternUnits::ObjectBoundingBox) { + if (_patternUnit.value().type() == SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox) { return l.value() * length_size_for_type(_viewport, t); } else { return l.value(); @@ -82,17 +89,8 @@ float SVGLengthContext::resolve(const SVGLength& l, LengthType t) const { } case SVGLength::Unit::kPX: return l.value(); - case SVGLength::Unit::kPercentage: { - if (_patternUnit.has_value()) { - if (_patternUnit.value() == SVGPatternUnits::ObjectBoundingBox) { - return l.value() * length_size_for_type(_viewport, t) / 100.f; - } else { - return l.value() / 100.f; - } - } else { - return l.value() * length_size_for_type(_viewport, t) / 100; - } - } + case SVGLength::Unit::kPercentage: + return l.value() * length_size_for_type(_viewport, t) / 100; case SVGLength::Unit::kCM: return l.value() * _dpi * kCMMultiplier; case SVGLength::Unit::kMM: @@ -145,7 +143,7 @@ LineJoin toSkJoin(const SVGLineJoin& join) { } } -std::unique_ptr dash_effect(const SkSVGPresentationAttributes& props, +std::unique_ptr dash_effect(const SVGPresentationAttributes& props, const SVGLengthContext& lctx) { if (props.fStrokeDashArray->type() != SVGDashArray::Type::kDashArray) { return nullptr; @@ -176,46 +174,65 @@ std::unique_ptr dash_effect(const SkSVGPresentationAttributes& props } // namespace SkSVGPresentationContext::SkSVGPresentationContext() - : _inherited(SkSVGPresentationAttributes::MakeInitial()) { + : _inherited(SVGPresentationAttributes::MakeInitial()) { } -SVGRenderContext::SVGRenderContext(Canvas* canvas, +SVGRenderContext::SVGRenderContext(Context* device, Canvas* canvas, const std::shared_ptr& fontManager, const SVGIDMapper& mapper, const SVGLengthContext& lctx, const SkSVGPresentationContext& pctx, const OBBScope& obbs) : _fontMgr(fontManager), _nodeIDMapper(mapper), _lengthContext(lctx), - _presentationContext(pctx), _canvas(canvas), _canvasSaveCount(canvas->getSaveCount()), - _scope(obbs) { + _presentationContext(pctx), _renderCanvas(canvas), _recorder(), + _canvas(_recorder.beginRecording()), _scope(obbs), _deviceContext(device) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other) - : SVGRenderContext(other._canvas, other._fontMgr, other._nodeIDMapper, *other._lengthContext, - *other._presentationContext, other._scope) { + : SVGRenderContext(other._deviceContext, other._canvas, other._fontMgr, other._nodeIDMapper, + *other._lengthContext, *other._presentationContext, other._scope) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, Canvas* canvas) - : SVGRenderContext(canvas, other._fontMgr, other._nodeIDMapper, *other._lengthContext, - *other._presentationContext, other._scope) { + : SVGRenderContext(other._deviceContext, canvas, other._fontMgr, other._nodeIDMapper, + *other._lengthContext, *other._presentationContext, other._scope) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, const SVGLengthContext& lengthCtx) - : SVGRenderContext(other._canvas, other._fontMgr, other._nodeIDMapper, lengthCtx, - *other._presentationContext, other._scope) { + : SVGRenderContext(other._deviceContext, other._canvas, other._fontMgr, other._nodeIDMapper, + lengthCtx, *other._presentationContext, other._scope) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, Canvas* canvas, const SVGLengthContext& lengthCtx) - : SVGRenderContext(canvas, other._fontMgr, other._nodeIDMapper, lengthCtx, + : SVGRenderContext(other._deviceContext, canvas, other._fontMgr, other._nodeIDMapper, lengthCtx, *other._presentationContext, other._scope) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, const SVGNode* node) - : SVGRenderContext(other._canvas, other._fontMgr, other._nodeIDMapper, *other._lengthContext, - *other._presentationContext, OBBScope{node, this}) { + : SVGRenderContext(other._deviceContext, other._canvas, other._fontMgr, other._nodeIDMapper, + *other._lengthContext, *other._presentationContext, OBBScope{node, this}) { } SVGRenderContext::~SVGRenderContext() { - _canvas->restoreToCount(_canvasSaveCount); + _canvas->restoreToCount(0); + auto picture = _recorder.finishRecordingAsPicture(); + if (!picture) { + return; + } + + _renderCanvas->save(); + if (_clipPath.has_value()) { + _renderCanvas->clipPath(_clipPath.value()); + } + auto matrix = Matrix::I(); + _renderCanvas->drawPicture(picture, &matrix, &_picturePaint); + _renderCanvas->restore(); +} + +SVGRenderContext SVGRenderContext::CopyForPaint(const SVGRenderContext& context, Canvas* canvas, + const SVGLengthContext& lengthCtx) { + SVGRenderContext copyContext(context, canvas, lengthCtx); + copyContext._deferredPaintOpacity = context._deferredPaintOpacity; + return copyContext; } std::shared_ptr SVGRenderContext::findNodeById(const SVGIRI& iri) const { @@ -226,9 +243,8 @@ std::shared_ptr SVGRenderContext::findNodeById(const SVGIRI& iri) const return p; } -void SVGRenderContext::applyPresentationAttributes(const SkSVGPresentationAttributes& attrs, +void SVGRenderContext::applyPresentationAttributes(const SVGPresentationAttributes& attrs, uint32_t flags) { - #define ApplyLazyInheritedAttribute(ATTR) \ do { \ /* All attributes should be defined on the inherited context. */ \ @@ -264,41 +280,30 @@ void SVGRenderContext::applyPresentationAttributes(const SkSVGPresentationAttrib #undef ApplyLazyInheritedAttribute - // Uninherited attributes. Only apply to the current context. + if (attrs.fClipPath.isValue()) { + _clipPath = this->applyClip(*attrs.fClipPath); + } const bool hasFilter = attrs.fFilter.isValue(); if (attrs.fOpacity.isValue()) { - this->applyOpacity(*attrs.fOpacity, flags, hasFilter); - } - - if (attrs.fClipPath.isValue()) { - this->applyClip(*attrs.fClipPath); + _picturePaint.setAlpha(this->applyOpacity(*attrs.fOpacity, flags, hasFilter)); } if (attrs.fMask.isValue()) { - this->applyMask(*attrs.fMask); + if (auto maskFilter = this->applyMask(*attrs.fMask)) { + _picturePaint.setMaskFilter(maskFilter); + } } - // TODO: when both a filter and opacity are present, we can apply both with a single layer if (hasFilter) { - this->applyFilter(*attrs.fFilter); + if (auto imageFilter = this->applyFilter(*attrs.fFilter)) { + _picturePaint.setImageFilter(imageFilter); + } } - - // Remaining uninherited presentation attributes are accessed as SkSVGNode fields, not via - // the render context. - // TODO: resolve these in a pre-render styling pass and assert here that they are values. - // - stop-color - // - stop-opacity - // - flood-color - // - flood-opacity - // - lighting-color } -void SVGRenderContext::applyOpacity(float opacity, uint32_t flags, bool hasFilter) { - if (opacity >= 1) { - return; - } - +float SVGRenderContext::applyOpacity(float opacity, uint32_t flags, bool hasFilter) { + opacity = std::clamp(opacity, 0.0f, 1.0f); const auto& props = _presentationContext->_inherited; const bool hasFill = props.fFill->type() != SVGPaint::Type::kNone; const bool hasStroke = props.fStroke->type() != SVGPaint::Type::kNone; @@ -311,104 +316,125 @@ void SVGRenderContext::applyOpacity(float opacity, uint32_t flags, bool hasFilte // Going forward, we may needto refine this heuristic (e.g. to accommodate markers). if ((flags & kLeaf) && (hasFill ^ hasStroke) && !hasFilter) { _deferredPaintOpacity *= opacity; + return 1.0f; } else { - // Expensive, layer-based fall back. - Paint opacityPaint; - opacityPaint.setAlpha(std::clamp(opacity, 0.0f, 1.0f)); - // Balanced in the destructor, via restoreToCount(). - - //TODO (YG) - // fCanvas.saveLayer(nullptr, &opacityPaint); + return opacity; } } -void SVGRenderContext::applyFilter(const SVGFuncIRI& filter) { +std::shared_ptr SVGRenderContext::applyFilter(const SVGFuncIRI& filter) { if (filter.type() != SVGFuncIRI::Type::kIRI) { - return; + return nullptr; } const auto node = this->findNodeById(filter.iri()); if (!node || node->tag() != SVGTag::kFilter) { - return; + return nullptr; } const auto* filterNode = reinterpret_cast(node.get()); - auto imageFilter = filterNode->buildFilterDAG(*this); - if (imageFilter) { - Paint filterPaint; - filterPaint.setImageFilter(imageFilter); - // Balanced in the destructor, via restoreToCount(). - //TODO (YG) - // fCanvas->saveLayer(nullptr, &filterPaint); - } + return filterNode->buildFilterDAG(*this); } void SVGRenderContext::saveOnce() { - // The canvas only needs to be saved once, per local SVGRenderContext. - if (_canvas->getSaveCount() == _canvasSaveCount) { - _canvas->save(); - } - ASSERT(_canvas->getSaveCount() > _canvasSaveCount); + _canvas->save(); } -void SVGRenderContext::applyClip(const SVGFuncIRI& clip) { +Path SVGRenderContext::applyClip(const SVGFuncIRI& clip) { if (clip.type() != SVGFuncIRI::Type::kIRI) { - return; + return Path(); } const auto clipNode = this->findNodeById(clip.iri()); if (!clipNode || clipNode->tag() != SVGTag::kClipPath) { - return; + return Path(); } - const Path clipPath = static_cast(clipNode.get())->resolveClip(*this); - - // We use the computed clip path in two ways: - // - // - apply to the current canvas, for drawing - // - track in the presentation context, for asPath() composition - // - // TODO: the two uses are exclusive, avoid canvas churn when non needed. - - this->saveOnce(); - - _canvas->clipPath(clipPath); - _clipPath = clipPath; + return static_cast(clipNode.get())->resolveClip(*this); } -void SVGRenderContext::applyMask(const SVGFuncIRI& mask) { +std::shared_ptr SVGRenderContext::applyMask(const SVGFuncIRI& mask) { if (mask.type() != SVGFuncIRI::Type::kIRI) { - return; + return nullptr; } const auto node = this->findNodeById(mask.iri()); if (!node || node->tag() != SVGTag::kMask) { - return; + return nullptr; } - const auto* mask_node = static_cast(node.get()); - const auto mask_bounds = mask_node->bounds(*this); - - // Isolation/mask layer. - // TODO (YG) - // fCanvas->saveLayer(mask_bounds, nullptr); - - // Render and filter mask content. - mask_node->renderMask(*this); - - // Content layer - Paint masking_paint; - masking_paint.setBlendMode(BlendMode::SrcIn); + auto maskNode = std::static_pointer_cast(node); + auto maskBound = maskNode->bounds(*this); - // TODO (YG) - // fCanvas->saveLayer(mask_bounds, &masking_paint); - - // Content is also clipped to the specified mask bounds. - _canvas->clipRect(mask_bounds); + Recorder maskRecorder; + auto* maskCanvas = maskRecorder.beginRecording(); + { + SVGRenderContext maskCtx(*this, maskCanvas); + maskNode->renderMask(maskCtx); + } + auto picture = maskRecorder.finishRecordingAsPicture(); + if (!picture) { + return nullptr; + } - // At this point we're set up for content rendering. - // The pending layers are restored in the destructor (render context scope exit). - // Restoring triggers srcIn-compositing the content against the mask. + auto bound = picture->getBounds(); + maskBound.join(bound); + + // if (!_deviceContext) { + // return nullptr; + // } + // auto tempSurface = Surface::Make(_deviceContext, static_cast(bound.width()), + // static_cast(bound.height())); + // if (!tempSurface) { + // return nullptr; + // } + // auto* tempCanvas = tempSurface->getCanvas(); + // tempCanvas->translate(bound.x(), bound.y()); + // // Matrix matrix = Matrix::I(); + // // Paint paint; + // // paint.setColor(Color::Green()); + // // tempCanvas->drawPicture(picture, &matrix, &paint); + // tempCanvas->drawPicture(picture); + // // Paint paint; + // // paint.setColor(Color::Black()); + // // tempCanvas->drawCircle(50, 50, 50, paint); + // _deviceContext->flushAndSubmit(); + // auto shaderImage = tempSurface->makeImageSnapshot(); + // if (!shaderImage) { + // return nullptr; + // } + auto matrix = Matrix::MakeTrans(-maskBound.left, -maskBound.top); + auto shaderImage = Image::MakeFrom(picture, static_cast(bound.width()), + static_cast(bound.height()), &matrix); + // { + // auto tempSurface = Surface::Make(_deviceContext, static_cast(bound.width()) * 2, + // static_cast(bound.height()) * 2); + // tempSurface->getCanvas()->clear(); + // tempSurface->getCanvas()->drawPicture(picture); + // tgfx::Bitmap bitmap = {}; + // bitmap.allocPixels(tempSurface->width(), tempSurface->height()); + // auto* pixels = bitmap.lockPixels(); + // auto success = tempSurface->readPixels(bitmap.info(), pixels); + // bitmap.unlockPixels(); + // if (!success) { + // return nullptr; + // } + // auto imageData = bitmap.encode(); + // imageData->bytes(); + + // std::ofstream out("/Users/yg/Downloads/yg.png", std::ios::binary); + // if (!out) { + // return nullptr; + // } + // out.write(reinterpret_cast(imageData->data()), + // static_cast(imageData->size())); + // } + auto shader = Shader::MakeImageShader(shaderImage, TileMode::Decal, TileMode::Decal); + if (!shader) { + return nullptr; + } + // return nullptr; + return MaskFilter::MakeShader(shader); } std::optional SVGRenderContext::commonPaint(const SVGPaint& paint_selector, @@ -418,7 +444,6 @@ std::optional SVGRenderContext::commonPaint(const SVGPaint& paint_selecto } std::optional p = Paint(); - p->getAlpha(); switch (paint_selector.type()) { case SVGPaint::Type::kColor: p->setColor(this->resolveSvgColor(paint_selector.color())); @@ -434,7 +459,8 @@ std::optional SVGRenderContext::commonPaint(const SVGPaint& paint_selecto // and node being rendered. SkSVGPresentationContext pctx; pctx._namedColors = _presentationContext->_namedColors; - SVGRenderContext local_ctx(_canvas, _fontMgr, _nodeIDMapper, *_lengthContext, pctx, _scope); + SVGRenderContext local_ctx(_deviceContext, _canvas, _fontMgr, _nodeIDMapper, *_lengthContext, + pctx, _scope); const auto node = this->findNodeById(paint_selector.iri()); if (!node || !node->asPaint(local_ctx, &(p.value()))) { diff --git a/src/svg/node/SVGFeColorMatrix.cpp b/src/svg/node/SVGFeColorMatrix.cpp index f939d32c..d3d6f662 100644 --- a/src/svg/node/SVGFeColorMatrix.cpp +++ b/src/svg/node/SVGFeColorMatrix.cpp @@ -57,7 +57,6 @@ ColorMatrix SkSVGFeColorMatrix::makeMatrixForType() const { return ColorMatrix(); } ColorMatrix m; - // m.setRowMajor(fValues.data()); std::copy_n(fValues.data(), 20, m.begin()); return m; } @@ -147,12 +146,8 @@ ColorMatrix SkSVGFeColorMatrix::MakeLuminanceToAlpha() { } std::shared_ptr SkSVGFeColorMatrix::onMakeImageFilter( - const SVGRenderContext& /*ctx*/, const SkSVGFilterContext& /*fctx*/) const { + const SVGRenderContext&, const SkSVGFilterContext&) const { return ImageFilter::ColorFilter(ColorFilter::Matrix(makeMatrixForType())); - // return ImageFilter::ColorFilter( - // SkColorFilters::Matrix(makeMatrixForType()), - // fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)), - // this->resolveFilterSubregion(ctx, fctx)); } #endif } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeGaussianBlur.cpp b/src/svg/node/SVGFeGaussianBlur.cpp index a2c97b68..9dafd8a0 100644 --- a/src/svg/node/SVGFeGaussianBlur.cpp +++ b/src/svg/node/SVGFeGaussianBlur.cpp @@ -33,14 +33,9 @@ bool SkSVGFeGaussianBlur::parseAndSetAttribute(const char* name, const char* val std::shared_ptr SkSVGFeGaussianBlur::onMakeImageFilter( const SVGRenderContext& ctx, const SkSVGFilterContext& fctx) const { auto scale = ctx.transformForCurrentOBB(fctx.primitiveUnits()).scale; - const auto sigmaX = fStdDeviation.fX * scale.x; - const auto sigmaY = fStdDeviation.fY * scale.y; + const auto sigmaX = fStdDeviation.fX * scale.x * 2; + const auto sigmaY = fStdDeviation.fY * scale.y * 2; return ImageFilter::Blur(sigmaX, sigmaY); - - //TODO (YG): Implement this - // return SkImageFilters::Blur( - // sigma.x, sigma.y, fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)), - // this->resolveFilterSubregion(ctx, fctx)); } #endif diff --git a/src/svg/node/SVGImage.cpp b/src/svg/node/SVGImage.cpp index 4168912a..914c5ea6 100644 --- a/src/svg/node/SVGImage.cpp +++ b/src/svg/node/SVGImage.cpp @@ -105,7 +105,6 @@ std::shared_ptr LoadImage(const SVGIRI& href) { SkSVGImage::ImageInfo SkSVGImage::LoadImage(const SVGIRI& iri, const Rect& viewPort, SVGPreserveAspectRatio /*par*/) { - // TODO: svg sources std::shared_ptr image = ::tgfx::LoadImage(iri); if (!image) { return {}; diff --git a/src/svg/node/SVGLinearGradient.cpp b/src/svg/node/SVGLinearGradient.cpp index 6336ff71..d3006585 100644 --- a/src/svg/node/SVGLinearGradient.cpp +++ b/src/svg/node/SVGLinearGradient.cpp @@ -41,10 +41,8 @@ std::shared_ptr SkSVGLinearGradient::onMakeShader(const SVGRenderContext const std::vector& colors, const std::vector& positions, TileMode, const Matrix&) const { - const SVGLengthContext lctx = - this->getGradientUnits().type() == SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox - ? SVGLengthContext({1, 1}) - : ctx.lengthContext(); + SVGLengthContext lctx = ctx.lengthContext(); + lctx.setPatternUnits(getGradientUnits()); auto startPoint = Point::Make(lctx.resolve(fX1, SVGLengthContext::LengthType::Horizontal), lctx.resolve(fY1, SVGLengthContext::LengthType::Vertical)); diff --git a/src/svg/node/SVGMask.cpp b/src/svg/node/SVGMask.cpp index 3040a6f3..79859cbf 100644 --- a/src/svg/node/SVGMask.cpp +++ b/src/svg/node/SVGMask.cpp @@ -17,9 +17,15 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGMask.h" +#include "tgfx/core/Color.h" +#include "tgfx/core/ColorFilter.h" +#include "tgfx/core/ImageFilter.h" #include "tgfx/core/MaskFilter.h" #include "tgfx/core/Paint.h" +#include "tgfx/core/Recorder.h" +#include "tgfx/core/Rect.h" #include "tgfx/svg/SVGAttributeParser.h" +#include "tgfx/svg/SVGTypes.h" namespace tgfx { @@ -36,46 +42,54 @@ bool SkSVGMask::parseAndSetAttribute(const char* n, const char* v) { } #ifndef RENDER_SVG -Rect SkSVGMask::bounds(const SVGRenderContext& ctx) const { - return ctx.resolveOBBRect(fX, fY, fWidth, fHeight, fMaskUnits); +Rect SkSVGMask::bounds(const SVGRenderContext& context) const { + auto lengthContext = context.lengthContext(); + lengthContext.setPatternUnits(fMaskUnits); + SVGRenderContext resolveContext(context, lengthContext); + if (fWidth.has_value() && fHeight.has_value()) { + return resolveContext.resolveOBBRect(fX.value_or(SVGLength(0, SVGLength::Unit::kNumber)), + fY.value_or(SVGLength(0, SVGLength::Unit::kNumber)), + fWidth.value(), fHeight.value(), fMaskUnits); + } + return Rect::MakeEmpty(); } -void SkSVGMask::renderMask(const SVGRenderContext& ctx) const { - //TODO (YG) +/** See ITU-R Recommendation BT.709 at http://www.itu.int/rec/R-REC-BT.709/ .*/ +constexpr float LUM_COEFF_R = 0.2126f; +constexpr float LUM_COEFF_G = 0.7152f; +constexpr float LUM_COEFF_B = 0.0722f; - // https://www.w3.org/TR/SVG11/masking.html#Masking +std::array MakeLuminanceToAlpha() { + return std::array{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, LUM_COEFF_R, LUM_COEFF_G, LUM_COEFF_B, 0, 0}; +} +void SkSVGMask::renderMask(const SVGRenderContext& context) const { + // https://www.w3.org/TR/SVG11/masking.html#Masking // Propagate any inherited properties that may impact mask effect behavior (e.g. // color-interpolation). We call this explicitly here because the SkSVGMask // nodes do not participate in the normal onRender path, which is when property // propagation currently occurs. // The local context also restores the filter layer created below on scope exit. - SVGRenderContext lctx(ctx); - this->onPrepareToRender(&lctx); - - // const auto ci = *lctx.presentationContext()._inherited.fColorInterpolation; - // auto ci_filter = (ci == SVGColorspace::kLinearRGB) ? ColorFilters::SRGBToLinearGamma() : nullptr; - - // Paint mask_filter; - // MaskFilter::Compose(); - // mask_filter.setMaskFilter() mask_filter.setColorFilter( - // SkColorFilters::Compose(SkLumaColorFilter::Make(), std::move(ci_filter))); - - // // Mask color filter layer. - // // Note: We could avoid this extra layer if we invert the stacking order - // // (mask/content -> content/mask, kSrcIn -> kDstIn) and apply the filter - // // via the top (mask) layer paint. That requires deferring mask rendering - // // until after node content, which introduces extra state/complexity. - // // Something to consider if masking performance ever becomes an issue. - // lctx.canvas()->saveLayer(nullptr, &mask_filter); - - // const auto obbt = ctx.transformForCurrentOBB(fMaskContentUnits); - // lctx.canvas()->translate(obbt.offset.x, obbt.offset.y); - // lctx.canvas()->scale(obbt.scale.x, obbt.scale.y); - // for (const auto& child : fChildren) { - // child->render(lctx); - // } + Recorder recorder; + auto* canvas = recorder.beginRecording(); + { + // Ensure the render context is destructed, drawing to the canvas upon destruction + SVGRenderContext localContext(context, canvas); + this->onPrepareToRender(&localContext); + for (const auto& child : fChildren) { + child->render(localContext); + } + } + auto picture = recorder.finishRecordingAsPicture(); + if (!picture) { + return; + } + auto luminanceFilter = ColorFilter::Matrix(MakeLuminanceToAlpha()); + Paint luminancePaint; + luminancePaint.setColorFilter(luminanceFilter); + context.canvas()->drawPicture(picture, nullptr, &luminancePaint); } #endif } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGPattern.cpp b/src/svg/node/SVGPattern.cpp index 38761c24..b381a08f 100644 --- a/src/svg/node/SVGPattern.cpp +++ b/src/svg/node/SVGPattern.cpp @@ -44,18 +44,9 @@ bool SkSVGPattern::parseAndSetAttribute(const char* name, const char* value) { SVGAttributeParser::parse("patternTransform", name, value)) || this->setHref(SVGAttributeParser::parse("xlink:href", name, value)) || this->setPatternUnits( - SVGAttributeParser::parse("patternUnits", name, value)) || - this->setContentUnits( - SVGAttributeParser::parse("patternContentUnits", name, value)); -} - -template <> -bool SVGAttributeParser::parse(SVGPatternUnits* type) { - static constexpr std::tuple gTypeMap[] = { - {"userSpaceOnUse", SVGPatternUnits::UserSpaceOnUse}, - {"objectBoundingBox", SVGPatternUnits::ObjectBoundingBox}, - }; - return this->parseEnumMap(gTypeMap, type) && this->parseEOSToken(); + SVGAttributeParser::parse("patternUnits", name, value)) || + this->setContentUnits(SVGAttributeParser::parse( + "patternContentUnits", name, value)); } #ifndef RENDER_SVG @@ -115,20 +106,19 @@ const SkSVGPattern* SkSVGPattern::resolveHref(const SVGRenderContext& ctx, break; } - // TODO: reference loop mitigation. currentNode = currentNode->hrefTarget(ctx); } while (currentNode); // To unify with Chrome and macOS preview, the width and height attributes here need to be // converted to percentages, direct numbers are not supported. - if (fPatternUnits == SVGPatternUnits::UserSpaceOnUse) { + if (fPatternUnits.type() == SVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse) { if (attrs->fWidth.has_value() && attrs->fWidth->unit() == SVGLength::Unit::kPercentage) { attrs->fWidth = SVGLength(attrs->fWidth->value() / 100.f, SVGLength::Unit::kNumber); } if (attrs->fHeight.has_value() && attrs->fHeight->unit() == SVGLength::Unit::kPercentage) { attrs->fHeight = SVGLength(attrs->fHeight->value() / 100.f, SVGLength::Unit::kNumber); } - } else if (fPatternUnits == SVGPatternUnits::ObjectBoundingBox) { + } else if (fPatternUnits.type() == SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox) { if (attrs->fWidth.has_value() && attrs->fWidth->unit() == SVGLength::Unit::kNumber) { attrs->fWidth = SVGLength(attrs->fWidth->value() * 100.f, SVGLength::Unit::kPercentage); } @@ -154,44 +144,17 @@ bool SkSVGPattern::onAsPaint(const SVGRenderContext& ctx, Paint* paint) const { return false; } - auto* context = ctx.canvas()->getSurface()->getContext(); - if (!context) { - return false; - } - auto tempSurface = - Surface::Make(context, static_cast(tile.width()), static_cast(tile.height())); - if (!tempSurface) { - return false; - } - auto* tempCanvas = tempSurface->getCanvas(); + Recorder patternRecorder; + auto* canvas = patternRecorder.beginRecording(); auto patternMatrix = attrs.fPatternTransform.value_or(Matrix::I()); - tempCanvas->concat(patternMatrix); - - lengthContext.setPatternUnits(fContentUnits); - SVGRenderContext recordingContext(ctx, tempCanvas, lengthContext); - contentNode->SkSVGContainer::onRender(recordingContext); - context->flushAndSubmit(); - // { - // tgfx::Bitmap bitmap = {}; - // bitmap.allocPixels(tempSurface->width(), tempSurface->height()); - // auto* pixels = bitmap.lockPixels(); - // auto success = tempSurface->readPixels(bitmap.info(), pixels); - // bitmap.unlockPixels(); - // if (!success) { - // return false; - // } - // auto imageData = bitmap.encode(); - // imageData->bytes(); - - // std::ofstream out("/Users/yg/Downloads/yg.png", std::ios::binary); - // if (!out) { - // return false; - // } - // out.write(reinterpret_cast(imageData->data()), - // static_cast(imageData->size())); - // } - auto shaderImage = tempSurface->makeImageSnapshot(); - shaderImage = shaderImage->makeTextureImage(context); + canvas->concat(patternMatrix); + { + SVGRenderContext recordingContext(ctx, canvas, lengthContext); + contentNode->SkSVGContainer::onRender(recordingContext); + } + auto picture = patternRecorder.finishRecordingAsPicture(); + auto shaderImage = + Image::MakeFrom(picture, static_cast(tile.width()), static_cast(tile.height())); auto shader = Shader::MakeImageShader(shaderImage, TileMode::Repeat, TileMode::Repeat); paint->setShader(shader); return true; diff --git a/src/svg/node/SVGRadialGradient.cpp b/src/svg/node/SVGRadialGradient.cpp index 45491575..75260c6f 100644 --- a/src/svg/node/SVGRadialGradient.cpp +++ b/src/svg/node/SVGRadialGradient.cpp @@ -42,26 +42,21 @@ bool SkSVGRadialGradient::parseAndSetAttribute(const char* name, const char* val std::shared_ptr SkSVGRadialGradient::onMakeShader(const SVGRenderContext& ctx, const std::vector& colors, const std::vector& position, - TileMode, const Matrix&) const { - SVGLengthContext lctx = - this->getGradientUnits().type() == SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox - ? SVGLengthContext({1, 1}) - : ctx.lengthContext(); + TileMode, const Matrix& matrix) const { + SVGLengthContext lctx = ctx.lengthContext(); + lctx.setPatternUnits(getGradientUnits()); auto radius = lctx.resolve(fR, SVGLengthContext::LengthType::Other); auto center = Point::Make(lctx.resolve(fCx, SVGLengthContext::LengthType::Horizontal), lctx.resolve(fCy, SVGLengthContext::LengthType::Vertical)); // TODO(YGAurora): MakeTwoPointConical are unimplemented in tgfx - // const auto focal = Point::Make( - // fFx.has_value() ? lctx.resolve(*fFx, SkSVGLengthContext::LengthType::kHorizontal) : center.x, - // fFy.has_value() ? lctx.resolve(*fFy, SkSVGLengthContext::LengthType::kVertical) : center.y); - if (radius == 0) { const auto lastColor = colors.size() > 0 ? *colors.end() : Color::Black(); return Shader::MakeColorShader(lastColor); } - + matrix.mapPoints(¢er, 1); + radius *= matrix.getAxisScales().x; return Shader::MakeRadialGradient(center, radius, colors, position); } #endif diff --git a/src/svg/node/SVGShape.cpp b/src/svg/node/SVGShape.cpp index c3ed26d7..b6698900 100644 --- a/src/svg/node/SVGShape.cpp +++ b/src/svg/node/SVGShape.cpp @@ -37,7 +37,7 @@ void SVGShape::onRender(const SVGRenderContext& ctx) const { auto selfRect = onObjectBoundingBox(ctx); auto lengthCtx = ctx.lengthContext(); lengthCtx.setViewPort(Size::Make(selfRect.width(), selfRect.height())); - SVGRenderContext paintCtx(ctx, ctx.canvas(), lengthCtx); + auto paintCtx = SVGRenderContext::CopyForPaint(ctx, ctx.canvas(), lengthCtx); const auto fillPaint = paintCtx.fillPaint(); const auto strokePaint = paintCtx.strokePaint(); diff --git a/src/svg/node/SVGTransformableNode.cpp b/src/svg/node/SVGTransformableNode.cpp index 97d8359d..4ed75d96 100644 --- a/src/svg/node/SVGTransformableNode.cpp +++ b/src/svg/node/SVGTransformableNode.cpp @@ -19,9 +19,9 @@ #include "tgfx/svg/node/SVGTransformableNode.h" #include "tgfx/core/Matrix.h" #include "tgfx/core/Path.h" -#include "tgfx/core/Point.h" #include "tgfx/core/Rect.h" #include "tgfx/svg/SVGAttribute.h" +#include "tgfx/svg/SVGTypes.h" #include "tgfx/svg/SVGValue.h" namespace tgfx { @@ -35,7 +35,8 @@ bool SkSVGTransformableNode::onPrepareToRender(SVGRenderContext* ctx) const { if (!fTransform.isIdentity()) { auto transform = fTransform; if (auto unit = ctx->lengthContext().getPatternUnits(); - unit.has_value() && unit.value() == SVGPatternUnits::ObjectBoundingBox) { + unit.has_value() && + unit.value().type() == SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox) { transform.postScale(ctx->lengthContext().viewPort().width, ctx->lengthContext().viewPort().height); } diff --git a/test/baseline/version.json b/test/baseline/version.json index 69220a0c..bd1efd20 100644 --- a/test/baseline/version.json +++ b/test/baseline/version.json @@ -151,6 +151,15 @@ "WebpCodec_Encode_RGB565": "d010fb8", "WebpCodec_Encode_RGBA": "d010fb8" }, + "SVGTest": { + "blur": "b1db872", + "jpg_image": "b1db872", + "mask": "b1db872", + "path": "b1db872", + "png_image": "b1db872", + "radialGradient": "b1db872", + "text": "b1db872" + }, "SurfaceTest": { "ImageSnapshot1": "d010fb8", "ImageSnapshot2": "d010fb8", diff --git a/test/src/SVGTest.cpp b/test/src/SVGTest.cpp new file mode 100644 index 00000000..dced6ea2 --- /dev/null +++ b/test/src/SVGTest.cpp @@ -0,0 +1,188 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/SVGDOM.h" +#include "utils/TestUtils.h" + +namespace tgfx { +TGFX_TEST(SVGTest, PathSVGRender) { + auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/path.svg")); + ASSERT_TRUE(data != nullptr); + auto fontManager = std::make_shared(); + ASSERT_TRUE(fontManager != nullptr); + auto SVGDom = SVGDOM::Builder().make(*data, fontManager); + + auto device = DevicePool::Make(); + ASSERT_TRUE(device != nullptr); + auto* context = device->lockContext(); + ASSERT_TRUE(context != nullptr); + auto rootNode = SVGDom->getRoot(); + ASSERT_TRUE(rootNode != nullptr); + auto surface = Surface::Make(context, static_cast(rootNode->getWidth().value()), + static_cast(rootNode->getHeight().value())); + ASSERT_TRUE(device != nullptr); + auto* canvas = surface->getCanvas(); + + SVGDom->render(canvas); + EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/path")); + device->unlock(); +} + +TGFX_TEST(SVGTest, PNGImageSVGRender) { + auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/png.svg")); + ASSERT_TRUE(data != nullptr); + auto fontManager = std::make_shared(); + ASSERT_TRUE(fontManager != nullptr); + auto SVGDom = SVGDOM::Builder().make(*data, fontManager); + + auto device = DevicePool::Make(); + ASSERT_TRUE(device != nullptr); + auto* context = device->lockContext(); + ASSERT_TRUE(context != nullptr); + auto rootNode = SVGDom->getRoot(); + ASSERT_TRUE(rootNode != nullptr); + auto surface = Surface::Make(context, static_cast(rootNode->getWidth().value()), + static_cast(rootNode->getHeight().value())); + ASSERT_TRUE(device != nullptr); + auto* canvas = surface->getCanvas(); + + SVGDom->render(canvas); + EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/png_image")); + device->unlock(); +} + +TGFX_TEST(SVGTest, JPGImageSVGRender) { + auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/jpg.svg")); + ASSERT_TRUE(data != nullptr); + auto fontManager = std::make_shared(); + ASSERT_TRUE(fontManager != nullptr); + auto SVGDom = SVGDOM::Builder().make(*data, fontManager); + + auto device = DevicePool::Make(); + ASSERT_TRUE(device != nullptr); + auto* context = device->lockContext(); + ASSERT_TRUE(context != nullptr); + auto rootNode = SVGDom->getRoot(); + ASSERT_TRUE(rootNode != nullptr); + auto surface = Surface::Make(context, static_cast(rootNode->getWidth().value()), + static_cast(rootNode->getHeight().value())); + ASSERT_TRUE(device != nullptr); + auto* canvas = surface->getCanvas(); + + SVGDom->render(canvas); + EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/jpg_image")); + device->unlock(); +} + +TGFX_TEST(SVGTest, MaskSVGRender) { + auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/mask.svg")); + ASSERT_TRUE(data != nullptr); + auto fontManager = std::make_shared(); + ASSERT_TRUE(fontManager != nullptr); + auto SVGDom = SVGDOM::Builder().make(*data, fontManager); + + auto device = DevicePool::Make(); + ASSERT_TRUE(device != nullptr); + auto* context = device->lockContext(); + ASSERT_TRUE(context != nullptr); + auto rootNode = SVGDom->getRoot(); + ASSERT_TRUE(rootNode != nullptr); + auto surface = Surface::Make(context, static_cast(rootNode->getWidth().value()), + static_cast(rootNode->getHeight().value())); + ASSERT_TRUE(device != nullptr); + auto* canvas = surface->getCanvas(); + + SVGDom->render(canvas); + EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/mask")); + device->unlock(); +} + +TGFX_TEST(SVGTest, GradientSVGRender) { + auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/radialGradient.svg")); + ASSERT_TRUE(data != nullptr); + auto fontManager = std::make_shared(); + ASSERT_TRUE(fontManager != nullptr); + auto SVGDom = SVGDOM::Builder().make(*data, fontManager); + + auto device = DevicePool::Make(); + ASSERT_TRUE(device != nullptr); + auto* context = device->lockContext(); + ASSERT_TRUE(context != nullptr); + auto rootNode = SVGDom->getRoot(); + ASSERT_TRUE(rootNode != nullptr); + auto surface = Surface::Make(context, static_cast(rootNode->getWidth().value()), + static_cast(rootNode->getHeight().value())); + ASSERT_TRUE(device != nullptr); + auto* canvas = surface->getCanvas(); + + SVGDom->render(canvas); + EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/radialGradient")); + device->unlock(); +} + +TGFX_TEST(SVGTest, BlurSVGRender) { + auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/blur.svg")); + ASSERT_TRUE(data != nullptr); + auto fontManager = std::make_shared(); + ASSERT_TRUE(fontManager != nullptr); + auto SVGDom = SVGDOM::Builder().make(*data, fontManager); + + auto device = DevicePool::Make(); + ASSERT_TRUE(device != nullptr); + auto* context = device->lockContext(); + ASSERT_TRUE(context != nullptr); + auto rootNode = SVGDom->getRoot(); + ASSERT_TRUE(rootNode != nullptr); + auto surface = Surface::Make(context, static_cast(rootNode->getWidth().value()), + static_cast(rootNode->getHeight().value())); + ASSERT_TRUE(device != nullptr); + auto* canvas = surface->getCanvas(); + + SVGDom->render(canvas); + EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/blur")); + device->unlock(); +} + +TGFX_TEST(SVGTest, TextSVGRender) { + auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/text.svg")); + ASSERT_TRUE(data != nullptr); + auto fontManager = std::make_shared(); + ASSERT_TRUE(fontManager != nullptr); + auto typeface = MakeTypeface("resources/font/NotoSansSC-Regular.otf"); + ASSERT_TRUE(typeface != nullptr); + fontManager->setDefaultTypeface(typeface); + + auto SVGDom = SVGDOM::Builder().make(*data, fontManager); + + auto device = DevicePool::Make(); + ASSERT_TRUE(device != nullptr); + auto* context = device->lockContext(); + ASSERT_TRUE(context != nullptr); + auto rootNode = SVGDom->getRoot(); + ASSERT_TRUE(rootNode != nullptr); + auto surface = Surface::Make(context, static_cast(rootNode->getWidth().value()), + static_cast(rootNode->getHeight().value())); + ASSERT_TRUE(device != nullptr); + auto* canvas = surface->getCanvas(); + + SVGDom->render(canvas); + EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/text")); + device->unlock(); +} + +} // namespace tgfx \ No newline at end of file diff --git a/vendor.json b/vendor.json index 7e607a55..2cce282c 100644 --- a/vendor.json +++ b/vendor.json @@ -231,7 +231,7 @@ "expat" ], "arguments": [ - "-DCMAKE_C_FLAGS=\"-w\"" + "-DBUILD_SHARED_LIBS=OFF" ] } } From 2d7b6ae4de24b79fab683a67bf350b1b8a8e72d1 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Mon, 25 Nov 2024 16:24:32 +0800 Subject: [PATCH 04/31] debug render --- include/tgfx/svg/SVGDOM.h | 5 ++++- include/tgfx/svg/SVGFontManager.h | 2 +- include/tgfx/svg/SVGParse.h | 1 - include/tgfx/svg/SVGRenderContext.h | 1 + src/svg/SVGDOM.cpp | 22 ++++++++++++++++------ src/svg/SVGFontManager.cpp | 11 +++++++---- src/svg/SVGRenderContext.cpp | 2 +- 7 files changed, 30 insertions(+), 14 deletions(-) diff --git a/include/tgfx/svg/SVGDOM.h b/include/tgfx/svg/SVGDOM.h index 90cfedb9..91e7a5ec 100644 --- a/include/tgfx/svg/SVGDOM.h +++ b/include/tgfx/svg/SVGDOM.h @@ -21,6 +21,7 @@ #include #include "tgfx/core/Canvas.h" #include "tgfx/core/Data.h" +#include "tgfx/core/Picture.h" #include "tgfx/core/Size.h" #include "tgfx/svg/SVGFontManager.h" #include "tgfx/svg/SVGIDMapper.h" @@ -98,7 +99,7 @@ class SVGDOM { // Returns the node with the given id, or nullptr if not found. std::shared_ptr findNodeById(const std::string& id); - void render(Canvas*) const; + void render(Canvas*); /** Render the node with the given id as if it were the only child of the root. */ void renderNode(Canvas*, SkSVGPresentationContext&, const char* id) const; @@ -110,5 +111,7 @@ class SVGDOM { const std::shared_ptr _fontMgr; const SVGIDMapper _nodeIDMapper; Size _containerSize; + + std::shared_ptr _renderPicture; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGFontManager.h b/include/tgfx/svg/SVGFontManager.h index be10646f..d6b92516 100644 --- a/include/tgfx/svg/SVGFontManager.h +++ b/include/tgfx/svg/SVGFontManager.h @@ -96,7 +96,7 @@ class SVGFontManager { SVGFontManager() = default; ~SVGFontManager() = default; - void setDefaultTypeface(const std::shared_ptr& typeface); + bool setDefaultTypeface(const std::shared_ptr& typeface); void addFontStyle(const std::string& fontFamily, FontStyle style); void setTypeface(const std::string& fontFamily, FontStyle style, diff --git a/include/tgfx/svg/SVGParse.h b/include/tgfx/svg/SVGParse.h index f04f2468..303c3111 100644 --- a/include/tgfx/svg/SVGParse.h +++ b/include/tgfx/svg/SVGParse.h @@ -18,7 +18,6 @@ #pragma once -#include #include #include #include diff --git a/include/tgfx/svg/SVGRenderContext.h b/include/tgfx/svg/SVGRenderContext.h index cbadd4e2..9adcba10 100644 --- a/include/tgfx/svg/SVGRenderContext.h +++ b/include/tgfx/svg/SVGRenderContext.h @@ -26,6 +26,7 @@ #include #include "tgfx/core/Canvas.h" #include "tgfx/core/MaskFilter.h" +#include "tgfx/core/Matrix.h" #include "tgfx/core/Paint.h" #include "tgfx/core/Path.h" #include "tgfx/core/Point.h" diff --git a/src/svg/SVGDOM.cpp b/src/svg/SVGDOM.cpp index ea01293b..c241f6d3 100644 --- a/src/svg/SVGDOM.cpp +++ b/src/svg/SVGDOM.cpp @@ -30,6 +30,7 @@ #include "core/utils/Log.h" #include "tgfx/core/Canvas.h" #include "tgfx/core/Data.h" +#include "tgfx/core/Recorder.h" #include "tgfx/core/Surface.h" #include "tgfx/svg/SVGAttribute.h" #include "tgfx/svg/SVGAttributeParser.h" @@ -469,13 +470,22 @@ SVGDOM::SVGDOM(std::shared_ptr root, SVGIDMapper&& mapper, : _root(std::move(root)), _fontMgr(std::move(fontManager)), _nodeIDMapper(std::move(mapper)) { } -void SVGDOM::render(Canvas* canvas) const { +void SVGDOM::render(Canvas* canvas) { if (_root) { - SVGLengthContext lctx(_containerSize); - SkSVGPresentationContext pctx; - SVGRenderContext renderCtx(canvas->getSurface()->getContext(), canvas, _fontMgr, _nodeIDMapper, - lctx, pctx, {nullptr, nullptr}); - _root->render(renderCtx); + if (!_renderPicture) { + SVGLengthContext lctx(_containerSize); + SkSVGPresentationContext pctx; + + Recorder recorder; + auto* drawCanvas = recorder.beginRecording(); + { + SVGRenderContext renderCtx(canvas->getSurface()->getContext(), drawCanvas, _fontMgr, + _nodeIDMapper, lctx, pctx, {nullptr, nullptr}); + _root->render(renderCtx); + } + _renderPicture = recorder.finishRecordingAsPicture(); + } + canvas->drawPicture(_renderPicture); } } diff --git a/src/svg/SVGFontManager.cpp b/src/svg/SVGFontManager.cpp index f647e57f..81f44bd2 100644 --- a/src/svg/SVGFontManager.cpp +++ b/src/svg/SVGFontManager.cpp @@ -17,13 +17,16 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/SVGFontManager.h" -#include "tgfx/core/Typeface.h" namespace tgfx { -void SVGFontManager::setDefaultTypeface(const std::shared_ptr& typeface) { - _defaultTypeface = typeface; -} +bool SVGFontManager::setDefaultTypeface(const std::shared_ptr& typeface) { + if (typeface) { + _defaultTypeface = typeface; + return true; + } + return false; +}; void SVGFontManager::addFontStyle(const std::string& fontFamily, FontStyle style) { if (_typefaces.at(fontFamily).find(style) == _typefaces.at(fontFamily).end()) { diff --git a/src/svg/SVGRenderContext.cpp b/src/svg/SVGRenderContext.cpp index b99e215d..261bcef7 100644 --- a/src/svg/SVGRenderContext.cpp +++ b/src/svg/SVGRenderContext.cpp @@ -403,7 +403,7 @@ std::shared_ptr SVGRenderContext::applyMask(const SVGFuncIRI& mask) // if (!shaderImage) { // return nullptr; // } - auto matrix = Matrix::MakeTrans(-maskBound.left, -maskBound.top); + auto matrix = _canvas->getMatrix() * Matrix::MakeTrans(-maskBound.left, -maskBound.top); auto shaderImage = Image::MakeFrom(picture, static_cast(bound.width()), static_cast(bound.height()), &matrix); // { From e24c3e9e909addbc38ecbf20d4005e316b2e9a45 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Thu, 28 Nov 2024 20:30:31 +0800 Subject: [PATCH 05/31] format --- qt/src/TGFXView.cpp | 6 ++---- qt/src/main.cpp | 3 --- src/gpu/RenderContext.cpp | 2 +- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/qt/src/TGFXView.cpp b/qt/src/TGFXView.cpp index 58a946e0..15ce809f 100644 --- a/qt/src/TGFXView.cpp +++ b/qt/src/TGFXView.cpp @@ -91,11 +91,9 @@ void TGFXView::createAppHost() { std::string DefaultFont = "Microsoft YaHei"; std::string DefualtEmojiFont = "Segoe UI Emoji"; #endif - auto typeface = tgfx::Typeface::MakeFromPath( - "/Users/yg/code/c++_space/tgfx/resources/font/NotoSansSC-Regular.otf"); + auto typeface = tgfx::Typeface::MakeFromName(DefaultFont, ""); appHost->addTypeface("default", typeface); - typeface = tgfx::Typeface::MakeFromPath( - "/Users/yg/code/c++_space/tgfx/resources/font/NotoColorEmoji.ttf"); + typeface = tgfx::Typeface::MakeFromName(DefualtEmojiFont, ""); appHost->addTypeface("emoji", typeface); } diff --git a/qt/src/main.cpp b/qt/src/main.cpp index 005faf62..2be61b83 100644 --- a/qt/src/main.cpp +++ b/qt/src/main.cpp @@ -23,11 +23,8 @@ #include #include #include -#include #include "TGFXView.h" #include "qobject.h" -#include "tgfx/core/Data.h" -#include "tgfx/svg/SVGDOM.h" int main(int argc, char* argv[]) { QApplication::setApplicationName("Hello2D"); diff --git a/src/gpu/RenderContext.cpp b/src/gpu/RenderContext.cpp index 114a9108..b4e828be 100644 --- a/src/gpu/RenderContext.cpp +++ b/src/gpu/RenderContext.cpp @@ -215,7 +215,7 @@ void RenderContext::drawGlyphRunList(std::shared_ptr glyphRunList, auto aaType = getAAType(style); auto rasterizer = Rasterizer::MakeFrom(width, height, std::move(glyphRunList), aaType == AAType::Coverage, rasterizeMatrix, stroke); - auto* proxyProvider = getContext()->proxyProvider(); + auto proxyProvider = getContext()->proxyProvider(); auto textureProxy = proxyProvider->createTextureProxy({}, rasterizer, false, renderFlags); auto processor = TextureEffect::Make(std::move(textureProxy), {}, &rasterizeMatrix, true); if (processor == nullptr) { From ccbed6f8396f3bc3c44790d3fa0734318ffc7d43 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Thu, 28 Nov 2024 21:02:28 +0800 Subject: [PATCH 06/31] typo --- test/src/{SVGTest.cpp => SVGRenderTest.cpp} | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) rename test/src/{SVGTest.cpp => SVGRenderTest.cpp} (96%) diff --git a/test/src/SVGTest.cpp b/test/src/SVGRenderTest.cpp similarity index 96% rename from test/src/SVGTest.cpp rename to test/src/SVGRenderTest.cpp index dced6ea2..8461d9cc 100644 --- a/test/src/SVGTest.cpp +++ b/test/src/SVGRenderTest.cpp @@ -20,7 +20,7 @@ #include "utils/TestUtils.h" namespace tgfx { -TGFX_TEST(SVGTest, PathSVGRender) { +TGFX_TEST(SVGRenderTest, PathSVG) { auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/path.svg")); ASSERT_TRUE(data != nullptr); auto fontManager = std::make_shared(); @@ -43,7 +43,7 @@ TGFX_TEST(SVGTest, PathSVGRender) { device->unlock(); } -TGFX_TEST(SVGTest, PNGImageSVGRender) { +TGFX_TEST(SVGRenderTest, PNGImageSVG) { auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/png.svg")); ASSERT_TRUE(data != nullptr); auto fontManager = std::make_shared(); @@ -66,7 +66,7 @@ TGFX_TEST(SVGTest, PNGImageSVGRender) { device->unlock(); } -TGFX_TEST(SVGTest, JPGImageSVGRender) { +TGFX_TEST(SVGRenderTest, JPGImageSVG) { auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/jpg.svg")); ASSERT_TRUE(data != nullptr); auto fontManager = std::make_shared(); @@ -89,7 +89,7 @@ TGFX_TEST(SVGTest, JPGImageSVGRender) { device->unlock(); } -TGFX_TEST(SVGTest, MaskSVGRender) { +TGFX_TEST(SVGRenderTest, MaskSVG) { auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/mask.svg")); ASSERT_TRUE(data != nullptr); auto fontManager = std::make_shared(); @@ -112,7 +112,7 @@ TGFX_TEST(SVGTest, MaskSVGRender) { device->unlock(); } -TGFX_TEST(SVGTest, GradientSVGRender) { +TGFX_TEST(SVGRenderTest, GradientSVG) { auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/radialGradient.svg")); ASSERT_TRUE(data != nullptr); auto fontManager = std::make_shared(); @@ -135,7 +135,7 @@ TGFX_TEST(SVGTest, GradientSVGRender) { device->unlock(); } -TGFX_TEST(SVGTest, BlurSVGRender) { +TGFX_TEST(SVGRenderTest, BlurSVG) { auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/blur.svg")); ASSERT_TRUE(data != nullptr); auto fontManager = std::make_shared(); @@ -158,7 +158,7 @@ TGFX_TEST(SVGTest, BlurSVGRender) { device->unlock(); } -TGFX_TEST(SVGTest, TextSVGRender) { +TGFX_TEST(SVGRenderTest, TextSVG) { auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/text.svg")); ASSERT_TRUE(data != nullptr); auto fontManager = std::make_shared(); From 0634b65aaa0526eaf5aa062b4a508dd8975342e1 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Mon, 2 Dec 2024 10:55:46 +0800 Subject: [PATCH 07/31] modify SVGFeGaussianBlur sigma scale --- src/svg/node/SVGFeGaussianBlur.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/svg/node/SVGFeGaussianBlur.cpp b/src/svg/node/SVGFeGaussianBlur.cpp index 9dafd8a0..eb6b272f 100644 --- a/src/svg/node/SVGFeGaussianBlur.cpp +++ b/src/svg/node/SVGFeGaussianBlur.cpp @@ -33,8 +33,8 @@ bool SkSVGFeGaussianBlur::parseAndSetAttribute(const char* name, const char* val std::shared_ptr SkSVGFeGaussianBlur::onMakeImageFilter( const SVGRenderContext& ctx, const SkSVGFilterContext& fctx) const { auto scale = ctx.transformForCurrentOBB(fctx.primitiveUnits()).scale; - const auto sigmaX = fStdDeviation.fX * scale.x * 2; - const auto sigmaY = fStdDeviation.fY * scale.y * 2; + const auto sigmaX = fStdDeviation.fX * scale.x * 4; + const auto sigmaY = fStdDeviation.fY * scale.y * 4; return ImageFilter::Blur(sigmaX, sigmaY); } #endif From 1c6d8f81d34a01b87a8385b754ceb8e7ad15b150 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Mon, 2 Dec 2024 18:54:50 +0800 Subject: [PATCH 08/31] save --- include/tgfx/svg/SVGRenderContext.h | 9 ++++++++- src/svg/SVGDOM.cpp | 5 +++-- src/svg/SVGRenderContext.cpp | 27 ++++++++++++++++----------- src/svg/node/SVGTransformableNode.cpp | 1 + 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/include/tgfx/svg/SVGRenderContext.h b/include/tgfx/svg/SVGRenderContext.h index 9adcba10..6b10152f 100644 --- a/include/tgfx/svg/SVGRenderContext.h +++ b/include/tgfx/svg/SVGRenderContext.h @@ -167,7 +167,8 @@ class SVGRenderContext { }; SVGRenderContext(Context*, Canvas*, const std::shared_ptr&, const SVGIDMapper&, - const SVGLengthContext&, const SkSVGPresentationContext&, const OBBScope&); + const SVGLengthContext&, const SkSVGPresentationContext&, const OBBScope&, + const Matrix& matrix); SVGRenderContext(const SVGRenderContext&); SVGRenderContext(const SVGRenderContext&, Canvas*); SVGRenderContext(const SVGRenderContext&, const SVGLengthContext&); @@ -198,6 +199,10 @@ class SVGRenderContext { } void saveOnce(); + void concat(const Matrix& matrix) { + _matrix.preConcat(matrix); + } + enum ApplyFlags { kLeaf = 1 << 0, // the target node doesn't have descendants }; @@ -271,5 +276,7 @@ class SVGRenderContext { Context* _deviceContext; Paint _picturePaint; + + Matrix _matrix = Matrix::I(); }; } // namespace tgfx diff --git a/src/svg/SVGDOM.cpp b/src/svg/SVGDOM.cpp index c241f6d3..3393746c 100644 --- a/src/svg/SVGDOM.cpp +++ b/src/svg/SVGDOM.cpp @@ -480,7 +480,8 @@ void SVGDOM::render(Canvas* canvas) { auto* drawCanvas = recorder.beginRecording(); { SVGRenderContext renderCtx(canvas->getSurface()->getContext(), drawCanvas, _fontMgr, - _nodeIDMapper, lctx, pctx, {nullptr, nullptr}); + _nodeIDMapper, lctx, pctx, {nullptr, nullptr}, + canvas->getMatrix()); _root->render(renderCtx); } _renderPicture = recorder.finishRecordingAsPicture(); @@ -493,7 +494,7 @@ void SVGDOM::renderNode(Canvas* canvas, SkSVGPresentationContext& pctx, const ch if (_root) { SVGLengthContext lctx(_containerSize); SVGRenderContext renderCtx(canvas->getSurface()->getContext(), canvas, _fontMgr, _nodeIDMapper, - lctx, pctx, {nullptr, nullptr}); + lctx, pctx, {nullptr, nullptr}, canvas->getMatrix()); _root->renderNode(renderCtx, SVGIRI(SVGIRI::Type::kLocal, SVGStringType(id))); } } diff --git a/src/svg/SVGRenderContext.cpp b/src/svg/SVGRenderContext.cpp index 261bcef7..c648ee3b 100644 --- a/src/svg/SVGRenderContext.cpp +++ b/src/svg/SVGRenderContext.cpp @@ -180,36 +180,40 @@ SkSVGPresentationContext::SkSVGPresentationContext() SVGRenderContext::SVGRenderContext(Context* device, Canvas* canvas, const std::shared_ptr& fontManager, const SVGIDMapper& mapper, const SVGLengthContext& lctx, - const SkSVGPresentationContext& pctx, const OBBScope& obbs) + const SkSVGPresentationContext& pctx, const OBBScope& obbs, + const Matrix& matrix) : _fontMgr(fontManager), _nodeIDMapper(mapper), _lengthContext(lctx), _presentationContext(pctx), _renderCanvas(canvas), _recorder(), - _canvas(_recorder.beginRecording()), _scope(obbs), _deviceContext(device) { + _canvas(_recorder.beginRecording()), _scope(obbs), _deviceContext(device), _matrix(matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other) : SVGRenderContext(other._deviceContext, other._canvas, other._fontMgr, other._nodeIDMapper, - *other._lengthContext, *other._presentationContext, other._scope) { + *other._lengthContext, *other._presentationContext, other._scope, + other._matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, Canvas* canvas) : SVGRenderContext(other._deviceContext, canvas, other._fontMgr, other._nodeIDMapper, - *other._lengthContext, *other._presentationContext, other._scope) { + *other._lengthContext, *other._presentationContext, other._scope, + other._matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, const SVGLengthContext& lengthCtx) : SVGRenderContext(other._deviceContext, other._canvas, other._fontMgr, other._nodeIDMapper, - lengthCtx, *other._presentationContext, other._scope) { + lengthCtx, *other._presentationContext, other._scope, other._matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, Canvas* canvas, const SVGLengthContext& lengthCtx) : SVGRenderContext(other._deviceContext, canvas, other._fontMgr, other._nodeIDMapper, lengthCtx, - *other._presentationContext, other._scope) { + *other._presentationContext, other._scope, other._matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, const SVGNode* node) : SVGRenderContext(other._deviceContext, other._canvas, other._fontMgr, other._nodeIDMapper, - *other._lengthContext, *other._presentationContext, OBBScope{node, this}) { + *other._lengthContext, *other._presentationContext, OBBScope{node, this}, + other._matrix) { } SVGRenderContext::~SVGRenderContext() { @@ -403,9 +407,10 @@ std::shared_ptr SVGRenderContext::applyMask(const SVGFuncIRI& mask) // if (!shaderImage) { // return nullptr; // } - auto matrix = _canvas->getMatrix() * Matrix::MakeTrans(-maskBound.left, -maskBound.top); - auto shaderImage = Image::MakeFrom(picture, static_cast(bound.width()), - static_cast(bound.height()), &matrix); + auto matrix = _matrix * Matrix::MakeTrans(-maskBound.left, -maskBound.top); + auto shaderImage = + Image::MakeFrom(picture, static_cast(bound.width() * matrix.getScaleX()), + static_cast(bound.height() * matrix.getScaleY()), &matrix); // { // auto tempSurface = Surface::Make(_deviceContext, static_cast(bound.width()) * 2, // static_cast(bound.height()) * 2); @@ -460,7 +465,7 @@ std::optional SVGRenderContext::commonPaint(const SVGPaint& paint_selecto SkSVGPresentationContext pctx; pctx._namedColors = _presentationContext->_namedColors; SVGRenderContext local_ctx(_deviceContext, _canvas, _fontMgr, _nodeIDMapper, *_lengthContext, - pctx, _scope); + pctx, _scope, Matrix::I()); const auto node = this->findNodeById(paint_selector.iri()); if (!node || !node->asPaint(local_ctx, &(p.value()))) { diff --git a/src/svg/node/SVGTransformableNode.cpp b/src/svg/node/SVGTransformableNode.cpp index 4ed75d96..0bddf3b0 100644 --- a/src/svg/node/SVGTransformableNode.cpp +++ b/src/svg/node/SVGTransformableNode.cpp @@ -42,6 +42,7 @@ bool SkSVGTransformableNode::onPrepareToRender(SVGRenderContext* ctx) const { } ctx->saveOnce(); ctx->canvas()->concat(transform); + ctx->concat(transform); } return this->INHERITED::onPrepareToRender(ctx); From 77ae5bc0b9a8ad3a76ff88e9983de2fd1ebb720f Mon Sep 17 00:00:00 2001 From: YGauroa Date: Thu, 5 Dec 2024 16:19:08 +0800 Subject: [PATCH 09/31] formate code --- include/tgfx/svg/SVGAttributeParser.h | 2 +- include/tgfx/svg/SVGTypes.h | 66 +++++------ include/tgfx/svg/SVGValue.h | 48 ++++---- include/tgfx/svg/node/SVGDefs.h | 2 +- include/tgfx/svg/node/SVGFe.h | 28 ++--- include/tgfx/svg/node/SVGFeBlend.h | 2 +- include/tgfx/svg/node/SVGFeColorMatrix.h | 2 +- .../tgfx/svg/node/SVGFeComponentTransfer.h | 10 +- include/tgfx/svg/node/SVGFeComposite.h | 2 +- include/tgfx/svg/node/SVGFeDisplacementMap.h | 2 +- include/tgfx/svg/node/SVGFeFlood.h | 2 +- include/tgfx/svg/node/SVGFeGaussianBlur.h | 2 +- include/tgfx/svg/node/SVGFeImage.h | 2 +- include/tgfx/svg/node/SVGFeLightSource.h | 6 +- include/tgfx/svg/node/SVGFeLighting.h | 4 +- include/tgfx/svg/node/SVGFeMerge.h | 4 +- include/tgfx/svg/node/SVGFeMorphology.h | 2 +- include/tgfx/svg/node/SVGFeOffset.h | 2 +- include/tgfx/svg/node/SVGFeTurbulence.h | 2 +- include/tgfx/svg/node/SVGFilter.h | 2 +- include/tgfx/svg/node/SVGG.h | 2 +- include/tgfx/svg/node/SVGImage.h | 2 +- include/tgfx/svg/node/SVGMask.h | 2 +- include/tgfx/svg/node/SVGNode.h | 108 +++++++++--------- include/tgfx/svg/node/SVGPoly.h | 4 +- include/tgfx/svg/node/SVGSVG.h | 2 +- include/tgfx/svg/node/SVGStop.h | 2 +- include/tgfx/svg/node/SVGText.h | 8 +- src/svg/SVGDOM.cpp | 2 +- src/svg/SVGRenderContext.cpp | 6 +- src/svg/node/SVGCircle.cpp | 2 +- src/svg/node/SVGClipPath.cpp | 2 +- src/svg/node/SVGEllipse.cpp | 2 +- src/svg/node/SVGGradient.cpp | 2 +- src/svg/node/SVGLine.cpp | 2 +- src/svg/node/SVGLinearGradient.cpp | 2 +- src/svg/node/SVGNode.cpp | 18 +-- src/svg/node/SVGPath.cpp | 2 +- src/svg/node/SVGPattern.cpp | 4 +- src/svg/node/SVGRadialGradient.cpp | 2 +- src/svg/node/SVGRect.cpp | 2 +- src/svg/node/SVGStop.cpp | 2 +- src/svg/node/SVGText.cpp | 6 +- src/svg/node/SVGUse.cpp | 2 +- 44 files changed, 189 insertions(+), 191 deletions(-) diff --git a/include/tgfx/svg/SVGAttributeParser.h b/include/tgfx/svg/SVGAttributeParser.h index 0817d0ea..a879acaf 100644 --- a/include/tgfx/svg/SVGAttributeParser.h +++ b/include/tgfx/svg/SVGAttributeParser.h @@ -74,7 +74,7 @@ class SVGAttributeParser { } if (!strcmp(value, "inherit")) { - PropertyT result(SVGPropertyState::kInherit); + PropertyT result(SVGPropertyState::Inherit); return ParseResult(result); } diff --git a/include/tgfx/svg/SVGTypes.h b/include/tgfx/svg/SVGTypes.h index 75ba2711..42f966da 100644 --- a/include/tgfx/svg/SVGTypes.h +++ b/include/tgfx/svg/SVGTypes.h @@ -42,9 +42,9 @@ using SVGTransformType = Matrix; using SVGPointsType = std::vector; enum class SVGPropertyState { - kUnspecified, - kInherit, - kValue, + Unspecified, + Inherit, + Value, }; // https://www.w3.org/TR/SVG11/intro.html#TermProperty @@ -53,24 +53,24 @@ class SVGProperty { public: using ValueT = T; - SVGProperty() : fState(SVGPropertyState::kUnspecified) { + SVGProperty() : _state(SVGPropertyState::Unspecified) { } - explicit SVGProperty(SVGPropertyState state) : fState(state) { + explicit SVGProperty(SVGPropertyState state) : _state(state) { } - explicit SVGProperty(const T& value) : fState(SVGPropertyState::kValue) { - fValue = value; + explicit SVGProperty(const T& value) : _state(SVGPropertyState::Value) { + _value = value; } - explicit SVGProperty(T&& value) : fState(SVGPropertyState::kValue) { - fValue = std::move(value); + explicit SVGProperty(T&& value) : _state(SVGPropertyState::Value) { + _value = std::move(value); } template void init(Args&&... args) { - fState = SVGPropertyState::kValue; - fValue.emplace(std::forward(args)...); + _state = SVGPropertyState::Value; + _value.emplace(std::forward(args)...); } constexpr bool isInheritable() const { @@ -78,57 +78,57 @@ class SVGProperty { } bool isValue() const { - return fState == SVGPropertyState::kValue; + return _state == SVGPropertyState::Value; } T* getMaybeNull() const { - return fValue.has_value() ? &fValue.value() : nullptr; + return _value.has_value() ? &_value.value() : nullptr; } void set(SVGPropertyState state) { - fState = state; - if (fState != SVGPropertyState::kValue) { - fValue.reset(); + _state = state; + if (_state != SVGPropertyState::Value) { + _value.reset(); } } void set(const T& value) { - fState = SVGPropertyState::kValue; - fValue = value; + _state = SVGPropertyState::Value; + _value = value; } void set(T&& value) { - fState = SVGPropertyState::kValue; - fValue = std::move(value); + _state = SVGPropertyState::Value; + _value = std::move(value); } T* operator->() { // ASSERT(fState == SVGPropertyState::kValue); // ASSERT(fValue.has_value()); - return &fValue.value(); + return &_value.value(); } const T* operator->() const { // ASSERT(fState == SVGPropertyState::kValue); // ASSERT(fValue.has_value()); - return &fValue.value(); + return &_value.value(); } T& operator*() { // ASSERT(fState == SVGPropertyState::kValue); // ASSERT(fValue.has_value()); - return *fValue; + return *_value; } const T& operator*() const { // ASSERT(fState == SVGPropertyState::kValue); // ASSERT(fValue.has_value()); - return *fValue; + return *_value; } private: - SVGPropertyState fState; - std::optional fValue; + SVGPropertyState _state; + std::optional _value; }; class SVGLength { @@ -147,30 +147,30 @@ class SVGLength { kPC, }; - constexpr SVGLength() : fValue(0), fUnit(Unit::kUnknown) { + constexpr SVGLength() : _value(0), _unit(Unit::kUnknown) { } - explicit constexpr SVGLength(float v, Unit u = Unit::kNumber) : fValue(v), fUnit(u) { + explicit constexpr SVGLength(float v, Unit u = Unit::kNumber) : _value(v), _unit(u) { } SVGLength(const SVGLength&) = default; SVGLength& operator=(const SVGLength&) = default; bool operator==(const SVGLength& other) const { - return fUnit == other.fUnit && fValue == other.fValue; + return _unit == other._unit && _value == other._value; } bool operator!=(const SVGLength& other) const { return !(*this == other); } const float& value() const { - return fValue; + return _value; } const Unit& unit() const { - return fUnit; + return _unit; } private: - float fValue; - Unit fUnit; + float _value; + Unit _unit; }; // https://www.w3.org/TR/SVG11/linking.html#IRIReference diff --git a/include/tgfx/svg/SVGValue.h b/include/tgfx/svg/SVGValue.h index 1eab0dfb..67d2b764 100644 --- a/include/tgfx/svg/SVGValue.h +++ b/include/tgfx/svg/SVGValue.h @@ -25,33 +25,33 @@ namespace tgfx { class SVGValue { public: enum class Type { - kColor, - kFilter, - kLength, - kNumber, - kObjectBoundingBoxUnits, - kPreserveAspectRatio, - kStopColor, - kString, - kTransform, - kViewBox, + Color, + Filter, + Length, + Number, + ObjectBoundingBoxUnits, + PreserveAspectRatio, + StopColor, + String, + Transform, + ViewBox, }; Type type() const { - return fType; + return _type; } template const T* as() const { - return fType == T::_type ? static_cast(this) : nullptr; + return _type == T::_type ? static_cast(this) : nullptr; } protected: - explicit SVGValue(Type t) : fType(t) { + explicit SVGValue(Type t) : _type(t) { } private: - Type fType; + Type _type; }; template @@ -83,17 +83,15 @@ class SVGWrapperValue final : public SVGValue { using INHERITED = SVGValue; }; -using SVGColorValue = SVGWrapperValue; -using SVGLengthValue = SVGWrapperValue; -using SVGTransformValue = SVGWrapperValue; -using SVGViewBoxValue = SVGWrapperValue; -using SVGNumberValue = SVGWrapperValue; -using SVGStringValue = SVGWrapperValue; -using SVGStopColorValue = SVGWrapperValue; - +using SVGColorValue = SVGWrapperValue; +using SVGLengthValue = SVGWrapperValue; +using SVGTransformValue = SVGWrapperValue; +using SVGViewBoxValue = SVGWrapperValue; +using SVGNumberValue = SVGWrapperValue; +using SVGStringValue = SVGWrapperValue; +using SVGStopColorValue = SVGWrapperValue; using SVGPreserveAspectRatioValue = - SVGWrapperValue; - + SVGWrapperValue; using SVGObjectBoundingBoxUnitsValue = - SVGWrapperValue; + SVGWrapperValue; } // namespace tgfx diff --git a/include/tgfx/svg/node/SVGDefs.h b/include/tgfx/svg/node/SVGDefs.h index ce070fe9..5d06e8d2 100644 --- a/include/tgfx/svg/node/SVGDefs.h +++ b/include/tgfx/svg/node/SVGDefs.h @@ -30,7 +30,7 @@ class SkSVGDefs : public SVGHiddenContainer { } private: - SkSVGDefs() : INHERITED(SVGTag::kDefs) { + SkSVGDefs() : INHERITED(SVGTag::Defs) { } using INHERITED = SVGHiddenContainer; diff --git a/include/tgfx/svg/node/SVGFe.h b/include/tgfx/svg/node/SVGFe.h index 1b76999d..a7323d52 100644 --- a/include/tgfx/svg/node/SVGFe.h +++ b/include/tgfx/svg/node/SVGFe.h @@ -36,20 +36,20 @@ class SkSVGFe : public SVGHiddenContainer { public: static bool IsFilterEffect(const std::shared_ptr& node) { switch (node->tag()) { - case SVGTag::kFeBlend: - case SVGTag::kFeColorMatrix: - case SVGTag::kFeComponentTransfer: - case SVGTag::kFeComposite: - case SVGTag::kFeDiffuseLighting: - case SVGTag::kFeDisplacementMap: - case SVGTag::kFeFlood: - case SVGTag::kFeGaussianBlur: - case SVGTag::kFeImage: - case SVGTag::kFeMerge: - case SVGTag::kFeMorphology: - case SVGTag::kFeOffset: - case SVGTag::kFeSpecularLighting: - case SVGTag::kFeTurbulence: + case SVGTag::FeBlend: + case SVGTag::FeColorMatrix: + case SVGTag::FeComponentTransfer: + case SVGTag::FeComposite: + case SVGTag::FeDiffuseLighting: + case SVGTag::FeDisplacementMap: + case SVGTag::FeFlood: + case SVGTag::FeGaussianBlur: + case SVGTag::FeImage: + case SVGTag::FeMerge: + case SVGTag::FeMorphology: + case SVGTag::FeOffset: + case SVGTag::FeSpecularLighting: + case SVGTag::FeTurbulence: return true; default: return false; diff --git a/include/tgfx/svg/node/SVGFeBlend.h b/include/tgfx/svg/node/SVGFeBlend.h index 6c41e579..485a014f 100644 --- a/include/tgfx/svg/node/SVGFeBlend.h +++ b/include/tgfx/svg/node/SVGFeBlend.h @@ -59,7 +59,7 @@ class SkSVGFeBlend : public SkSVGFe { bool parseAndSetAttribute(const char*, const char*) override; private: - SkSVGFeBlend() : INHERITED(SVGTag::kFeBlend) { + SkSVGFeBlend() : INHERITED(SVGTag::FeBlend) { } using INHERITED = SkSVGFe; diff --git a/include/tgfx/svg/node/SVGFeColorMatrix.h b/include/tgfx/svg/node/SVGFeColorMatrix.h index cd5fea93..0a764747 100644 --- a/include/tgfx/svg/node/SVGFeColorMatrix.h +++ b/include/tgfx/svg/node/SVGFeColorMatrix.h @@ -52,7 +52,7 @@ class SkSVGFeColorMatrix final : public SkSVGFe { bool parseAndSetAttribute(const char*, const char*) override; private: - SkSVGFeColorMatrix() : INHERITED(SVGTag::kFeColorMatrix) { + SkSVGFeColorMatrix() : INHERITED(SVGTag::FeColorMatrix) { } #ifndef RENDER_SVG diff --git a/include/tgfx/svg/node/SVGFeComponentTransfer.h b/include/tgfx/svg/node/SVGFeComponentTransfer.h index 718fed5c..92e6152f 100644 --- a/include/tgfx/svg/node/SVGFeComponentTransfer.h +++ b/include/tgfx/svg/node/SVGFeComponentTransfer.h @@ -35,19 +35,19 @@ namespace tgfx { class SkSVGFeFunc final : public SVGHiddenContainer { public: static std::shared_ptr MakeFuncA() { - return std::shared_ptr(new SkSVGFeFunc(SVGTag::kFeFuncA)); + return std::shared_ptr(new SkSVGFeFunc(SVGTag::FeFuncA)); } static std::shared_ptr MakeFuncR() { - return std::shared_ptr(new SkSVGFeFunc(SVGTag::kFeFuncR)); + return std::shared_ptr(new SkSVGFeFunc(SVGTag::FeFuncR)); } static std::shared_ptr MakeFuncG() { - return std::shared_ptr(new SkSVGFeFunc(SVGTag::kFeFuncG)); + return std::shared_ptr(new SkSVGFeFunc(SVGTag::FeFuncG)); } static std::shared_ptr MakeFuncB() { - return std::shared_ptr(new SkSVGFeFunc(SVGTag::kFeFuncB)); + return std::shared_ptr(new SkSVGFeFunc(SVGTag::FeFuncB)); } SVG_ATTR(Amplitude, SVGNumberType, 1) @@ -88,7 +88,7 @@ class SkSVGFeComponentTransfer final : public SkSVGFe { } private: - SkSVGFeComponentTransfer() : INHERITED(SVGTag::kFeComponentTransfer) { + SkSVGFeComponentTransfer() : INHERITED(SVGTag::FeComponentTransfer) { } using INHERITED = SkSVGFe; diff --git a/include/tgfx/svg/node/SVGFeComposite.h b/include/tgfx/svg/node/SVGFeComposite.h index d82c809d..8bf8366f 100644 --- a/include/tgfx/svg/node/SVGFeComposite.h +++ b/include/tgfx/svg/node/SVGFeComposite.h @@ -48,7 +48,7 @@ class SkSVGFeComposite final : public SkSVGFe { bool parseAndSetAttribute(const char*, const char*) override; private: - SkSVGFeComposite() : INHERITED(SVGTag::kFeComposite) { + SkSVGFeComposite() : INHERITED(SVGTag::FeComposite) { } using INHERITED = SkSVGFe; diff --git a/include/tgfx/svg/node/SVGFeDisplacementMap.h b/include/tgfx/svg/node/SVGFeDisplacementMap.h index 49a751ac..fd158e1b 100644 --- a/include/tgfx/svg/node/SVGFeDisplacementMap.h +++ b/include/tgfx/svg/node/SVGFeDisplacementMap.h @@ -50,7 +50,7 @@ class SkSVGFeDisplacementMap : public SkSVGFe { bool parseAndSetAttribute(const char*, const char*) override; private: - SkSVGFeDisplacementMap() : INHERITED(SVGTag::kFeDisplacementMap) { + SkSVGFeDisplacementMap() : INHERITED(SVGTag::FeDisplacementMap) { } using INHERITED = SkSVGFe; diff --git a/include/tgfx/svg/node/SVGFeFlood.h b/include/tgfx/svg/node/SVGFeFlood.h index 4ff2555b..34162aa8 100644 --- a/include/tgfx/svg/node/SVGFeFlood.h +++ b/include/tgfx/svg/node/SVGFeFlood.h @@ -51,7 +51,7 @@ class SkSVGFeFlood : public SkSVGFe { } private: - SkSVGFeFlood() : INHERITED(SVGTag::kFeFlood) { + SkSVGFeFlood() : INHERITED(SVGTag::FeFlood) { } #ifdef RENDER_SVG SkColor resolveFloodColor(const SVGRenderContext&) const; diff --git a/include/tgfx/svg/node/SVGFeGaussianBlur.h b/include/tgfx/svg/node/SVGFeGaussianBlur.h index cbe00e38..628e6f4a 100644 --- a/include/tgfx/svg/node/SVGFeGaussianBlur.h +++ b/include/tgfx/svg/node/SVGFeGaussianBlur.h @@ -55,7 +55,7 @@ class SkSVGFeGaussianBlur : public SkSVGFe { bool parseAndSetAttribute(const char*, const char*) override; private: - SkSVGFeGaussianBlur() : INHERITED(SVGTag::kFeGaussianBlur) { + SkSVGFeGaussianBlur() : INHERITED(SVGTag::FeGaussianBlur) { } using INHERITED = SkSVGFe; diff --git a/include/tgfx/svg/node/SVGFeImage.h b/include/tgfx/svg/node/SVGFeImage.h index fca0b944..0a959c49 100644 --- a/include/tgfx/svg/node/SVGFeImage.h +++ b/include/tgfx/svg/node/SVGFeImage.h @@ -51,7 +51,7 @@ class SkSVGFeImage : public SkSVGFe { } private: - SkSVGFeImage() : INHERITED(SVGTag::kFeImage) { + SkSVGFeImage() : INHERITED(SVGTag::FeImage) { } using INHERITED = SkSVGFe; diff --git a/include/tgfx/svg/node/SVGFeLightSource.h b/include/tgfx/svg/node/SVGFeLightSource.h index dc3bcf7c..a291acd5 100644 --- a/include/tgfx/svg/node/SVGFeLightSource.h +++ b/include/tgfx/svg/node/SVGFeLightSource.h @@ -50,7 +50,7 @@ class SkSVGFeDistantLight final : public SkSVGFeLightSource { SVG_ATTR(Elevation, SVGNumberType, 0) private: - SkSVGFeDistantLight() : INHERITED(SVGTag::kFeDistantLight) { + SkSVGFeDistantLight() : INHERITED(SVGTag::FeDistantLight) { } bool parseAndSetAttribute(const char*, const char*) override; @@ -69,7 +69,7 @@ class SkSVGFePointLight final : public SkSVGFeLightSource { SVG_ATTR(Z, SVGNumberType, 0) private: - SkSVGFePointLight() : INHERITED(SVGTag::kFePointLight) { + SkSVGFePointLight() : INHERITED(SVGTag::FePointLight) { } bool parseAndSetAttribute(const char*, const char*) override; @@ -94,7 +94,7 @@ class SkSVGFeSpotLight final : public SkSVGFeLightSource { SVG_OPTIONAL_ATTR(LimitingConeAngle, SVGNumberType) private: - SkSVGFeSpotLight() : INHERITED(SVGTag::kFeSpotLight) { + SkSVGFeSpotLight() : INHERITED(SVGTag::FeSpotLight) { } bool parseAndSetAttribute(const char*, const char*) override; diff --git a/include/tgfx/svg/node/SVGFeLighting.h b/include/tgfx/svg/node/SVGFeLighting.h index cbb2e42b..3b1cee8f 100644 --- a/include/tgfx/svg/node/SVGFeLighting.h +++ b/include/tgfx/svg/node/SVGFeLighting.h @@ -104,7 +104,7 @@ class SkSVGFeSpecularLighting final : public SkSVGFeLighting { #endif private: - SkSVGFeSpecularLighting() : INHERITED(SVGTag::kFeSpecularLighting) { + SkSVGFeSpecularLighting() : INHERITED(SVGTag::FeSpecularLighting) { } using INHERITED = SkSVGFeLighting; @@ -132,7 +132,7 @@ class SkSVGFeDiffuseLighting final : public SkSVGFeLighting { const SkSVGFeSpotLight*) const final; #endif private: - SkSVGFeDiffuseLighting() : INHERITED(SVGTag::kFeDiffuseLighting) { + SkSVGFeDiffuseLighting() : INHERITED(SVGTag::FeDiffuseLighting) { } using INHERITED = SkSVGFeLighting; diff --git a/include/tgfx/svg/node/SVGFeMerge.h b/include/tgfx/svg/node/SVGFeMerge.h index f9ac49d4..d8ef7dd4 100644 --- a/include/tgfx/svg/node/SVGFeMerge.h +++ b/include/tgfx/svg/node/SVGFeMerge.h @@ -33,7 +33,7 @@ namespace tgfx { // https://www.w3.org/TR/SVG11/filters.html#feMergeNodeElement class SkSVGFeMergeNode : public SVGHiddenContainer { public: - static constexpr SVGTag tag = SVGTag::kFeMergeNode; + static constexpr SVGTag tag = SVGTag::FeMergeNode; static std::shared_ptr Make() { return std::shared_ptr(new SkSVGFeMergeNode()); @@ -67,7 +67,7 @@ class SkSVGFeMerge : public SkSVGFe { std::vector getInputs() const override; private: - SkSVGFeMerge() : INHERITED(SVGTag::kFeMerge) { + SkSVGFeMerge() : INHERITED(SVGTag::FeMerge) { } using INHERITED = SkSVGFe; diff --git a/include/tgfx/svg/node/SVGFeMorphology.h b/include/tgfx/svg/node/SVGFeMorphology.h index 7e170bc5..0a4a3a30 100644 --- a/include/tgfx/svg/node/SVGFeMorphology.h +++ b/include/tgfx/svg/node/SVGFeMorphology.h @@ -61,7 +61,7 @@ class SkSVGFeMorphology : public SkSVGFe { bool parseAndSetAttribute(const char*, const char*) override; private: - SkSVGFeMorphology() : INHERITED(SVGTag::kFeMorphology) { + SkSVGFeMorphology() : INHERITED(SVGTag::FeMorphology) { } using INHERITED = SkSVGFe; diff --git a/include/tgfx/svg/node/SVGFeOffset.h b/include/tgfx/svg/node/SVGFeOffset.h index 621d0938..9767bf23 100644 --- a/include/tgfx/svg/node/SVGFeOffset.h +++ b/include/tgfx/svg/node/SVGFeOffset.h @@ -52,7 +52,7 @@ class SkSVGFeOffset : public SkSVGFe { bool parseAndSetAttribute(const char*, const char*) override; private: - SkSVGFeOffset() : INHERITED(SVGTag::kFeOffset) { + SkSVGFeOffset() : INHERITED(SVGTag::FeOffset) { } using INHERITED = SkSVGFe; diff --git a/include/tgfx/svg/node/SVGFeTurbulence.h b/include/tgfx/svg/node/SVGFeTurbulence.h index c085c765..3f04d7b9 100644 --- a/include/tgfx/svg/node/SVGFeTurbulence.h +++ b/include/tgfx/svg/node/SVGFeTurbulence.h @@ -51,7 +51,7 @@ class SkSVGFeTurbulence : public SkSVGFe { bool parseAndSetAttribute(const char*, const char*) override; private: - SkSVGFeTurbulence() : INHERITED(SVGTag::kFeTurbulence) { + SkSVGFeTurbulence() : INHERITED(SVGTag::FeTurbulence) { } using INHERITED = SkSVGFe; diff --git a/include/tgfx/svg/node/SVGFilter.h b/include/tgfx/svg/node/SVGFilter.h index f366296b..917f1385 100644 --- a/include/tgfx/svg/node/SVGFilter.h +++ b/include/tgfx/svg/node/SVGFilter.h @@ -47,7 +47,7 @@ class SkSVGFilter final : public SVGHiddenContainer { SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse)) private: - SkSVGFilter() : INHERITED(SVGTag::kFilter) { + SkSVGFilter() : INHERITED(SVGTag::Filter) { } bool parseAndSetAttribute(const char*, const char*) override; diff --git a/include/tgfx/svg/node/SVGG.h b/include/tgfx/svg/node/SVGG.h index 0ced7f36..286f8073 100644 --- a/include/tgfx/svg/node/SVGG.h +++ b/include/tgfx/svg/node/SVGG.h @@ -29,7 +29,7 @@ class SkSVGG : public SkSVGContainer { } private: - SkSVGG() : INHERITED(SVGTag::kG) { + SkSVGG() : INHERITED(SVGTag::G) { } using INHERITED = SkSVGContainer; diff --git a/include/tgfx/svg/node/SVGImage.h b/include/tgfx/svg/node/SVGImage.h index 5033900e..bf397e84 100644 --- a/include/tgfx/svg/node/SVGImage.h +++ b/include/tgfx/svg/node/SVGImage.h @@ -72,7 +72,7 @@ class SkSVGImage final : public SkSVGTransformableNode { bool parseAndSetAttribute(const char*, const char*) override; private: - SkSVGImage() : INHERITED(SVGTag::kImage) { + SkSVGImage() : INHERITED(SVGTag::Image) { } using INHERITED = SkSVGTransformableNode; diff --git a/include/tgfx/svg/node/SVGMask.h b/include/tgfx/svg/node/SVGMask.h index d3ec7e54..fb9625d5 100644 --- a/include/tgfx/svg/node/SVGMask.h +++ b/include/tgfx/svg/node/SVGMask.h @@ -44,7 +44,7 @@ class SkSVGMask final : public SVGHiddenContainer { private: friend class SVGRenderContext; - SkSVGMask() : INHERITED(SVGTag::kMask) { + SkSVGMask() : INHERITED(SVGTag::Mask) { } bool parseAndSetAttribute(const char*, const char*) override; diff --git a/include/tgfx/svg/node/SVGNode.h b/include/tgfx/svg/node/SVGNode.h index 463ce2bd..409b1b41 100644 --- a/include/tgfx/svg/node/SVGNode.h +++ b/include/tgfx/svg/node/SVGNode.h @@ -32,51 +32,51 @@ namespace tgfx { class SVGValue; enum class SVGTag { - kCircle, - kClipPath, - kDefs, - kEllipse, - kFeBlend, - kFeColorMatrix, - kFeComponentTransfer, - kFeComposite, - kFeDiffuseLighting, - kFeDisplacementMap, - kFeDistantLight, - kFeFlood, - kFeFuncA, - kFeFuncR, - kFeFuncG, - kFeFuncB, - kFeGaussianBlur, - kFeImage, - kFeMerge, - kFeMergeNode, - kFeMorphology, - kFeOffset, - kFePointLight, - kFeSpecularLighting, - kFeSpotLight, - kFeTurbulence, - kFilter, - kG, - kImage, - kLine, - kLinearGradient, - kMask, - kPath, - kPattern, - kPolygon, - kPolyline, - kRadialGradient, - kRect, - kStop, - kSvg, - kText, - kTextLiteral, - kTextPath, - kTSpan, - kUse + Circle, + ClipPath, + Defs, + Ellipse, + FeBlend, + FeColorMatrix, + FeComponentTransfer, + FeComposite, + FeDiffuseLighting, + FeDisplacementMap, + FeDistantLight, + FeFlood, + FeFuncA, + FeFuncR, + FeFuncG, + FeFuncB, + FeGaussianBlur, + FeImage, + FeMerge, + FeMergeNode, + FeMorphology, + FeOffset, + FePointLight, + FeSpecularLighting, + FeSpotLight, + FeTurbulence, + Filter, + G, + Image, + Line, + LinearGradient, + Mask, + Path, + Pattern, + Polygon, + Polyline, + RadialGradient, + Rect, + Stop, + Svg, + Text, + TextLiteral, + TextPath, + TSpan, + Use }; #define SVG_PRES_ATTR(attr_name, attr_type, attr_inherited) \ @@ -91,24 +91,24 @@ enum class SVGTag { \ public: \ const SVGProperty& get##attr_name() const { \ - return fPresentationAttributes.f##attr_name; \ + return _presentationAttributes.f##attr_name; \ } \ void set##attr_name(const SVGProperty& v) { \ - auto* dest = &fPresentationAttributes.f##attr_name; \ + auto* dest = &_presentationAttributes.f##attr_name; \ if (!dest->isInheritable() || v.isValue()) { \ /* TODO: If dest is not inheritable, handle v == "inherit" */ \ *dest = v; \ } else { \ - dest->set(SVGPropertyState::kInherit); \ + dest->set(SVGPropertyState::Inherit); \ } \ } \ void set##attr_name(SVGProperty&& v) { \ - auto* dest = &fPresentationAttributes.f##attr_name; \ + auto* dest = &_presentationAttributes.f##attr_name; \ if (!dest->isInheritable() || v.isValue()) { \ /* TODO: If dest is not inheritable, handle v == "inherit" */ \ *dest = std::move(v); \ } else { \ - dest->set(SVGPropertyState::kInherit); \ + dest->set(SVGPropertyState::Inherit); \ } \ } @@ -117,11 +117,11 @@ class SVGNode { virtual ~SVGNode(); SVGTag tag() const { - return fTag; + return _tag; } const SVGPresentationAttributes* presentationAttributes() const { - return &fPresentationAttributes; + return &_presentationAttributes; } virtual void appendChild(std::shared_ptr) = 0; @@ -208,10 +208,10 @@ class SVGNode { #endif private: - SVGTag fTag; + SVGTag _tag; // FIXME: this should be sparse - SVGPresentationAttributes fPresentationAttributes; + SVGPresentationAttributes _presentationAttributes; }; //NOLINTBEGIN diff --git a/include/tgfx/svg/node/SVGPoly.h b/include/tgfx/svg/node/SVGPoly.h index 2b33f492..dc01cc05 100644 --- a/include/tgfx/svg/node/SVGPoly.h +++ b/include/tgfx/svg/node/SVGPoly.h @@ -32,11 +32,11 @@ namespace tgfx { class SkSVGPoly final : public SVGShape { public: static std::shared_ptr MakePolygon() { - return std::shared_ptr(new SkSVGPoly(SVGTag::kPolygon)); + return std::shared_ptr(new SkSVGPoly(SVGTag::Polygon)); } static std::shared_ptr MakePolyline() { - return std::shared_ptr(new SkSVGPoly(SVGTag::kPolyline)); + return std::shared_ptr(new SkSVGPoly(SVGTag::Polyline)); } SVG_ATTR(Points, SVGPointsType, SVGPointsType()) diff --git a/include/tgfx/svg/node/SVGSVG.h b/include/tgfx/svg/node/SVGSVG.h index 8a529194..0909515e 100644 --- a/include/tgfx/svg/node/SVGSVG.h +++ b/include/tgfx/svg/node/SVGSVG.h @@ -59,7 +59,7 @@ class SVGSVG : public SkSVGContainer { void onSetAttribute(SVGAttribute, const SVGValue&) override; private: - explicit SVGSVG(Type t) : INHERITED(SVGTag::kSvg), fType(t) { + explicit SVGSVG(Type t) : INHERITED(SVGTag::Svg), fType(t) { } // Some attributes behave differently for the outermost svg element. diff --git a/include/tgfx/svg/node/SVGStop.h b/include/tgfx/svg/node/SVGStop.h index 64e493a4..e38d5daa 100644 --- a/include/tgfx/svg/node/SVGStop.h +++ b/include/tgfx/svg/node/SVGStop.h @@ -27,7 +27,7 @@ namespace tgfx { class SkSVGStop : public SVGHiddenContainer { public: - static constexpr SVGTag tag = SVGTag::kStop; + static constexpr SVGTag tag = SVGTag::Stop; static std::shared_ptr Make() { return std::shared_ptr(new SkSVGStop()); diff --git a/include/tgfx/svg/node/SVGText.h b/include/tgfx/svg/node/SVGText.h index 3db33e20..78fc6761 100644 --- a/include/tgfx/svg/node/SVGText.h +++ b/include/tgfx/svg/node/SVGText.h @@ -87,7 +87,7 @@ class SkSVGText final : public SkSVGTextContainer { } private: - SkSVGText() : INHERITED(SVGTag::kText) { + SkSVGText() : INHERITED(SVGTag::Text) { } void onRender(const SVGRenderContext&) const override; @@ -104,7 +104,7 @@ class SkSVGTSpan final : public SkSVGTextContainer { } private: - SkSVGTSpan() : INHERITED(SVGTag::kTSpan) { + SkSVGTSpan() : INHERITED(SVGTag::TSpan) { } using INHERITED = SkSVGTextContainer; @@ -119,7 +119,7 @@ class SkSVGTextLiteral final : public SkSVGTextFragment { SVG_ATTR(Text, SVGStringType, SVGStringType()) private: - SkSVGTextLiteral() : INHERITED(SVGTag::kTextLiteral) { + SkSVGTextLiteral() : INHERITED(SVGTag::TextLiteral) { } void onShapeText(const SVGRenderContext&, const ShapedTextCallback&) const override; @@ -140,7 +140,7 @@ class SkSVGTextPath final : public SkSVGTextContainer { SVG_ATTR(StartOffset, SVGLength, SVGLength(0)) private: - SkSVGTextPath() : INHERITED(SVGTag::kTextPath) { + SkSVGTextPath() : INHERITED(SVGTag::TextPath) { } void onShapeText(const SVGRenderContext&, const ShapedTextCallback&) const override; diff --git a/src/svg/SVGDOM.cpp b/src/svg/SVGDOM.cpp index 3393746c..f8f29961 100644 --- a/src/svg/SVGDOM.cpp +++ b/src/svg/SVGDOM.cpp @@ -448,7 +448,7 @@ std::shared_ptr SVGDOM::Builder::make(Data& data, ConstructionContext ctx(&mapper); auto root = construct_svg_node(ctx, xmlDom->getRootNode().get()); - if (!root || root->tag() != SVGTag::kSvg) { + if (!root || root->tag() != SVGTag::Svg) { return nullptr; } diff --git a/src/svg/SVGRenderContext.cpp b/src/svg/SVGRenderContext.cpp index c648ee3b..1e11ce73 100644 --- a/src/svg/SVGRenderContext.cpp +++ b/src/svg/SVGRenderContext.cpp @@ -332,7 +332,7 @@ std::shared_ptr SVGRenderContext::applyFilter(const SVGFuncIRI& fil } const auto node = this->findNodeById(filter.iri()); - if (!node || node->tag() != SVGTag::kFilter) { + if (!node || node->tag() != SVGTag::Filter) { return nullptr; } @@ -350,7 +350,7 @@ Path SVGRenderContext::applyClip(const SVGFuncIRI& clip) { } const auto clipNode = this->findNodeById(clip.iri()); - if (!clipNode || clipNode->tag() != SVGTag::kClipPath) { + if (!clipNode || clipNode->tag() != SVGTag::ClipPath) { return Path(); } @@ -363,7 +363,7 @@ std::shared_ptr SVGRenderContext::applyMask(const SVGFuncIRI& mask) } const auto node = this->findNodeById(mask.iri()); - if (!node || node->tag() != SVGTag::kMask) { + if (!node || node->tag() != SVGTag::Mask) { return nullptr; } diff --git a/src/svg/node/SVGCircle.cpp b/src/svg/node/SVGCircle.cpp index 5e2777c1..040d4e83 100644 --- a/src/svg/node/SVGCircle.cpp +++ b/src/svg/node/SVGCircle.cpp @@ -25,7 +25,7 @@ namespace tgfx { -SVGCircle::SVGCircle() : INHERITED(SVGTag::kCircle) { +SVGCircle::SVGCircle() : INHERITED(SVGTag::Circle) { } bool SVGCircle::parseAndSetAttribute(const char* n, const char* v) { diff --git a/src/svg/node/SVGClipPath.cpp b/src/svg/node/SVGClipPath.cpp index 4c0dc3e8..e681b317 100644 --- a/src/svg/node/SVGClipPath.cpp +++ b/src/svg/node/SVGClipPath.cpp @@ -23,7 +23,7 @@ namespace tgfx { -SVGClipPath::SVGClipPath() : INHERITED(SVGTag::kClipPath) { +SVGClipPath::SVGClipPath() : INHERITED(SVGTag::ClipPath) { } bool SVGClipPath::parseAndSetAttribute(const char* n, const char* v) { diff --git a/src/svg/node/SVGEllipse.cpp b/src/svg/node/SVGEllipse.cpp index a95b8740..0cfa8cb3 100644 --- a/src/svg/node/SVGEllipse.cpp +++ b/src/svg/node/SVGEllipse.cpp @@ -23,7 +23,7 @@ namespace tgfx { -SkSVGEllipse::SkSVGEllipse() : INHERITED(SVGTag::kEllipse) { +SkSVGEllipse::SkSVGEllipse() : INHERITED(SVGTag::Ellipse) { } bool SkSVGEllipse::parseAndSetAttribute(const char* n, const char* v) { diff --git a/src/svg/node/SVGGradient.cpp b/src/svg/node/SVGGradient.cpp index ffd81753..4d2c7d57 100644 --- a/src/svg/node/SVGGradient.cpp +++ b/src/svg/node/SVGGradient.cpp @@ -55,7 +55,7 @@ void SkSVGGradient::collectColorStops(const SVGRenderContext& ctx, std::vectortag() == SVGTag::kLinearGradient || ref->tag() == SVGTag::kRadialGradient)) { + if (ref && (ref->tag() == SVGTag::LinearGradient || ref->tag() == SVGTag::RadialGradient)) { static_cast(ref.get())->collectColorStops(ctx, colors, positions); } } diff --git a/src/svg/node/SVGLine.cpp b/src/svg/node/SVGLine.cpp index 42e735df..8b8aac1a 100644 --- a/src/svg/node/SVGLine.cpp +++ b/src/svg/node/SVGLine.cpp @@ -24,7 +24,7 @@ namespace tgfx { -SkSVGLine::SkSVGLine() : INHERITED(SVGTag::kLine) { +SkSVGLine::SkSVGLine() : INHERITED(SVGTag::Line) { } bool SkSVGLine::parseAndSetAttribute(const char* n, const char* v) { diff --git a/src/svg/node/SVGLinearGradient.cpp b/src/svg/node/SVGLinearGradient.cpp index d3006585..2ed4786f 100644 --- a/src/svg/node/SVGLinearGradient.cpp +++ b/src/svg/node/SVGLinearGradient.cpp @@ -25,7 +25,7 @@ namespace tgfx { -SkSVGLinearGradient::SkSVGLinearGradient() : INHERITED(SVGTag::kLinearGradient) { +SkSVGLinearGradient::SkSVGLinearGradient() : INHERITED(SVGTag::LinearGradient) { } bool SkSVGLinearGradient::parseAndSetAttribute(const char* name, const char* value) { diff --git a/src/svg/node/SVGNode.cpp b/src/svg/node/SVGNode.cpp index aee3d2e3..13b83845 100644 --- a/src/svg/node/SVGNode.cpp +++ b/src/svg/node/SVGNode.cpp @@ -32,13 +32,13 @@ namespace tgfx { -SVGNode::SVGNode(SVGTag t) : fTag(t) { +SVGNode::SVGNode(SVGTag t) : _tag(t) { // Uninherited presentation attributes need a non-null default value. - fPresentationAttributes.fStopColor.set(SVGColor(Color::Black())); - fPresentationAttributes.fStopOpacity.set(static_cast(1.0f)); - fPresentationAttributes.fFloodColor.set(SVGColor(Color::Black())); - fPresentationAttributes.fFloodOpacity.set(static_cast(1.0f)); - fPresentationAttributes.fLightingColor.set(SVGColor(Color::White())); + _presentationAttributes.fStopColor.set(SVGColor(Color::Black())); + _presentationAttributes.fStopOpacity.set(static_cast(1.0f)); + _presentationAttributes.fFloodColor.set(SVGColor(Color::Black())); + _presentationAttributes.fFloodOpacity.set(static_cast(1.0f)); + _presentationAttributes.fLightingColor.set(SVGColor(Color::White())); } SVGNode::~SVGNode() { @@ -80,14 +80,14 @@ Rect SVGNode::objectBoundingBox(const SVGRenderContext& ctx) const { } bool SVGNode::onPrepareToRender(SVGRenderContext* ctx) const { - ctx->applyPresentationAttributes(fPresentationAttributes, + ctx->applyPresentationAttributes(_presentationAttributes, this->hasChildren() ? 0 : SVGRenderContext::kLeaf); // visibility:hidden and display:none disable rendering. // TODO: if display is not a value (true when display="inherit"), we currently // ignore it. Eventually we should be able to add SkASSERT(display.isValue()). const auto visibility = ctx->presentationContext()._inherited.fVisibility->type(); - const auto display = fPresentationAttributes.fDisplay; // display is uninherited + const auto display = _presentationAttributes.fDisplay; // display is uninherited return visibility != SVGVisibility::Type::kHidden && (!display.isValue() || *display != SVGDisplay::kNone); } @@ -111,7 +111,7 @@ void SetInheritedByDefault(std::optional& presentation_attribute, const T& va bool SVGNode::parseAndSetAttribute(const char* n, const char* v) { #define PARSE_AND_SET(svgName, attrName) \ this->set##attrName( \ - SVGAttributeParser::parseProperty(svgName, n, \ + SVGAttributeParser::parseProperty(svgName, n, \ v)) return PARSE_AND_SET("clip-path", ClipPath) || PARSE_AND_SET("clip-rule", ClipRule) || diff --git a/src/svg/node/SVGPath.cpp b/src/svg/node/SVGPath.cpp index 9469bb41..a78cfd9e 100644 --- a/src/svg/node/SVGPath.cpp +++ b/src/svg/node/SVGPath.cpp @@ -26,7 +26,7 @@ namespace tgfx { -SkSVGPath::SkSVGPath() : INHERITED(SVGTag::kPath) { +SkSVGPath::SkSVGPath() : INHERITED(SVGTag::Path) { } bool SkSVGPath::parseAndSetAttribute(const char* n, const char* v) { diff --git a/src/svg/node/SVGPattern.cpp b/src/svg/node/SVGPattern.cpp index b381a08f..d758b677 100644 --- a/src/svg/node/SVGPattern.cpp +++ b/src/svg/node/SVGPattern.cpp @@ -31,7 +31,7 @@ namespace tgfx { -SkSVGPattern::SkSVGPattern() : INHERITED(SVGTag::kPattern) { +SkSVGPattern::SkSVGPattern() : INHERITED(SVGTag::Pattern) { } bool SkSVGPattern::parseAndSetAttribute(const char* name, const char* value) { @@ -56,7 +56,7 @@ const SkSVGPattern* SkSVGPattern::hrefTarget(const SVGRenderContext& ctx) const } const auto href = ctx.findNodeById(fHref); - if (!href || href->tag() != SVGTag::kPattern) { + if (!href || href->tag() != SVGTag::Pattern) { return nullptr; } diff --git a/src/svg/node/SVGRadialGradient.cpp b/src/svg/node/SVGRadialGradient.cpp index 75260c6f..b6bc6b72 100644 --- a/src/svg/node/SVGRadialGradient.cpp +++ b/src/svg/node/SVGRadialGradient.cpp @@ -26,7 +26,7 @@ namespace tgfx { -SkSVGRadialGradient::SkSVGRadialGradient() : INHERITED(SVGTag::kRadialGradient) { +SkSVGRadialGradient::SkSVGRadialGradient() : INHERITED(SVGTag::RadialGradient) { } bool SkSVGRadialGradient::parseAndSetAttribute(const char* name, const char* value) { diff --git a/src/svg/node/SVGRect.cpp b/src/svg/node/SVGRect.cpp index 278b84d1..1572d1fd 100644 --- a/src/svg/node/SVGRect.cpp +++ b/src/svg/node/SVGRect.cpp @@ -29,7 +29,7 @@ namespace tgfx { -SkSVGRect::SkSVGRect() : INHERITED(SVGTag::kRect) { +SkSVGRect::SkSVGRect() : INHERITED(SVGTag::Rect) { } bool SkSVGRect::parseAndSetAttribute(const char* n, const char* v) { diff --git a/src/svg/node/SVGStop.cpp b/src/svg/node/SVGStop.cpp index 57eaa88d..3356e4f5 100644 --- a/src/svg/node/SVGStop.cpp +++ b/src/svg/node/SVGStop.cpp @@ -21,7 +21,7 @@ namespace tgfx { -SkSVGStop::SkSVGStop() : INHERITED(SVGTag::kStop) { +SkSVGStop::SkSVGStop() : INHERITED(SVGTag::Stop) { } bool SkSVGStop::parseAndSetAttribute(const char* n, const char* v) { diff --git a/src/svg/node/SVGText.cpp b/src/svg/node/SVGText.cpp index 4c321b60..16cc2678 100644 --- a/src/svg/node/SVGText.cpp +++ b/src/svg/node/SVGText.cpp @@ -131,9 +131,9 @@ float ComputeAlignmentFactor(const SkSVGPresentationContext& context) { void SkSVGTextContainer::appendChild(std::shared_ptr child) { // Only allow text content child nodes. switch (child->tag()) { - case SVGTag::kTextLiteral: - case SVGTag::kTextPath: - case SVGTag::kTSpan: + case SVGTag::TextLiteral: + case SVGTag::TextPath: + case SVGTag::TSpan: fChildren.push_back(std::static_pointer_cast(child)); break; default: diff --git a/src/svg/node/SVGUse.cpp b/src/svg/node/SVGUse.cpp index 9a7d8260..ff4a210a 100644 --- a/src/svg/node/SVGUse.cpp +++ b/src/svg/node/SVGUse.cpp @@ -26,7 +26,7 @@ namespace tgfx { -SkSVGUse::SkSVGUse() : INHERITED(SVGTag::kUse) { +SkSVGUse::SkSVGUse() : INHERITED(SVGTag::Use) { } bool SkSVGUse::parseAndSetAttribute(const char* n, const char* v) { From 89ad98516ec69d1c712394e9ace6d1a6c897c19d Mon Sep 17 00:00:00 2001 From: YGauroa Date: Fri, 6 Dec 2024 15:46:57 +0800 Subject: [PATCH 10/31] ... --- include/tgfx/svg/SVGTypes.h | 403 +++++++++++----------- include/tgfx/svg/SVGValue.h | 2 +- include/tgfx/svg/node/SVGClipPath.h | 2 +- include/tgfx/svg/node/SVGFilter.h | 12 +- include/tgfx/svg/node/SVGGradient.h | 4 +- include/tgfx/svg/node/SVGLinearGradient.h | 8 +- include/tgfx/svg/node/SVGMask.h | 4 +- include/tgfx/svg/node/SVGPattern.h | 4 +- include/tgfx/svg/node/SVGRadialGradient.h | 6 +- include/tgfx/svg/node/SVGSVG.h | 4 +- include/tgfx/svg/node/SVGStop.h | 2 +- src/svg/SVGAttribute.cpp | 18 +- src/svg/SVGAttributeParser.cpp | 114 +++--- src/svg/SVGDOM.cpp | 2 +- src/svg/SVGRenderContext.cpp | 60 ++-- src/svg/node/SVGFe.cpp | 8 +- src/svg/node/SVGFeDisplacementMap.cpp | 4 +- src/svg/node/SVGGradient.cpp | 12 +- src/svg/node/SVGMask.cpp | 4 +- src/svg/node/SVGNode.cpp | 11 +- src/svg/node/SVGPattern.cpp | 20 +- src/svg/node/SVGSVG.cpp | 4 +- src/svg/node/SVGText.cpp | 36 +- src/svg/node/SVGTransformableNode.cpp | 2 +- 24 files changed, 381 insertions(+), 365 deletions(-) diff --git a/include/tgfx/svg/SVGTypes.h b/include/tgfx/svg/SVGTypes.h index 42f966da..e13777b5 100644 --- a/include/tgfx/svg/SVGTypes.h +++ b/include/tgfx/svg/SVGTypes.h @@ -134,29 +134,33 @@ class SVGProperty { class SVGLength { public: enum class Unit { - kUnknown, - kNumber, - kPercentage, - kEMS, - kEXS, - kPX, - kCM, - kMM, - kIN, - kPT, - kPC, + Unknown, + Number, + Percentage, + EMS, + EXS, + PX, + CM, + MM, + IN, + PT, + PC, }; - constexpr SVGLength() : _value(0), _unit(Unit::kUnknown) { + constexpr SVGLength() : _value(0), _unit(Unit::Unknown) { } - explicit constexpr SVGLength(float v, Unit u = Unit::kNumber) : _value(v), _unit(u) { + + explicit constexpr SVGLength(float v, Unit u = Unit::Number) : _value(v), _unit(u) { } + SVGLength(const SVGLength&) = default; + SVGLength& operator=(const SVGLength&) = default; bool operator==(const SVGLength& other) const { return _unit == other._unit && _value == other._value; } + bool operator!=(const SVGLength& other) const { return !(*this == other); } @@ -164,6 +168,7 @@ class SVGLength { const float& value() const { return _value; } + const Unit& unit() const { return _unit; } @@ -177,56 +182,63 @@ class SVGLength { class SVGIRI { public: enum class Type { - kLocal, - kNonlocal, - kDataURI, + Local, + Nonlocal, + DataURI, }; - SVGIRI() : fType(Type::kLocal) { + SVGIRI() : _type(Type::Local) { } - SVGIRI(Type t, SVGStringType iri) : fType(t), fIRI(std::move(iri)) { + + SVGIRI(Type t, SVGStringType iri) : _type(t), _iri(std::move(iri)) { } Type type() const { - return fType; + return _type; } + const SVGStringType& iri() const { - return fIRI; + return _iri; } bool operator==(const SVGIRI& other) const { - return fType == other.fType && fIRI == other.fIRI; + return _type == other._type && _iri == other._iri; } + bool operator!=(const SVGIRI& other) const { return !(*this == other); } private: - Type fType; - SVGStringType fIRI; + Type _type; + SVGStringType _iri; }; // https://www.w3.org/TR/SVG11/types.html#InterfaceSVGColor class SVGColor { public: enum class Type { - kCurrentColor, - kColor, - kICCColor, + CurrentColor, + Color, + ICCColor, }; + using Vars = std::vector; SVGColor() : SVGColor(Color::Black()) { } - explicit SVGColor(const SVGColorType& c) : fType(Type::kColor), fColor(c), fVars(nullptr) { + + explicit SVGColor(const SVGColorType& c) : _type(Type::Color), _color(c), _vars(nullptr) { } - explicit SVGColor(Type t, Vars&& vars) - : fType(t), fColor(Color::Black()), - fVars(vars.empty() ? nullptr : std::make_shared(std::move(vars))) { + + SVGColor(Type t, Vars&& vars) + : _type(t), _color(Color::Black()), + _vars(vars.empty() ? nullptr : std::make_shared(std::move(vars))) { } - explicit SVGColor(const SVGColorType& c, Vars&& vars) - : fType(Type::kColor), fColor(c), - fVars(vars.empty() ? nullptr : std::make_shared(std::move(vars))) { + + SVGColor(const SVGColorType& c, Vars&& vars) + : _type(Type::Color), _color(c), + _vars(vars.empty() ? nullptr : std::make_shared(std::move(vars))) { } SVGColor(const SVGColor&) = default; @@ -235,48 +247,51 @@ class SVGColor { SVGColor& operator=(SVGColor&&) = default; bool operator==(const SVGColor& other) const { - return fType == other.fType && fColor == other.fColor && fVars == other.fVars; + return _type == other._type && _color == other._color && _vars == other._vars; } + bool operator!=(const SVGColor& other) const { return !(*this == other); } Type type() const { - return fType; + return _type; } + const SVGColorType& color() const { - return fColor; + return _color; } - const std::shared_ptr vars() const { - return fVars ? fVars : nullptr; + + std::shared_ptr vars() const { + return _vars ? _vars : nullptr; } std::shared_ptr vars() { - return fVars ? fVars : nullptr; + return _vars ? _vars : nullptr; } private: - Type fType; - SVGColorType fColor; - std::shared_ptr fVars; + Type _type; + SVGColorType _color; + std::shared_ptr _vars; }; class SVGPaint { public: enum class Type { - kNone, - kColor, - kIRI, + None, + Color, + IRI, }; - SVGPaint() : fType(Type::kNone), fColor(Color::Black()) { + SVGPaint() : _type(Type::None), _color(Color::Black()) { } - explicit SVGPaint(Type t) : fType(t), fColor(Color::Black()) { + explicit SVGPaint(Type t) : _type(t), _color(Color::Black()) { } - explicit SVGPaint(SVGColor c) : fType(Type::kColor), fColor(std::move(c)) { + explicit SVGPaint(SVGColor c) : _type(Type::Color), _color(std::move(c)) { } SVGPaint(SVGIRI iri, SVGColor fallback_color) - : fType(Type::kIRI), fColor(std::move(fallback_color)), fIRI(std::move(iri)) { + : _type(Type::IRI), _color(std::move(fallback_color)), _iri(std::move(iri)) { } SVGPaint(const SVGPaint&) = default; @@ -285,237 +300,239 @@ class SVGPaint { SVGPaint& operator=(SVGPaint&&) = default; bool operator==(const SVGPaint& other) const { - return fType == other.fType && fColor == other.fColor && fIRI == other.fIRI; + return _type == other._type && _color == other._color && _iri == other._iri; } bool operator!=(const SVGPaint& other) const { return !(*this == other); } Type type() const { - return fType; + return _type; } const SVGColor& color() const { - return fColor; + return _color; } const SVGIRI& iri() const { - return fIRI; + return _iri; } private: - Type fType; - - // Logical union. - SVGColor fColor; - SVGIRI fIRI; + Type _type; + SVGColor _color; + SVGIRI _iri; }; // | none (used for clip/mask/filter properties) class SVGFuncIRI { public: enum class Type { - kNone, - kIRI, + None, + IRI, }; - SVGFuncIRI() : fType(Type::kNone) { + SVGFuncIRI() : _type(Type::None) { } - explicit SVGFuncIRI(Type t) : fType(t) { + + explicit SVGFuncIRI(Type t) : _type(t) { } - explicit SVGFuncIRI(SVGIRI&& iri) : fType(Type::kIRI), fIRI(std::move(iri)) { + + explicit SVGFuncIRI(SVGIRI&& iri) : _type(Type::IRI), _iri(std::move(iri)) { } bool operator==(const SVGFuncIRI& other) const { - return fType == other.fType && fIRI == other.fIRI; + return _type == other._type && _iri == other._iri; } + bool operator!=(const SVGFuncIRI& other) const { return !(*this == other); } Type type() const { - return fType; + return _type; } + const SVGIRI& iri() const { - return fIRI; + return _iri; } private: - Type fType; - SVGIRI fIRI; + Type _type; + SVGIRI _iri; }; enum class SVGLineCap { - kButt, - kRound, - kSquare, + Butt, + Round, + Square, }; class SVGLineJoin { public: enum class Type { - kMiter, - kRound, - kBevel, - kInherit, + Miter, + Round, + Bevel, + Inherit, }; - constexpr SVGLineJoin() : fType(Type::kInherit) { + constexpr SVGLineJoin() : _type(Type::Inherit) { } - constexpr explicit SVGLineJoin(Type t) : fType(t) { + constexpr explicit SVGLineJoin(Type t) : _type(t) { } SVGLineJoin(const SVGLineJoin&) = default; SVGLineJoin& operator=(const SVGLineJoin&) = default; bool operator==(const SVGLineJoin& other) const { - return fType == other.fType; + return _type == other._type; } bool operator!=(const SVGLineJoin& other) const { return !(*this == other); } Type type() const { - return fType; + return _type; } private: - Type fType; + Type _type; }; class SVGSpreadMethod { public: enum class Type { - kPad, - kRepeat, - kReflect, + Pad, + Repeat, + Reflect, }; - constexpr SVGSpreadMethod() : fType(Type::kPad) { + constexpr SVGSpreadMethod() : _type(Type::Pad) { } - constexpr explicit SVGSpreadMethod(Type t) : fType(t) { + constexpr explicit SVGSpreadMethod(Type t) : _type(t) { } SVGSpreadMethod(const SVGSpreadMethod&) = default; SVGSpreadMethod& operator=(const SVGSpreadMethod&) = default; bool operator==(const SVGSpreadMethod& other) const { - return fType == other.fType; + return _type == other._type; } bool operator!=(const SVGSpreadMethod& other) const { return !(*this == other); } Type type() const { - return fType; + return _type; } private: - Type fType; + Type _type; }; class SVGFillRule { public: enum class Type { - kNonZero, - kEvenOdd, - kInherit, + NonZero, + EvenOdd, + Inherit, }; - constexpr SVGFillRule() : fType(Type::kInherit) { + constexpr SVGFillRule() : _type(Type::Inherit) { } - constexpr explicit SVGFillRule(Type t) : fType(t) { + constexpr explicit SVGFillRule(Type t) : _type(t) { } SVGFillRule(const SVGFillRule&) = default; SVGFillRule& operator=(const SVGFillRule&) = default; bool operator==(const SVGFillRule& other) const { - return fType == other.fType; + return _type == other._type; } bool operator!=(const SVGFillRule& other) const { return !(*this == other); } Type type() const { - return fType; + return _type; } PathFillType asFillType() const { - return fType == Type::kEvenOdd ? PathFillType::EvenOdd : PathFillType::Winding; + return _type == Type::EvenOdd ? PathFillType::EvenOdd : PathFillType::Winding; } private: - Type fType; + Type _type; }; class SVGVisibility { public: enum class Type { - kVisible, - kHidden, - kCollapse, - kInherit, + Visible, + Hidden, + Collapse, + Inherit, }; - constexpr SVGVisibility() : fType(Type::kVisible) { + constexpr SVGVisibility() : _type(Type::Visible) { } - constexpr explicit SVGVisibility(Type t) : fType(t) { + constexpr explicit SVGVisibility(Type t) : _type(t) { } SVGVisibility(const SVGVisibility&) = default; SVGVisibility& operator=(const SVGVisibility&) = default; bool operator==(const SVGVisibility& other) const { - return fType == other.fType; + return _type == other._type; } bool operator!=(const SVGVisibility& other) const { return !(*this == other); } Type type() const { - return fType; + return _type; } private: - Type fType; + Type _type; }; class SVGDashArray { public: enum class Type { - kNone, - kDashArray, - kInherit, + None, + DashArray, + Inherit, }; - SVGDashArray() : fType(Type::kNone) { + SVGDashArray() : _type(Type::None) { } - explicit SVGDashArray(Type t) : fType(t) { + explicit SVGDashArray(Type t) : _type(t) { } explicit SVGDashArray(std::vector&& dashArray) - : fType(Type::kDashArray), fDashArray(std::move(dashArray)) { + : _type(Type::DashArray), _dashArray(std::move(dashArray)) { } SVGDashArray(const SVGDashArray&) = default; SVGDashArray& operator=(const SVGDashArray&) = default; bool operator==(const SVGDashArray& other) const { - return fType == other.fType && fDashArray == other.fDashArray; + return _type == other._type && _dashArray == other._dashArray; } bool operator!=(const SVGDashArray& other) const { return !(*this == other); } Type type() const { - return fType; + return _type; } const std::vector& dashArray() const { - return fDashArray; + return _dashArray; } private: - Type fType; - std::vector fDashArray; + Type _type; + std::vector _dashArray; }; class SVGStopColor { @@ -527,218 +544,218 @@ class SVGStopColor { kInherit, }; - SVGStopColor() : fType(Type::kColor), fColor(Color::Black()) { + SVGStopColor() : _type(Type::kColor), _color(Color::Black()) { } - explicit SVGStopColor(Type t) : fType(t), fColor(Color::Black()) { + explicit SVGStopColor(Type t) : _type(t), _color(Color::Black()) { } - explicit SVGStopColor(const SVGColorType& c) : fType(Type::kColor), fColor(c) { + explicit SVGStopColor(const SVGColorType& c) : _type(Type::kColor), _color(c) { } SVGStopColor(const SVGStopColor&) = default; SVGStopColor& operator=(const SVGStopColor&) = default; bool operator==(const SVGStopColor& other) const { - return fType == other.fType && fColor == other.fColor; + return _type == other._type && _color == other._color; } bool operator!=(const SVGStopColor& other) const { return !(*this == other); } Type type() const { - return fType; + return _type; } const SVGColorType& color() const { - return fColor; + return _color; } private: - Type fType; - SVGColorType fColor; + Type _type; + SVGColorType _color; }; class SVGObjectBoundingBoxUnits { public: enum class Type { - kUserSpaceOnUse, - kObjectBoundingBox, + UserSpaceOnUse, + ObjectBoundingBox, }; - SVGObjectBoundingBoxUnits() : fType(Type::kUserSpaceOnUse) { + SVGObjectBoundingBoxUnits() : _type(Type::UserSpaceOnUse) { } - explicit SVGObjectBoundingBoxUnits(Type t) : fType(t) { + explicit SVGObjectBoundingBoxUnits(Type t) : _type(t) { } bool operator==(const SVGObjectBoundingBoxUnits& other) const { - return fType == other.fType; + return _type == other._type; } bool operator!=(const SVGObjectBoundingBoxUnits& other) const { return !(*this == other); } Type type() const { - return fType; + return _type; } private: - Type fType; + Type _type; }; class SVGFontFamily { public: enum class Type { - kFamily, - kInherit, + Family, + Inherit, }; - SVGFontFamily() : fType(Type::kInherit) { + SVGFontFamily() : _type(Type::Inherit) { } - explicit SVGFontFamily(const char family[]) : fType(Type::kFamily), fFamily(family) { + explicit SVGFontFamily(std::string family) : _type(Type::Family), _family(std::move(family)) { } bool operator==(const SVGFontFamily& other) const { - return fType == other.fType && fFamily == other.fFamily; + return _type == other._type && _family == other._family; } bool operator!=(const SVGFontFamily& other) const { return !(*this == other); } Type type() const { - return fType; + return _type; } const std::string& family() const { - return fFamily; + return _family; } private: - Type fType; - std::string fFamily; + Type _type; + std::string _family; }; class SVGFontStyle { public: enum class Type { - kNormal, - kItalic, - kOblique, - kInherit, + Normal, + Italic, + Oblique, + Inherit, }; - SVGFontStyle() : fType(Type::kInherit) { + SVGFontStyle() : _type(Type::Inherit) { } - explicit SVGFontStyle(Type t) : fType(t) { + explicit SVGFontStyle(Type t) : _type(t) { } bool operator==(const SVGFontStyle& other) const { - return fType == other.fType; + return _type == other._type; } bool operator!=(const SVGFontStyle& other) const { return !(*this == other); } Type type() const { - return fType; + return _type; } private: - Type fType; + Type _type; }; class SVGFontSize { public: enum class Type { - kLength, - kInherit, + Length, + Inherit, }; - SVGFontSize() : fType(Type::kInherit), fSize(0) { + SVGFontSize() : _type(Type::Inherit), _size(0) { } - explicit SVGFontSize(const SVGLength& s) : fType(Type::kLength), fSize(s) { + explicit SVGFontSize(const SVGLength& s) : _type(Type::Length), _size(s) { } bool operator==(const SVGFontSize& other) const { - return fType == other.fType && fSize == other.fSize; + return _type == other._type && _size == other._size; } bool operator!=(const SVGFontSize& other) const { return !(*this == other); } Type type() const { - return fType; + return _type; } const SVGLength& size() const { - return fSize; + return _size; } private: - Type fType; - SVGLength fSize; + Type _type; + SVGLength _size; }; class SVGFontWeight { public: enum class Type { - k100, - k200, - k300, - k400, - k500, - k600, - k700, - k800, - k900, - kNormal, - kBold, - kBolder, - kLighter, - kInherit, + W100, + W200, + W300, + W400, + W500, + W600, + W700, + W800, + W900, + Normal, + Bold, + Bolder, + Lighter, + Inherit, }; - SVGFontWeight() : fType(Type::kInherit) { + SVGFontWeight() : _type(Type::Inherit) { } - explicit SVGFontWeight(Type t) : fType(t) { + explicit SVGFontWeight(Type t) : _type(t) { } bool operator==(const SVGFontWeight& other) const { - return fType == other.fType; + return _type == other._type; } bool operator!=(const SVGFontWeight& other) const { return !(*this == other); } Type type() const { - return fType; + return _type; } private: - Type fType; + Type _type; }; struct SVGPreserveAspectRatio { enum Align : uint8_t { // These values are chosen such that bits [0,1] encode X alignment, and // bits [2,3] encode Y alignment. - kXMinYMin = 0x00, - kXMidYMin = 0x01, - kXMaxYMin = 0x02, - kXMinYMid = 0x04, - kXMidYMid = 0x05, - kXMaxYMid = 0x06, - kXMinYMax = 0x08, - kXMidYMax = 0x09, - kXMaxYMax = 0x0a, - - kNone = 0x10, + XMinYMin = 0x00, + XMidYMin = 0x01, + XMaxYMin = 0x02, + XMinYMid = 0x04, + XMidYMid = 0x05, + XMaxYMid = 0x06, + XMinYMax = 0x08, + XMidYMax = 0x09, + XMaxYMax = 0x0a, + + None = 0x10, }; enum Scale { - kMeet, - kSlice, + Meet, + Slice, }; - Align fAlign = kXMidYMid; - Scale fScale = kMeet; + Align align = XMidYMid; + Scale scale = Meet; }; class SVGTextAnchor { diff --git a/include/tgfx/svg/SVGValue.h b/include/tgfx/svg/SVGValue.h index 67d2b764..794fa246 100644 --- a/include/tgfx/svg/SVGValue.h +++ b/include/tgfx/svg/SVGValue.h @@ -73,11 +73,11 @@ class SVGWrapperValue final : public SVGValue { return &_wrappedValue; } - private: // Stack-only void* operator new(size_t) = delete; void* operator new(size_t, void*) = delete; + private: const T& _wrappedValue; using INHERITED = SVGValue; diff --git a/include/tgfx/svg/node/SVGClipPath.h b/include/tgfx/svg/node/SVGClipPath.h index b973af6b..915571c5 100644 --- a/include/tgfx/svg/node/SVGClipPath.h +++ b/include/tgfx/svg/node/SVGClipPath.h @@ -35,7 +35,7 @@ class SVGClipPath final : public SVGHiddenContainer { } SVG_ATTR(ClipPathUnits, SVGObjectBoundingBoxUnits, - SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse)) + SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::UserSpaceOnUse)) private: friend class SVGRenderContext; diff --git a/include/tgfx/svg/node/SVGFilter.h b/include/tgfx/svg/node/SVGFilter.h index 917f1385..d63a1f33 100644 --- a/include/tgfx/svg/node/SVGFilter.h +++ b/include/tgfx/svg/node/SVGFilter.h @@ -37,14 +37,14 @@ class SkSVGFilter final : public SVGHiddenContainer { std::shared_ptr buildFilterDAG(const SVGRenderContext&) const; - SVG_ATTR(X, SVGLength, SVGLength(-10, SVGLength::Unit::kPercentage)) - SVG_ATTR(Y, SVGLength, SVGLength(-10, SVGLength::Unit::kPercentage)) - SVG_ATTR(Width, SVGLength, SVGLength(120, SVGLength::Unit::kPercentage)) - SVG_ATTR(Height, SVGLength, SVGLength(120, SVGLength::Unit::kPercentage)) + SVG_ATTR(X, SVGLength, SVGLength(-10, SVGLength::Unit::Percentage)) + SVG_ATTR(Y, SVGLength, SVGLength(-10, SVGLength::Unit::Percentage)) + SVG_ATTR(Width, SVGLength, SVGLength(120, SVGLength::Unit::Percentage)) + SVG_ATTR(Height, SVGLength, SVGLength(120, SVGLength::Unit::Percentage)) SVG_ATTR(FilterUnits, SVGObjectBoundingBoxUnits, - SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox)) + SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox)) SVG_ATTR(PrimitiveUnits, SVGObjectBoundingBoxUnits, - SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse)) + SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::UserSpaceOnUse)) private: SkSVGFilter() : INHERITED(SVGTag::Filter) { diff --git a/include/tgfx/svg/node/SVGGradient.h b/include/tgfx/svg/node/SVGGradient.h index e9ef7d8e..e1f3a26c 100644 --- a/include/tgfx/svg/node/SVGGradient.h +++ b/include/tgfx/svg/node/SVGGradient.h @@ -34,9 +34,9 @@ class SkSVGGradient : public SVGHiddenContainer { public: SVG_ATTR(Href, SVGIRI, SVGIRI()) SVG_ATTR(GradientTransform, SVGTransformType, SVGTransformType(Matrix::I())) - SVG_ATTR(SpreadMethod, SVGSpreadMethod, SVGSpreadMethod(SVGSpreadMethod::Type::kPad)) + SVG_ATTR(SpreadMethod, SVGSpreadMethod, SVGSpreadMethod(SVGSpreadMethod::Type::Pad)) SVG_ATTR(GradientUnits, SVGObjectBoundingBoxUnits, - SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox)) + SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox)) protected: explicit SkSVGGradient(SVGTag t) : INHERITED(t) { diff --git a/include/tgfx/svg/node/SVGLinearGradient.h b/include/tgfx/svg/node/SVGLinearGradient.h index 70b3e20e..0272d2f1 100644 --- a/include/tgfx/svg/node/SVGLinearGradient.h +++ b/include/tgfx/svg/node/SVGLinearGradient.h @@ -32,10 +32,10 @@ class SkSVGLinearGradient final : public SkSVGGradient { return std::shared_ptr(new SkSVGLinearGradient()); } - SVG_ATTR(X1, SVGLength, SVGLength(0, SVGLength::Unit::kPercentage)) - SVG_ATTR(Y1, SVGLength, SVGLength(0, SVGLength::Unit::kPercentage)) - SVG_ATTR(X2, SVGLength, SVGLength(100, SVGLength::Unit::kPercentage)) - SVG_ATTR(Y2, SVGLength, SVGLength(0, SVGLength::Unit::kPercentage)) + SVG_ATTR(X1, SVGLength, SVGLength(0, SVGLength::Unit::Percentage)) + SVG_ATTR(Y1, SVGLength, SVGLength(0, SVGLength::Unit::Percentage)) + SVG_ATTR(X2, SVGLength, SVGLength(100, SVGLength::Unit::Percentage)) + SVG_ATTR(Y2, SVGLength, SVGLength(0, SVGLength::Unit::Percentage)) protected: bool parseAndSetAttribute(const char*, const char*) override; diff --git a/include/tgfx/svg/node/SVGMask.h b/include/tgfx/svg/node/SVGMask.h index fb9625d5..9b885a01 100644 --- a/include/tgfx/svg/node/SVGMask.h +++ b/include/tgfx/svg/node/SVGMask.h @@ -37,9 +37,9 @@ class SkSVGMask final : public SVGHiddenContainer { SVG_OPTIONAL_ATTR(Height, SVGLength) SVG_ATTR(MaskUnits, SVGObjectBoundingBoxUnits, - SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox)) + SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox)) SVG_ATTR(MaskContentUnits, SVGObjectBoundingBoxUnits, - SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse)) + SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::UserSpaceOnUse)) private: friend class SVGRenderContext; diff --git a/include/tgfx/svg/node/SVGPattern.h b/include/tgfx/svg/node/SVGPattern.h index 1dced3e8..1bf92246 100644 --- a/include/tgfx/svg/node/SVGPattern.h +++ b/include/tgfx/svg/node/SVGPattern.h @@ -40,9 +40,9 @@ class SkSVGPattern final : public SVGHiddenContainer { SVG_OPTIONAL_ATTR(Height, SVGLength) SVG_OPTIONAL_ATTR(PatternTransform, SVGTransformType) SVG_ATTR(PatternUnits, SVGObjectBoundingBoxUnits, - SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox)) + SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox)) SVG_ATTR(ContentUnits, SVGObjectBoundingBoxUnits, - SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse)) + SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::UserSpaceOnUse)) protected: SkSVGPattern(); diff --git a/include/tgfx/svg/node/SVGRadialGradient.h b/include/tgfx/svg/node/SVGRadialGradient.h index 56a19c90..56331722 100644 --- a/include/tgfx/svg/node/SVGRadialGradient.h +++ b/include/tgfx/svg/node/SVGRadialGradient.h @@ -33,9 +33,9 @@ class SkSVGRadialGradient final : public SkSVGGradient { return std::shared_ptr(new SkSVGRadialGradient()); } - SVG_ATTR(Cx, SVGLength, SVGLength(50, SVGLength::Unit::kPercentage)) - SVG_ATTR(Cy, SVGLength, SVGLength(50, SVGLength::Unit::kPercentage)) - SVG_ATTR(R, SVGLength, SVGLength(50, SVGLength::Unit::kPercentage)) + SVG_ATTR(Cx, SVGLength, SVGLength(50, SVGLength::Unit::Percentage)) + SVG_ATTR(Cy, SVGLength, SVGLength(50, SVGLength::Unit::Percentage)) + SVG_ATTR(R, SVGLength, SVGLength(50, SVGLength::Unit::Percentage)) SVG_OPTIONAL_ATTR(Fx, SVGLength) SVG_OPTIONAL_ATTR(Fy, SVGLength) diff --git a/include/tgfx/svg/node/SVGSVG.h b/include/tgfx/svg/node/SVGSVG.h index 0909515e..ddc5fe98 100644 --- a/include/tgfx/svg/node/SVGSVG.h +++ b/include/tgfx/svg/node/SVGSVG.h @@ -39,8 +39,8 @@ class SVGSVG : public SkSVGContainer { SVG_ATTR(X, SVGLength, SVGLength(0)) SVG_ATTR(Y, SVGLength, SVGLength(0)) - SVG_ATTR(Width, SVGLength, SVGLength(100, SVGLength::Unit::kPercentage)) - SVG_ATTR(Height, SVGLength, SVGLength(100, SVGLength::Unit::kPercentage)) + SVG_ATTR(Width, SVGLength, SVGLength(100, SVGLength::Unit::Percentage)) + SVG_ATTR(Height, SVGLength, SVGLength(100, SVGLength::Unit::Percentage)) SVG_ATTR(PreserveAspectRatio, SVGPreserveAspectRatio, SVGPreserveAspectRatio()) SVG_OPTIONAL_ATTR(ViewBox, SVGViewBoxType) diff --git a/include/tgfx/svg/node/SVGStop.h b/include/tgfx/svg/node/SVGStop.h index e38d5daa..e533a4d1 100644 --- a/include/tgfx/svg/node/SVGStop.h +++ b/include/tgfx/svg/node/SVGStop.h @@ -33,7 +33,7 @@ class SkSVGStop : public SVGHiddenContainer { return std::shared_ptr(new SkSVGStop()); } - SVG_ATTR(Offset, SVGLength, SVGLength(0, SVGLength::Unit::kPercentage)) + SVG_ATTR(Offset, SVGLength, SVGLength(0, SVGLength::Unit::Percentage)) protected: bool parseAndSetAttribute(const char*, const char*) override; diff --git a/src/svg/SVGAttribute.cpp b/src/svg/SVGAttribute.cpp index 55a2d5fa..98702821 100644 --- a/src/svg/SVGAttribute.cpp +++ b/src/svg/SVGAttribute.cpp @@ -25,28 +25,28 @@ SVGPresentationAttributes SVGPresentationAttributes::MakeInitial() { result.fFill.set(SVGPaint(SVGColor(Color::Black()))); result.fFillOpacity.set(static_cast(1)); - result.fFillRule.set(SVGFillRule(SVGFillRule::Type::kNonZero)); - result.fClipRule.set(SVGFillRule(SVGFillRule::Type::kNonZero)); + result.fFillRule.set(SVGFillRule(SVGFillRule::Type::NonZero)); + result.fClipRule.set(SVGFillRule(SVGFillRule::Type::NonZero)); - result.fStroke.set(SVGPaint(SVGPaint::Type::kNone)); - result.fStrokeDashArray.set(SVGDashArray(SVGDashArray::Type::kNone)); + result.fStroke.set(SVGPaint(SVGPaint::Type::None)); + result.fStrokeDashArray.set(SVGDashArray(SVGDashArray::Type::None)); result.fStrokeDashOffset.set(SVGLength(0)); - result.fStrokeLineCap.set(SVGLineCap::kButt); - result.fStrokeLineJoin.set(SVGLineJoin(SVGLineJoin::Type::kMiter)); + result.fStrokeLineCap.set(SVGLineCap::Butt); + result.fStrokeLineJoin.set(SVGLineJoin(SVGLineJoin::Type::Miter)); result.fStrokeMiterLimit.set(static_cast(4)); result.fStrokeOpacity.set(static_cast(1)); result.fStrokeWidth.set(SVGLength(1)); - result.fVisibility.set(SVGVisibility(SVGVisibility::Type::kVisible)); + result.fVisibility.set(SVGVisibility(SVGVisibility::Type::Visible)); result.fColor.set(SVGColorType(Color::Black())); result.fColorInterpolation.set(SVGColorspace::kSRGB); result.fColorInterpolationFilters.set(SVGColorspace::kLinearRGB); result.fFontFamily.init("default"); - result.fFontStyle.init(SVGFontStyle::Type::kNormal); + result.fFontStyle.init(SVGFontStyle::Type::Normal); result.fFontSize.init(SVGLength(24)); - result.fFontWeight.init(SVGFontWeight::Type::kNormal); + result.fFontWeight.init(SVGFontWeight::Type::Normal); result.fTextAnchor.init(SVGTextAnchor::Type::kStart); result.fDisplay.init(SVGDisplay::kInline); diff --git a/src/svg/SVGAttributeParser.cpp b/src/svg/SVGAttributeParser.cpp index d8f67038..1d0961c9 100644 --- a/src/svg/SVGAttributeParser.cpp +++ b/src/svg/SVGAttributeParser.cpp @@ -241,11 +241,11 @@ bool SVGAttributeParser::parseLengthUnitToken(SVGLength::Unit& unit) { }; static const std::array unitInfo = { - Unit("%", SVGLength::Unit::kPercentage), Unit("em", SVGLength::Unit::kEMS), - Unit("ex", SVGLength::Unit::kEXS), Unit("px", SVGLength::Unit::kPX), - Unit("cm", SVGLength::Unit::kCM), Unit("mm", SVGLength::Unit::kMM), - Unit("in", SVGLength::Unit::kIN), Unit("pt", SVGLength::Unit::kPT), - Unit("pc", SVGLength::Unit::kPC), + Unit("%", SVGLength::Unit::Percentage), Unit("em", SVGLength::Unit::EMS), + Unit("ex", SVGLength::Unit::EXS), Unit("px", SVGLength::Unit::PX), + Unit("cm", SVGLength::Unit::CM), Unit("mm", SVGLength::Unit::MM), + Unit("in", SVGLength::Unit::IN), Unit("pt", SVGLength::Unit::PT), + Unit("pc", SVGLength::Unit::PC), }; return std::any_of(unitInfo.begin(), unitInfo.end(), [&](const Unit& item) { @@ -424,7 +424,7 @@ bool SVGAttributeParser::parseSVGColor(SVGColor* color, SVGColor::Vars&& vars) { return true; } if (this->parseExpectedStringToken("currentColor")) { - *color = SVGColor(SVGColor::Type::kCurrentColor, std::move(vars)); + *color = SVGColor(SVGColor::Type::CurrentColor, std::move(vars)); return true; } // https://drafts.csswg.org/css-variables/#using-variables @@ -475,11 +475,11 @@ bool SVGAttributeParser::parse(SVGIRI* iri) { SVGIRI::Type iriType; if (this->parseExpectedStringToken("#")) { - iriType = SVGIRI::Type::kLocal; + iriType = SVGIRI::Type::Local; } else if (this->matchStringToken("data:")) { - iriType = SVGIRI::Type::kDataURI; + iriType = SVGIRI::Type::DataURI; } else { - iriType = SVGIRI::Type::kNonlocal; + iriType = SVGIRI::Type::Nonlocal; } const auto* start = fCurPos; @@ -555,7 +555,7 @@ bool SVGAttributeParser::parseInteger(SVGIntegerType* number) { template <> bool SVGAttributeParser::parse(SVGLength* length) { float s; - SVGLength::Unit u = SVGLength::Unit::kNumber; + SVGLength::Unit u = SVGLength::Unit::Number; if (this->parseScalarToken(&s) && (this->parseLengthUnitToken(u) || this->parseSepToken() || this->parseEOSToken())) { @@ -763,7 +763,7 @@ bool SVGAttributeParser::parse(SVGPaint* paint) { *paint = SVGPaint(std::move(c)); parsedValue = true; } else if (this->parseExpectedStringToken("none")) { - *paint = SVGPaint(SVGPaint::Type::kNone); + *paint = SVGPaint(SVGPaint::Type::None); parsedValue = true; } else if (this->parseFuncIRI(&iri)) { // optional fallback color @@ -801,9 +801,9 @@ bool SVGAttributeParser::parse(SVGLineCap* cap) { SVGLineCap fType; const char* fName; } gCapInfo[] = { - {SVGLineCap::kButt, "butt"}, - {SVGLineCap::kRound, "round"}, - {SVGLineCap::kSquare, "square"}, + {SVGLineCap::Butt, "butt"}, + {SVGLineCap::Round, "round"}, + {SVGLineCap::Square, "square"}, }; bool parsedValue = false; @@ -825,10 +825,10 @@ bool SVGAttributeParser::parse(SVGLineJoin* join) { SVGLineJoin::Type fType; const char* fName; } gJoinInfo[] = { - {SVGLineJoin::Type::kMiter, "miter"}, - {SVGLineJoin::Type::kRound, "round"}, - {SVGLineJoin::Type::kBevel, "bevel"}, - {SVGLineJoin::Type::kInherit, "inherit"}, + {SVGLineJoin::Type::Miter, "miter"}, + {SVGLineJoin::Type::Round, "round"}, + {SVGLineJoin::Type::Bevel, "bevel"}, + {SVGLineJoin::Type::Inherit, "inherit"}, }; bool parsedValue = false; @@ -849,11 +849,11 @@ bool SVGAttributeParser::parse(SVGObjectBoundingBoxUnits* objectBoundingBoxUnits bool parsedValue = false; if (this->parseExpectedStringToken("userSpaceOnUse")) { *objectBoundingBoxUnits = - SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse); + SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::UserSpaceOnUse); parsedValue = true; } else if (this->parseExpectedStringToken("objectBoundingBox")) { *objectBoundingBoxUnits = - SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox); + SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox); parsedValue = true; } return parsedValue && this->parseEOSToken(); @@ -915,15 +915,15 @@ bool SVGAttributeParser::parse(SVGFillRule* fillRule) { SVGFillRule::Type fType; const char* fName; } gFillRuleInfo[] = { - {SVGFillRule::Type::kNonZero, "nonzero"}, - {SVGFillRule::Type::kEvenOdd, "evenodd"}, - {SVGFillRule::Type::kInherit, "inherit"}, + {SVGFillRule::Type::NonZero, "nonzero"}, + {SVGFillRule::Type::EvenOdd, "evenodd"}, + {SVGFillRule::Type::Inherit, "inherit"}, }; bool parsedValue = false; - for (size_t i = 0; i < std::size(gFillRuleInfo); ++i) { - if (this->parseExpectedStringToken(gFillRuleInfo[i].fName)) { - *fillRule = SVGFillRule(gFillRuleInfo[i].fType); + for (auto i : gFillRuleInfo) { + if (this->parseExpectedStringToken(i.fName)) { + *fillRule = SVGFillRule(i.fType); parsedValue = true; break; } @@ -939,10 +939,10 @@ bool SVGAttributeParser::parse(SVGVisibility* visibility) { SVGVisibility::Type fType; const char* fName; } gVisibilityInfo[] = { - {SVGVisibility::Type::kVisible, "visible"}, - {SVGVisibility::Type::kHidden, "hidden"}, - {SVGVisibility::Type::kCollapse, "collapse"}, - {SVGVisibility::Type::kInherit, "inherit"}, + {SVGVisibility::Type::Visible, "visible"}, + {SVGVisibility::Type::Hidden, "hidden"}, + {SVGVisibility::Type::Collapse, "collapse"}, + {SVGVisibility::Type::Inherit, "inherit"}, }; bool parsedValue = false; @@ -962,10 +962,10 @@ template <> bool SVGAttributeParser::parse(SVGDashArray* dashArray) { bool parsedValue = false; if (this->parseExpectedStringToken("none")) { - *dashArray = SVGDashArray(SVGDashArray::Type::kNone); + *dashArray = SVGDashArray(SVGDashArray::Type::None); parsedValue = true; } else if (this->parseExpectedStringToken("inherit")) { - *dashArray = SVGDashArray(SVGDashArray::Type::kInherit); + *dashArray = SVGDashArray(SVGDashArray::Type::Inherit); parsedValue = true; } else { std::vector dashes; @@ -1031,10 +1031,10 @@ bool SVGAttributeParser::parse(SVGFontSize* size) { template <> bool SVGAttributeParser::parse(SVGFontStyle* style) { static constexpr std::tuple gStyleMap[] = { - {"normal", SVGFontStyle::Type::kNormal}, - {"italic", SVGFontStyle::Type::kItalic}, - {"oblique", SVGFontStyle::Type::kOblique}, - {"inherit", SVGFontStyle::Type::kInherit}, + {"normal", SVGFontStyle::Type::Normal}, + {"italic", SVGFontStyle::Type::Italic}, + {"oblique", SVGFontStyle::Type::Oblique}, + {"inherit", SVGFontStyle::Type::Inherit}, }; bool parsedValue = false; @@ -1052,13 +1052,13 @@ bool SVGAttributeParser::parse(SVGFontStyle* style) { template <> bool SVGAttributeParser::parse(SVGFontWeight* weight) { static constexpr std::tuple gWeightMap[] = { - {"normal", SVGFontWeight::Type::kNormal}, {"bold", SVGFontWeight::Type::kBold}, - {"bolder", SVGFontWeight::Type::kBolder}, {"lighter", SVGFontWeight::Type::kLighter}, - {"100", SVGFontWeight::Type::k100}, {"200", SVGFontWeight::Type::k200}, - {"300", SVGFontWeight::Type::k300}, {"400", SVGFontWeight::Type::k400}, - {"500", SVGFontWeight::Type::k500}, {"600", SVGFontWeight::Type::k600}, - {"700", SVGFontWeight::Type::k700}, {"800", SVGFontWeight::Type::k800}, - {"900", SVGFontWeight::Type::k900}, {"inherit", SVGFontWeight::Type::kInherit}, + {"normal", SVGFontWeight::Type::Normal}, {"bold", SVGFontWeight::Type::Bold}, + {"bolder", SVGFontWeight::Type::Bolder}, {"lighter", SVGFontWeight::Type::Lighter}, + {"100", SVGFontWeight::Type::W100}, {"200", SVGFontWeight::Type::W200}, + {"300", SVGFontWeight::Type::W300}, {"400", SVGFontWeight::Type::W400}, + {"500", SVGFontWeight::Type::W500}, {"600", SVGFontWeight::Type::W600}, + {"700", SVGFontWeight::Type::W700}, {"800", SVGFontWeight::Type::W800}, + {"900", SVGFontWeight::Type::W900}, {"inherit", SVGFontWeight::Type::Inherit}, }; bool parsedValue = false; @@ -1096,21 +1096,21 @@ bool SVGAttributeParser::parse(SVGTextAnchor* anchor) { // https://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute bool SVGAttributeParser::parsePreserveAspectRatio(SVGPreserveAspectRatio* par) { static constexpr std::tuple gAlignMap[] = { - {"none", SVGPreserveAspectRatio::kNone}, - {"xMinYMin", SVGPreserveAspectRatio::kXMinYMin}, - {"xMidYMin", SVGPreserveAspectRatio::kXMidYMin}, - {"xMaxYMin", SVGPreserveAspectRatio::kXMaxYMin}, - {"xMinYMid", SVGPreserveAspectRatio::kXMinYMid}, - {"xMidYMid", SVGPreserveAspectRatio::kXMidYMid}, - {"xMaxYMid", SVGPreserveAspectRatio::kXMaxYMid}, - {"xMinYMax", SVGPreserveAspectRatio::kXMinYMax}, - {"xMidYMax", SVGPreserveAspectRatio::kXMidYMax}, - {"xMaxYMax", SVGPreserveAspectRatio::kXMaxYMax}, + {"none", SVGPreserveAspectRatio::None}, + {"xMinYMin", SVGPreserveAspectRatio::XMinYMin}, + {"xMidYMin", SVGPreserveAspectRatio::XMidYMin}, + {"xMaxYMin", SVGPreserveAspectRatio::XMaxYMin}, + {"xMinYMid", SVGPreserveAspectRatio::XMinYMid}, + {"xMidYMid", SVGPreserveAspectRatio::XMidYMid}, + {"xMaxYMid", SVGPreserveAspectRatio::XMaxYMid}, + {"xMinYMax", SVGPreserveAspectRatio::XMinYMax}, + {"xMidYMax", SVGPreserveAspectRatio::XMidYMax}, + {"xMaxYMax", SVGPreserveAspectRatio::XMaxYMax}, }; static constexpr std::tuple gScaleMap[] = { - {"meet", SVGPreserveAspectRatio::kMeet}, - {"slice", SVGPreserveAspectRatio::kSlice}, + {"meet", SVGPreserveAspectRatio::Meet}, + {"slice", SVGPreserveAspectRatio::Slice}, }; bool parsedValue = false; @@ -1119,12 +1119,12 @@ bool SVGAttributeParser::parsePreserveAspectRatio(SVGPreserveAspectRatio* par) { this->parseExpectedStringToken("defer"); this->parseWSToken(); - if (this->parseEnumMap(gAlignMap, &par->fAlign)) { + if (this->parseEnumMap(gAlignMap, &par->align)) { parsedValue = true; // optional scaling selector this->parseWSToken(); - this->parseEnumMap(gScaleMap, &par->fScale); + this->parseEnumMap(gScaleMap, &par->scale); } return parsedValue && this->parseEOSToken(); diff --git a/src/svg/SVGDOM.cpp b/src/svg/SVGDOM.cpp index f8f29961..873607d5 100644 --- a/src/svg/SVGDOM.cpp +++ b/src/svg/SVGDOM.cpp @@ -495,7 +495,7 @@ void SVGDOM::renderNode(Canvas* canvas, SkSVGPresentationContext& pctx, const ch SVGLengthContext lctx(_containerSize); SVGRenderContext renderCtx(canvas->getSurface()->getContext(), canvas, _fontMgr, _nodeIDMapper, lctx, pctx, {nullptr, nullptr}, canvas->getMatrix()); - _root->renderNode(renderCtx, SVGIRI(SVGIRI::Type::kLocal, SVGStringType(id))); + _root->renderNode(renderCtx, SVGIRI(SVGIRI::Type::Local, SVGStringType(id))); } } diff --git a/src/svg/SVGRenderContext.cpp b/src/svg/SVGRenderContext.cpp index 1e11ce73..6354bf30 100644 --- a/src/svg/SVGRenderContext.cpp +++ b/src/svg/SVGRenderContext.cpp @@ -76,9 +76,9 @@ constexpr float kCMMultiplier = kMMMultiplier * 10; float SVGLengthContext::resolve(const SVGLength& l, LengthType t) const { switch (l.unit()) { - case SVGLength::Unit::kNumber: { + case SVGLength::Unit::Number: { if (_patternUnit.has_value()) { - if (_patternUnit.value().type() == SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox) { + if (_patternUnit.value().type() == SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox) { return l.value() * length_size_for_type(_viewport, t); } else { return l.value(); @@ -87,19 +87,19 @@ float SVGLengthContext::resolve(const SVGLength& l, LengthType t) const { return l.value(); } } - case SVGLength::Unit::kPX: + case SVGLength::Unit::PX: return l.value(); - case SVGLength::Unit::kPercentage: + case SVGLength::Unit::Percentage: return l.value() * length_size_for_type(_viewport, t) / 100; - case SVGLength::Unit::kCM: + case SVGLength::Unit::CM: return l.value() * _dpi * kCMMultiplier; - case SVGLength::Unit::kMM: + case SVGLength::Unit::MM: return l.value() * _dpi * kMMMultiplier; - case SVGLength::Unit::kIN: + case SVGLength::Unit::IN: return l.value() * _dpi * kINMultiplier; - case SVGLength::Unit::kPT: + case SVGLength::Unit::PT: return l.value() * _dpi * kPTMultiplier; - case SVGLength::Unit::kPC: + case SVGLength::Unit::PC: return l.value() * _dpi * kPCMultiplier; default: //unsupported unit type @@ -120,22 +120,22 @@ namespace { LineCap toSkCap(const SVGLineCap& cap) { switch (cap) { - case SVGLineCap::kButt: + case SVGLineCap::Butt: return LineCap::Butt; - case SVGLineCap::kRound: + case SVGLineCap::Round: return LineCap::Round; - case SVGLineCap::kSquare: + case SVGLineCap::Square: return LineCap::Square; } } LineJoin toSkJoin(const SVGLineJoin& join) { switch (join.type()) { - case SVGLineJoin::Type::kMiter: + case SVGLineJoin::Type::Miter: return LineJoin::Miter; - case SVGLineJoin::Type::kRound: + case SVGLineJoin::Type::Round: return LineJoin::Round; - case SVGLineJoin::Type::kBevel: + case SVGLineJoin::Type::Bevel: return LineJoin::Bevel; default: ASSERT(false); @@ -145,7 +145,7 @@ LineJoin toSkJoin(const SVGLineJoin& join) { std::unique_ptr dash_effect(const SVGPresentationAttributes& props, const SVGLengthContext& lctx) { - if (props.fStrokeDashArray->type() != SVGDashArray::Type::kDashArray) { + if (props.fStrokeDashArray->type() != SVGDashArray::Type::DashArray) { return nullptr; } @@ -240,7 +240,7 @@ SVGRenderContext SVGRenderContext::CopyForPaint(const SVGRenderContext& context, } std::shared_ptr SVGRenderContext::findNodeById(const SVGIRI& iri) const { - if (iri.type() != SVGIRI::Type::kLocal) { + if (iri.type() != SVGIRI::Type::Local) { return nullptr; } auto p = _nodeIDMapper.find(iri.iri())->second; @@ -309,8 +309,8 @@ void SVGRenderContext::applyPresentationAttributes(const SVGPresentationAttribut float SVGRenderContext::applyOpacity(float opacity, uint32_t flags, bool hasFilter) { opacity = std::clamp(opacity, 0.0f, 1.0f); const auto& props = _presentationContext->_inherited; - const bool hasFill = props.fFill->type() != SVGPaint::Type::kNone; - const bool hasStroke = props.fStroke->type() != SVGPaint::Type::kNone; + const bool hasFill = props.fFill->type() != SVGPaint::Type::None; + const bool hasStroke = props.fStroke->type() != SVGPaint::Type::None; // We can apply the opacity as paint alpha if it only affects one atomic draw. // For now, this means all of the following must be true: @@ -327,7 +327,7 @@ float SVGRenderContext::applyOpacity(float opacity, uint32_t flags, bool hasFilt } std::shared_ptr SVGRenderContext::applyFilter(const SVGFuncIRI& filter) { - if (filter.type() != SVGFuncIRI::Type::kIRI) { + if (filter.type() != SVGFuncIRI::Type::IRI) { return nullptr; } @@ -345,7 +345,7 @@ void SVGRenderContext::saveOnce() { } Path SVGRenderContext::applyClip(const SVGFuncIRI& clip) { - if (clip.type() != SVGFuncIRI::Type::kIRI) { + if (clip.type() != SVGFuncIRI::Type::IRI) { return Path(); } @@ -358,7 +358,7 @@ Path SVGRenderContext::applyClip(const SVGFuncIRI& clip) { } std::shared_ptr SVGRenderContext::applyMask(const SVGFuncIRI& mask) { - if (mask.type() != SVGFuncIRI::Type::kIRI) { + if (mask.type() != SVGFuncIRI::Type::IRI) { return nullptr; } @@ -444,16 +444,16 @@ std::shared_ptr SVGRenderContext::applyMask(const SVGFuncIRI& mask) std::optional SVGRenderContext::commonPaint(const SVGPaint& paint_selector, float paint_opacity) const { - if (paint_selector.type() == SVGPaint::Type::kNone) { + if (paint_selector.type() == SVGPaint::Type::None) { return std::optional(); } std::optional p = Paint(); switch (paint_selector.type()) { - case SVGPaint::Type::kColor: + case SVGPaint::Type::Color: p->setColor(this->resolveSvgColor(paint_selector.color())); break; - case SVGPaint::Type::kIRI: { + case SVGPaint::Type::IRI: { // Our property inheritance is borked as it follows the render path and not the tree // hierarchy. To avoid gross transgressions like leaf node presentation attributes // leaking into the paint server context, use a pristine presentation context when @@ -529,18 +529,18 @@ SVGColorType SVGRenderContext::resolveSvgColor(const SVGColor& color) const { } } switch (color.type()) { - case SVGColor::Type::kColor: + case SVGColor::Type::Color: return color.color(); - case SVGColor::Type::kCurrentColor: + case SVGColor::Type::CurrentColor: return *_presentationContext->_inherited.fColor; - case SVGColor::Type::kICCColor: + case SVGColor::Type::ICCColor: return Color::Black(); } } SVGRenderContext::OBBTransform SVGRenderContext::transformForCurrentOBB( SVGObjectBoundingBoxUnits u) const { - if (!_scope.node || u.type() == SVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse) { + if (!_scope.node || u.type() == SVGObjectBoundingBoxUnits::Type::UserSpaceOnUse) { return {{0, 0}, {1, 1}}; } ASSERT(_scope.context); @@ -553,7 +553,7 @@ Rect SVGRenderContext::resolveOBBRect(const SVGLength& x, const SVGLength& y, co const SVGLength& h, SVGObjectBoundingBoxUnits obbu) const { CopyOnWrite lctx(_lengthContext); - if (obbu.type() == SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox) { + if (obbu.type() == SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox) { *lctx.writable() = SVGLengthContext({1, 1}); } diff --git a/src/svg/node/SVGFe.cpp b/src/svg/node/SVGFe.cpp index 056d9aea..d45f3fe8 100644 --- a/src/svg/node/SVGFe.cpp +++ b/src/svg/node/SVGFe.cpp @@ -35,10 +35,10 @@ std::shared_ptr SkSVGFe::makeImageFilter( Rect SkSVGFe::resolveBoundaries(const SVGRenderContext& ctx, const SkSVGFilterContext& filterContext) const { - const auto x = fX.has_value() ? *fX : SVGLength(0, SVGLength::Unit::kPercentage); - const auto y = fY.has_value() ? *fY : SVGLength(0, SVGLength::Unit::kPercentage); - const auto w = fWidth.has_value() ? *fWidth : SVGLength(100, SVGLength::Unit::kPercentage); - const auto h = fHeight.has_value() ? *fHeight : SVGLength(100, SVGLength::Unit::kPercentage); + const auto x = fX.has_value() ? *fX : SVGLength(0, SVGLength::Unit::Percentage); + const auto y = fY.has_value() ? *fY : SVGLength(0, SVGLength::Unit::Percentage); + const auto w = fWidth.has_value() ? *fWidth : SVGLength(100, SVGLength::Unit::Percentage); + const auto h = fHeight.has_value() ? *fHeight : SVGLength(100, SVGLength::Unit::Percentage); return ctx.resolveOBBRect(x, y, w, h, filterContext.primitiveUnits()); } diff --git a/src/svg/node/SVGFeDisplacementMap.cpp b/src/svg/node/SVGFeDisplacementMap.cpp index 28302761..14f60691 100644 --- a/src/svg/node/SVGFeDisplacementMap.cpp +++ b/src/svg/node/SVGFeDisplacementMap.cpp @@ -49,10 +49,10 @@ std::shared_ptr SkSVGFeDisplacementMap::onMakeImageFilter( std::shared_ptr in2 = fctx.resolveInput(ctx, this->getIn2(), colorspace); float scale = fScale; - if (fctx.primitiveUnits().type() == SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox) { + if (fctx.primitiveUnits().type() == SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox) { const auto obbt = ctx.transformForCurrentOBB(fctx.primitiveUnits()); scale = SVGLengthContext({obbt.scale.x, obbt.scale.y}) - .resolve(SVGLength(scale, SVGLength::Unit::kPercentage), + .resolve(SVGLength(scale, SVGLength::Unit::Percentage), SVGLengthContext::LengthType::Other); } diff --git a/src/svg/node/SVGGradient.cpp b/src/svg/node/SVGGradient.cpp index 4d2c7d57..8e722174 100644 --- a/src/svg/node/SVGGradient.cpp +++ b/src/svg/node/SVGGradient.cpp @@ -86,11 +86,11 @@ bool SkSVGGradient::onAsPaint(const SVGRenderContext& ctx, Paint* paint) const { // * href attribute inheritance (not just color stops) // * objectBoundingBox units support - static_assert(static_cast(SVGSpreadMethod::Type::kPad) == TileMode::Clamp, + static_assert(static_cast(SVGSpreadMethod::Type::Pad) == TileMode::Clamp, "SkSVGSpreadMethod::Type is out of sync"); - static_assert(static_cast(SVGSpreadMethod::Type::kRepeat) == TileMode::Repeat, + static_assert(static_cast(SVGSpreadMethod::Type::Repeat) == TileMode::Repeat, "SkSVGSpreadMethod::Type is out of sync"); - static_assert(static_cast(SVGSpreadMethod::Type::kReflect) == TileMode::Mirror, + static_assert(static_cast(SVGSpreadMethod::Type::Reflect) == TileMode::Mirror, "SkSVGSpreadMethod::Type is out of sync"); const auto tileMode = static_cast(fSpreadMethod.type()); @@ -111,9 +111,9 @@ bool SVGAttributeParser::parse(SVGSpreadMethod* spread) { const char* fName; }; static const TileInfo spreadInfoSet[] = { - {SVGSpreadMethod::Type::kPad, "pad"}, - {SVGSpreadMethod::Type::kReflect, "reflect"}, - {SVGSpreadMethod::Type::kRepeat, "repeat"}, + {SVGSpreadMethod::Type::Pad, "pad"}, + {SVGSpreadMethod::Type::Reflect, "reflect"}, + {SVGSpreadMethod::Type::Repeat, "repeat"}, }; bool parsedValue = false; diff --git a/src/svg/node/SVGMask.cpp b/src/svg/node/SVGMask.cpp index 79859cbf..7bf6cd5e 100644 --- a/src/svg/node/SVGMask.cpp +++ b/src/svg/node/SVGMask.cpp @@ -47,8 +47,8 @@ Rect SkSVGMask::bounds(const SVGRenderContext& context) const { lengthContext.setPatternUnits(fMaskUnits); SVGRenderContext resolveContext(context, lengthContext); if (fWidth.has_value() && fHeight.has_value()) { - return resolveContext.resolveOBBRect(fX.value_or(SVGLength(0, SVGLength::Unit::kNumber)), - fY.value_or(SVGLength(0, SVGLength::Unit::kNumber)), + return resolveContext.resolveOBBRect(fX.value_or(SVGLength(0, SVGLength::Unit::Number)), + fY.value_or(SVGLength(0, SVGLength::Unit::Number)), fWidth.value(), fHeight.value(), fMaskUnits); } return Rect::MakeEmpty(); diff --git a/src/svg/node/SVGNode.cpp b/src/svg/node/SVGNode.cpp index 13b83845..269cf2d2 100644 --- a/src/svg/node/SVGNode.cpp +++ b/src/svg/node/SVGNode.cpp @@ -88,7 +88,7 @@ bool SVGNode::onPrepareToRender(SVGRenderContext* ctx) const { // ignore it. Eventually we should be able to add SkASSERT(display.isValue()). const auto visibility = ctx->presentationContext()._inherited.fVisibility->type(); const auto display = _presentationAttributes.fDisplay; // display is uninherited - return visibility != SVGVisibility::Type::kHidden && + return visibility != SVGVisibility::Type::Hidden && (!display.isValue() || *display != SVGDisplay::kNone); } #endif @@ -150,14 +150,13 @@ Matrix SVGNode::ComputeViewboxMatrix(const Rect& viewBox, const Rect& viewPort, const auto sx = viewPort.width() / viewBox.width(); const auto sy = viewPort.height() / viewBox.height(); - if (par.fAlign == SVGPreserveAspectRatio::kNone) { + if (par.align == SVGPreserveAspectRatio::None) { // none -> anisotropic scaling, regardless of fScale return {sx, sy}; } // isotropic scaling - const auto s = - par.fScale == SVGPreserveAspectRatio::kMeet ? std::min(sx, sy) : std::max(sx, sy); + const auto s = par.fScale == SVGPreserveAspectRatio::Meet ? std::min(sx, sy) : std::max(sx, sy); return {s, s}; }; @@ -168,8 +167,8 @@ Matrix SVGNode::ComputeViewboxMatrix(const Rect& viewBox, const Rect& viewPort, 1.0f // Max }; - const size_t x_coeff = par.fAlign >> 0 & 0x03; - const size_t y_coeff = par.fAlign >> 2 & 0x03; + const size_t x_coeff = par.align >> 0 & 0x03; + const size_t y_coeff = par.align >> 2 & 0x03; const auto tx = -viewBox.x() * scale.x; const auto ty = -viewBox.y() * scale.y; diff --git a/src/svg/node/SVGPattern.cpp b/src/svg/node/SVGPattern.cpp index d758b677..9ae102fc 100644 --- a/src/svg/node/SVGPattern.cpp +++ b/src/svg/node/SVGPattern.cpp @@ -111,19 +111,19 @@ const SkSVGPattern* SkSVGPattern::resolveHref(const SVGRenderContext& ctx, // To unify with Chrome and macOS preview, the width and height attributes here need to be // converted to percentages, direct numbers are not supported. - if (fPatternUnits.type() == SVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse) { - if (attrs->fWidth.has_value() && attrs->fWidth->unit() == SVGLength::Unit::kPercentage) { - attrs->fWidth = SVGLength(attrs->fWidth->value() / 100.f, SVGLength::Unit::kNumber); + if (fPatternUnits.type() == SVGObjectBoundingBoxUnits::Type::UserSpaceOnUse) { + if (attrs->fWidth.has_value() && attrs->fWidth->unit() == SVGLength::Unit::Percentage) { + attrs->fWidth = SVGLength(attrs->fWidth->value() / 100.f, SVGLength::Unit::Number); } - if (attrs->fHeight.has_value() && attrs->fHeight->unit() == SVGLength::Unit::kPercentage) { - attrs->fHeight = SVGLength(attrs->fHeight->value() / 100.f, SVGLength::Unit::kNumber); + if (attrs->fHeight.has_value() && attrs->fHeight->unit() == SVGLength::Unit::Percentage) { + attrs->fHeight = SVGLength(attrs->fHeight->value() / 100.f, SVGLength::Unit::Number); } - } else if (fPatternUnits.type() == SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox) { - if (attrs->fWidth.has_value() && attrs->fWidth->unit() == SVGLength::Unit::kNumber) { - attrs->fWidth = SVGLength(attrs->fWidth->value() * 100.f, SVGLength::Unit::kPercentage); + } else if (fPatternUnits.type() == SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox) { + if (attrs->fWidth.has_value() && attrs->fWidth->unit() == SVGLength::Unit::Number) { + attrs->fWidth = SVGLength(attrs->fWidth->value() * 100.f, SVGLength::Unit::Percentage); } - if (attrs->fHeight.has_value() && attrs->fHeight->unit() == SVGLength::Unit::kNumber) { - attrs->fHeight = SVGLength(attrs->fHeight->value() * 100.f, SVGLength::Unit::kPercentage); + if (attrs->fHeight.has_value() && attrs->fHeight->unit() == SVGLength::Unit::Number) { + attrs->fHeight = SVGLength(attrs->fHeight->value() * 100.f, SVGLength::Unit::Percentage); } } diff --git a/src/svg/node/SVGSVG.cpp b/src/svg/node/SVGSVG.cpp index d4e4a546..ce87ef73 100644 --- a/src/svg/node/SVGSVG.cpp +++ b/src/svg/node/SVGSVG.cpp @@ -79,8 +79,8 @@ bool SVGSVG::onPrepareToRender(SVGRenderContext* ctx) const { // https://www.w3.org/TR/SVG11/coords.html#IntrinsicSizing Size SVGSVG::intrinsicSize(const SVGLengthContext& lctx) const { // Percentage values do not provide an intrinsic size. - if (fWidth.unit() == SVGLength::Unit::kPercentage || - fHeight.unit() == SVGLength::Unit::kPercentage) { + if (fWidth.unit() == SVGLength::Unit::Percentage || + fHeight.unit() == SVGLength::Unit::Percentage) { return Size::Make(0, 0); } diff --git a/src/svg/node/SVGText.cpp b/src/svg/node/SVGText.cpp index 16cc2678..196f9da7 100644 --- a/src/svg/node/SVGText.cpp +++ b/src/svg/node/SVGText.cpp @@ -33,33 +33,33 @@ namespace { std::tuple ResolveFont(const SVGRenderContext& context) { auto weight = [](const SVGFontWeight& w) { switch (w.type()) { - case SVGFontWeight::Type::k100: + case SVGFontWeight::Type::W100: return FontStyle::Weight::kThin_Weight; - case SVGFontWeight::Type::k200: + case SVGFontWeight::Type::W200: return FontStyle::Weight::kExtraLight_Weight; - case SVGFontWeight::Type::k300: + case SVGFontWeight::Type::W300: return FontStyle::Weight::kLight_Weight; - case SVGFontWeight::Type::k400: + case SVGFontWeight::Type::W400: return FontStyle::Weight::kNormal_Weight; - case SVGFontWeight::Type::k500: + case SVGFontWeight::Type::W500: return FontStyle::Weight::kMedium_Weight; - case SVGFontWeight::Type::k600: + case SVGFontWeight::Type::W600: return FontStyle::Weight::kSemiBold_Weight; - case SVGFontWeight::Type::k700: + case SVGFontWeight::Type::W700: return FontStyle::Weight::kBold_Weight; - case SVGFontWeight::Type::k800: + case SVGFontWeight::Type::W800: return FontStyle::Weight::kExtraBold_Weight; - case SVGFontWeight::Type::k900: + case SVGFontWeight::Type::W900: return FontStyle::Weight::kBlack_Weight; - case SVGFontWeight::Type::kNormal: + case SVGFontWeight::Type::Normal: return FontStyle::Weight::kNormal_Weight; - case SVGFontWeight::Type::kBold: + case SVGFontWeight::Type::Bold: return FontStyle::Weight::kBold_Weight; - case SVGFontWeight::Type::kBolder: + case SVGFontWeight::Type::Bolder: return FontStyle::Weight::kExtraBold_Weight; - case SVGFontWeight::Type::kLighter: + case SVGFontWeight::Type::Lighter: return FontStyle::Weight::kLight_Weight; - case SVGFontWeight::Type::kInherit: { + case SVGFontWeight::Type::Inherit: { ASSERT(false); return FontStyle::Weight::kNormal_Weight; } @@ -68,13 +68,13 @@ std::tuple ResolveFont(const SVGRenderContext& context) { auto slant = [](const SVGFontStyle& style) { switch (style.type()) { - case SVGFontStyle::Type::kNormal: + case SVGFontStyle::Type::Normal: return FontStyle::Slant::kUpright_Slant; - case SVGFontStyle::Type::kItalic: + case SVGFontStyle::Type::Italic: return FontStyle::Slant::kItalic_Slant; - case SVGFontStyle::Type::kOblique: + case SVGFontStyle::Type::Oblique: return FontStyle::Slant::kOblique_Slant; - case SVGFontStyle::Type::kInherit: { + case SVGFontStyle::Type::Inherit: { ASSERT(false); return FontStyle::Slant::kUpright_Slant; } diff --git a/src/svg/node/SVGTransformableNode.cpp b/src/svg/node/SVGTransformableNode.cpp index 0bddf3b0..faa13223 100644 --- a/src/svg/node/SVGTransformableNode.cpp +++ b/src/svg/node/SVGTransformableNode.cpp @@ -36,7 +36,7 @@ bool SkSVGTransformableNode::onPrepareToRender(SVGRenderContext* ctx) const { auto transform = fTransform; if (auto unit = ctx->lengthContext().getPatternUnits(); unit.has_value() && - unit.value().type() == SVGObjectBoundingBoxUnits::Type::kObjectBoundingBox) { + unit.value().type() == SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox) { transform.postScale(ctx->lengthContext().viewPort().width, ctx->lengthContext().viewPort().height); } From b481e1461d0c048561f1e71ce67a9ba7498e4a29 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Thu, 12 Dec 2024 14:05:06 +0800 Subject: [PATCH 11/31] format --- include/tgfx/svg/SVGAttribute.h | 162 +++++++++--------- include/tgfx/svg/SVGDOM.h | 8 +- include/tgfx/svg/SVGFontManager.h | 64 +++---- include/tgfx/svg/SVGRenderContext.h | 81 +++++---- include/tgfx/svg/SVGTypes.h | 112 ++++++------ include/tgfx/svg/node/SVGFe.h | 1 + include/tgfx/svg/node/SVGFeBlend.h | 2 +- include/tgfx/svg/node/SVGFeColorMatrix.h | 2 +- .../tgfx/svg/node/SVGFeComponentTransfer.h | 2 +- include/tgfx/svg/node/SVGFeComposite.h | 2 +- include/tgfx/svg/node/SVGFeGaussianBlur.h | 2 +- include/tgfx/svg/node/SVGFeLighting.h | 2 +- include/tgfx/svg/node/SVGFeMorphology.h | 4 +- include/tgfx/svg/node/SVGFeTurbulence.h | 2 +- include/tgfx/svg/node/SVGFilterContext.h | 2 +- include/tgfx/svg/node/SVGNode.h | 51 +++--- include/tgfx/svg/node/SVGPath.h | 2 +- include/tgfx/svg/node/SVGPattern.h | 4 +- include/tgfx/svg/node/SVGSVG.h | 4 +- src/svg/SVGAttribute.cpp | 66 +++---- src/svg/SVGAttributeParser.cpp | 18 +- src/svg/SVGDOM.cpp | 66 +++---- src/svg/SVGFontManager.cpp | 26 +-- src/svg/SVGRenderContext.cpp | 154 ++++++++--------- src/svg/node/SVGCircle.cpp | 6 +- src/svg/node/SVGClipPath.cpp | 2 +- src/svg/node/SVGEllipse.cpp | 6 +- src/svg/node/SVGFe.cpp | 50 +++--- src/svg/node/SVGFeBlend.cpp | 4 +- src/svg/node/SVGFeColorMatrix.cpp | 28 +-- src/svg/node/SVGFeComponentTransfer.cpp | 25 +-- src/svg/node/SVGFeComposite.cpp | 18 +- src/svg/node/SVGFeDisplacementMap.cpp | 2 +- src/svg/node/SVGFeGaussianBlur.cpp | 6 +- src/svg/node/SVGFeLighting.cpp | 2 +- src/svg/node/SVGFeMorphology.cpp | 4 +- src/svg/node/SVGFeTurbulence.cpp | 4 +- src/svg/node/SVGFilter.cpp | 7 +- src/svg/node/SVGFilterContext.cpp | 18 +- src/svg/node/SVGGradient.cpp | 10 +- src/svg/node/SVGImage.cpp | 8 +- src/svg/node/SVGLine.cpp | 8 +- src/svg/node/SVGLinearGradient.cpp | 8 +- src/svg/node/SVGMask.cpp | 10 +- src/svg/node/SVGNode.cpp | 26 +-- src/svg/node/SVGPath.cpp | 10 +- src/svg/node/SVGPattern.cpp | 45 +++-- src/svg/node/SVGPoly.cpp | 2 +- src/svg/node/SVGRadialGradient.cpp | 6 +- src/svg/node/SVGRect.cpp | 6 +- src/svg/node/SVGSVG.cpp | 33 ++-- src/svg/node/SVGShape.cpp | 2 +- src/svg/node/SVGText.cpp | 66 +++---- src/svg/node/SVGTransformableNode.cpp | 2 +- src/svg/node/SVGUse.cpp | 16 +- 55 files changed, 638 insertions(+), 641 deletions(-) diff --git a/include/tgfx/svg/SVGAttribute.h b/include/tgfx/svg/SVGAttribute.h index d89f81c0..d2da36f6 100644 --- a/include/tgfx/svg/SVGAttribute.h +++ b/include/tgfx/svg/SVGAttribute.h @@ -22,99 +22,97 @@ namespace tgfx { enum class SVGAttribute { - kClipRule, - kColor, - kColorInterpolation, - kColorInterpolationFilters, - kCx, // , , : center x position - kCy, // , , : center y position - kFill, - kFillOpacity, - kFillRule, - kFilter, - kFilterUnits, - kFontFamily, - kFontSize, - kFontStyle, - kFontWeight, - kFx, // : focal point x position - kFy, // : focal point y position - kGradientUnits, - kGradientTransform, - kHeight, - kHref, - kOpacity, - kPoints, - kPreserveAspectRatio, - kR, // , : radius - kRx, // ,: horizontal (corner) radius - kRy, // ,: vertical (corner) radius - kSpreadMethod, - kStroke, - kStrokeDashArray, - kStrokeDashOffset, - kStrokeOpacity, - kStrokeLineCap, - kStrokeLineJoin, - kStrokeMiterLimit, - kStrokeWidth, - kTransform, - kText, - kTextAnchor, - kViewBox, - kVisibility, - kWidth, - kX, - kX1, // : first endpoint x - kX2, // : second endpoint x - kY, - kY1, // : first endpoint y - kY2, // : second endpoint y + ClipRule, + Color, + ColorInterpolation, + ColorInterpolationFilters, + Cx, // , , : center x position + Cy, // , , : center y position + Fill, + FillOpacity, + FillRule, + Filter, + FilterUnits, + FontFamily, + FontSize, + FontStyle, + FontWeight, + Fx, // : focal point x position + Fy, // : focal point y position + GradientUnits, + GradientTransform, + Height, + Href, + Opacity, + Points, + PreserveAspectRatio, + R, // , : radius + Rx, // ,: horizontal (corner) radius + Ry, // ,: vertical (corner) radius + SpreadMethod, + Stroke, + StrokeDashArray, + StrokeDashOffset, + StrokeOpacity, + StrokeLineCap, + StrokeLineJoin, + StrokeMiterLimit, + StrokeWidth, + Transform, + Text, + TextAnchor, + ViewBox, + Visibility, + Width, + X, + X1, // : first endpoint x + X2, // : second endpoint x + Y, + Y1, // : first endpoint y + Y2, // : second endpoint y - kUnknown, + Unknown, }; struct SVGPresentationAttributes { static SVGPresentationAttributes MakeInitial(); - // TODO: SVGProperty adds an extra ptr per attribute; refactor to reduce overhead. + SVGProperty Fill; + SVGProperty FillOpacity; + SVGProperty FillRule; + SVGProperty ClipRule; - SVGProperty fFill; - SVGProperty fFillOpacity; - SVGProperty fFillRule; - SVGProperty fClipRule; + SVGProperty Stroke; + SVGProperty StrokeDashArray; + SVGProperty StrokeDashOffset; + SVGProperty StrokeLineCap; + SVGProperty StrokeLineJoin; + SVGProperty StrokeMiterLimit; + SVGProperty StrokeOpacity; + SVGProperty StrokeWidth; - SVGProperty fStroke; - SVGProperty fStrokeDashArray; - SVGProperty fStrokeDashOffset; - SVGProperty fStrokeLineCap; - SVGProperty fStrokeLineJoin; - SVGProperty fStrokeMiterLimit; - SVGProperty fStrokeOpacity; - SVGProperty fStrokeWidth; + SVGProperty Visibility; - SVGProperty fVisibility; + SVGProperty Color; + SVGProperty ColorInterpolation; + SVGProperty ColorInterpolationFilters; - SVGProperty fColor; - SVGProperty fColorInterpolation; - SVGProperty fColorInterpolationFilters; - - SVGProperty fFontFamily; - SVGProperty fFontStyle; - SVGProperty fFontSize; - SVGProperty fFontWeight; - SVGProperty fTextAnchor; + SVGProperty FontFamily; + SVGProperty FontStyle; + SVGProperty FontSize; + SVGProperty FontWeight; + SVGProperty TextAnchor; // uninherited - SVGProperty fOpacity; - SVGProperty fClipPath; - SVGProperty fDisplay; - SVGProperty fMask; - SVGProperty fFilter; - SVGProperty fStopColor; - SVGProperty fStopOpacity; - SVGProperty fFloodColor; - SVGProperty fFloodOpacity; - SVGProperty fLightingColor; + SVGProperty Opacity; + SVGProperty ClipPath; + SVGProperty Display; + SVGProperty Mask; + SVGProperty Filter; + SVGProperty StopColor; + SVGProperty StopOpacity; + SVGProperty FloodColor; + SVGProperty FloodOpacity; + SVGProperty LightingColor; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGDOM.h b/include/tgfx/svg/SVGDOM.h index 91e7a5ec..a3dffce4 100644 --- a/include/tgfx/svg/SVGDOM.h +++ b/include/tgfx/svg/SVGDOM.h @@ -68,7 +68,7 @@ class SVGDOM { * Returns the root (outermost) SVG element. */ const std::shared_ptr& getRoot() const { - return _root; + return root; } /** @@ -107,11 +107,11 @@ class SVGDOM { private: SVGDOM(std::shared_ptr, SVGIDMapper&&, std::shared_ptr fontManager); - const std::shared_ptr _root; - const std::shared_ptr _fontMgr; + const std::shared_ptr root; + const std::shared_ptr fontManager; const SVGIDMapper _nodeIDMapper; Size _containerSize; - std::shared_ptr _renderPicture; + std::shared_ptr renderPicture; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGFontManager.h b/include/tgfx/svg/SVGFontManager.h index d6b92516..b906d5c9 100644 --- a/include/tgfx/svg/SVGFontManager.h +++ b/include/tgfx/svg/SVGFontManager.h @@ -35,60 +35,60 @@ class FontStyle { }; enum Weight { - kInvisible_Weight = 0, - kThin_Weight = 100, - kExtraLight_Weight = 200, - kLight_Weight = 300, - kNormal_Weight = 400, - kMedium_Weight = 500, - kSemiBold_Weight = 600, - kBold_Weight = 700, - kExtraBold_Weight = 800, - kBlack_Weight = 900, - kExtraBlack_Weight = 1000, + Invisible_Weight = 0, + Thin_Weight = 100, + ExtraLight_Weight = 200, + Light_Weight = 300, + Normal_Weight = 400, + Medium_Weight = 500, + SemiBold_Weight = 600, + Bold_Weight = 700, + ExtraBold_Weight = 800, + Black_Weight = 900, + ExtraBlack_Weight = 1000, }; enum Width { - kUltraCondensed_Width = 1, - kExtraCondensed_Width = 2, - kCondensed_Width = 3, - kSemiCondensed_Width = 4, - kNormal_Width = 5, - kSemiExpanded_Width = 6, - kExpanded_Width = 7, - kExtraExpanded_Width = 8, - kUltraExpanded_Width = 9, + UltraCondensed_Width = 1, + ExtraCondensed_Width = 2, + Condensed_Width = 3, + SemiCondensed_Width = 4, + Normal_Width = 5, + SemiExpanded_Width = 6, + Expanded_Width = 7, + ExtraExpanded_Width = 8, + UltraExpanded_Width = 9, }; enum Slant { - kUpright_Slant, - kItalic_Slant, - kOblique_Slant, + Upright_Slant, + Italic_Slant, + Oblique_Slant, }; constexpr FontStyle(Weight weight, Width width, Slant slant) - : _value(weight + (width << 16) + (slant << 24)) { + : value(weight + (width << 16) + (slant << 24)) { } - constexpr FontStyle() : FontStyle{kNormal_Weight, kNormal_Width, kUpright_Slant} { + constexpr FontStyle() : FontStyle{Normal_Weight, Normal_Width, Upright_Slant} { } bool operator==(const FontStyle& rhs) const { - return _value == rhs._value; + return value == rhs.value; } int weight() const { - return _value & 0xFFFF; + return value & 0xFFFF; } int width() const { - return (_value >> 16) & 0xFF; + return (value >> 16) & 0xFF; } Slant slant() const { - return static_cast((_value >> 24) & 0xFF); + return static_cast((value >> 24) & 0xFF); } private: - int _value; + int value; }; class SVGFontManager { @@ -112,8 +112,8 @@ class SVGFontManager { private: std::unordered_map, FontStyle::Hash>> - _typefaces; - std::shared_ptr _defaultTypeface; + typefaceMap; + std::shared_ptr defaultTypeface; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGRenderContext.h b/include/tgfx/svg/SVGRenderContext.h index 6b10152f..82fc5bd4 100644 --- a/include/tgfx/svg/SVGRenderContext.h +++ b/include/tgfx/svg/SVGRenderContext.h @@ -22,7 +22,6 @@ #include #include #include -#include #include #include "tgfx/core/Canvas.h" #include "tgfx/core/MaskFilter.h" @@ -46,14 +45,14 @@ class SVGNode; template class CopyOnWrite { public: - explicit CopyOnWrite(const T& initial) : _obj(&initial) { + explicit CopyOnWrite(const T& initial) : object(&initial) { } - explicit CopyOnWrite(const T* initial) : _obj(initial) { + explicit CopyOnWrite(const T* initial) : object(initial) { } // Constructor for delayed initialization. - CopyOnWrite() : _obj(nullptr) { + CopyOnWrite() : object(nullptr) { } CopyOnWrite(const CopyOnWrite& that) { @@ -65,14 +64,14 @@ class CopyOnWrite { } CopyOnWrite& operator=(const CopyOnWrite& that) { - _optional = that._optional; - _obj = _optional.has_value() ? &_optional.value() : that._obj; + optional = that.optional; + object = optional.has_value() ? &optional.value() : that.object; return *this; } CopyOnWrite& operator=(CopyOnWrite&& that) { - _optional = std::move(that._optional); - _obj = _optional.has_value() ? &_optional.value() : that._obj; + optional = std::move(that.optional); + object = optional.has_value() ? &optional.value() : that.object; return *this; } @@ -80,15 +79,15 @@ class CopyOnWrite { * Returns a writable T*. The first time this is called the initial object is cloned. */ T* writable() { - if (!_optional.has_value()) { - _optional = *_obj; - _obj = &_optional.value(); + if (!optional.has_value()) { + optional = *object; + object = &optional.value(); } - return &_optional.value(); + return &optional.value(); } const T* get() const { - return _obj; + return object; } /** @@ -96,21 +95,21 @@ class CopyOnWrite { */ const T* operator->() const { - return _obj; + return object; } const T& operator*() const { - return *_obj; + return *object; } private: - const T* _obj; - std::optional _optional; + const T* object; + std::optional optional; }; class SVGLengthContext { public: - explicit SVGLengthContext(const Size& viewport, float dpi = 90) : _viewport(viewport), _dpi(dpi) { + explicit SVGLengthContext(const Size& viewport, float dpi = 90) : _viewPort(viewport), dpi(dpi) { } enum class LengthType { @@ -120,10 +119,10 @@ class SVGLengthContext { }; const Size& viewPort() const { - return _viewport; + return _viewPort; } - void setViewPort(const Size& viewport) { - _viewport = viewport; + void setViewPort(const Size& viewPort) { + _viewPort = viewPort; } float resolve(const SVGLength&, LengthType) const; @@ -131,21 +130,21 @@ class SVGLengthContext { const SVGLength& h) const; void setPatternUnits(SVGObjectBoundingBoxUnits unit) { - _patternUnit = unit; + patternUnit = unit; } void clearPatternUnits() { - _patternUnit.reset(); + patternUnit.reset(); } std::optional getPatternUnits() const { - return _patternUnit; + return patternUnit; } private: - Size _viewport; - float _dpi; - std::optional _patternUnit; + Size _viewPort; + float dpi; + std::optional patternUnit; }; struct SkSVGPresentationContext { @@ -199,8 +198,8 @@ class SVGRenderContext { } void saveOnce(); - void concat(const Matrix& matrix) { - _matrix.preConcat(matrix); + void concat(const Matrix& inputMatrix) { + matrix.preConcat(inputMatrix); } enum ApplyFlags { @@ -223,13 +222,13 @@ class SVGRenderContext { }; const std::shared_ptr& fontMgr() const { - return _fontMgr; + return fontManager; } std::shared_ptr& fontMgr() { // It is probably an oversight to try to render without having set the SVGFontManager. // We will assert this in debug mode, but fallback to an empty _fontMgr in release builds. - return _fontMgr; + return fontManager; } // Returns the translate/scale transformation required to map into the current OBB scope, @@ -250,33 +249,33 @@ class SVGRenderContext { private: float applyOpacity(float opacity, uint32_t flags, bool hasFilter); - std::shared_ptr applyFilter(const SVGFuncIRI&); - Path applyClip(const SVGFuncIRI&); + std::shared_ptr applyFilter(const SVGFuncIRI&) const; + Path applyClip(const SVGFuncIRI&) const; std::shared_ptr applyMask(const SVGFuncIRI&); std::optional commonPaint(const SVGPaint&, float opacity) const; - std::shared_ptr _fontMgr; - const SVGIDMapper& _nodeIDMapper; + std::shared_ptr fontManager; + const SVGIDMapper& nodeIDMapper; CopyOnWrite _lengthContext; CopyOnWrite _presentationContext; - Canvas* _renderCanvas; + Canvas* renderCanvas; - Recorder _recorder; + Recorder recorder; Canvas* _canvas; // clipPath, if present for the current context (not inherited). std::optional _clipPath; // Deferred opacity optimization for leaf nodes. - float _deferredPaintOpacity = 1; + float deferredPaintOpacity = 1; // Current object bounding box scope. - const OBBScope _scope; + const OBBScope scope; Context* _deviceContext; - Paint _picturePaint; + Paint picturePaint; - Matrix _matrix = Matrix::I(); + Matrix matrix = Matrix::I(); }; } // namespace tgfx diff --git a/include/tgfx/svg/SVGTypes.h b/include/tgfx/svg/SVGTypes.h index e13777b5..73cd47c5 100644 --- a/include/tgfx/svg/SVGTypes.h +++ b/include/tgfx/svg/SVGTypes.h @@ -761,144 +761,144 @@ struct SVGPreserveAspectRatio { class SVGTextAnchor { public: enum class Type { - kStart, - kMiddle, - kEnd, - kInherit, + Start, + Middle, + End, + Inherit, }; - SVGTextAnchor() : fType(Type::kInherit) { + SVGTextAnchor() : _type(Type::Inherit) { } - explicit SVGTextAnchor(Type t) : fType(t) { + explicit SVGTextAnchor(Type t) : _type(t) { } bool operator==(const SVGTextAnchor& other) const { - return fType == other.fType; + return _type == other._type; } bool operator!=(const SVGTextAnchor& other) const { return !(*this == other); } Type type() const { - return fType; + return _type; } private: - Type fType; + Type _type; }; // https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveInAttribute class SVGFeInputType { public: enum class Type { - kSourceGraphic, - kSourceAlpha, - kBackgroundImage, - kBackgroundAlpha, - kFillPaint, - kStrokePaint, - kFilterPrimitiveReference, - kUnspecified, + SourceGraphic, + SourceAlpha, + BackgroundImage, + BackgroundAlpha, + FillPaint, + StrokePaint, + FilterPrimitiveReference, + Unspecified, }; - SVGFeInputType() : fType(Type::kUnspecified) { + SVGFeInputType() : _type(Type::Unspecified) { } - explicit SVGFeInputType(Type t) : fType(t) { + explicit SVGFeInputType(Type t) : _type(t) { } explicit SVGFeInputType(SVGStringType id) - : fType(Type::kFilterPrimitiveReference), fId(std::move(id)) { + : _type(Type::FilterPrimitiveReference), _id(std::move(id)) { } bool operator==(const SVGFeInputType& other) const { - return fType == other.fType && fId == other.fId; + return _type == other._type && _id == other._id; } bool operator!=(const SVGFeInputType& other) const { return !(*this == other); } const std::string& id() const { - return fId; + return _id; } Type type() const { - return fType; + return _type; } private: - Type fType; - std::string fId; + Type _type; + std::string _id; }; enum class SVGFeColorMatrixType { - kMatrix, - kSaturate, - kHueRotate, - kLuminanceToAlpha, + Matrix, + Saturate, + HueRotate, + LuminanceToAlpha, }; using SVGFeColorMatrixValues = std::vector; enum class SVGFeCompositeOperator { - kOver, - kIn, - kOut, - kAtop, - kXor, - kArithmetic, + Over, + In, + Out, + Atop, + Xor, + Arithmetic, }; class SVGFeTurbulenceBaseFrequency { public: - SVGFeTurbulenceBaseFrequency() : fFreqX(0), fFreqY(0) { + SVGFeTurbulenceBaseFrequency() : _freqX(0), _freqY(0) { } SVGFeTurbulenceBaseFrequency(SVGNumberType freqX, SVGNumberType freqY) - : fFreqX(freqX), fFreqY(freqY) { + : _freqX(freqX), _freqY(freqY) { } SVGNumberType freqX() const { - return fFreqX; + return _freqX; } SVGNumberType freqY() const { - return fFreqY; + return _freqY; } private: - SVGNumberType fFreqX; - SVGNumberType fFreqY; + SVGNumberType _freqX; + SVGNumberType _freqY; }; struct SVGFeTurbulenceType { enum Type { - kFractalNoise, - kTurbulence, + FractalNoise, + Turbulence, }; - Type fType; + Type type; - SVGFeTurbulenceType() : fType(kTurbulence) { + SVGFeTurbulenceType() : type(Turbulence) { } - explicit SVGFeTurbulenceType(Type type) : fType(type) { + explicit SVGFeTurbulenceType(Type inputType) : type(inputType) { } }; enum class SVGColorspace { - kAuto, - kSRGB, - kLinearRGB, + Auto, + SRGB, + LinearRGB, }; // https://www.w3.org/TR/SVG11/painting.html#DisplayProperty enum class SVGDisplay { - kInline, - kNone, + Inline, + None, }; // https://www.w3.org/TR/SVG11/filters.html#TransferFunctionElementAttributes enum class SVGFeFuncType { - kIdentity, - kTable, - kDiscrete, - kLinear, - kGamma, + Identity, + Table, + Discrete, + Linear, + Gamma, }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGFe.h b/include/tgfx/svg/node/SVGFe.h index a7323d52..e38cb72e 100644 --- a/include/tgfx/svg/node/SVGFe.h +++ b/include/tgfx/svg/node/SVGFe.h @@ -73,6 +73,7 @@ class SkSVGFe : public SVGHiddenContainer { void applyProperties(SVGRenderContext*) const; SVG_ATTR(In, SVGFeInputType, SVGFeInputType()) + SVG_ATTR(Result, SVGStringType, SVGStringType()) SVG_OPTIONAL_ATTR(X, SVGLength) SVG_OPTIONAL_ATTR(Y, SVGLength) diff --git a/include/tgfx/svg/node/SVGFeBlend.h b/include/tgfx/svg/node/SVGFeBlend.h index 485a014f..d898cf04 100644 --- a/include/tgfx/svg/node/SVGFeBlend.h +++ b/include/tgfx/svg/node/SVGFeBlend.h @@ -43,7 +43,7 @@ class SkSVGFeBlend : public SkSVGFe { return std::shared_ptr(new SkSVGFeBlend()); } - SVG_ATTR(Mode, Mode, Mode::kNormal) + SVG_ATTR(BlendMode, Mode, Mode::kNormal) SVG_ATTR(In2, SVGFeInputType, SVGFeInputType()) protected: diff --git a/include/tgfx/svg/node/SVGFeColorMatrix.h b/include/tgfx/svg/node/SVGFeColorMatrix.h index 0a764747..dbb07bb8 100644 --- a/include/tgfx/svg/node/SVGFeColorMatrix.h +++ b/include/tgfx/svg/node/SVGFeColorMatrix.h @@ -37,7 +37,7 @@ class SkSVGFeColorMatrix final : public SkSVGFe { return std::shared_ptr(new SkSVGFeColorMatrix()); } - SVG_ATTR(Type, SVGFeColorMatrixType, SVGFeColorMatrixType(SVGFeColorMatrixType::kMatrix)) + SVG_ATTR(Type, SVGFeColorMatrixType, SVGFeColorMatrixType(SVGFeColorMatrixType::Matrix)) SVG_ATTR(Values, SVGFeColorMatrixValues, SVGFeColorMatrixValues()) protected: diff --git a/include/tgfx/svg/node/SVGFeComponentTransfer.h b/include/tgfx/svg/node/SVGFeComponentTransfer.h index 92e6152f..91993af4 100644 --- a/include/tgfx/svg/node/SVGFeComponentTransfer.h +++ b/include/tgfx/svg/node/SVGFeComponentTransfer.h @@ -56,7 +56,7 @@ class SkSVGFeFunc final : public SVGHiddenContainer { SVG_ATTR(Offset, SVGNumberType, 0) SVG_ATTR(Slope, SVGNumberType, 1) SVG_ATTR(TableValues, std::vector, {}) - SVG_ATTR(Type, SVGFeFuncType, SVGFeFuncType::kIdentity) + SVG_ATTR(Type, SVGFeFuncType, SVGFeFuncType::Identity) std::vector getTable() const; diff --git a/include/tgfx/svg/node/SVGFeComposite.h b/include/tgfx/svg/node/SVGFeComposite.h index 8bf8366f..520051d4 100644 --- a/include/tgfx/svg/node/SVGFeComposite.h +++ b/include/tgfx/svg/node/SVGFeComposite.h @@ -38,7 +38,7 @@ class SkSVGFeComposite final : public SkSVGFe { SVG_ATTR(K2, SVGNumberType, SVGNumberType(0)) SVG_ATTR(K3, SVGNumberType, SVGNumberType(0)) SVG_ATTR(K4, SVGNumberType, SVGNumberType(0)) - SVG_ATTR(Operator, SVGFeCompositeOperator, SVGFeCompositeOperator::kOver) + SVG_ATTR(Operator, SVGFeCompositeOperator, SVGFeCompositeOperator::Over) protected: std::vector getInputs() const override { diff --git a/include/tgfx/svg/node/SVGFeGaussianBlur.h b/include/tgfx/svg/node/SVGFeGaussianBlur.h index 628e6f4a..10cc339e 100644 --- a/include/tgfx/svg/node/SVGFeGaussianBlur.h +++ b/include/tgfx/svg/node/SVGFeGaussianBlur.h @@ -40,7 +40,7 @@ class SkSVGFeGaussianBlur : public SkSVGFe { return std::shared_ptr(new SkSVGFeGaussianBlur()); } - SVG_ATTR(StdDeviation, StdDeviation, StdDeviation({0, 0})) + SVG_ATTR(stdDeviation, StdDeviation, StdDeviation({0, 0})) protected: #ifndef RENDER_SVG diff --git a/include/tgfx/svg/node/SVGFeLighting.h b/include/tgfx/svg/node/SVGFeLighting.h index 3b1cee8f..66d5833c 100644 --- a/include/tgfx/svg/node/SVGFeLighting.h +++ b/include/tgfx/svg/node/SVGFeLighting.h @@ -41,7 +41,7 @@ class SkSVGFeLighting : public SkSVGFe { }; SVG_ATTR(SurfaceScale, SVGNumberType, 1) - SVG_OPTIONAL_ATTR(KernelUnitLength, KernelUnitLength) + SVG_OPTIONAL_ATTR(UnitLength, KernelUnitLength) protected: explicit SkSVGFeLighting(SVGTag t) : INHERITED(t) { diff --git a/include/tgfx/svg/node/SVGFeMorphology.h b/include/tgfx/svg/node/SVGFeMorphology.h index 0a4a3a30..6e2d799f 100644 --- a/include/tgfx/svg/node/SVGFeMorphology.h +++ b/include/tgfx/svg/node/SVGFeMorphology.h @@ -45,8 +45,8 @@ class SkSVGFeMorphology : public SkSVGFe { return std::shared_ptr(new SkSVGFeMorphology()); } - SVG_ATTR(Operator, Operator, Operator::kErode) - SVG_ATTR(Radius, Radius, Radius({0, 0})) + SVG_ATTR(MorphOperator, Operator, Operator::kErode) + SVG_ATTR(MorphRadius, Radius, Radius({0, 0})) protected: #ifndef RENDER_SVG diff --git a/include/tgfx/svg/node/SVGFeTurbulence.h b/include/tgfx/svg/node/SVGFeTurbulence.h index 3f04d7b9..b19c7d1a 100644 --- a/include/tgfx/svg/node/SVGFeTurbulence.h +++ b/include/tgfx/svg/node/SVGFeTurbulence.h @@ -36,7 +36,7 @@ class SkSVGFeTurbulence : public SkSVGFe { SVG_ATTR(NumOctaves, SVGIntegerType, SVGIntegerType(1)) SVG_ATTR(Seed, SVGNumberType, SVGNumberType(0)) SVG_ATTR(TurbulenceType, SVGFeTurbulenceType, - SVGFeTurbulenceType(SVGFeTurbulenceType::Type::kTurbulence)) + SVGFeTurbulenceType(SVGFeTurbulenceType::Type::Turbulence)) protected: #ifndef RENDER_SVG diff --git a/include/tgfx/svg/node/SVGFilterContext.h b/include/tgfx/svg/node/SVGFilterContext.h index 3a9357fd..c1cdfbd2 100644 --- a/include/tgfx/svg/node/SVGFilterContext.h +++ b/include/tgfx/svg/node/SVGFilterContext.h @@ -32,7 +32,7 @@ class SkSVGFilterContext { SkSVGFilterContext(const Rect& filterEffectsRegion, const SVGObjectBoundingBoxUnits& primitiveUnits) : fFilterEffectsRegion(filterEffectsRegion), fPrimitiveUnits(primitiveUnits), - fPreviousResult({nullptr, filterEffectsRegion, SVGColorspace::kSRGB}) { + fPreviousResult({nullptr, filterEffectsRegion, SVGColorspace::SRGB}) { } const Rect& filterEffectsRegion() const { diff --git a/include/tgfx/svg/node/SVGNode.h b/include/tgfx/svg/node/SVGNode.h index 409b1b41..777408e0 100644 --- a/include/tgfx/svg/node/SVGNode.h +++ b/include/tgfx/svg/node/SVGNode.h @@ -91,10 +91,10 @@ enum class SVGTag { \ public: \ const SVGProperty& get##attr_name() const { \ - return _presentationAttributes.f##attr_name; \ + return _presentationAttributes.attr_name; \ } \ void set##attr_name(const SVGProperty& v) { \ - auto* dest = &_presentationAttributes.f##attr_name; \ + auto* dest = &_presentationAttributes.attr_name; \ if (!dest->isInheritable() || v.isValue()) { \ /* TODO: If dest is not inheritable, handle v == "inherit" */ \ *dest = v; \ @@ -103,7 +103,7 @@ enum class SVGTag { } \ } \ void set##attr_name(SVGProperty&& v) { \ - auto* dest = &_presentationAttributes.f##attr_name; \ + auto* dest = &_presentationAttributes.attr_name; \ if (!dest->isInheritable() || v.isValue()) { \ /* TODO: If dest is not inheritable, handle v == "inherit" */ \ *dest = std::move(v); \ @@ -143,7 +143,6 @@ class SVGNode { bool setAttribute(const std::string& attributeName, const std::string& attributeValue); virtual bool parseAndSetAttribute(const char* name, const char* value); - // inherited SVG_PRES_ATTR(ClipRule, SVGFillRule, true) SVG_PRES_ATTR(Color, SVGColorType, true) SVG_PRES_ATTR(ColorInterpolation, SVGColorspace, true) @@ -240,29 +239,29 @@ class SVGNode { set_mv(std::move(a)); \ } -#define SVG_ATTR(attr_name, attr_type, attr_default) \ - private: \ - attr_type f##attr_name = attr_default; \ - \ - public: \ - const attr_type& get##attr_name() const { \ - return f##attr_name; \ - } \ - SVG_ATTR_SETTERS( \ - attr_name, attr_type, attr_default, [this](const attr_type& a) { this->f##attr_name = a; }, \ - [this](attr_type&& a) { this->f##attr_name = std::move(a); }) +#define SVG_ATTR(attr_name, attr_type, attr_default) \ + private: \ + attr_type attr_name = attr_default; \ + \ + public: \ + const attr_type& get##attr_name() const { \ + return attr_name; \ + } \ + SVG_ATTR_SETTERS( \ + attr_name, attr_type, attr_default, [this](const attr_type& a) { this->attr_name = a; }, \ + [this](attr_type&& a) { this->attr_name = std::move(a); }) -#define SVG_OPTIONAL_ATTR(attr_name, attr_type) \ - private: \ - std::optional f##attr_name; \ - \ - public: \ - const std::optional& get##attr_name() const { \ - return f##attr_name; \ - } \ - SVG_ATTR_SETTERS( \ - attr_name, attr_type, attr_default, [this](const attr_type& a) { this->f##attr_name = a; }, \ - [this](attr_type&& a) { this->f##attr_name = a; }) +#define SVG_OPTIONAL_ATTR(attr_name, attr_type) \ + private: \ + std::optional attr_name; \ + \ + public: \ + const std::optional& get##attr_name() const { \ + return attr_name; \ + } \ + SVG_ATTR_SETTERS( \ + attr_name, attr_type, attr_default, [this](const attr_type& a) { this->attr_name = a; }, \ + [this](attr_type&& a) { this->attr_name = a; }) //NOLINTEND } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGPath.h b/include/tgfx/svg/node/SVGPath.h index b63406cd..ddf3af20 100644 --- a/include/tgfx/svg/node/SVGPath.h +++ b/include/tgfx/svg/node/SVGPath.h @@ -33,7 +33,7 @@ class SkSVGPath final : public SVGShape { return std::shared_ptr(new SkSVGPath()); } - SVG_ATTR(Path, Path, Path()) + SVG_ATTR(ShapePath, Path, Path()) protected: bool parseAndSetAttribute(const char*, const char*) override; diff --git a/include/tgfx/svg/node/SVGPattern.h b/include/tgfx/svg/node/SVGPattern.h index 1bf92246..4ed1a1c6 100644 --- a/include/tgfx/svg/node/SVGPattern.h +++ b/include/tgfx/svg/node/SVGPattern.h @@ -55,8 +55,8 @@ class SkSVGPattern final : public SVGHiddenContainer { private: struct PatternAttributes { - std::optional fX, fY, fWidth, fHeight; - std::optional fPatternTransform; + std::optional x, y, width, height; + std::optional patternTransform; }; #ifndef RENDER_SVG diff --git a/include/tgfx/svg/node/SVGSVG.h b/include/tgfx/svg/node/SVGSVG.h index ddc5fe98..8a7208bc 100644 --- a/include/tgfx/svg/node/SVGSVG.h +++ b/include/tgfx/svg/node/SVGSVG.h @@ -59,11 +59,11 @@ class SVGSVG : public SkSVGContainer { void onSetAttribute(SVGAttribute, const SVGValue&) override; private: - explicit SVGSVG(Type t) : INHERITED(SVGTag::Svg), fType(t) { + explicit SVGSVG(Type t) : INHERITED(SVGTag::Svg), type(t) { } // Some attributes behave differently for the outermost svg element. - const Type fType; + const Type type; using INHERITED = SkSVGContainer; }; diff --git a/src/svg/SVGAttribute.cpp b/src/svg/SVGAttribute.cpp index 98702821..74d9b8a4 100644 --- a/src/svg/SVGAttribute.cpp +++ b/src/svg/SVGAttribute.cpp @@ -23,39 +23,39 @@ namespace tgfx { SVGPresentationAttributes SVGPresentationAttributes::MakeInitial() { SVGPresentationAttributes result; - result.fFill.set(SVGPaint(SVGColor(Color::Black()))); - result.fFillOpacity.set(static_cast(1)); - result.fFillRule.set(SVGFillRule(SVGFillRule::Type::NonZero)); - result.fClipRule.set(SVGFillRule(SVGFillRule::Type::NonZero)); - - result.fStroke.set(SVGPaint(SVGPaint::Type::None)); - result.fStrokeDashArray.set(SVGDashArray(SVGDashArray::Type::None)); - result.fStrokeDashOffset.set(SVGLength(0)); - result.fStrokeLineCap.set(SVGLineCap::Butt); - result.fStrokeLineJoin.set(SVGLineJoin(SVGLineJoin::Type::Miter)); - result.fStrokeMiterLimit.set(static_cast(4)); - result.fStrokeOpacity.set(static_cast(1)); - result.fStrokeWidth.set(SVGLength(1)); - - result.fVisibility.set(SVGVisibility(SVGVisibility::Type::Visible)); - - result.fColor.set(SVGColorType(Color::Black())); - result.fColorInterpolation.set(SVGColorspace::kSRGB); - result.fColorInterpolationFilters.set(SVGColorspace::kLinearRGB); - - result.fFontFamily.init("default"); - result.fFontStyle.init(SVGFontStyle::Type::Normal); - result.fFontSize.init(SVGLength(24)); - result.fFontWeight.init(SVGFontWeight::Type::Normal); - result.fTextAnchor.init(SVGTextAnchor::Type::kStart); - - result.fDisplay.init(SVGDisplay::kInline); - - result.fStopColor.set(SVGColor(Color::Black())); - result.fStopOpacity.set(static_cast(1)); - result.fFloodColor.set(SVGColor(Color::Black())); - result.fFloodOpacity.set(static_cast(1)); - result.fLightingColor.set(SVGColor(Color::White())); + result.Fill.set(SVGPaint(SVGColor(Color::Black()))); + result.FillOpacity.set(static_cast(1)); + result.FillRule.set(SVGFillRule(SVGFillRule::Type::NonZero)); + result.ClipRule.set(SVGFillRule(SVGFillRule::Type::NonZero)); + + result.Stroke.set(SVGPaint(SVGPaint::Type::None)); + result.StrokeDashArray.set(SVGDashArray(SVGDashArray::Type::None)); + result.StrokeDashOffset.set(SVGLength(0)); + result.StrokeLineCap.set(SVGLineCap::Butt); + result.StrokeLineJoin.set(SVGLineJoin(SVGLineJoin::Type::Miter)); + result.StrokeMiterLimit.set(static_cast(4)); + result.StrokeOpacity.set(static_cast(1)); + result.StrokeWidth.set(SVGLength(1)); + + result.Visibility.set(SVGVisibility(SVGVisibility::Type::Visible)); + + result.Color.set(SVGColorType(Color::Black())); + result.ColorInterpolation.set(SVGColorspace::SRGB); + result.ColorInterpolationFilters.set(SVGColorspace::LinearRGB); + + result.FontFamily.init("default"); + result.FontStyle.init(SVGFontStyle::Type::Normal); + result.FontSize.init(SVGLength(24)); + result.FontWeight.init(SVGFontWeight::Type::Normal); + result.TextAnchor.init(SVGTextAnchor::Type::Start); + + result.Display.init(SVGDisplay::Inline); + + result.StopColor.set(SVGColor(Color::Black())); + result.StopOpacity.set(static_cast(1)); + result.FloodColor.set(SVGColor(Color::Black())); + result.FloodOpacity.set(static_cast(1)); + result.LightingColor.set(SVGColor(Color::White())); return result; } diff --git a/src/svg/SVGAttributeParser.cpp b/src/svg/SVGAttributeParser.cpp index 1d0961c9..d6efd8f6 100644 --- a/src/svg/SVGAttributeParser.cpp +++ b/src/svg/SVGAttributeParser.cpp @@ -1076,10 +1076,10 @@ bool SVGAttributeParser::parse(SVGFontWeight* weight) { template <> bool SVGAttributeParser::parse(SVGTextAnchor* anchor) { static constexpr std::tuple gAnchorMap[] = { - {"start", SVGTextAnchor::Type::kStart}, - {"middle", SVGTextAnchor::Type::kMiddle}, - {"end", SVGTextAnchor::Type::kEnd}, - {"inherit", SVGTextAnchor::Type::kInherit}, + {"start", SVGTextAnchor::Type::Start}, + {"middle", SVGTextAnchor::Type::Middle}, + {"end", SVGTextAnchor::Type::End}, + {"inherit", SVGTextAnchor::Type::Inherit}, }; bool parsedValue = false; @@ -1167,9 +1167,9 @@ bool SVGAttributeParser::parse(std::vector* numbers) { template <> bool SVGAttributeParser::parse(SVGColorspace* colorspace) { static constexpr std::tuple gColorspaceMap[] = { - {"auto", SVGColorspace::kAuto}, - {"sRGB", SVGColorspace::kSRGB}, - {"linearRGB", SVGColorspace::kLinearRGB}, + {"auto", SVGColorspace::Auto}, + {"sRGB", SVGColorspace::SRGB}, + {"linearRGB", SVGColorspace::LinearRGB}, }; return this->parseEnumMap(gColorspaceMap, colorspace) && this->parseEOSToken(); @@ -1182,8 +1182,8 @@ bool SVGAttributeParser::parse(SVGDisplay* display) { SVGDisplay fType; const char* fName; } gDisplayInfo[] = { - {SVGDisplay::kInline, "inline"}, - {SVGDisplay::kNone, "none"}, + {SVGDisplay::Inline, "inline"}, + {SVGDisplay::None, "none"}, }; bool parsedValue = false; diff --git a/src/svg/SVGDOM.cpp b/src/svg/SVGDOM.cpp index 873607d5..755fa942 100644 --- a/src/svg/SVGDOM.cpp +++ b/src/svg/SVGDOM.cpp @@ -230,29 +230,29 @@ struct AttrParseInfo { }; std::vector> gAttributeParseInfo = { - {"cx", {SVGAttribute::kCx, SetLengthAttribute}}, - {"cy", {SVGAttribute::kCy, SetLengthAttribute}}, - {"filterUnits", {SVGAttribute::kFilterUnits, SetObjectBoundingBoxUnitsAttribute}}, + {"cx", {SVGAttribute::Cx, SetLengthAttribute}}, + {"cy", {SVGAttribute::Cy, SetLengthAttribute}}, + {"filterUnits", {SVGAttribute::FilterUnits, SetObjectBoundingBoxUnitsAttribute}}, // focal point x & y - {"fx", {SVGAttribute::kFx, SetLengthAttribute}}, - {"fy", {SVGAttribute::kFy, SetLengthAttribute}}, - {"height", {SVGAttribute::kHeight, SetLengthAttribute}}, - {"preserveAspectRatio", {SVGAttribute::kPreserveAspectRatio, SetPreserveAspectRatioAttribute}}, - {"r", {SVGAttribute::kR, SetLengthAttribute}}, - {"rx", {SVGAttribute::kRx, SetLengthAttribute}}, - {"ry", {SVGAttribute::kRy, SetLengthAttribute}}, - {"style", {SVGAttribute::kUnknown, SetStyleAttributes}}, - {"text", {SVGAttribute::kText, SetStringAttribute}}, - {"transform", {SVGAttribute::kTransform, SetTransformAttribute}}, - {"viewBox", {SVGAttribute::kViewBox, SetViewBoxAttribute}}, - {"width", {SVGAttribute::kWidth, SetLengthAttribute}}, - {"x", {SVGAttribute::kX, SetLengthAttribute}}, - {"x1", {SVGAttribute::kX1, SetLengthAttribute}}, - {"x2", {SVGAttribute::kX2, SetLengthAttribute}}, - {"xlink:href", {SVGAttribute::kHref, SetIRIAttribute}}, - {"y", {SVGAttribute::kY, SetLengthAttribute}}, - {"y1", {SVGAttribute::kY1, SetLengthAttribute}}, - {"y2", {SVGAttribute::kY2, SetLengthAttribute}}, + {"fx", {SVGAttribute::Fx, SetLengthAttribute}}, + {"fy", {SVGAttribute::Fy, SetLengthAttribute}}, + {"height", {SVGAttribute::Height, SetLengthAttribute}}, + {"preserveAspectRatio", {SVGAttribute::PreserveAspectRatio, SetPreserveAspectRatioAttribute}}, + {"r", {SVGAttribute::R, SetLengthAttribute}}, + {"rx", {SVGAttribute::Rx, SetLengthAttribute}}, + {"ry", {SVGAttribute::Ry, SetLengthAttribute}}, + {"style", {SVGAttribute::Unknown, SetStyleAttributes}}, + {"text", {SVGAttribute::Text, SetStringAttribute}}, + {"transform", {SVGAttribute::Transform, SetTransformAttribute}}, + {"viewBox", {SVGAttribute::ViewBox, SetViewBoxAttribute}}, + {"width", {SVGAttribute::Width, SetLengthAttribute}}, + {"x", {SVGAttribute::X, SetLengthAttribute}}, + {"x1", {SVGAttribute::X1, SetLengthAttribute}}, + {"x2", {SVGAttribute::X2, SetLengthAttribute}}, + {"xlink:href", {SVGAttribute::Href, SetIRIAttribute}}, + {"y", {SVGAttribute::Y, SetLengthAttribute}}, + {"y1", {SVGAttribute::Y1, SetLengthAttribute}}, + {"y2", {SVGAttribute::Y2, SetLengthAttribute}}, }; std::vector (*)()>> gTagFactories = { @@ -467,35 +467,35 @@ std::shared_ptr SVGDOM::Builder::make(Data& data, SVGDOM::SVGDOM(std::shared_ptr root, SVGIDMapper&& mapper, std::shared_ptr fontManager) - : _root(std::move(root)), _fontMgr(std::move(fontManager)), _nodeIDMapper(std::move(mapper)) { + : root(std::move(root)), fontManager(std::move(fontManager)), _nodeIDMapper(std::move(mapper)) { } void SVGDOM::render(Canvas* canvas) { - if (_root) { - if (!_renderPicture) { + if (root) { + if (!renderPicture) { SVGLengthContext lctx(_containerSize); SkSVGPresentationContext pctx; Recorder recorder; auto* drawCanvas = recorder.beginRecording(); { - SVGRenderContext renderCtx(canvas->getSurface()->getContext(), drawCanvas, _fontMgr, + SVGRenderContext renderCtx(canvas->getSurface()->getContext(), drawCanvas, fontManager, _nodeIDMapper, lctx, pctx, {nullptr, nullptr}, canvas->getMatrix()); - _root->render(renderCtx); + root->render(renderCtx); } - _renderPicture = recorder.finishRecordingAsPicture(); + renderPicture = recorder.finishRecordingAsPicture(); } - canvas->drawPicture(_renderPicture); + canvas->drawPicture(renderPicture); } } void SVGDOM::renderNode(Canvas* canvas, SkSVGPresentationContext& pctx, const char* id) const { - if (_root) { + if (root) { SVGLengthContext lctx(_containerSize); - SVGRenderContext renderCtx(canvas->getSurface()->getContext(), canvas, _fontMgr, _nodeIDMapper, - lctx, pctx, {nullptr, nullptr}, canvas->getMatrix()); - _root->renderNode(renderCtx, SVGIRI(SVGIRI::Type::Local, SVGStringType(id))); + SVGRenderContext renderCtx(canvas->getSurface()->getContext(), canvas, fontManager, + _nodeIDMapper, lctx, pctx, {nullptr, nullptr}, canvas->getMatrix()); + root->renderNode(renderCtx, SVGIRI(SVGIRI::Type::Local, SVGStringType(id))); } } diff --git a/src/svg/SVGFontManager.cpp b/src/svg/SVGFontManager.cpp index 81f44bd2..cfbee1c9 100644 --- a/src/svg/SVGFontManager.cpp +++ b/src/svg/SVGFontManager.cpp @@ -22,34 +22,34 @@ namespace tgfx { bool SVGFontManager::setDefaultTypeface(const std::shared_ptr& typeface) { if (typeface) { - _defaultTypeface = typeface; + defaultTypeface = typeface; return true; } return false; }; void SVGFontManager::addFontStyle(const std::string& fontFamily, FontStyle style) { - if (_typefaces.at(fontFamily).find(style) == _typefaces.at(fontFamily).end()) { - _typefaces[fontFamily][style] = nullptr; + if (typefaceMap.at(fontFamily).find(style) == typefaceMap.at(fontFamily).end()) { + typefaceMap[fontFamily][style] = nullptr; } } void SVGFontManager::setTypeface(const std::string& fontFamily, FontStyle style, const std::shared_ptr& typeface) { - _typefaces[fontFamily][style] = typeface; + typefaceMap[fontFamily][style] = typeface; } std::vector SVGFontManager::getFontFamilies() const { std::vector families; - families.reserve(_typefaces.size()); - for (const auto& [family, _] : _typefaces) { + families.reserve(typefaceMap.size()); + for (const auto& [family, _] : typefaceMap) { families.push_back(family); } return families; } std::vector SVGFontManager::getFontStyles(const std::string& fontFamily) const { - if (auto iter = _typefaces.find(fontFamily); iter != _typefaces.end()) { + if (auto iter = typefaceMap.find(fontFamily); iter != typefaceMap.end()) { std::vector styles; styles.reserve(iter->second.size()); for (const auto& [style, _] : iter->second) { @@ -63,8 +63,8 @@ std::vector SVGFontManager::getFontStyles(const std::string& fontFami std::shared_ptr SVGFontManager::getTypefaceForConfig(const std::string& fontFamily, FontStyle style) const { - auto familyIter = _typefaces.find(fontFamily); - if (familyIter == _typefaces.end()) { + auto familyIter = typefaceMap.find(fontFamily); + if (familyIter == typefaceMap.end()) { return nullptr; } auto styleIter = familyIter->second.find(style); @@ -77,13 +77,13 @@ std::shared_ptr SVGFontManager::getTypefaceForConfig(const std::string std::shared_ptr SVGFontManager::getTypefaceForRender(const std::string& fontFamily, FontStyle style) const { - auto familyIter = _typefaces.find(fontFamily); - if (familyIter == _typefaces.end()) { - return _defaultTypeface; + auto familyIter = typefaceMap.find(fontFamily); + if (familyIter == typefaceMap.end()) { + return defaultTypeface; } auto styleIter = familyIter->second.find(style); if (styleIter == familyIter->second.end()) { - return _defaultTypeface; + return defaultTypeface; } else { return styleIter->second; } diff --git a/src/svg/SVGRenderContext.cpp b/src/svg/SVGRenderContext.cpp index 6354bf30..ae266605 100644 --- a/src/svg/SVGRenderContext.cpp +++ b/src/svg/SVGRenderContext.cpp @@ -77,9 +77,9 @@ constexpr float kCMMultiplier = kMMMultiplier * 10; float SVGLengthContext::resolve(const SVGLength& l, LengthType t) const { switch (l.unit()) { case SVGLength::Unit::Number: { - if (_patternUnit.has_value()) { - if (_patternUnit.value().type() == SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox) { - return l.value() * length_size_for_type(_viewport, t); + if (patternUnit.has_value()) { + if (patternUnit.value().type() == SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox) { + return l.value() * length_size_for_type(_viewPort, t); } else { return l.value(); } @@ -90,17 +90,17 @@ float SVGLengthContext::resolve(const SVGLength& l, LengthType t) const { case SVGLength::Unit::PX: return l.value(); case SVGLength::Unit::Percentage: - return l.value() * length_size_for_type(_viewport, t) / 100; + return l.value() * length_size_for_type(_viewPort, t) / 100; case SVGLength::Unit::CM: - return l.value() * _dpi * kCMMultiplier; + return l.value() * dpi * kCMMultiplier; case SVGLength::Unit::MM: - return l.value() * _dpi * kMMMultiplier; + return l.value() * dpi * kMMMultiplier; case SVGLength::Unit::IN: - return l.value() * _dpi * kINMultiplier; + return l.value() * dpi * kINMultiplier; case SVGLength::Unit::PT: - return l.value() * _dpi * kPTMultiplier; + return l.value() * dpi * kPTMultiplier; case SVGLength::Unit::PC: - return l.value() * _dpi * kPCMultiplier; + return l.value() * dpi * kPCMultiplier; default: //unsupported unit type ASSERT(false); @@ -145,11 +145,11 @@ LineJoin toSkJoin(const SVGLineJoin& join) { std::unique_ptr dash_effect(const SVGPresentationAttributes& props, const SVGLengthContext& lctx) { - if (props.fStrokeDashArray->type() != SVGDashArray::Type::DashArray) { + if (props.StrokeDashArray->type() != SVGDashArray::Type::DashArray) { return nullptr; } - const auto& da = *props.fStrokeDashArray; + const auto& da = *props.StrokeDashArray; const auto count = da.dashArray().size(); std::vector intervals; intervals.reserve(count); @@ -166,7 +166,7 @@ std::unique_ptr dash_effect(const SVGPresentationAttributes& props, ASSERT((intervals.size() & 1) == 0); - const auto phase = lctx.resolve(*props.fStrokeDashOffset, SVGLengthContext::LengthType::Other); + const auto phase = lctx.resolve(*props.StrokeDashOffset, SVGLengthContext::LengthType::Other); return PathEffect::MakeDash(intervals.data(), static_cast(intervals.size()), phase); } @@ -182,60 +182,60 @@ SVGRenderContext::SVGRenderContext(Context* device, Canvas* canvas, const SVGIDMapper& mapper, const SVGLengthContext& lctx, const SkSVGPresentationContext& pctx, const OBBScope& obbs, const Matrix& matrix) - : _fontMgr(fontManager), _nodeIDMapper(mapper), _lengthContext(lctx), - _presentationContext(pctx), _renderCanvas(canvas), _recorder(), - _canvas(_recorder.beginRecording()), _scope(obbs), _deviceContext(device), _matrix(matrix) { + : fontManager(fontManager), nodeIDMapper(mapper), _lengthContext(lctx), + _presentationContext(pctx), renderCanvas(canvas), recorder(), + _canvas(recorder.beginRecording()), scope(obbs), _deviceContext(device), matrix(matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other) - : SVGRenderContext(other._deviceContext, other._canvas, other._fontMgr, other._nodeIDMapper, - *other._lengthContext, *other._presentationContext, other._scope, - other._matrix) { + : SVGRenderContext(other._deviceContext, other._canvas, other.fontManager, other.nodeIDMapper, + *other._lengthContext, *other._presentationContext, other.scope, + other.matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, Canvas* canvas) - : SVGRenderContext(other._deviceContext, canvas, other._fontMgr, other._nodeIDMapper, - *other._lengthContext, *other._presentationContext, other._scope, - other._matrix) { + : SVGRenderContext(other._deviceContext, canvas, other.fontManager, other.nodeIDMapper, + *other._lengthContext, *other._presentationContext, other.scope, + other.matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, const SVGLengthContext& lengthCtx) - : SVGRenderContext(other._deviceContext, other._canvas, other._fontMgr, other._nodeIDMapper, - lengthCtx, *other._presentationContext, other._scope, other._matrix) { + : SVGRenderContext(other._deviceContext, other._canvas, other.fontManager, other.nodeIDMapper, + lengthCtx, *other._presentationContext, other.scope, other.matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, Canvas* canvas, const SVGLengthContext& lengthCtx) - : SVGRenderContext(other._deviceContext, canvas, other._fontMgr, other._nodeIDMapper, lengthCtx, - *other._presentationContext, other._scope, other._matrix) { + : SVGRenderContext(other._deviceContext, canvas, other.fontManager, other.nodeIDMapper, + lengthCtx, *other._presentationContext, other.scope, other.matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, const SVGNode* node) - : SVGRenderContext(other._deviceContext, other._canvas, other._fontMgr, other._nodeIDMapper, + : SVGRenderContext(other._deviceContext, other._canvas, other.fontManager, other.nodeIDMapper, *other._lengthContext, *other._presentationContext, OBBScope{node, this}, - other._matrix) { + other.matrix) { } SVGRenderContext::~SVGRenderContext() { _canvas->restoreToCount(0); - auto picture = _recorder.finishRecordingAsPicture(); + auto picture = recorder.finishRecordingAsPicture(); if (!picture) { return; } - _renderCanvas->save(); + renderCanvas->save(); if (_clipPath.has_value()) { - _renderCanvas->clipPath(_clipPath.value()); + renderCanvas->clipPath(_clipPath.value()); } auto matrix = Matrix::I(); - _renderCanvas->drawPicture(picture, &matrix, &_picturePaint); - _renderCanvas->restore(); + renderCanvas->drawPicture(picture, &matrix, &picturePaint); + renderCanvas->restore(); } SVGRenderContext SVGRenderContext::CopyForPaint(const SVGRenderContext& context, Canvas* canvas, const SVGLengthContext& lengthCtx) { SVGRenderContext copyContext(context, canvas, lengthCtx); - copyContext._deferredPaintOpacity = context._deferredPaintOpacity; + copyContext.deferredPaintOpacity = context.deferredPaintOpacity; return copyContext; } @@ -243,21 +243,21 @@ std::shared_ptr SVGRenderContext::findNodeById(const SVGIRI& iri) const if (iri.type() != SVGIRI::Type::Local) { return nullptr; } - auto p = _nodeIDMapper.find(iri.iri())->second; + auto p = nodeIDMapper.find(iri.iri())->second; return p; } void SVGRenderContext::applyPresentationAttributes(const SVGPresentationAttributes& attrs, uint32_t flags) { -#define ApplyLazyInheritedAttribute(ATTR) \ - do { \ - /* All attributes should be defined on the inherited context. */ \ - ASSERT(_presentationContext->_inherited.f##ATTR.isValue()); \ - const auto& attr = attrs.f##ATTR; \ - if (attr.isValue() && *attr != *_presentationContext->_inherited.f##ATTR) { \ - /* Update the local attribute value */ \ - _presentationContext.writable()->_inherited.f##ATTR.set(*attr); \ - } \ +#define ApplyLazyInheritedAttribute(ATTR) \ + do { \ + /* All attributes should be defined on the inherited context. */ \ + ASSERT(_presentationContext->_inherited.ATTR.isValue()); \ + const auto& attr = attrs.ATTR; \ + if (attr.isValue() && *attr != *_presentationContext->_inherited.ATTR) { \ + /* Update the local attribute value */ \ + _presentationContext.writable()->_inherited.ATTR.set(*attr); \ + } \ } while (false) ApplyLazyInheritedAttribute(Fill); @@ -284,24 +284,24 @@ void SVGRenderContext::applyPresentationAttributes(const SVGPresentationAttribut #undef ApplyLazyInheritedAttribute - if (attrs.fClipPath.isValue()) { - _clipPath = this->applyClip(*attrs.fClipPath); + if (attrs.ClipPath.isValue()) { + _clipPath = this->applyClip(*attrs.ClipPath); } - const bool hasFilter = attrs.fFilter.isValue(); - if (attrs.fOpacity.isValue()) { - _picturePaint.setAlpha(this->applyOpacity(*attrs.fOpacity, flags, hasFilter)); + const bool hasFilter = attrs.Filter.isValue(); + if (attrs.Opacity.isValue()) { + picturePaint.setAlpha(this->applyOpacity(*attrs.Opacity, flags, hasFilter)); } - if (attrs.fMask.isValue()) { - if (auto maskFilter = this->applyMask(*attrs.fMask)) { - _picturePaint.setMaskFilter(maskFilter); + if (attrs.Mask.isValue()) { + if (auto maskFilter = this->applyMask(*attrs.Mask)) { + picturePaint.setMaskFilter(maskFilter); } } if (hasFilter) { - if (auto imageFilter = this->applyFilter(*attrs.fFilter)) { - _picturePaint.setImageFilter(imageFilter); + if (auto imageFilter = this->applyFilter(*attrs.Filter)) { + picturePaint.setImageFilter(imageFilter); } } } @@ -309,8 +309,8 @@ void SVGRenderContext::applyPresentationAttributes(const SVGPresentationAttribut float SVGRenderContext::applyOpacity(float opacity, uint32_t flags, bool hasFilter) { opacity = std::clamp(opacity, 0.0f, 1.0f); const auto& props = _presentationContext->_inherited; - const bool hasFill = props.fFill->type() != SVGPaint::Type::None; - const bool hasStroke = props.fStroke->type() != SVGPaint::Type::None; + const bool hasFill = props.Fill->type() != SVGPaint::Type::None; + const bool hasStroke = props.Stroke->type() != SVGPaint::Type::None; // We can apply the opacity as paint alpha if it only affects one atomic draw. // For now, this means all of the following must be true: @@ -319,14 +319,14 @@ float SVGRenderContext::applyOpacity(float opacity, uint32_t flags, bool hasFilt // - it does not have a filter. // Going forward, we may needto refine this heuristic (e.g. to accommodate markers). if ((flags & kLeaf) && (hasFill ^ hasStroke) && !hasFilter) { - _deferredPaintOpacity *= opacity; + deferredPaintOpacity *= opacity; return 1.0f; } else { return opacity; } } -std::shared_ptr SVGRenderContext::applyFilter(const SVGFuncIRI& filter) { +std::shared_ptr SVGRenderContext::applyFilter(const SVGFuncIRI& filter) const { if (filter.type() != SVGFuncIRI::Type::IRI) { return nullptr; } @@ -344,7 +344,7 @@ void SVGRenderContext::saveOnce() { _canvas->save(); } -Path SVGRenderContext::applyClip(const SVGFuncIRI& clip) { +Path SVGRenderContext::applyClip(const SVGFuncIRI& clip) const { if (clip.type() != SVGFuncIRI::Type::IRI) { return Path(); } @@ -407,10 +407,10 @@ std::shared_ptr SVGRenderContext::applyMask(const SVGFuncIRI& mask) // if (!shaderImage) { // return nullptr; // } - auto matrix = _matrix * Matrix::MakeTrans(-maskBound.left, -maskBound.top); + auto transMatrix = matrix * Matrix::MakeTrans(-maskBound.left, -maskBound.top); auto shaderImage = Image::MakeFrom(picture, static_cast(bound.width() * matrix.getScaleX()), - static_cast(bound.height() * matrix.getScaleY()), &matrix); + static_cast(bound.height() * matrix.getScaleY()), &transMatrix); // { // auto tempSurface = Surface::Make(_deviceContext, static_cast(bound.width()) * 2, // static_cast(bound.height()) * 2); @@ -464,8 +464,8 @@ std::optional SVGRenderContext::commonPaint(const SVGPaint& paint_selecto // and node being rendered. SkSVGPresentationContext pctx; pctx._namedColors = _presentationContext->_namedColors; - SVGRenderContext local_ctx(_deviceContext, _canvas, _fontMgr, _nodeIDMapper, *_lengthContext, - pctx, _scope, Matrix::I()); + SVGRenderContext local_ctx(_deviceContext, _canvas, fontManager, nodeIDMapper, + *_lengthContext, pctx, scope, Matrix::I()); const auto node = this->findNodeById(paint_selector.iri()); if (!node || !node->asPaint(local_ctx, &(p.value()))) { @@ -476,19 +476,19 @@ std::optional SVGRenderContext::commonPaint(const SVGPaint& paint_selecto default: break; } - p->setAntiAlias(true); // TODO: shape-rendering support + p->setAntiAlias(true); // We observe 3 opacity components: // - initial paint server opacity (e.g. color stop opacity) // - paint-specific opacity (e.g. 'fill-opacity', 'stroke-opacity') // - deferred opacity override (optimization for leaf nodes 'opacity') - p->setAlpha(std::clamp(p->getAlpha() * paint_opacity * _deferredPaintOpacity, 0.0f, 1.0f)); + p->setAlpha(std::clamp(p->getAlpha() * paint_opacity * deferredPaintOpacity, 0.0f, 1.0f)); return p; } std::optional SVGRenderContext::fillPaint() const { const auto& props = _presentationContext->_inherited; - auto p = this->commonPaint(*props.fFill, *props.fFillOpacity); + auto p = this->commonPaint(*props.Fill, *props.FillOpacity); if (p.has_value()) { p->setStyle(PaintStyle::Fill); @@ -499,16 +499,16 @@ std::optional SVGRenderContext::fillPaint() const { std::optional SVGRenderContext::strokePaint() const { const auto& props = _presentationContext->_inherited; - auto p = this->commonPaint(*props.fStroke, *props.fStrokeOpacity); + auto p = this->commonPaint(*props.Stroke, *props.StrokeOpacity); if (p.has_value()) { p->setStyle(PaintStyle::Stroke); p->setStrokeWidth( - _lengthContext->resolve(*props.fStrokeWidth, SVGLengthContext::LengthType::Other)); + _lengthContext->resolve(*props.StrokeWidth, SVGLengthContext::LengthType::Other)); Stroke stroke; - stroke.cap = toSkCap(*props.fStrokeLineCap); - stroke.join = toSkJoin(*props.fStrokeLineJoin); - stroke.miterLimit = *props.fStrokeMiterLimit; + stroke.cap = toSkCap(*props.StrokeLineCap); + stroke.join = toSkJoin(*props.StrokeLineJoin); + stroke.miterLimit = *props.StrokeMiterLimit; p->setStroke(stroke); //TODO (YG) @@ -532,7 +532,7 @@ SVGColorType SVGRenderContext::resolveSvgColor(const SVGColor& color) const { case SVGColor::Type::Color: return color.color(); case SVGColor::Type::CurrentColor: - return *_presentationContext->_inherited.fColor; + return *_presentationContext->_inherited.Color; case SVGColor::Type::ICCColor: return Color::Black(); } @@ -540,25 +540,25 @@ SVGColorType SVGRenderContext::resolveSvgColor(const SVGColor& color) const { SVGRenderContext::OBBTransform SVGRenderContext::transformForCurrentOBB( SVGObjectBoundingBoxUnits u) const { - if (!_scope.node || u.type() == SVGObjectBoundingBoxUnits::Type::UserSpaceOnUse) { + if (!scope.node || u.type() == SVGObjectBoundingBoxUnits::Type::UserSpaceOnUse) { return {{0, 0}, {1, 1}}; } - ASSERT(_scope.context); + ASSERT(scope.context); - const auto obb = _scope.node->objectBoundingBox(*_scope.context); + const auto obb = scope.node->objectBoundingBox(*scope.context); return {{obb.x(), obb.y()}, {obb.width(), obb.height()}}; } Rect SVGRenderContext::resolveOBBRect(const SVGLength& x, const SVGLength& y, const SVGLength& w, - const SVGLength& h, SVGObjectBoundingBoxUnits obbu) const { + const SVGLength& h, SVGObjectBoundingBoxUnits unit) const { CopyOnWrite lctx(_lengthContext); - if (obbu.type() == SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox) { + if (unit.type() == SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox) { *lctx.writable() = SVGLengthContext({1, 1}); } auto r = lctx->resolveRect(x, y, w, h); - const auto obbt = this->transformForCurrentOBB(obbu); + const auto obbt = this->transformForCurrentOBB(unit); return Rect::MakeXYWH(obbt.scale.x * r.x() + obbt.offset.x, obbt.scale.y * r.y() + obbt.offset.y, obbt.scale.x * r.width(), obbt.scale.y * r.height()); diff --git a/src/svg/node/SVGCircle.cpp b/src/svg/node/SVGCircle.cpp index 040d4e83..32273b8f 100644 --- a/src/svg/node/SVGCircle.cpp +++ b/src/svg/node/SVGCircle.cpp @@ -37,9 +37,9 @@ bool SVGCircle::parseAndSetAttribute(const char* n, const char* v) { #ifndef RENDER_SVG std::tuple SVGCircle::resolve(const SVGLengthContext& lctx) const { - const auto cx = lctx.resolve(fCx, SVGLengthContext::LengthType::Horizontal); - const auto cy = lctx.resolve(fCy, SVGLengthContext::LengthType::Vertical); - const auto r = lctx.resolve(fR, SVGLengthContext::LengthType::Other); + const auto cx = lctx.resolve(Cx, SVGLengthContext::LengthType::Horizontal); + const auto cy = lctx.resolve(Cy, SVGLengthContext::LengthType::Vertical); + const auto r = lctx.resolve(R, SVGLengthContext::LengthType::Other); return std::make_tuple(Point::Make(cx, cy), r); } diff --git a/src/svg/node/SVGClipPath.cpp b/src/svg/node/SVGClipPath.cpp index e681b317..76cad966 100644 --- a/src/svg/node/SVGClipPath.cpp +++ b/src/svg/node/SVGClipPath.cpp @@ -36,7 +36,7 @@ bool SVGClipPath::parseAndSetAttribute(const char* n, const char* v) { Path SVGClipPath::resolveClip(const SVGRenderContext& ctx) const { auto clip = this->asPath(ctx); - const auto obbt = ctx.transformForCurrentOBB(fClipPathUnits); + const auto obbt = ctx.transformForCurrentOBB(ClipPathUnits); const auto m = Matrix::MakeTrans(obbt.offset.x, obbt.offset.y) * Matrix::MakeScale(obbt.scale.x, obbt.scale.y); clip.transform(m); diff --git a/src/svg/node/SVGEllipse.cpp b/src/svg/node/SVGEllipse.cpp index 0cfa8cb3..454986f3 100644 --- a/src/svg/node/SVGEllipse.cpp +++ b/src/svg/node/SVGEllipse.cpp @@ -38,14 +38,14 @@ bool SkSVGEllipse::parseAndSetAttribute(const char* n, const char* v) { #ifndef RENDER_SVG Rect SkSVGEllipse::resolve(const SVGLengthContext& lctx) const { - const auto cx = lctx.resolve(fCx, SVGLengthContext::LengthType::Horizontal); - const auto cy = lctx.resolve(fCy, SVGLengthContext::LengthType::Vertical); + const auto cx = lctx.resolve(Cx, SVGLengthContext::LengthType::Horizontal); + const auto cy = lctx.resolve(Cy, SVGLengthContext::LengthType::Vertical); // https://www.w3.org/TR/SVG2/shapes.html#EllipseElement // // An auto value for either rx or ry is converted to a used value, following the rules given // above for rectangles (but without any clamping based on width or height). - const auto [rx, ry] = ResolveOptionalRadii(fRx, fRy, lctx); + const auto [rx, ry] = ResolveOptionalRadii(Rx, Ry, lctx); // A computed value of zero for either dimension, or a computed value of auto for both // dimensions, disables rendering of the element. diff --git a/src/svg/node/SVGFe.cpp b/src/svg/node/SVGFe.cpp index d45f3fe8..1ba144c9 100644 --- a/src/svg/node/SVGFe.cpp +++ b/src/svg/node/SVGFe.cpp @@ -35,10 +35,10 @@ std::shared_ptr SkSVGFe::makeImageFilter( Rect SkSVGFe::resolveBoundaries(const SVGRenderContext& ctx, const SkSVGFilterContext& filterContext) const { - const auto x = fX.has_value() ? *fX : SVGLength(0, SVGLength::Unit::Percentage); - const auto y = fY.has_value() ? *fY : SVGLength(0, SVGLength::Unit::Percentage); - const auto w = fWidth.has_value() ? *fWidth : SVGLength(100, SVGLength::Unit::Percentage); - const auto h = fHeight.has_value() ? *fHeight : SVGLength(100, SVGLength::Unit::Percentage); + const auto x = X.has_value() ? *X : SVGLength(0, SVGLength::Unit::Percentage); + const auto y = Y.has_value() ? *Y : SVGLength(0, SVGLength::Unit::Percentage); + const auto w = Width.has_value() ? *Width : SVGLength(100, SVGLength::Unit::Percentage); + const auto h = Height.has_value() ? *Height : SVGLength(100, SVGLength::Unit::Percentage); return ctx.resolveOBBRect(x, y, w, h, filterContext.primitiveUnits()); } @@ -47,16 +47,16 @@ static bool AnyIsStandardInput(const SkSVGFilterContext& fctx, const std::vector& inputs) { for (const auto& in : inputs) { switch (in.type()) { - case SVGFeInputType::Type::kFilterPrimitiveReference: + case SVGFeInputType::Type::FilterPrimitiveReference: break; - case SVGFeInputType::Type::kSourceGraphic: - case SVGFeInputType::Type::kSourceAlpha: - case SVGFeInputType::Type::kBackgroundImage: - case SVGFeInputType::Type::kBackgroundAlpha: - case SVGFeInputType::Type::kFillPaint: - case SVGFeInputType::Type::kStrokePaint: + case SVGFeInputType::Type::SourceGraphic: + case SVGFeInputType::Type::SourceAlpha: + case SVGFeInputType::Type::BackgroundImage: + case SVGFeInputType::Type::BackgroundAlpha: + case SVGFeInputType::Type::FillPaint: + case SVGFeInputType::Type::StrokePaint: return true; - case SVGFeInputType::Type::kUnspecified: + case SVGFeInputType::Type::Unspecified: // Unspecified means previous result (which may be SourceGraphic). if (fctx.previousResultIsSourceGraphic()) { return true; @@ -92,17 +92,17 @@ Rect SkSVGFe::resolveFilterSubregion(const SVGRenderContext& ctx, const Rect boundaries = this->resolveBoundaries(ctx, fctx); // Compute and return the fully resolved subregion. - return Rect::MakeXYWH(fX.has_value() ? boundaries.left : defaultSubregion.left, - fY.has_value() ? boundaries.top : defaultSubregion.top, - fWidth.has_value() ? boundaries.width() : defaultSubregion.width(), - fHeight.has_value() ? boundaries.height() : defaultSubregion.height()); + return Rect::MakeXYWH(X.has_value() ? boundaries.left : defaultSubregion.left, + Y.has_value() ? boundaries.top : defaultSubregion.top, + Width.has_value() ? boundaries.width() : defaultSubregion.width(), + Height.has_value() ? boundaries.height() : defaultSubregion.height()); } SVGColorspace SkSVGFe::resolveColorspace(const SVGRenderContext& ctx, const SkSVGFilterContext&) const { - constexpr SVGColorspace kDefaultCS = SVGColorspace::kSRGB; - const SVGColorspace cs = *ctx.presentationContext()._inherited.fColorInterpolationFilters; - return cs == SVGColorspace::kAuto ? kDefaultCS : cs; + constexpr SVGColorspace kDefaultCS = SVGColorspace::SRGB; + const SVGColorspace cs = *ctx.presentationContext()._inherited.ColorInterpolationFilters; + return cs == SVGColorspace::Auto ? kDefaultCS : cs; } void SkSVGFe::applyProperties(SVGRenderContext* ctx) const { @@ -123,12 +123,12 @@ bool SkSVGFe::parseAndSetAttribute(const char* name, const char* value) { template <> bool SVGAttributeParser::parse(SVGFeInputType* type) { static constexpr std::tuple gTypeMap[] = { - {"SourceGraphic", SVGFeInputType::Type::kSourceGraphic}, - {"SourceAlpha", SVGFeInputType::Type::kSourceAlpha}, - {"BackgroundImage", SVGFeInputType::Type::kBackgroundImage}, - {"BackgroundAlpha", SVGFeInputType::Type::kBackgroundAlpha}, - {"FillPaint", SVGFeInputType::Type::kFillPaint}, - {"StrokePaint", SVGFeInputType::Type::kStrokePaint}, + {"SourceGraphic", SVGFeInputType::Type::SourceGraphic}, + {"SourceAlpha", SVGFeInputType::Type::SourceAlpha}, + {"BackgroundImage", SVGFeInputType::Type::BackgroundImage}, + {"BackgroundAlpha", SVGFeInputType::Type::BackgroundAlpha}, + {"FillPaint", SVGFeInputType::Type::FillPaint}, + {"StrokePaint", SVGFeInputType::Type::StrokePaint}, }; SVGStringType resultId; diff --git a/src/svg/node/SVGFeBlend.cpp b/src/svg/node/SVGFeBlend.cpp index 5605f090..5e56f0d4 100644 --- a/src/svg/node/SVGFeBlend.cpp +++ b/src/svg/node/SVGFeBlend.cpp @@ -30,7 +30,7 @@ class SVGRenderContext; bool SkSVGFeBlend::parseAndSetAttribute(const char* name, const char* value) { return INHERITED::parseAndSetAttribute(name, value) || this->setIn2(SVGAttributeParser::parse("in2", name, value)) || - this->setMode(SVGAttributeParser::parse("mode", name, value)); + this->setBlendMode(SVGAttributeParser::parse("mode", name, value)); } #ifndef RENDER_SVG @@ -55,7 +55,7 @@ std::shared_ptr SkSVGFeBlend::onMakeImageFilter(const SVGRenderCont // const Rect cropRect = this->resolveFilterSubregion(ctx, fctx); // const BlendMode blendMode = GetBlendMode(this->getMode()); const SVGColorspace colorspace = this->resolveColorspace(ctx, fctx); - const std::shared_ptr background = fctx.resolveInput(ctx, fIn2, colorspace); + const std::shared_ptr background = fctx.resolveInput(ctx, In2, colorspace); const std::shared_ptr foreground = fctx.resolveInput(ctx, this->getIn(), colorspace); return ImageFilter::Compose(background, foreground); // TODO (YG),relay on ImageFilters::Blend to implement this diff --git a/src/svg/node/SVGFeColorMatrix.cpp b/src/svg/node/SVGFeColorMatrix.cpp index d3d6f662..f31230d2 100644 --- a/src/svg/node/SVGFeColorMatrix.cpp +++ b/src/svg/node/SVGFeColorMatrix.cpp @@ -36,10 +36,10 @@ bool SkSVGFeColorMatrix::parseAndSetAttribute(const char* name, const char* valu template <> bool SVGAttributeParser::parse(SVGFeColorMatrixType* type) { static constexpr std::tuple gTypeMap[] = { - {"matrix", SVGFeColorMatrixType::kMatrix}, - {"saturate", SVGFeColorMatrixType::kSaturate}, - {"hueRotate", SVGFeColorMatrixType::kHueRotate}, - {"luminanceToAlpha", SVGFeColorMatrixType::kLuminanceToAlpha}, + {"matrix", SVGFeColorMatrixType::Matrix}, + {"saturate", SVGFeColorMatrixType::Saturate}, + {"hueRotate", SVGFeColorMatrixType::HueRotate}, + {"luminanceToAlpha", SVGFeColorMatrixType::LuminanceToAlpha}, }; return this->parseEnumMap(gTypeMap, type) && this->parseEOSToken(); @@ -47,24 +47,24 @@ bool SVGAttributeParser::parse(SVGFeColorMatrixType* type) { #ifndef RENDER_SVG ColorMatrix SkSVGFeColorMatrix::makeMatrixForType() const { - if (fValues.empty() && fType != SVGFeColorMatrixType::kLuminanceToAlpha) { + if (Values.empty() && Type != SVGFeColorMatrixType::LuminanceToAlpha) { return ColorMatrix(); } - switch (fType) { - case SVGFeColorMatrixType::kMatrix: { - if (fValues.size() < 20) { + switch (Type) { + case SVGFeColorMatrixType::Matrix: { + if (Values.size() < 20) { return ColorMatrix(); } ColorMatrix m; - std::copy_n(fValues.data(), 20, m.begin()); + std::copy_n(Values.data(), 20, m.begin()); return m; } - case SVGFeColorMatrixType::kSaturate: - return MakeSaturate(!fValues.empty() ? fValues[0] : 1); - case SVGFeColorMatrixType::kHueRotate: - return MakeHueRotate(!fValues.empty() ? fValues[0] : 0); - case SVGFeColorMatrixType::kLuminanceToAlpha: + case SVGFeColorMatrixType::Saturate: + return MakeSaturate(!Values.empty() ? Values[0] : 1); + case SVGFeColorMatrixType::HueRotate: + return MakeHueRotate(!Values.empty() ? Values[0] : 0); + case SVGFeColorMatrixType::LuminanceToAlpha: return MakeLuminanceToAlpha(); } } diff --git a/src/svg/node/SVGFeComponentTransfer.cpp b/src/svg/node/SVGFeComponentTransfer.cpp index bf52a904..8a400e6b 100644 --- a/src/svg/node/SVGFeComponentTransfer.cpp +++ b/src/svg/node/SVGFeComponentTransfer.cpp @@ -74,7 +74,8 @@ std::vector SkSVGFeFunc::getTable() const { // https://www.w3.org/TR/SVG11/filters.html#feComponentTransferTypeAttribute const auto make_linear = [this]() -> std::vector { std::vector tbl(256); - const float slope = this->getSlope(), intercept255 = this->getIntercept() * 255; + const float slope = this->getSlope(); + const float intercept255 = this->getIntercept() * 255; for (size_t i = 0; i < 256; ++i) { tbl[i] = static_cast( @@ -86,7 +87,8 @@ std::vector SkSVGFeFunc::getTable() const { const auto make_gamma = [this]() -> std::vector { std::vector tbl(256); - const float exponent = this->getExponent(), offset = this->getOffset(); + const float exponent = this->getExponent(); + const float offset = this->getOffset(); for (size_t i = 0; i < 256; ++i) { const float component = offset + std::pow(static_cast(i) * (1 / 255.f), exponent); @@ -112,7 +114,8 @@ std::vector SkSVGFeFunc::getTable() const { const SVGNumberType v1 = std::clamp(vals[k + 1], 0.f, 1.f); // start/end component table indices - const size_t c_start = k * 255 / n, c_end = (k + 1) * 255 / n; + const size_t c_start = k * 255 / n; + const size_t c_end = (k + 1) * 255 / n; ASSERT(c_end <= 255); for (size_t ci = c_start; ci < c_end; ++ci) { @@ -138,15 +141,15 @@ std::vector SkSVGFeFunc::getTable() const { }; switch (this->getType()) { - case SVGFeFuncType::kIdentity: + case SVGFeFuncType::Identity: return {}; - case SVGFeFuncType::kTable: + case SVGFeFuncType::Table: return make_table(); - case SVGFeFuncType::kDiscrete: + case SVGFeFuncType::Discrete: return make_discrete(); - case SVGFeFuncType::kLinear: + case SVGFeFuncType::Linear: return make_linear(); - case SVGFeFuncType::kGamma: + case SVGFeFuncType::Gamma: return make_gamma(); } @@ -168,9 +171,9 @@ bool SkSVGFeFunc::parseAndSetAttribute(const char* name, const char* val) { template <> bool SVGAttributeParser::parse(SVGFeFuncType* type) { static constexpr std::tuple gTypeMap[] = { - {"identity", SVGFeFuncType::kIdentity}, {"table", SVGFeFuncType::kTable}, - {"discrete", SVGFeFuncType::kDiscrete}, {"linear", SVGFeFuncType::kLinear}, - {"gamma", SVGFeFuncType::kGamma}, + {"identity", SVGFeFuncType::Identity}, {"table", SVGFeFuncType::Table}, + {"discrete", SVGFeFuncType::Discrete}, {"linear", SVGFeFuncType::Linear}, + {"gamma", SVGFeFuncType::Gamma}, }; return this->parseEnumMap(gTypeMap, type) && this->parseEOSToken(); diff --git a/src/svg/node/SVGFeComposite.cpp b/src/svg/node/SVGFeComposite.cpp index f895cc52..366a1554 100644 --- a/src/svg/node/SVGFeComposite.cpp +++ b/src/svg/node/SVGFeComposite.cpp @@ -41,17 +41,17 @@ bool SkSVGFeComposite::parseAndSetAttribute(const char* name, const char* value) #ifndef RENDER_SVG BlendMode SkSVGFeComposite::BlendModeForOperator(SVGFeCompositeOperator op) { switch (op) { - case SVGFeCompositeOperator::kOver: + case SVGFeCompositeOperator::Over: return BlendMode::SrcOver; - case SVGFeCompositeOperator::kIn: + case SVGFeCompositeOperator::In: return BlendMode::SrcIn; - case SVGFeCompositeOperator::kOut: + case SVGFeCompositeOperator::Out: return BlendMode::SrcOut; - case SVGFeCompositeOperator::kAtop: + case SVGFeCompositeOperator::Atop: return BlendMode::SrcATop; - case SVGFeCompositeOperator::kXor: + case SVGFeCompositeOperator::Xor: return BlendMode::Xor; - case SVGFeCompositeOperator::kArithmetic: + case SVGFeCompositeOperator::Arithmetic: // Arithmetic is not handled with a blend ASSERT(false); return BlendMode::SrcOver; @@ -80,9 +80,9 @@ std::shared_ptr SkSVGFeComposite::onMakeImageFilter( template <> bool SVGAttributeParser::parse(SVGFeCompositeOperator* op) { static constexpr std::tuple gOpMap[] = { - {"over", SVGFeCompositeOperator::kOver}, {"in", SVGFeCompositeOperator::kIn}, - {"out", SVGFeCompositeOperator::kOut}, {"atop", SVGFeCompositeOperator::kAtop}, - {"xor", SVGFeCompositeOperator::kXor}, {"arithmetic", SVGFeCompositeOperator::kArithmetic}, + {"over", SVGFeCompositeOperator::Over}, {"in", SVGFeCompositeOperator::In}, + {"out", SVGFeCompositeOperator::Out}, {"atop", SVGFeCompositeOperator::Atop}, + {"xor", SVGFeCompositeOperator::Xor}, {"arithmetic", SVGFeCompositeOperator::Arithmetic}, }; return this->parseEnumMap(gOpMap, op) && this->parseEOSToken(); diff --git a/src/svg/node/SVGFeDisplacementMap.cpp b/src/svg/node/SVGFeDisplacementMap.cpp index 14f60691..3e7144fe 100644 --- a/src/svg/node/SVGFeDisplacementMap.cpp +++ b/src/svg/node/SVGFeDisplacementMap.cpp @@ -48,7 +48,7 @@ std::shared_ptr SkSVGFeDisplacementMap::onMakeImageFilter( std::shared_ptr in = fctx.resolveInput(ctx, this->getIn()); std::shared_ptr in2 = fctx.resolveInput(ctx, this->getIn2(), colorspace); - float scale = fScale; + float scale = Scale; if (fctx.primitiveUnits().type() == SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox) { const auto obbt = ctx.transformForCurrentOBB(fctx.primitiveUnits()); scale = SVGLengthContext({obbt.scale.x, obbt.scale.y}) diff --git a/src/svg/node/SVGFeGaussianBlur.cpp b/src/svg/node/SVGFeGaussianBlur.cpp index eb6b272f..ca5287e9 100644 --- a/src/svg/node/SVGFeGaussianBlur.cpp +++ b/src/svg/node/SVGFeGaussianBlur.cpp @@ -25,7 +25,7 @@ namespace tgfx { bool SkSVGFeGaussianBlur::parseAndSetAttribute(const char* name, const char* value) { return INHERITED::parseAndSetAttribute(name, value) || - this->setStdDeviation(SVGAttributeParser::parse( + this->setstdDeviation(SVGAttributeParser::parse( "stdDeviation", name, value)); } @@ -33,8 +33,8 @@ bool SkSVGFeGaussianBlur::parseAndSetAttribute(const char* name, const char* val std::shared_ptr SkSVGFeGaussianBlur::onMakeImageFilter( const SVGRenderContext& ctx, const SkSVGFilterContext& fctx) const { auto scale = ctx.transformForCurrentOBB(fctx.primitiveUnits()).scale; - const auto sigmaX = fStdDeviation.fX * scale.x * 4; - const auto sigmaY = fStdDeviation.fY * scale.y * 4; + const auto sigmaX = stdDeviation.fX * scale.x * 4; + const auto sigmaY = stdDeviation.fY * scale.y * 4; return ImageFilter::Blur(sigmaX, sigmaY); } #endif diff --git a/src/svg/node/SVGFeLighting.cpp b/src/svg/node/SVGFeLighting.cpp index bd415e98..aefe2396 100644 --- a/src/svg/node/SVGFeLighting.cpp +++ b/src/svg/node/SVGFeLighting.cpp @@ -24,7 +24,7 @@ namespace tgfx { bool SkSVGFeLighting::parseAndSetAttribute(const char* n, const char* v) { return INHERITED::parseAndSetAttribute(n, v) || this->setSurfaceScale(SVGAttributeParser::parse("surfaceScale", n, v)) || - this->setKernelUnitLength(SVGAttributeParser::parse( + this->setUnitLength(SVGAttributeParser::parse( "kernelUnitLength", n, v)); } diff --git a/src/svg/node/SVGFeMorphology.cpp b/src/svg/node/SVGFeMorphology.cpp index e6309435..a42e2844 100644 --- a/src/svg/node/SVGFeMorphology.cpp +++ b/src/svg/node/SVGFeMorphology.cpp @@ -24,9 +24,9 @@ namespace tgfx { bool SkSVGFeMorphology::parseAndSetAttribute(const char* name, const char* value) { return INHERITED::parseAndSetAttribute(name, value) || - this->setOperator( + this->setMorphOperator( SVGAttributeParser::parse("operator", name, value)) || - this->setRadius( + this->setMorphRadius( SVGAttributeParser::parse("radius", name, value)); } diff --git a/src/svg/node/SVGFeTurbulence.cpp b/src/svg/node/SVGFeTurbulence.cpp index 0eacc41d..65ca684a 100644 --- a/src/svg/node/SVGFeTurbulence.cpp +++ b/src/svg/node/SVGFeTurbulence.cpp @@ -45,10 +45,10 @@ bool SVGAttributeParser::parse(SVGFeTurbulenceType* type) { bool parsedValue = false; if (this->parseExpectedStringToken("fractalNoise")) { - *type = SVGFeTurbulenceType(SVGFeTurbulenceType::kFractalNoise); + *type = SVGFeTurbulenceType(SVGFeTurbulenceType::FractalNoise); parsedValue = true; } else if (this->parseExpectedStringToken("turbulence")) { - *type = SVGFeTurbulenceType(SVGFeTurbulenceType::kTurbulence); + *type = SVGFeTurbulenceType(SVGFeTurbulenceType::Turbulence); parsedValue = true; } diff --git a/src/svg/node/SVGFilter.cpp b/src/svg/node/SVGFilter.cpp index fcea1cdd..b7f38562 100644 --- a/src/svg/node/SVGFilter.cpp +++ b/src/svg/node/SVGFilter.cpp @@ -45,11 +45,10 @@ void SkSVGFilter::applyProperties(SVGRenderContext* ctx) const { std::shared_ptr SkSVGFilter::buildFilterDAG(const SVGRenderContext& ctx) const { std::shared_ptr filter; - SkSVGFilterContext fctx(ctx.resolveOBBRect(fX, fY, fWidth, fHeight, fFilterUnits), - fPrimitiveUnits); + SkSVGFilterContext fctx(ctx.resolveOBBRect(X, Y, Width, Height, FilterUnits), PrimitiveUnits); SVGRenderContext localCtx(ctx); this->applyProperties(&localCtx); - SVGColorspace cs = SVGColorspace::kSRGB; + SVGColorspace cs = SVGColorspace::SRGB; for (const auto& child : fChildren) { if (!SkSVGFe::IsFilterEffect(child)) { continue; @@ -78,7 +77,7 @@ std::shared_ptr SkSVGFilter::buildFilterDAG(const SVGRenderContext& } // Convert to final destination colorspace - if (cs != SVGColorspace::kSRGB) { + if (cs != SVGColorspace::SRGB) { // filter = SkImageFilters::ColorFilter(SkColorFilters::LinearToSRGBGamma(), filter); //TODO (YG) diff --git a/src/svg/node/SVGFilterContext.cpp b/src/svg/node/SVGFilterContext.cpp index 6de1326f..ff38aed3 100644 --- a/src/svg/node/SVGFilterContext.cpp +++ b/src/svg/node/SVGFilterContext.cpp @@ -69,10 +69,10 @@ const SkSVGFilterContext::Result* SkSVGFilterContext::findResultById( const Rect& SkSVGFilterContext::filterPrimitiveSubregion(const SVGFeInputType& input) const { const Result* res = nullptr; - if (input.type() == SVGFeInputType::Type::kFilterPrimitiveReference) { + if (input.type() == SVGFeInputType::Type::FilterPrimitiveReference) { auto iter = fResults.find(input.id()); res = iter != fResults.end() ? &iter->second : nullptr; - } else if (input.type() == SVGFeInputType::Type::kUnspecified) { + } else if (input.type() == SVGFeInputType::Type::Unspecified) { res = &fPreviousResult; } return res ? res->fFilterSubregion : fFilterEffectsRegion; @@ -98,10 +98,10 @@ bool SkSVGFilterContext::previousResultIsSourceGraphic() const { // https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveInAttribute std::tuple, SVGColorspace> SkSVGFilterContext::getInput( const SVGRenderContext& ctx, const SVGFeInputType& inputType) const { - SVGColorspace inputCS = SVGColorspace::kSRGB; + SVGColorspace inputCS = SVGColorspace::SRGB; std::shared_ptr result; switch (inputType.type()) { - case SVGFeInputType::Type::kSourceAlpha: { + case SVGFeInputType::Type::SourceAlpha: { //TODO (YG) - Implement this with class ColorMatrix std::array colorMatrix{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}; @@ -109,10 +109,10 @@ std::tuple, SVGColorspace> SkSVGFilterContext::getI result = ImageFilter::ColorFilter(colorFilter); break; } - case SVGFeInputType::Type::kSourceGraphic: + case SVGFeInputType::Type::SourceGraphic: // Do nothing. break; - case SVGFeInputType::Type::kFillPaint: { + case SVGFeInputType::Type::FillPaint: { const auto& fillPaint = ctx.fillPaint(); if (fillPaint.has_value()) { //TODO (YG) - Implement this by dither and shader image filter @@ -122,7 +122,7 @@ std::tuple, SVGColorspace> SkSVGFilterContext::getI } break; } - case SVGFeInputType::Type::kStrokePaint: { + case SVGFeInputType::Type::StrokePaint: { // The paint filter doesn't apply fill/stroke styling, but use the paint settings // defined for strokes. const auto& strokePaint = ctx.strokePaint(); @@ -134,7 +134,7 @@ std::tuple, SVGColorspace> SkSVGFilterContext::getI } break; } - case SVGFeInputType::Type::kFilterPrimitiveReference: { + case SVGFeInputType::Type::FilterPrimitiveReference: { const Result* res = findResultById(inputType.id()); if (res) { result = res->fImageFilter; @@ -142,7 +142,7 @@ std::tuple, SVGColorspace> SkSVGFilterContext::getI } break; } - case SVGFeInputType::Type::kUnspecified: { + case SVGFeInputType::Type::Unspecified: { result = fPreviousResult.fImageFilter; inputCS = fPreviousResult.fColorspace; break; diff --git a/src/svg/node/SVGGradient.cpp b/src/svg/node/SVGGradient.cpp index 8e722174..94629f70 100644 --- a/src/svg/node/SVGGradient.cpp +++ b/src/svg/node/SVGGradient.cpp @@ -53,8 +53,8 @@ void SkSVGGradient::collectColorStops(const SVGRenderContext& ctx, std::vectortag() == SVGTag::LinearGradient || ref->tag() == SVGTag::RadialGradient)) { static_cast(ref.get())->collectColorStops(ctx, colors, positions); } @@ -92,11 +92,11 @@ bool SkSVGGradient::onAsPaint(const SVGRenderContext& ctx, Paint* paint) const { "SkSVGSpreadMethod::Type is out of sync"); static_assert(static_cast(SVGSpreadMethod::Type::Reflect) == TileMode::Mirror, "SkSVGSpreadMethod::Type is out of sync"); - const auto tileMode = static_cast(fSpreadMethod.type()); + const auto tileMode = static_cast(SpreadMethod.type()); - const auto obbt = ctx.transformForCurrentOBB(fGradientUnits); + const auto obbt = ctx.transformForCurrentOBB(GradientUnits); const auto localMatrix = Matrix::MakeTrans(obbt.offset.x, obbt.offset.y) * - Matrix::MakeScale(obbt.scale.x, obbt.scale.y) * fGradientTransform; + Matrix::MakeScale(obbt.scale.x, obbt.scale.y) * GradientTransform; paint->setShader(this->onMakeShader(ctx, colors, positions, tileMode, localMatrix)); return true; diff --git a/src/svg/node/SVGImage.cpp b/src/svg/node/SVGImage.cpp index 914c5ea6..81eb94a3 100644 --- a/src/svg/node/SVGImage.cpp +++ b/src/svg/node/SVGImage.cpp @@ -45,7 +45,7 @@ bool SkSVGImage::parseAndSetAttribute(const char* n, const char* v) { bool SkSVGImage::onPrepareToRender(SVGRenderContext* ctx) const { // Width or height of 0 disables rendering per spec: // https://www.w3.org/TR/SVG11/struct.html#ImageElement - return !fHref.iri().empty() && fWidth.value() > 0 && fHeight.value() > 0 && + return !Href.iri().empty() && Width.value() > 0 && Height.value() > 0 && INHERITED::onPrepareToRender(ctx); } @@ -124,11 +124,11 @@ SkSVGImage::ImageInfo SkSVGImage::LoadImage(const SVGIRI& iri, const Rect& viewP void SkSVGImage::onRender(const SVGRenderContext& ctx) const { // Per spec: x, w, width, height attributes establish the new viewport. const SVGLengthContext& lctx = ctx.lengthContext(); - const Rect viewPort = lctx.resolveRect(fX, fY, fWidth, fHeight); + const Rect viewPort = lctx.resolveRect(X, Y, Width, Height); //TODO (YG) ImageInfo image; - const auto imgInfo = LoadImage(fHref, viewPort, fPreserveAspectRatio); + const auto imgInfo = LoadImage(Href, viewPort, PreserveAspectRatio); if (!imgInfo.fImage) { LOGE("can't render image: load image failed\n"); return; @@ -150,7 +150,7 @@ Path SkSVGImage::onAsPath(const SVGRenderContext&) const { Rect SkSVGImage::onObjectBoundingBox(const SVGRenderContext& ctx) const { const SVGLengthContext& lctx = ctx.lengthContext(); - return lctx.resolveRect(fX, fY, fWidth, fHeight); + return lctx.resolveRect(X, Y, Width, Height); } #endif } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGLine.cpp b/src/svg/node/SVGLine.cpp index 8b8aac1a..b95167a2 100644 --- a/src/svg/node/SVGLine.cpp +++ b/src/svg/node/SVGLine.cpp @@ -37,10 +37,10 @@ bool SkSVGLine::parseAndSetAttribute(const char* n, const char* v) { #ifndef RENDER_SVG std::tuple SkSVGLine::resolve(const SVGLengthContext& lctx) const { - return std::make_tuple(Point::Make(lctx.resolve(fX1, SVGLengthContext::LengthType::Horizontal), - lctx.resolve(fY1, SVGLengthContext::LengthType::Vertical)), - Point::Make(lctx.resolve(fX2, SVGLengthContext::LengthType::Horizontal), - lctx.resolve(fY2, SVGLengthContext::LengthType::Vertical))); + return std::make_tuple(Point::Make(lctx.resolve(X1, SVGLengthContext::LengthType::Horizontal), + lctx.resolve(Y1, SVGLengthContext::LengthType::Vertical)), + Point::Make(lctx.resolve(X2, SVGLengthContext::LengthType::Horizontal), + lctx.resolve(Y2, SVGLengthContext::LengthType::Vertical))); } void SkSVGLine::onDraw(Canvas* canvas, const SVGLengthContext& lctx, const Paint& paint, diff --git a/src/svg/node/SVGLinearGradient.cpp b/src/svg/node/SVGLinearGradient.cpp index 2ed4786f..502d4b3d 100644 --- a/src/svg/node/SVGLinearGradient.cpp +++ b/src/svg/node/SVGLinearGradient.cpp @@ -44,10 +44,10 @@ std::shared_ptr SkSVGLinearGradient::onMakeShader(const SVGRenderContext SVGLengthContext lctx = ctx.lengthContext(); lctx.setPatternUnits(getGradientUnits()); - auto startPoint = Point::Make(lctx.resolve(fX1, SVGLengthContext::LengthType::Horizontal), - lctx.resolve(fY1, SVGLengthContext::LengthType::Vertical)); - auto endPoint = Point::Make(lctx.resolve(fX2, SVGLengthContext::LengthType::Horizontal), - lctx.resolve(fY2, SVGLengthContext::LengthType::Vertical)); + auto startPoint = Point::Make(lctx.resolve(X1, SVGLengthContext::LengthType::Horizontal), + lctx.resolve(Y1, SVGLengthContext::LengthType::Vertical)); + auto endPoint = Point::Make(lctx.resolve(X2, SVGLengthContext::LengthType::Horizontal), + lctx.resolve(Y2, SVGLengthContext::LengthType::Vertical)); return Shader::MakeLinearGradient(startPoint, endPoint, colors, positions); } diff --git a/src/svg/node/SVGMask.cpp b/src/svg/node/SVGMask.cpp index 7bf6cd5e..41e96700 100644 --- a/src/svg/node/SVGMask.cpp +++ b/src/svg/node/SVGMask.cpp @@ -44,12 +44,12 @@ bool SkSVGMask::parseAndSetAttribute(const char* n, const char* v) { #ifndef RENDER_SVG Rect SkSVGMask::bounds(const SVGRenderContext& context) const { auto lengthContext = context.lengthContext(); - lengthContext.setPatternUnits(fMaskUnits); + lengthContext.setPatternUnits(MaskUnits); SVGRenderContext resolveContext(context, lengthContext); - if (fWidth.has_value() && fHeight.has_value()) { - return resolveContext.resolveOBBRect(fX.value_or(SVGLength(0, SVGLength::Unit::Number)), - fY.value_or(SVGLength(0, SVGLength::Unit::Number)), - fWidth.value(), fHeight.value(), fMaskUnits); + if (Width.has_value() && Height.has_value()) { + return resolveContext.resolveOBBRect(X.value_or(SVGLength(0, SVGLength::Unit::Number)), + Y.value_or(SVGLength(0, SVGLength::Unit::Number)), + Width.value(), Height.value(), MaskUnits); } return Rect::MakeEmpty(); } diff --git a/src/svg/node/SVGNode.cpp b/src/svg/node/SVGNode.cpp index 269cf2d2..1df7d990 100644 --- a/src/svg/node/SVGNode.cpp +++ b/src/svg/node/SVGNode.cpp @@ -34,11 +34,11 @@ namespace tgfx { SVGNode::SVGNode(SVGTag t) : _tag(t) { // Uninherited presentation attributes need a non-null default value. - _presentationAttributes.fStopColor.set(SVGColor(Color::Black())); - _presentationAttributes.fStopOpacity.set(static_cast(1.0f)); - _presentationAttributes.fFloodColor.set(SVGColor(Color::Black())); - _presentationAttributes.fFloodOpacity.set(static_cast(1.0f)); - _presentationAttributes.fLightingColor.set(SVGColor(Color::White())); + _presentationAttributes.StopColor.set(SVGColor(Color::Black())); + _presentationAttributes.StopOpacity.set(static_cast(1.0f)); + _presentationAttributes.FloodColor.set(SVGColor(Color::Black())); + _presentationAttributes.FloodOpacity.set(static_cast(1.0f)); + _presentationAttributes.LightingColor.set(SVGColor(Color::White())); } SVGNode::~SVGNode() { @@ -86,10 +86,10 @@ bool SVGNode::onPrepareToRender(SVGRenderContext* ctx) const { // visibility:hidden and display:none disable rendering. // TODO: if display is not a value (true when display="inherit"), we currently // ignore it. Eventually we should be able to add SkASSERT(display.isValue()). - const auto visibility = ctx->presentationContext()._inherited.fVisibility->type(); - const auto display = _presentationAttributes.fDisplay; // display is uninherited + const auto visibility = ctx->presentationContext()._inherited.Visibility->type(); + const auto display = _presentationAttributes.Display; // display is uninherited return visibility != SVGVisibility::Type::Hidden && - (!display.isValue() || *display != SVGDisplay::kNone); + (!display.isValue() || *display != SVGDisplay::None); } #endif @@ -109,10 +109,10 @@ void SetInheritedByDefault(std::optional& presentation_attribute, const T& va } bool SVGNode::parseAndSetAttribute(const char* n, const char* v) { -#define PARSE_AND_SET(svgName, attrName) \ - this->set##attrName( \ - SVGAttributeParser::parseProperty(svgName, n, \ - v)) +#define PARSE_AND_SET(svgName, attrName) \ + this->set##attrName( \ + SVGAttributeParser::parseProperty(svgName, n, \ + v)) return PARSE_AND_SET("clip-path", ClipPath) || PARSE_AND_SET("clip-rule", ClipRule) || PARSE_AND_SET("color", Color) || @@ -156,7 +156,7 @@ Matrix SVGNode::ComputeViewboxMatrix(const Rect& viewBox, const Rect& viewPort, } // isotropic scaling - const auto s = par.fScale == SVGPreserveAspectRatio::Meet ? std::min(sx, sy) : std::max(sx, sy); + const auto s = par.scale == SVGPreserveAspectRatio::Meet ? std::min(sx, sy) : std::max(sx, sy); return {s, s}; }; diff --git a/src/svg/node/SVGPath.cpp b/src/svg/node/SVGPath.cpp index a78cfd9e..1b694874 100644 --- a/src/svg/node/SVGPath.cpp +++ b/src/svg/node/SVGPath.cpp @@ -31,7 +31,7 @@ SkSVGPath::SkSVGPath() : INHERITED(SVGTag::Path) { bool SkSVGPath::parseAndSetAttribute(const char* n, const char* v) { return INHERITED::parseAndSetAttribute(n, v) || - this->setPath(SVGAttributeParser::parse("d", n, v)); + this->setShapePath(SVGAttributeParser::parse("d", n, v)); } template <> @@ -47,21 +47,21 @@ bool SVGAttributeParser::parse(Path* path) { void SkSVGPath::onDraw(Canvas* canvas, const SVGLengthContext&, const Paint& paint, PathFillType fillType) const { // the passed fillType follows inheritance rules and needs to be applied at draw time. - Path path = fPath; // Note: point and verb data are CoW + Path path = ShapePath; // Note: point and verb data are CoW path.setFillType(fillType); canvas->drawPath(path, paint); } Path SkSVGPath::onAsPath(const SVGRenderContext& ctx) const { - Path path = fPath; + Path path = ShapePath; // clip-rule can be inherited and needs to be applied at clip time. - path.setFillType(ctx.presentationContext()._inherited.fClipRule->asFillType()); + path.setFillType(ctx.presentationContext()._inherited.ClipRule->asFillType()); this->mapToParent(&path); return path; } Rect SkSVGPath::onObjectBoundingBox(const SVGRenderContext&) const { - return fPath.getBounds(); + return ShapePath.getBounds(); //TODO (YG): Implement this // return fPath.computeTightBounds(); } diff --git a/src/svg/node/SVGPattern.cpp b/src/svg/node/SVGPattern.cpp index 9ae102fc..e6b64d6a 100644 --- a/src/svg/node/SVGPattern.cpp +++ b/src/svg/node/SVGPattern.cpp @@ -51,11 +51,11 @@ bool SkSVGPattern::parseAndSetAttribute(const char* name, const char* value) { #ifndef RENDER_SVG const SkSVGPattern* SkSVGPattern::hrefTarget(const SVGRenderContext& ctx) const { - if (fHref.iri().empty()) { + if (Href.iri().empty()) { return nullptr; } - const auto href = ctx.findNodeById(fHref); + const auto href = ctx.findNodeById(Href); if (!href || href->tag() != SVGTag::Pattern) { return nullptr; } @@ -90,11 +90,10 @@ const SkSVGPattern* SkSVGPattern::resolveHref(const SVGRenderContext& ctx, do { // Bitwise OR to avoid short-circuiting. const bool didInherit = - inherit_if_needed(currentNode->fX, attrs->fX) | - inherit_if_needed(currentNode->fY, attrs->fY) | - inherit_if_needed(currentNode->fWidth, attrs->fWidth) | - inherit_if_needed(currentNode->fHeight, attrs->fHeight) | - inherit_if_needed(currentNode->fPatternTransform, attrs->fPatternTransform); + inherit_if_needed(currentNode->X, attrs->x) | inherit_if_needed(currentNode->Y, attrs->y) | + inherit_if_needed(currentNode->Width, attrs->width) | + inherit_if_needed(currentNode->Height, attrs->height) | + inherit_if_needed(currentNode->PatternTransform, attrs->patternTransform); if (!contentNode->hasChildren()) { contentNode = currentNode; @@ -111,19 +110,19 @@ const SkSVGPattern* SkSVGPattern::resolveHref(const SVGRenderContext& ctx, // To unify with Chrome and macOS preview, the width and height attributes here need to be // converted to percentages, direct numbers are not supported. - if (fPatternUnits.type() == SVGObjectBoundingBoxUnits::Type::UserSpaceOnUse) { - if (attrs->fWidth.has_value() && attrs->fWidth->unit() == SVGLength::Unit::Percentage) { - attrs->fWidth = SVGLength(attrs->fWidth->value() / 100.f, SVGLength::Unit::Number); + if (PatternUnits.type() == SVGObjectBoundingBoxUnits::Type::UserSpaceOnUse) { + if (attrs->width.has_value() && attrs->width->unit() == SVGLength::Unit::Percentage) { + attrs->width = SVGLength(attrs->width->value() / 100.f, SVGLength::Unit::Number); } - if (attrs->fHeight.has_value() && attrs->fHeight->unit() == SVGLength::Unit::Percentage) { - attrs->fHeight = SVGLength(attrs->fHeight->value() / 100.f, SVGLength::Unit::Number); + if (attrs->height.has_value() && attrs->height->unit() == SVGLength::Unit::Percentage) { + attrs->height = SVGLength(attrs->height->value() / 100.f, SVGLength::Unit::Number); } - } else if (fPatternUnits.type() == SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox) { - if (attrs->fWidth.has_value() && attrs->fWidth->unit() == SVGLength::Unit::Number) { - attrs->fWidth = SVGLength(attrs->fWidth->value() * 100.f, SVGLength::Unit::Percentage); + } else if (PatternUnits.type() == SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox) { + if (attrs->width.has_value() && attrs->width->unit() == SVGLength::Unit::Number) { + attrs->width = SVGLength(attrs->width->value() * 100.f, SVGLength::Unit::Percentage); } - if (attrs->fHeight.has_value() && attrs->fHeight->unit() == SVGLength::Unit::Number) { - attrs->fHeight = SVGLength(attrs->fHeight->value() * 100.f, SVGLength::Unit::Percentage); + if (attrs->height.has_value() && attrs->height->unit() == SVGLength::Unit::Number) { + attrs->height = SVGLength(attrs->height->value() * 100.f, SVGLength::Unit::Percentage); } } @@ -134,11 +133,11 @@ bool SkSVGPattern::onAsPaint(const SVGRenderContext& ctx, Paint* paint) const { PatternAttributes attrs; const auto* contentNode = this->resolveHref(ctx, &attrs); auto lengthContext = ctx.lengthContext(); - lengthContext.setPatternUnits(fPatternUnits); - Rect tile = lengthContext.resolveRect(attrs.fX.has_value() ? *attrs.fX : SVGLength(0), - attrs.fY.has_value() ? *attrs.fY : SVGLength(0), - attrs.fWidth.has_value() ? *attrs.fWidth : SVGLength(0), - attrs.fHeight.has_value() ? *attrs.fHeight : SVGLength(0)); + lengthContext.setPatternUnits(PatternUnits); + Rect tile = lengthContext.resolveRect(attrs.x.has_value() ? *attrs.x : SVGLength(0), + attrs.y.has_value() ? *attrs.y : SVGLength(0), + attrs.width.has_value() ? *attrs.width : SVGLength(0), + attrs.height.has_value() ? *attrs.height : SVGLength(0)); if (tile.isEmpty()) { return false; @@ -146,7 +145,7 @@ bool SkSVGPattern::onAsPaint(const SVGRenderContext& ctx, Paint* paint) const { Recorder patternRecorder; auto* canvas = patternRecorder.beginRecording(); - auto patternMatrix = attrs.fPatternTransform.value_or(Matrix::I()); + auto patternMatrix = attrs.patternTransform.value_or(Matrix::I()); canvas->concat(patternMatrix); { SVGRenderContext recordingContext(ctx, canvas, lengthContext); diff --git a/src/svg/node/SVGPoly.cpp b/src/svg/node/SVGPoly.cpp index a0329155..a808415e 100644 --- a/src/svg/node/SVGPoly.cpp +++ b/src/svg/node/SVGPoly.cpp @@ -53,7 +53,7 @@ Path SkSVGPoly::onAsPath(const SVGRenderContext& ctx) const { Path path = fPath; // clip-rule can be inherited and needs to be applied at clip time. - path.setFillType(ctx.presentationContext()._inherited.fClipRule->asFillType()); + path.setFillType(ctx.presentationContext()._inherited.ClipRule->asFillType()); this->mapToParent(&path); return path; diff --git a/src/svg/node/SVGRadialGradient.cpp b/src/svg/node/SVGRadialGradient.cpp index b6bc6b72..1825c345 100644 --- a/src/svg/node/SVGRadialGradient.cpp +++ b/src/svg/node/SVGRadialGradient.cpp @@ -46,9 +46,9 @@ std::shared_ptr SkSVGRadialGradient::onMakeShader(const SVGRenderContext SVGLengthContext lctx = ctx.lengthContext(); lctx.setPatternUnits(getGradientUnits()); - auto radius = lctx.resolve(fR, SVGLengthContext::LengthType::Other); - auto center = Point::Make(lctx.resolve(fCx, SVGLengthContext::LengthType::Horizontal), - lctx.resolve(fCy, SVGLengthContext::LengthType::Vertical)); + auto radius = lctx.resolve(R, SVGLengthContext::LengthType::Other); + auto center = Point::Make(lctx.resolve(Cx, SVGLengthContext::LengthType::Horizontal), + lctx.resolve(Cy, SVGLengthContext::LengthType::Vertical)); // TODO(YGAurora): MakeTwoPointConical are unimplemented in tgfx if (radius == 0) { diff --git a/src/svg/node/SVGRect.cpp b/src/svg/node/SVGRect.cpp index 1572d1fd..ce7a0370 100644 --- a/src/svg/node/SVGRect.cpp +++ b/src/svg/node/SVGRect.cpp @@ -74,8 +74,8 @@ std::tuple ResolveOptionalRadii(const std::optional& op } RRect SkSVGRect::resolve(const SVGLengthContext& lctx) const { - const auto rect = lctx.resolveRect(fX, fY, fWidth, fHeight); - const auto [rx, ry] = ResolveOptionalRadii(fRx, fRy, lctx); + const auto rect = lctx.resolveRect(X, Y, Width, Height); + const auto [rx, ry] = ResolveOptionalRadii(Rx, Ry, lctx); // https://www.w3.org/TR/SVG2/shapes.html#RectElement // ... @@ -132,7 +132,7 @@ Path SkSVGRect::onAsPath(const SVGRenderContext& ctx) const { } Rect SkSVGRect::onObjectBoundingBox(const SVGRenderContext& ctx) const { - return ctx.lengthContext().resolveRect(fX, fY, fWidth, fHeight); + return ctx.lengthContext().resolveRect(X, Y, Width, Height); } #endif } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGSVG.cpp b/src/svg/node/SVGSVG.cpp index ce87ef73..0fc7f3d9 100644 --- a/src/svg/node/SVGSVG.cpp +++ b/src/svg/node/SVGSVG.cpp @@ -43,15 +43,15 @@ void SVGSVG::renderNode(const SVGRenderContext& ctx, const SVGIRI& iri) const { bool SVGSVG::onPrepareToRender(SVGRenderContext* ctx) const { // x/y are ignored for outermost svg elements - const auto x = fType == Type::kInner ? fX : SVGLength(0); - const auto y = fType == Type::kInner ? fY : SVGLength(0); + const auto x = type == Type::kInner ? X : SVGLength(0); + const auto y = type == Type::kInner ? Y : SVGLength(0); - auto viewPortRect = ctx->lengthContext().resolveRect(x, y, fWidth, fHeight); + auto viewPortRect = ctx->lengthContext().resolveRect(x, y, Width, Height); auto contentMatrix = Matrix::MakeTrans(viewPortRect.x(), viewPortRect.y()); auto viewPort = Size::Make(viewPortRect.width(), viewPortRect.height()); - if (fViewBox.has_value()) { - const Rect& viewBox = *fViewBox; + if (ViewBox.has_value()) { + const Rect& viewBox = *ViewBox; // An empty viewbox disables rendering. if (viewBox.isEmpty()) { @@ -61,7 +61,7 @@ bool SVGSVG::onPrepareToRender(SVGRenderContext* ctx) const { // A viewBox overrides the intrinsic viewport. viewPort = Size::Make(viewBox.width(), viewBox.height()); - contentMatrix.preConcat(ComputeViewboxMatrix(viewBox, viewPortRect, fPreserveAspectRatio)); + contentMatrix.preConcat(ComputeViewboxMatrix(viewBox, viewPortRect, PreserveAspectRatio)); } if (!contentMatrix.isIdentity()) { @@ -79,50 +79,49 @@ bool SVGSVG::onPrepareToRender(SVGRenderContext* ctx) const { // https://www.w3.org/TR/SVG11/coords.html#IntrinsicSizing Size SVGSVG::intrinsicSize(const SVGLengthContext& lctx) const { // Percentage values do not provide an intrinsic size. - if (fWidth.unit() == SVGLength::Unit::Percentage || - fHeight.unit() == SVGLength::Unit::Percentage) { + if (Width.unit() == SVGLength::Unit::Percentage || Height.unit() == SVGLength::Unit::Percentage) { return Size::Make(0, 0); } - return Size::Make(lctx.resolve(fWidth, SVGLengthContext::LengthType::Horizontal), - lctx.resolve(fHeight, SVGLengthContext::LengthType::Vertical)); + return Size::Make(lctx.resolve(Width, SVGLengthContext::LengthType::Horizontal), + lctx.resolve(Height, SVGLengthContext::LengthType::Vertical)); } #endif void SVGSVG::onSetAttribute(SVGAttribute attr, const SVGValue& v) { - if (fType != Type::kInner && fType != Type::kRoot) return; + if (type != Type::kInner && type != Type::kRoot) return; switch (attr) { - case SVGAttribute::kX: + case SVGAttribute::X: if (const auto* x = v.as()) { SVGLength xValue = *x; this->setX(xValue); } break; - case SVGAttribute::kY: + case SVGAttribute::Y: if (const auto* y = v.as()) { SVGLength yValue = *y; this->setY(yValue); } break; - case SVGAttribute::kWidth: + case SVGAttribute::Width: if (const auto* w = v.as()) { SVGLength wValue = *w; this->setWidth(wValue); } break; - case SVGAttribute::kHeight: + case SVGAttribute::Height: if (const auto* h = v.as()) { SVGLength hValue = *h; this->setHeight(hValue); } break; - case SVGAttribute::kViewBox: + case SVGAttribute::ViewBox: if (const auto* vb = v.as()) { SVGViewBoxType vbValue = *vb; this->setViewBox(vbValue); } break; - case SVGAttribute::kPreserveAspectRatio: + case SVGAttribute::PreserveAspectRatio: if (const auto* par = v.as()) { SVGPreserveAspectRatio parValue = *par; this->setPreserveAspectRatio(parValue); diff --git a/src/svg/node/SVGShape.cpp b/src/svg/node/SVGShape.cpp index b6698900..47b428ac 100644 --- a/src/svg/node/SVGShape.cpp +++ b/src/svg/node/SVGShape.cpp @@ -32,7 +32,7 @@ SVGShape::SVGShape(SVGTag t) : INHERITED(t) { #ifndef RENDER_SVG void SVGShape::onRender(const SVGRenderContext& ctx) const { - const auto fillType = ctx.presentationContext()._inherited.fFillRule->asFillType(); + const auto fillType = ctx.presentationContext()._inherited.FillRule->asFillType(); auto selfRect = onObjectBoundingBox(ctx); auto lengthCtx = ctx.lengthContext(); diff --git a/src/svg/node/SVGText.cpp b/src/svg/node/SVGText.cpp index 196f9da7..5ce2dd02 100644 --- a/src/svg/node/SVGText.cpp +++ b/src/svg/node/SVGText.cpp @@ -34,34 +34,34 @@ std::tuple ResolveFont(const SVGRenderContext& context) { auto weight = [](const SVGFontWeight& w) { switch (w.type()) { case SVGFontWeight::Type::W100: - return FontStyle::Weight::kThin_Weight; + return FontStyle::Weight::Thin_Weight; case SVGFontWeight::Type::W200: - return FontStyle::Weight::kExtraLight_Weight; + return FontStyle::Weight::ExtraLight_Weight; case SVGFontWeight::Type::W300: - return FontStyle::Weight::kLight_Weight; + return FontStyle::Weight::Light_Weight; case SVGFontWeight::Type::W400: - return FontStyle::Weight::kNormal_Weight; + return FontStyle::Weight::Normal_Weight; case SVGFontWeight::Type::W500: - return FontStyle::Weight::kMedium_Weight; + return FontStyle::Weight::Medium_Weight; case SVGFontWeight::Type::W600: - return FontStyle::Weight::kSemiBold_Weight; + return FontStyle::Weight::SemiBold_Weight; case SVGFontWeight::Type::W700: - return FontStyle::Weight::kBold_Weight; + return FontStyle::Weight::Bold_Weight; case SVGFontWeight::Type::W800: - return FontStyle::Weight::kExtraBold_Weight; + return FontStyle::Weight::ExtraBold_Weight; case SVGFontWeight::Type::W900: - return FontStyle::Weight::kBlack_Weight; + return FontStyle::Weight::Black_Weight; case SVGFontWeight::Type::Normal: - return FontStyle::Weight::kNormal_Weight; + return FontStyle::Weight::Normal_Weight; case SVGFontWeight::Type::Bold: - return FontStyle::Weight::kBold_Weight; + return FontStyle::Weight::Bold_Weight; case SVGFontWeight::Type::Bolder: - return FontStyle::Weight::kExtraBold_Weight; + return FontStyle::Weight::ExtraBold_Weight; case SVGFontWeight::Type::Lighter: - return FontStyle::Weight::kLight_Weight; + return FontStyle::Weight::Light_Weight; case SVGFontWeight::Type::Inherit: { ASSERT(false); - return FontStyle::Weight::kNormal_Weight; + return FontStyle::Weight::Normal_Weight; } } }; @@ -69,23 +69,23 @@ std::tuple ResolveFont(const SVGRenderContext& context) { auto slant = [](const SVGFontStyle& style) { switch (style.type()) { case SVGFontStyle::Type::Normal: - return FontStyle::Slant::kUpright_Slant; + return FontStyle::Slant::Upright_Slant; case SVGFontStyle::Type::Italic: - return FontStyle::Slant::kItalic_Slant; + return FontStyle::Slant::Italic_Slant; case SVGFontStyle::Type::Oblique: - return FontStyle::Slant::kOblique_Slant; + return FontStyle::Slant::Oblique_Slant; case SVGFontStyle::Type::Inherit: { ASSERT(false); - return FontStyle::Slant::kUpright_Slant; + return FontStyle::Slant::Upright_Slant; } } }; - const std::string& family = context.presentationContext()._inherited.fFontFamily->family(); + const std::string& family = context.presentationContext()._inherited.FontFamily->family(); - auto fontWeight = weight(*context.presentationContext()._inherited.fFontWeight); - auto fontWidth = FontStyle::Width::kNormal_Width; - auto fontSlant = slant(*context.presentationContext()._inherited.fFontStyle); + auto fontWeight = weight(*context.presentationContext()._inherited.FontWeight); + auto fontWidth = FontStyle::Width::Normal_Width; + auto fontSlant = slant(*context.presentationContext()._inherited.FontStyle); FontStyle style(fontWeight, fontWidth, fontSlant); auto typeface = context.fontMgr()->getTypefaceForRender(family, style); @@ -95,7 +95,7 @@ std::tuple ResolveFont(const SVGRenderContext& context) { } float size = - context.lengthContext().resolve(context.presentationContext()._inherited.fFontSize->size(), + context.lengthContext().resolve(context.presentationContext()._inherited.FontSize->size(), SVGLengthContext::LengthType::Vertical); return {true, Font(typeface, size)}; } @@ -114,14 +114,14 @@ std::vector ResolveLengths(const SVGLengthContext& lengthCtx, } float ComputeAlignmentFactor(const SkSVGPresentationContext& context) { - switch (context._inherited.fTextAnchor->type()) { - case SVGTextAnchor::Type::kStart: + switch (context._inherited.TextAnchor->type()) { + case SVGTextAnchor::Type::Start: return 0.0f; - case SVGTextAnchor::Type::kMiddle: + case SVGTextAnchor::Type::Middle: return -0.5f; - case SVGTextAnchor::Type::kEnd: + case SVGTextAnchor::Type::End: return -1.0f; - case SVGTextAnchor::Type::kInherit: + case SVGTextAnchor::Type::Inherit: ASSERT(false); return 0.0f; } @@ -160,10 +160,10 @@ Path SkSVGTextFragment::onAsPath(const SVGRenderContext&) const { void SkSVGTextContainer::onShapeText(const SVGRenderContext& context, const ShapedTextCallback& function) const { - auto x = ResolveLengths(context.lengthContext(), fX, SVGLengthContext::LengthType::Horizontal); - auto y = ResolveLengths(context.lengthContext(), fY, SVGLengthContext::LengthType::Vertical); - auto dx = ResolveLengths(context.lengthContext(), fDx, SVGLengthContext::LengthType::Horizontal); - auto dy = ResolveLengths(context.lengthContext(), fDy, SVGLengthContext::LengthType::Vertical); + auto x = ResolveLengths(context.lengthContext(), X, SVGLengthContext::LengthType::Horizontal); + auto y = ResolveLengths(context.lengthContext(), Y, SVGLengthContext::LengthType::Vertical); + auto dx = ResolveLengths(context.lengthContext(), Dx, SVGLengthContext::LengthType::Horizontal); + auto dy = ResolveLengths(context.lengthContext(), Dy, SVGLengthContext::LengthType::Vertical); // TODO (YGAurora) : Handle rotate for (uint32_t i = 0; i < fChildren.size(); i++) { @@ -194,7 +194,7 @@ void SkSVGTextLiteral::onShapeText(const SVGRenderContext& context, if (!success) { return; } - auto textBlob = TextBlob::MakeFrom(fText, font); + auto textBlob = TextBlob::MakeFrom(Text, font); function(context, textBlob); } diff --git a/src/svg/node/SVGTransformableNode.cpp b/src/svg/node/SVGTransformableNode.cpp index faa13223..297388bc 100644 --- a/src/svg/node/SVGTransformableNode.cpp +++ b/src/svg/node/SVGTransformableNode.cpp @@ -51,7 +51,7 @@ bool SkSVGTransformableNode::onPrepareToRender(SVGRenderContext* ctx) const { void SkSVGTransformableNode::onSetAttribute(SVGAttribute attr, const SVGValue& v) { switch (attr) { - case SVGAttribute::kTransform: + case SVGAttribute::Transform: if (const auto* transform = v.as()) { this->setTransform(*transform); } diff --git a/src/svg/node/SVGUse.cpp b/src/svg/node/SVGUse.cpp index ff4a210a..0e22e282 100644 --- a/src/svg/node/SVGUse.cpp +++ b/src/svg/node/SVGUse.cpp @@ -38,14 +38,14 @@ bool SkSVGUse::parseAndSetAttribute(const char* n, const char* v) { #ifndef RENDER_SVG bool SkSVGUse::onPrepareToRender(SVGRenderContext* ctx) const { - if (fHref.iri().empty() || !INHERITED::onPrepareToRender(ctx)) { + if (Href.iri().empty() || !INHERITED::onPrepareToRender(ctx)) { return false; } - if (!FloatNearlyZero(fX.value()) || !FloatNearlyZero(fY.value())) { + if (!FloatNearlyZero(X.value()) || !FloatNearlyZero(Y.value())) { // Restored when the local SVGRenderContext leaves scope. ctx->saveOnce(); - ctx->canvas()->translate(fX.value(), fY.value()); + ctx->canvas()->translate(X.value(), Y.value()); } // TODO: width/height override for targets. @@ -54,7 +54,7 @@ bool SkSVGUse::onPrepareToRender(SVGRenderContext* ctx) const { } void SkSVGUse::onRender(const SVGRenderContext& ctx) const { - const auto ref = ctx.findNodeById(fHref); + const auto ref = ctx.findNodeById(Href); if (!ref) { return; } @@ -66,7 +66,7 @@ void SkSVGUse::onRender(const SVGRenderContext& ctx) const { } Path SkSVGUse::onAsPath(const SVGRenderContext& ctx) const { - const auto ref = ctx.findNodeById(fHref); + const auto ref = ctx.findNodeById(Href); if (!ref) { return Path(); } @@ -78,15 +78,15 @@ Path SkSVGUse::onAsPath(const SVGRenderContext& ctx) const { } Rect SkSVGUse::onObjectBoundingBox(const SVGRenderContext& ctx) const { - const auto ref = ctx.findNodeById(fHref); + const auto ref = ctx.findNodeById(Href); if (!ref) { return Rect::MakeEmpty(); } auto lengthContext = ctx.lengthContext(); lengthContext.clearPatternUnits(); - float x = lengthContext.resolve(fX, SVGLengthContext::LengthType::Horizontal); - float y = lengthContext.resolve(fY, SVGLengthContext::LengthType::Vertical); + float x = lengthContext.resolve(X, SVGLengthContext::LengthType::Horizontal); + float y = lengthContext.resolve(Y, SVGLengthContext::LengthType::Vertical); Rect bounds = ref->objectBoundingBox(ctx); bounds.offset(x, y); From 6a5cd29f0baddd0b9754fbaad23fb5b979e9870d Mon Sep 17 00:00:00 2001 From: YGauroa Date: Thu, 12 Dec 2024 19:25:04 +0800 Subject: [PATCH 12/31] format code --- include/tgfx/svg/SVGDOM.h | 85 ++--- include/tgfx/svg/SVGIDMapper.h | 28 -- include/tgfx/svg/SVGTypes.h | 2 - include/tgfx/svg/node/SVGCircle.h | 4 +- include/tgfx/svg/node/SVGClipPath.h | 4 +- include/tgfx/svg/node/SVGContainer.h | 14 +- include/tgfx/svg/node/SVGDefs.h | 8 +- include/tgfx/svg/node/SVGEllipse.h | 14 +- include/tgfx/svg/node/SVGFe.h | 30 +- include/tgfx/svg/node/SVGFeBlend.h | 18 +- include/tgfx/svg/node/SVGFeColorMatrix.h | 21 +- .../tgfx/svg/node/SVGFeComponentTransfer.h | 39 ++- include/tgfx/svg/node/SVGFeComposite.h | 26 +- include/tgfx/svg/node/SVGFeDisplacementMap.h | 28 +- include/tgfx/svg/node/SVGFeFlood.h | 21 +- include/tgfx/svg/node/SVGFeGaussianBlur.h | 18 +- include/tgfx/svg/node/SVGFeImage.h | 18 +- include/tgfx/svg/node/SVGFeLightSource.h | 30 +- include/tgfx/svg/node/SVGFeLighting.h | 81 ++--- include/tgfx/svg/node/SVGFeMerge.h | 27 +- include/tgfx/svg/node/SVGFeMorphology.h | 19 +- include/tgfx/svg/node/SVGFeOffset.h | 18 +- include/tgfx/svg/node/SVGFeTurbulence.h | 16 +- include/tgfx/svg/node/SVGFilter.h | 10 +- include/tgfx/svg/node/SVGFilterContext.h | 33 +- include/tgfx/svg/node/SVGG.h | 10 +- include/tgfx/svg/node/SVGGradient.h | 8 +- include/tgfx/svg/node/SVGHiddenContainer.h | 6 +- include/tgfx/svg/node/SVGImage.h | 19 +- include/tgfx/svg/node/SVGLine.h | 14 +- include/tgfx/svg/node/SVGLinearGradient.h | 14 +- include/tgfx/svg/node/SVGMask.h | 12 +- include/tgfx/svg/node/SVGNode.h | 122 ++++--- include/tgfx/svg/node/SVGPath.h | 12 +- include/tgfx/svg/node/SVGPattern.h | 18 +- include/tgfx/svg/node/SVGPoly.h | 16 +- include/tgfx/svg/node/SVGRadialGradient.h | 14 +- include/tgfx/svg/node/SVGRect.h | 15 +- include/tgfx/svg/node/SVGSVG.h | 10 +- include/tgfx/svg/node/SVGShape.h | 6 +- include/tgfx/svg/node/SVGStop.h | 10 +- include/tgfx/svg/node/SVGText.h | 58 ++-- include/tgfx/svg/node/SVGTransformableNode.h | 9 +- include/tgfx/svg/node/SVGUse.h | 14 +- include/tgfx/svg/xml/XMLDOM.h | 5 +- src/svg/SVGAttributeParser.cpp | 42 +-- .../tgfx => src}/svg/SVGAttributeParser.h | 33 +- src/svg/SVGDOM.cpp | 306 +++++++----------- src/svg/SVGParse.cpp | 5 +- {include/tgfx => src}/svg/SVGParse.h | 0 src/svg/SVGParseColor.cpp | 6 +- src/svg/SVGRenderContext.cpp | 74 +---- {include/tgfx => src}/svg/SVGRenderContext.h | 16 +- src/svg/node/SVGCircle.cpp | 7 +- src/svg/node/SVGClipPath.cpp | 8 +- src/svg/node/SVGContainer.cpp | 29 +- src/svg/node/SVGEllipse.cpp | 18 +- src/svg/node/SVGFe.cpp | 28 +- src/svg/node/SVGFeBlend.cpp | 43 +-- src/svg/node/SVGFeColorMatrix.cpp | 19 +- src/svg/node/SVGFeComponentTransfer.cpp | 64 +--- src/svg/node/SVGFeComposite.cpp | 14 +- src/svg/node/SVGFeDisplacementMap.cpp | 42 ++- src/svg/node/SVGFeFlood.cpp | 42 --- src/svg/node/SVGFeGaussianBlur.cpp | 17 +- src/svg/node/SVGFeImage.cpp | 37 +-- src/svg/node/SVGFeLightSource.cpp | 21 +- src/svg/node/SVGFeLighting.cpp | 154 +-------- src/svg/node/SVGFeMerge.cpp | 30 +- src/svg/node/SVGFeMorphology.cpp | 36 +-- src/svg/node/SVGFeOffset.cpp | 19 +- src/svg/node/SVGFeTurbulence.cpp | 28 +- src/svg/node/SVGFilter.cpp | 25 +- src/svg/node/SVGFilterContext.cpp | 120 +++---- src/svg/node/SVGGradient.cpp | 20 +- src/svg/node/SVGImage.cpp | 25 +- src/svg/node/SVGLine.cpp | 18 +- src/svg/node/SVGLinearGradient.cpp | 18 +- src/svg/node/SVGMask.cpp | 17 +- src/svg/node/SVGNode.cpp | 7 +- src/svg/node/SVGPath.cpp | 22 +- src/svg/node/SVGPattern.cpp | 27 +- src/svg/node/SVGPoly.cpp | 18 +- src/svg/node/SVGRadialGradient.cpp | 18 +- src/svg/node/SVGRect.cpp | 43 +-- src/svg/node/SVGSVG.cpp | 3 +- src/svg/node/SVGShape.cpp | 5 +- src/svg/node/SVGStop.cpp | 6 +- src/svg/node/SVGText.cpp | 44 ++- src/svg/node/SVGTransformableNode.cpp | 17 +- src/svg/node/SVGUse.cpp | 22 +- test/src/SVGRenderTest.cpp | 14 +- 92 files changed, 929 insertions(+), 1702 deletions(-) delete mode 100644 include/tgfx/svg/SVGIDMapper.h rename {include/tgfx => src}/svg/SVGAttributeParser.h (83%) rename {include/tgfx => src}/svg/SVGParse.h (100%) rename {include/tgfx => src}/svg/SVGRenderContext.h (94%) delete mode 100644 src/svg/node/SVGFeFlood.cpp diff --git a/include/tgfx/svg/SVGDOM.h b/include/tgfx/svg/SVGDOM.h index a3dffce4..4711472c 100644 --- a/include/tgfx/svg/SVGDOM.h +++ b/include/tgfx/svg/SVGDOM.h @@ -24,85 +24,41 @@ #include "tgfx/core/Picture.h" #include "tgfx/core/Size.h" #include "tgfx/svg/SVGFontManager.h" -#include "tgfx/svg/SVGIDMapper.h" #include "tgfx/svg/node/SVGSVG.h" namespace tgfx { +class SVGNode; +using SVGIDMapper = std::unordered_map>; class SVGDOM { public: - class Builder final { - public: - // /** - // * Specify a font manager for loading fonts (e.g. from the system) to render - // * SVG nodes. - // * If this is not set, but a font is required as part of rendering, the text will - // * not be displayed. - // */ - // Builder& setFontManager(sk_sp); - - // /** - // * Specify a resource provider for loading images etc. - // */ - // Builder& setResourceProvider(sk_sp); - - // /** - // * Specify the callbacks for dealing with shaping text. See also - // * modules/skshaper/utils/FactoryHelpers.h - // */ - // Builder& setTextShapingFactory(sk_sp); - - std::shared_ptr make(Data&, std::shared_ptr) const; - - private: - // sk_sp fFontMgr; - // sk_sp fResourceProvider; - // sk_sp fTextShapingFactory; - }; - - // static std::shared_ptr MakeFromData(Data& data) { - // return Builder().make(data); - // } + static std::shared_ptr Make(const std::shared_ptr&, + std::shared_ptr); /** - * Returns the root (outermost) SVG element. - */ + * Returns the root SVG node. + */ const std::shared_ptr& getRoot() const { return root; } /** - * Specify a "container size" for the SVG dom. - * - * This is used to resolve the initial viewport when the root SVG width/height are specified - * in relative units. - * - * If the root dimensions are in absolute units, then the container size has no effect since - * the initial viewport is fixed. - */ - void setContainerSize(const Size&); + * Renders the SVG to the provided canvas. + */ + void render(Canvas*); /** - * DEPRECATED: use getRoot()->intrinsicSize() to query the root element intrinsic size. - * - * Returns the SVG dom container size. - * - * If the client specified a container size via setContainerSize(), then the same size is - * returned. - * - * When unspecified by clients, this returns the intrinsic size of the root element, as defined - * by its width/height attributes. If either width or height is specified in relative units - * (e.g. "100%"), then the corresponding intrinsic size dimension is zero. - */ - const Size& containerSize() const; - - // Returns the node with the given id, or nullptr if not found. - std::shared_ptr findNodeById(const std::string& id); - - void render(Canvas*); + * Specify a "container size" for the SVG dom. + * + * This is used to resolve the initial viewport when the root SVG width/height are specified + * in relative units. + * + * If the root dimensions are in absolute units, then the container size has no effect since + * the initial viewport is fixed. + */ + void setContainerSize(const Size&); - /** Render the node with the given id as if it were the only child of the root. */ - void renderNode(Canvas*, SkSVGPresentationContext&, const char* id) const; + const Size& getContainerSize() const; private: SVGDOM(std::shared_ptr, SVGIDMapper&&, std::shared_ptr fontManager); @@ -110,8 +66,7 @@ class SVGDOM { const std::shared_ptr root; const std::shared_ptr fontManager; const SVGIDMapper _nodeIDMapper; - Size _containerSize; - + Size containerSize; std::shared_ptr renderPicture; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGIDMapper.h b/include/tgfx/svg/SVGIDMapper.h deleted file mode 100644 index 85a96b22..00000000 --- a/include/tgfx/svg/SVGIDMapper.h +++ /dev/null @@ -1,28 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making tgfx available. -// -// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// unless required by applicable law or agreed to in writing, software distributed under the -// license is distributed on an "as is" basis, without warranties or conditions of any kind, -// either express or implied. see the license for the specific language governing permissions -// and limitations under the license. -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include -#include -#include - -namespace tgfx { -class SVGNode; -using SVGIDMapper = std::unordered_map>; -} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGTypes.h b/include/tgfx/svg/SVGTypes.h index 73cd47c5..2325ed60 100644 --- a/include/tgfx/svg/SVGTypes.h +++ b/include/tgfx/svg/SVGTypes.h @@ -29,8 +29,6 @@ #include "tgfx/core/Point.h" #include "tgfx/core/Rect.h" -// #define RENDER_SVG - namespace tgfx { using SVGColorType = Color; diff --git a/include/tgfx/svg/node/SVGCircle.h b/include/tgfx/svg/node/SVGCircle.h index 86e59729..22563644 100644 --- a/include/tgfx/svg/node/SVGCircle.h +++ b/include/tgfx/svg/node/SVGCircle.h @@ -41,9 +41,8 @@ class SVGCircle final : public SVGShape { SVG_ATTR(R, SVGLength, SVGLength(0)) protected: - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; -#ifndef RENDER_SVG void onDraw(Canvas*, const SVGLengthContext&, const Paint&, PathFillType) const override; Path onAsPath(const SVGRenderContext&) const override; @@ -53,7 +52,6 @@ class SVGCircle final : public SVGShape { private: // resolve and return the center and radius values std::tuple resolve(const SVGLengthContext&) const; -#endif SVGCircle(); diff --git a/include/tgfx/svg/node/SVGClipPath.h b/include/tgfx/svg/node/SVGClipPath.h index 915571c5..39e0f278 100644 --- a/include/tgfx/svg/node/SVGClipPath.h +++ b/include/tgfx/svg/node/SVGClipPath.h @@ -42,11 +42,9 @@ class SVGClipPath final : public SVGHiddenContainer { SVGClipPath(); - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; -#ifndef RENDER_SVG Path resolveClip(const SVGRenderContext&) const; -#endif using INHERITED = SVGHiddenContainer; }; diff --git a/include/tgfx/svg/node/SVGContainer.h b/include/tgfx/svg/node/SVGContainer.h index 5e9571fc..54390296 100644 --- a/include/tgfx/svg/node/SVGContainer.h +++ b/include/tgfx/svg/node/SVGContainer.h @@ -28,36 +28,34 @@ namespace tgfx { class SVGRenderContext; -class SkSVGContainer : public SkSVGTransformableNode { +class SVGContainer : public SVGTransformableNode { public: void appendChild(std::shared_ptr) override; const std::vector>& getChildren() const; bool hasChildren() const final; protected: - explicit SkSVGContainer(SVGTag); -#ifndef RENDER_SVG + explicit SVGContainer(SVGTag); + void onRender(const SVGRenderContext&) const override; Path onAsPath(const SVGRenderContext&) const override; Rect onObjectBoundingBox(const SVGRenderContext&) const override; -#endif template void forEachChild(Func func) const { - for (const auto& child : fChildren) { + for (const auto& child : children) { if (child->tag() == NodeType::tag) { func(static_cast(child.get())); } } } - // TODO: convert remaining direct users to iterators, and make the container private. - std::vector> fChildren; + std::vector> children; private: - using INHERITED = SkSVGTransformableNode; + using INHERITED = SVGTransformableNode; }; } // namespace tgfx diff --git a/include/tgfx/svg/node/SVGDefs.h b/include/tgfx/svg/node/SVGDefs.h index 5d06e8d2..e6943cad 100644 --- a/include/tgfx/svg/node/SVGDefs.h +++ b/include/tgfx/svg/node/SVGDefs.h @@ -23,14 +23,14 @@ namespace tgfx { -class SkSVGDefs : public SVGHiddenContainer { +class SVGDefs : public SVGHiddenContainer { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGDefs()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGDefs()); } private: - SkSVGDefs() : INHERITED(SVGTag::Defs) { + SVGDefs() : INHERITED(SVGTag::Defs) { } using INHERITED = SVGHiddenContainer; diff --git a/include/tgfx/svg/node/SVGEllipse.h b/include/tgfx/svg/node/SVGEllipse.h index 7b00e134..06bd8eb2 100644 --- a/include/tgfx/svg/node/SVGEllipse.h +++ b/include/tgfx/svg/node/SVGEllipse.h @@ -30,10 +30,10 @@ namespace tgfx { class SVGLengthContext; class SVGRenderContext; -class SkSVGEllipse final : public SVGShape { +class SVGEllipse final : public SVGShape { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGEllipse()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGEllipse()); } SVG_ATTR(Cx, SVGLength, SVGLength(0)) @@ -43,20 +43,16 @@ class SkSVGEllipse final : public SVGShape { SVG_OPTIONAL_ATTR(Ry, SVGLength) protected: - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; -#ifndef RENDER_SVG void onDraw(Canvas*, const SVGLengthContext&, const Paint&, PathFillType) const override; Path onAsPath(const SVGRenderContext&) const override; -#endif private: - SkSVGEllipse(); + SVGEllipse(); -#ifndef RENDER_SVG Rect resolve(const SVGLengthContext&) const; -#endif using INHERITED = SVGShape; }; diff --git a/include/tgfx/svg/node/SVGFe.h b/include/tgfx/svg/node/SVGFe.h index e38cb72e..f97c322a 100644 --- a/include/tgfx/svg/node/SVGFe.h +++ b/include/tgfx/svg/node/SVGFe.h @@ -27,12 +27,12 @@ #include "tgfx/svg/node/SVGHiddenContainer.h" #include "tgfx/svg/node/SVGNode.h" -class SkSVGFilterContext; +class SVGFilterContext; class SVGRenderContext; namespace tgfx { -class SkSVGFe : public SVGHiddenContainer { +class SVGFe : public SVGHiddenContainer { public: static bool IsFilterEffect(const std::shared_ptr& node) { switch (node->tag()) { @@ -57,17 +57,17 @@ class SkSVGFe : public SVGHiddenContainer { } std::shared_ptr makeImageFilter(const SVGRenderContext& ctx, - const SkSVGFilterContext& filterContext) const; + const SVGFilterContext& filterContext) const; // https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveSubRegion - Rect resolveFilterSubregion(const SVGRenderContext&, const SkSVGFilterContext&) const; + Rect resolveFilterSubregion(const SVGRenderContext&, const SVGFilterContext&) const; /** - * Resolves the colorspace within which this filter effect should be applied. - * Spec: https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperties - * 'color-interpolation-filters' property. - */ - virtual SVGColorspace resolveColorspace(const SVGRenderContext&, const SkSVGFilterContext&) const; + * Resolves the colorspace within which this filter effect should be applied. + * Spec: https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperties + * 'color-interpolation-filters' property. + */ + virtual SVGColorspace resolveColorspace(const SVGRenderContext&, const SVGFilterContext&) const; /** Propagates any inherited presentation attributes in the given context. */ void applyProperties(SVGRenderContext*) const; @@ -81,27 +81,23 @@ class SkSVGFe : public SVGHiddenContainer { SVG_OPTIONAL_ATTR(Height, SVGLength) protected: - explicit SkSVGFe(SVGTag t) : INHERITED(t) { + explicit SVGFe(SVGTag t) : INHERITED(t) { } -#ifndef RENDER_SVG virtual std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SkSVGFilterContext&) const = 0; -#endif + const SVGFilterContext&) const = 0; virtual std::vector getInputs() const = 0; - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; private: -#ifndef RENDER_SVG /** * Resolves the rect specified by the x, y, width and height attributes (if specified) on this * filter effect. These attributes are resolved according to the given length context and * the value of 'primitiveUnits' on the parent element. */ - Rect resolveBoundaries(const SVGRenderContext&, const SkSVGFilterContext&) const; -#endif + Rect resolveBoundaries(const SVGRenderContext&, const SVGFilterContext&) const; using INHERITED = SVGHiddenContainer; }; diff --git a/include/tgfx/svg/node/SVGFeBlend.h b/include/tgfx/svg/node/SVGFeBlend.h index d898cf04..f19adcc0 100644 --- a/include/tgfx/svg/node/SVGFeBlend.h +++ b/include/tgfx/svg/node/SVGFeBlend.h @@ -24,12 +24,12 @@ #include "tgfx/svg/node/SVGFe.h" #include "tgfx/svg/node/SVGNode.h" -class SkSVGFilterContext; +class SVGFilterContext; class SVGRenderContext; namespace tgfx { -class SkSVGFeBlend : public SkSVGFe { +class SVGFeBlend : public SVGFe { public: enum class Mode { kNormal, @@ -39,29 +39,27 @@ class SkSVGFeBlend : public SkSVGFe { kLighten, }; - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGFeBlend()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGFeBlend()); } SVG_ATTR(BlendMode, Mode, Mode::kNormal) SVG_ATTR(In2, SVGFeInputType, SVGFeInputType()) protected: -#ifndef RENDER_SVG std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SkSVGFilterContext&) const override; -#endif + const SVGFilterContext&) const override; std::vector getInputs() const override { return {this->getIn(), this->getIn2()}; } - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; private: - SkSVGFeBlend() : INHERITED(SVGTag::FeBlend) { + SVGFeBlend() : INHERITED(SVGTag::FeBlend) { } - using INHERITED = SkSVGFe; + using INHERITED = SVGFe; }; } // namespace tgfx diff --git a/include/tgfx/svg/node/SVGFeColorMatrix.h b/include/tgfx/svg/node/SVGFeColorMatrix.h index dbb07bb8..9390bf58 100644 --- a/include/tgfx/svg/node/SVGFeColorMatrix.h +++ b/include/tgfx/svg/node/SVGFeColorMatrix.h @@ -24,38 +24,36 @@ #include "tgfx/svg/node/SVGFe.h" #include "tgfx/svg/node/SVGNode.h" -class SkSVGFilterContext; +class SVGFilterContext; class SVGRenderContext; namespace tgfx { using ColorMatrix = std::array; -class SkSVGFeColorMatrix final : public SkSVGFe { +class SVGFeColorMatrix final : public SVGFe { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGFeColorMatrix()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGFeColorMatrix()); } SVG_ATTR(Type, SVGFeColorMatrixType, SVGFeColorMatrixType(SVGFeColorMatrixType::Matrix)) SVG_ATTR(Values, SVGFeColorMatrixValues, SVGFeColorMatrixValues()) protected: -#ifndef RENDER_SVG std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SkSVGFilterContext&) const override; -#endif + const SVGFilterContext&) const override; + std::vector getInputs() const override { return {this->getIn()}; } - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; private: - SkSVGFeColorMatrix() : INHERITED(SVGTag::FeColorMatrix) { + SVGFeColorMatrix() : INHERITED(SVGTag::FeColorMatrix) { } -#ifndef RENDER_SVG ColorMatrix makeMatrixForType() const; static ColorMatrix MakeSaturate(SVGNumberType s); @@ -63,8 +61,7 @@ class SkSVGFeColorMatrix final : public SkSVGFe { static ColorMatrix MakeHueRotate(SVGNumberType degrees); static ColorMatrix MakeLuminanceToAlpha(); -#endif - using INHERITED = SkSVGFe; + using INHERITED = SVGFe; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGFeComponentTransfer.h b/include/tgfx/svg/node/SVGFeComponentTransfer.h index 91993af4..b03ed2c4 100644 --- a/include/tgfx/svg/node/SVGFeComponentTransfer.h +++ b/include/tgfx/svg/node/SVGFeComponentTransfer.h @@ -27,27 +27,27 @@ #include "tgfx/svg/node/SVGHiddenContainer.h" #include "tgfx/svg/node/SVGNode.h" -class SkSVGFilterContext; +class SVGFilterContext; class SVGRenderContext; namespace tgfx { -class SkSVGFeFunc final : public SVGHiddenContainer { +class SVGFeFunc final : public SVGHiddenContainer { public: - static std::shared_ptr MakeFuncA() { - return std::shared_ptr(new SkSVGFeFunc(SVGTag::FeFuncA)); + static std::shared_ptr MakeFuncA() { + return std::shared_ptr(new SVGFeFunc(SVGTag::FeFuncA)); } - static std::shared_ptr MakeFuncR() { - return std::shared_ptr(new SkSVGFeFunc(SVGTag::FeFuncR)); + static std::shared_ptr MakeFuncR() { + return std::shared_ptr(new SVGFeFunc(SVGTag::FeFuncR)); } - static std::shared_ptr MakeFuncG() { - return std::shared_ptr(new SkSVGFeFunc(SVGTag::FeFuncG)); + static std::shared_ptr MakeFuncG() { + return std::shared_ptr(new SVGFeFunc(SVGTag::FeFuncG)); } - static std::shared_ptr MakeFuncB() { - return std::shared_ptr(new SkSVGFeFunc(SVGTag::FeFuncB)); + static std::shared_ptr MakeFuncB() { + return std::shared_ptr(new SVGFeFunc(SVGTag::FeFuncB)); } SVG_ATTR(Amplitude, SVGNumberType, 1) @@ -61,36 +61,35 @@ class SkSVGFeFunc final : public SVGHiddenContainer { std::vector getTable() const; protected: - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; private: - SkSVGFeFunc(SVGTag tag) : INHERITED(tag) { + SVGFeFunc(SVGTag tag) : INHERITED(tag) { } using INHERITED = SVGHiddenContainer; }; -class SkSVGFeComponentTransfer final : public SkSVGFe { +class SVGFeComponentTransfer final : public SVGFe { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGFeComponentTransfer()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGFeComponentTransfer()); } protected: -#ifndef RENDER_SVG std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SkSVGFilterContext&) const override { + const SVGFilterContext&) const override { return nullptr; }; -#endif + std::vector getInputs() const override { return {this->getIn()}; } private: - SkSVGFeComponentTransfer() : INHERITED(SVGTag::FeComponentTransfer) { + SVGFeComponentTransfer() : INHERITED(SVGTag::FeComponentTransfer) { } - using INHERITED = SkSVGFe; + using INHERITED = SVGFe; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGFeComposite.h b/include/tgfx/svg/node/SVGFeComposite.h index 520051d4..c5b057d8 100644 --- a/include/tgfx/svg/node/SVGFeComposite.h +++ b/include/tgfx/svg/node/SVGFeComposite.h @@ -24,13 +24,13 @@ namespace tgfx { -class SkSVGFilterContext; +class SVGFilterContext; class SVGRenderContext; -class SkSVGFeComposite final : public SkSVGFe { +class SVGFeComposite final : public SVGFe { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGFeComposite()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGFeComposite()); } SVG_ATTR(In2, SVGFeInputType, SVGFeInputType()) @@ -45,21 +45,17 @@ class SkSVGFeComposite final : public SkSVGFe { return {this->getIn(), this->getIn2()}; } - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; - private: - SkSVGFeComposite() : INHERITED(SVGTag::FeComposite) { - } - - using INHERITED = SkSVGFe; - -#ifndef RENDER_SVG - protected: std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SkSVGFilterContext&) const override; + const SVGFilterContext&) const override; private: + SVGFeComposite() : INHERITED(SVGTag::FeComposite) { + } + static BlendMode BlendModeForOperator(SVGFeCompositeOperator); -#endif + + using INHERITED = SVGFe; }; } // namespace tgfx diff --git a/include/tgfx/svg/node/SVGFeDisplacementMap.h b/include/tgfx/svg/node/SVGFeDisplacementMap.h index fd158e1b..3f381512 100644 --- a/include/tgfx/svg/node/SVGFeDisplacementMap.h +++ b/include/tgfx/svg/node/SVGFeDisplacementMap.h @@ -26,15 +26,15 @@ namespace tgfx { -class SkSVGFilterContext; +class SVGFilterContext; class SVGRenderContext; -class SkSVGFeDisplacementMap : public SkSVGFe { +class SVGFeDisplacementMap : public SVGFe { public: using ChannelSelector = ColorChannel; - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGFeDisplacementMap()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGFeDisplacementMap()); } SVG_ATTR(In2, SVGFeInputType, SVGFeInputType()) @@ -42,27 +42,23 @@ class SkSVGFeDisplacementMap : public SkSVGFe { SVG_ATTR(YChannelSelector, ChannelSelector, ChannelSelector::A) SVG_ATTR(Scale, SVGNumberType, SVGNumberType(0)) + SVGColorspace resolveColorspace(const SVGRenderContext&, const SVGFilterContext&) const final; + protected: std::vector getInputs() const override { return {this->getIn(), this->getIn2()}; } - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; + + std::shared_ptr onMakeImageFilter(const SVGRenderContext&, + const SVGFilterContext&) const override; private: - SkSVGFeDisplacementMap() : INHERITED(SVGTag::FeDisplacementMap) { + SVGFeDisplacementMap() : INHERITED(SVGTag::FeDisplacementMap) { } - using INHERITED = SkSVGFe; - -#ifndef RENDER_SVG - public: - SVGColorspace resolveColorspace(const SVGRenderContext&, const SkSVGFilterContext&) const final; - - protected: - std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SkSVGFilterContext&) const override; -#endif + using INHERITED = SVGFe; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGFeFlood.h b/include/tgfx/svg/node/SVGFeFlood.h index 34162aa8..9a66b056 100644 --- a/include/tgfx/svg/node/SVGFeFlood.h +++ b/include/tgfx/svg/node/SVGFeFlood.h @@ -27,36 +27,29 @@ namespace tgfx { class SkImageFilter; -class SkSVGFilterContext; +class SVGFilterContext; class SVGRenderContext; -class SkSVGFeFlood : public SkSVGFe { +class SVGFeFlood : public SVGFe { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGFeFlood()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGFeFlood()); } protected: std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SkSVGFilterContext&) const override { + const SVGFilterContext&) const override { return nullptr; }; -#ifdef RENDER_SVG - sk_sp onMakeImageFilter(const SVGRenderContext&, - const SkSVGFilterContext&) const override; -#endif std::vector getInputs() const override { return {}; } private: - SkSVGFeFlood() : INHERITED(SVGTag::FeFlood) { + SVGFeFlood() : INHERITED(SVGTag::FeFlood) { } -#ifdef RENDER_SVG - SkColor resolveFloodColor(const SVGRenderContext&) const; -#endif - using INHERITED = SkSVGFe; + using INHERITED = SVGFe; }; } // namespace tgfx diff --git a/include/tgfx/svg/node/SVGFeGaussianBlur.h b/include/tgfx/svg/node/SVGFeGaussianBlur.h index 10cc339e..64f1fbcb 100644 --- a/include/tgfx/svg/node/SVGFeGaussianBlur.h +++ b/include/tgfx/svg/node/SVGFeGaussianBlur.h @@ -26,39 +26,37 @@ namespace tgfx { -class SkSVGFilterContext; +class SVGFilterContext; class SVGRenderContext; -class SkSVGFeGaussianBlur : public SkSVGFe { +class SVGFeGaussianBlur : public SVGFe { public: struct StdDeviation { SVGNumberType fX; SVGNumberType fY; }; - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGFeGaussianBlur()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGFeGaussianBlur()); } SVG_ATTR(stdDeviation, StdDeviation, StdDeviation({0, 0})) protected: -#ifndef RENDER_SVG std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SkSVGFilterContext&) const override; -#endif + const SVGFilterContext&) const override; std::vector getInputs() const override { return {this->getIn()}; } - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; private: - SkSVGFeGaussianBlur() : INHERITED(SVGTag::FeGaussianBlur) { + SVGFeGaussianBlur() : INHERITED(SVGTag::FeGaussianBlur) { } - using INHERITED = SkSVGFe; + using INHERITED = SVGFe; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGFeImage.h b/include/tgfx/svg/node/SVGFeImage.h index 0a959c49..14b4c672 100644 --- a/include/tgfx/svg/node/SVGFeImage.h +++ b/include/tgfx/svg/node/SVGFeImage.h @@ -24,37 +24,35 @@ #include "tgfx/svg/node/SVGFe.h" #include "tgfx/svg/node/SVGNode.h" -class SkSVGFilterContext; +class SVGFilterContext; class SVGRenderContext; namespace tgfx { -class SkSVGFeImage : public SkSVGFe { +class SVGFeImage : public SVGFe { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGFeImage()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGFeImage()); } SVG_ATTR(Href, SVGIRI, SVGIRI()) SVG_ATTR(PreserveAspectRatio, SVGPreserveAspectRatio, SVGPreserveAspectRatio()) protected: - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; -#ifndef RENDER_SVG std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SkSVGFilterContext&) const override; -#endif + const SVGFilterContext&) const override; std::vector getInputs() const override { return {}; } private: - SkSVGFeImage() : INHERITED(SVGTag::FeImage) { + SVGFeImage() : INHERITED(SVGTag::FeImage) { } - using INHERITED = SkSVGFe; + using INHERITED = SVGFe; }; } // namespace tgfx diff --git a/include/tgfx/svg/node/SVGFeLightSource.h b/include/tgfx/svg/node/SVGFeLightSource.h index a291acd5..8661f8d3 100644 --- a/include/tgfx/svg/node/SVGFeLightSource.h +++ b/include/tgfx/svg/node/SVGFeLightSource.h @@ -38,10 +38,10 @@ class SkSVGFeLightSource : public SVGHiddenContainer { using INHERITED = SVGHiddenContainer; }; -class SkSVGFeDistantLight final : public SkSVGFeLightSource { +class SVGFeDistantLight final : public SkSVGFeLightSource { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGFeDistantLight()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGFeDistantLight()); } // pk::SkPoint3 computeDirection() const; @@ -50,18 +50,18 @@ class SkSVGFeDistantLight final : public SkSVGFeLightSource { SVG_ATTR(Elevation, SVGNumberType, 0) private: - SkSVGFeDistantLight() : INHERITED(SVGTag::FeDistantLight) { + SVGFeDistantLight() : INHERITED(SVGTag::FeDistantLight) { } - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; using INHERITED = SkSVGFeLightSource; }; -class SkSVGFePointLight final : public SkSVGFeLightSource { +class SVGFePointLight final : public SkSVGFeLightSource { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGFePointLight()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGFePointLight()); } SVG_ATTR(X, SVGNumberType, 0) @@ -69,18 +69,18 @@ class SkSVGFePointLight final : public SkSVGFeLightSource { SVG_ATTR(Z, SVGNumberType, 0) private: - SkSVGFePointLight() : INHERITED(SVGTag::FePointLight) { + SVGFePointLight() : INHERITED(SVGTag::FePointLight) { } - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; using INHERITED = SkSVGFeLightSource; }; -class SkSVGFeSpotLight final : public SkSVGFeLightSource { +class SVGFeSpotLight final : public SkSVGFeLightSource { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGFeSpotLight()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGFeSpotLight()); } SVG_ATTR(X, SVGNumberType, 0) @@ -94,10 +94,10 @@ class SkSVGFeSpotLight final : public SkSVGFeLightSource { SVG_OPTIONAL_ATTR(LimitingConeAngle, SVGNumberType) private: - SkSVGFeSpotLight() : INHERITED(SVGTag::FeSpotLight) { + SVGFeSpotLight() : INHERITED(SVGTag::FeSpotLight) { } - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; using INHERITED = SkSVGFeLightSource; }; diff --git a/include/tgfx/svg/node/SVGFeLighting.h b/include/tgfx/svg/node/SVGFeLighting.h index 66d5833c..d0e368bf 100644 --- a/include/tgfx/svg/node/SVGFeLighting.h +++ b/include/tgfx/svg/node/SVGFeLighting.h @@ -27,13 +27,11 @@ namespace tgfx { -class SkSVGFeDistantLight; -class SkSVGFePointLight; -class SkSVGFeSpotLight; -// class SkSVGFilterContext; -// class SVGRenderContext; +class SVGFeDistantLight; +class SVGFePointLight; +class SVGFeSpotLight; -class SkSVGFeLighting : public SkSVGFe { +class SVGFeLighting : public SVGFe { public: struct KernelUnitLength { SVGNumberType fDx; @@ -44,98 +42,59 @@ class SkSVGFeLighting : public SkSVGFe { SVG_OPTIONAL_ATTR(UnitLength, KernelUnitLength) protected: - explicit SkSVGFeLighting(SVGTag t) : INHERITED(t) { + explicit SVGFeLighting(SVGTag t) : INHERITED(t) { } std::vector getInputs() const final { return {this->getIn()}; } - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SkSVGFilterContext&) const final { + const SVGFilterContext&) const final { return nullptr; }; -#ifdef RENDER_SVG - sk_sp onMakeImageFilter(const SVGRenderContext&, - const SkSVGFilterContext&) const final; - - virtual sk_sp makeDistantLight(const SVGRenderContext&, const SkSVGFilterContext&, - const SkSVGFeDistantLight*) const = 0; - - virtual sk_sp makePointLight(const SVGRenderContext&, const SkSVGFilterContext&, - const SkSVGFePointLight*) const = 0; - - virtual sk_sp makeSpotLight(const SVGRenderContext&, const SkSVGFilterContext&, - const SkSVGFeSpotLight*) const = 0; - - SkColor resolveLightingColor(const SVGRenderContext&) const; - - SkPoint3 resolveXYZ(const SVGRenderContext&, const SkSVGFilterContext&, SkSVGNumberType, - SkSVGNumberType, SkSVGNumberType) const; -#endif private: - using INHERITED = SkSVGFe; + using INHERITED = SVGFe; }; -class SkSVGFeSpecularLighting final : public SkSVGFeLighting { +class SVGFeSpecularLighting final : public SVGFeLighting { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGFeSpecularLighting()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGFeSpecularLighting()); } SVG_ATTR(SpecularConstant, SVGNumberType, 1) SVG_ATTR(SpecularExponent, SVGNumberType, 1) protected: - bool parseAndSetAttribute(const char*, const char*) override; - -#ifdef RENDER_SVG - sk_sp makeDistantLight(const SVGRenderContext&, const SkSVGFilterContext&, - const SkSVGFeDistantLight*) const final; - - sk_sp makePointLight(const SVGRenderContext&, const SkSVGFilterContext&, - const SkSVGFePointLight*) const final; - - sk_sp makeSpotLight(const SVGRenderContext&, const SkSVGFilterContext&, - const SkSVGFeSpotLight*) const final; -#endif + bool parseAndSetAttribute(const std::string&, const std::string&) override; private: - SkSVGFeSpecularLighting() : INHERITED(SVGTag::FeSpecularLighting) { + SVGFeSpecularLighting() : INHERITED(SVGTag::FeSpecularLighting) { } - using INHERITED = SkSVGFeLighting; + using INHERITED = SVGFeLighting; }; -class SkSVGFeDiffuseLighting final : public SkSVGFeLighting { +class SVGFeDiffuseLighting final : public SVGFeLighting { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGFeDiffuseLighting()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGFeDiffuseLighting()); } SVG_ATTR(DiffuseConstant, SVGNumberType, 1) protected: - bool parseAndSetAttribute(const char*, const char*) override; - -#ifdef RENDER_SVG - sk_sp makeDistantLight(const SVGRenderContext&, const SkSVGFilterContext&, - const SkSVGFeDistantLight*) const final; - - sk_sp makePointLight(const SVGRenderContext&, const SkSVGFilterContext&, - const SkSVGFePointLight*) const final; + bool parseAndSetAttribute(const std::string&, const std::string&) override; - sk_sp makeSpotLight(const SVGRenderContext&, const SkSVGFilterContext&, - const SkSVGFeSpotLight*) const final; -#endif private: - SkSVGFeDiffuseLighting() : INHERITED(SVGTag::FeDiffuseLighting) { + SVGFeDiffuseLighting() : INHERITED(SVGTag::FeDiffuseLighting) { } - using INHERITED = SkSVGFeLighting; + using INHERITED = SVGFeLighting; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGFeMerge.h b/include/tgfx/svg/node/SVGFeMerge.h index d8ef7dd4..1445a54f 100644 --- a/include/tgfx/svg/node/SVGFeMerge.h +++ b/include/tgfx/svg/node/SVGFeMerge.h @@ -25,52 +25,47 @@ #include "tgfx/svg/node/SVGHiddenContainer.h" #include "tgfx/svg/node/SVGNode.h" -// class SkSVGFilterContext; -// class SVGRenderContext; - namespace tgfx { // https://www.w3.org/TR/SVG11/filters.html#feMergeNodeElement -class SkSVGFeMergeNode : public SVGHiddenContainer { +class SVGFeMergeNode : public SVGHiddenContainer { public: static constexpr SVGTag tag = SVGTag::FeMergeNode; - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGFeMergeNode()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGFeMergeNode()); } SVG_ATTR(In, SVGFeInputType, SVGFeInputType()) protected: - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; private: - SkSVGFeMergeNode() : INHERITED(tag) { + SVGFeMergeNode() : INHERITED(tag) { } using INHERITED = SVGHiddenContainer; }; // https://www.w3.org/TR/SVG11/filters.html#feMergeElement -class SkSVGFeMerge : public SkSVGFe { +class SVGFeMerge : public SVGFe { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGFeMerge()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGFeMerge()); } protected: -#ifndef RENDER_SVG std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SkSVGFilterContext&) const override; -#endif + const SVGFilterContext&) const override; std::vector getInputs() const override; private: - SkSVGFeMerge() : INHERITED(SVGTag::FeMerge) { + SVGFeMerge() : INHERITED(SVGTag::FeMerge) { } - using INHERITED = SkSVGFe; + using INHERITED = SVGFe; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGFeMorphology.h b/include/tgfx/svg/node/SVGFeMorphology.h index 6e2d799f..896630bf 100644 --- a/include/tgfx/svg/node/SVGFeMorphology.h +++ b/include/tgfx/svg/node/SVGFeMorphology.h @@ -24,12 +24,9 @@ #include "tgfx/svg/node/SVGFe.h" #include "tgfx/svg/node/SVGNode.h" -// class SkSVGFilterContext; -// class SVGRenderContext; - namespace tgfx { -class SkSVGFeMorphology : public SkSVGFe { +class SVGFeMorphology : public SVGFe { public: struct Radius { SVGNumberType fX; @@ -41,30 +38,28 @@ class SkSVGFeMorphology : public SkSVGFe { kDilate, }; - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGFeMorphology()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGFeMorphology()); } SVG_ATTR(MorphOperator, Operator, Operator::kErode) SVG_ATTR(MorphRadius, Radius, Radius({0, 0})) protected: -#ifndef RENDER_SVG std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SkSVGFilterContext&) const override; -#endif + const SVGFilterContext&) const override; std::vector getInputs() const override { return {this->getIn()}; } - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; private: - SkSVGFeMorphology() : INHERITED(SVGTag::FeMorphology) { + SVGFeMorphology() : INHERITED(SVGTag::FeMorphology) { } - using INHERITED = SkSVGFe; + using INHERITED = SVGFe; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGFeOffset.h b/include/tgfx/svg/node/SVGFeOffset.h index 9767bf23..5182805d 100644 --- a/include/tgfx/svg/node/SVGFeOffset.h +++ b/include/tgfx/svg/node/SVGFeOffset.h @@ -27,34 +27,32 @@ namespace tgfx { class SkImageFilter; -class SkSVGFilterContext; +class SVGFilterContext; class SVGRenderContext; -class SkSVGFeOffset : public SkSVGFe { +class SVGFeOffset : public SVGFe { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGFeOffset()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGFeOffset()); } SVG_ATTR(Dx, SVGNumberType, SVGNumberType(0)) SVG_ATTR(Dy, SVGNumberType, SVGNumberType(0)) protected: -#ifndef RENDER_SVG std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SkSVGFilterContext&) const override; -#endif + const SVGFilterContext&) const override; std::vector getInputs() const override { return {this->getIn()}; } - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; private: - SkSVGFeOffset() : INHERITED(SVGTag::FeOffset) { + SVGFeOffset() : INHERITED(SVGTag::FeOffset) { } - using INHERITED = SkSVGFe; + using INHERITED = SVGFe; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGFeTurbulence.h b/include/tgfx/svg/node/SVGFeTurbulence.h index b19c7d1a..01b2c776 100644 --- a/include/tgfx/svg/node/SVGFeTurbulence.h +++ b/include/tgfx/svg/node/SVGFeTurbulence.h @@ -26,10 +26,10 @@ namespace tgfx { -class SkSVGFeTurbulence : public SkSVGFe { +class SVGFeTurbulence : public SVGFe { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGFeTurbulence()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGFeTurbulence()); } SVG_ATTR(BaseFrequency, SVGFeTurbulenceBaseFrequency, SVGFeTurbulenceBaseFrequency({})) @@ -39,22 +39,20 @@ class SkSVGFeTurbulence : public SkSVGFe { SVGFeTurbulenceType(SVGFeTurbulenceType::Type::Turbulence)) protected: -#ifndef RENDER_SVG std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SkSVGFilterContext&) const override; -#endif + const SVGFilterContext&) const override; std::vector getInputs() const override { return {}; } - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; private: - SkSVGFeTurbulence() : INHERITED(SVGTag::FeTurbulence) { + SVGFeTurbulence() : INHERITED(SVGTag::FeTurbulence) { } - using INHERITED = SkSVGFe; + using INHERITED = SVGFe; }; } // namespace tgfx diff --git a/include/tgfx/svg/node/SVGFilter.h b/include/tgfx/svg/node/SVGFilter.h index d63a1f33..7211bec2 100644 --- a/include/tgfx/svg/node/SVGFilter.h +++ b/include/tgfx/svg/node/SVGFilter.h @@ -26,10 +26,10 @@ namespace tgfx { -class SkSVGFilter final : public SVGHiddenContainer { +class SVGFilter final : public SVGHiddenContainer { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGFilter()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGFilter()); } /** Propagates any inherited presentation attributes in the given context. */ @@ -47,10 +47,10 @@ class SkSVGFilter final : public SVGHiddenContainer { SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::UserSpaceOnUse)) private: - SkSVGFilter() : INHERITED(SVGTag::Filter) { + SVGFilter() : INHERITED(SVGTag::Filter) { } - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; using INHERITED = SVGHiddenContainer; }; diff --git a/include/tgfx/svg/node/SVGFilterContext.h b/include/tgfx/svg/node/SVGFilterContext.h index c1cdfbd2..7a69b527 100644 --- a/include/tgfx/svg/node/SVGFilterContext.h +++ b/include/tgfx/svg/node/SVGFilterContext.h @@ -22,27 +22,26 @@ #include #include "tgfx/core/ImageFilter.h" #include "tgfx/core/Rect.h" -#include "tgfx/svg/SVGRenderContext.h" #include "tgfx/svg/SVGTypes.h" namespace tgfx { -class SkSVGFilterContext { +class SVGRenderContext; +class SVGFilterContext { public: - SkSVGFilterContext(const Rect& filterEffectsRegion, - const SVGObjectBoundingBoxUnits& primitiveUnits) - : fFilterEffectsRegion(filterEffectsRegion), fPrimitiveUnits(primitiveUnits), - fPreviousResult({nullptr, filterEffectsRegion, SVGColorspace::SRGB}) { + SVGFilterContext(const Rect& filterEffectsRegion, const SVGObjectBoundingBoxUnits& primitiveUnits) + : _filterEffectsRegion(filterEffectsRegion), _primitiveUnits(primitiveUnits), + previousResult({nullptr, filterEffectsRegion, SVGColorspace::SRGB}) { } const Rect& filterEffectsRegion() const { - return fFilterEffectsRegion; + return _filterEffectsRegion; } const Rect& filterPrimitiveSubregion(const SVGFeInputType&) const; const SVGObjectBoundingBoxUnits& primitiveUnits() const { - return fPrimitiveUnits; + return _primitiveUnits; } void registerResult(const SVGStringType&, const std::shared_ptr&, const Rect&, @@ -52,36 +51,32 @@ class SkSVGFilterContext { bool previousResultIsSourceGraphic() const; -#ifndef RENDER_SVG SVGColorspace resolveInputColorspace(const SVGRenderContext&, const SVGFeInputType&) const; std::shared_ptr resolveInput(const SVGRenderContext&, const SVGFeInputType&) const; std::shared_ptr resolveInput(const SVGRenderContext&, const SVGFeInputType&, SVGColorspace) const; -#endif private: struct Result { - std::shared_ptr fImageFilter; - Rect fFilterSubregion; - SVGColorspace fColorspace; + std::shared_ptr imageFilter; + Rect filterSubregion; + SVGColorspace colorspace; }; const Result* findResultById(const SVGStringType&) const; -#ifndef RENDER_SVG std::tuple, SVGColorspace> getInput(const SVGRenderContext&, const SVGFeInputType&) const; -#endif - Rect fFilterEffectsRegion; + Rect _filterEffectsRegion; - SVGObjectBoundingBoxUnits fPrimitiveUnits; + SVGObjectBoundingBoxUnits _primitiveUnits; - std::unordered_map fResults; + std::unordered_map results; - Result fPreviousResult; + Result previousResult; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGG.h b/include/tgfx/svg/node/SVGG.h index 286f8073..e4b9b6a3 100644 --- a/include/tgfx/svg/node/SVGG.h +++ b/include/tgfx/svg/node/SVGG.h @@ -22,16 +22,16 @@ #include "tgfx/svg/node/SVGContainer.h" namespace tgfx { -class SkSVGG : public SkSVGContainer { +class SVGG : public SVGContainer { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGG()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGG()); } private: - SkSVGG() : INHERITED(SVGTag::G) { + SVGG() : INHERITED(SVGTag::G) { } - using INHERITED = SkSVGContainer; + using INHERITED = SVGContainer; }; } // namespace tgfx diff --git a/include/tgfx/svg/node/SVGGradient.h b/include/tgfx/svg/node/SVGGradient.h index e1f3a26c..0bde2c70 100644 --- a/include/tgfx/svg/node/SVGGradient.h +++ b/include/tgfx/svg/node/SVGGradient.h @@ -30,7 +30,7 @@ namespace tgfx { -class SkSVGGradient : public SVGHiddenContainer { +class SVGGradient : public SVGHiddenContainer { public: SVG_ATTR(Href, SVGIRI, SVGIRI()) SVG_ATTR(GradientTransform, SVGTransformType, SVGTransformType(Matrix::I())) @@ -39,10 +39,10 @@ class SkSVGGradient : public SVGHiddenContainer { SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox)) protected: - explicit SkSVGGradient(SVGTag t) : INHERITED(t) { + explicit SVGGradient(SVGTag t) : INHERITED(t) { } - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; bool onAsPaint(const SVGRenderContext&, Paint*) const final; @@ -52,7 +52,7 @@ class SkSVGGradient : public SVGHiddenContainer { private: void collectColorStops(const SVGRenderContext&, std::vector&, std::vector&) const; - Color resolveStopColor(const SVGRenderContext&, const SkSVGStop&) const; + Color resolveStopColor(const SVGRenderContext&, const SVGStop&) const; using INHERITED = SVGHiddenContainer; }; diff --git a/include/tgfx/svg/node/SVGHiddenContainer.h b/include/tgfx/svg/node/SVGHiddenContainer.h index b1fb718e..34698dff 100644 --- a/include/tgfx/svg/node/SVGHiddenContainer.h +++ b/include/tgfx/svg/node/SVGHiddenContainer.h @@ -22,18 +22,16 @@ namespace tgfx { -class SVGHiddenContainer : public SkSVGContainer { +class SVGHiddenContainer : public SVGContainer { protected: explicit SVGHiddenContainer(SVGTag t) : INHERITED(t) { } -#ifndef RENDER_SVG void onRender(const SVGRenderContext&) const final { //abort rendering children nodes } -#endif private: - using INHERITED = SkSVGContainer; + using INHERITED = SVGContainer; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGImage.h b/include/tgfx/svg/node/SVGImage.h index bf397e84..cb188d07 100644 --- a/include/tgfx/svg/node/SVGImage.h +++ b/include/tgfx/svg/node/SVGImage.h @@ -19,11 +19,6 @@ #pragma once #include -// #include "include/core/SkImage.h" -// #include "include/core/SkRect.h" -// #include "include/core/SkRefCnt.h" -// #include "include/private/base/SkAPI.h" -// #include "include/private/base/SkDebug.h" #include "tgfx/core/Image.h" #include "tgfx/core/Path.h" #include "tgfx/core/Rect.h" @@ -39,10 +34,10 @@ namespace skresources { class ResourceProvider; } -class SkSVGImage final : public SkSVGTransformableNode { +class SVGImage final : public SVGTransformableNode { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGImage()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGImage()); } void appendChild(std::shared_ptr) override { @@ -53,13 +48,11 @@ class SkSVGImage final : public SkSVGTransformableNode { Rect fDst; }; -#ifndef RENDER_SVG bool onPrepareToRender(SVGRenderContext*) const override; void onRender(const SVGRenderContext&) const override; Path onAsPath(const SVGRenderContext&) const override; Rect onObjectBoundingBox(const SVGRenderContext&) const override; static ImageInfo LoadImage(const SVGIRI&, const Rect&, SVGPreserveAspectRatio); -#endif SVG_ATTR(X, SVGLength, SVGLength(0)) SVG_ATTR(Y, SVGLength, SVGLength(0)) @@ -69,13 +62,13 @@ class SkSVGImage final : public SkSVGTransformableNode { SVG_ATTR(PreserveAspectRatio, SVGPreserveAspectRatio, SVGPreserveAspectRatio()) protected: - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; private: - SkSVGImage() : INHERITED(SVGTag::Image) { + SVGImage() : INHERITED(SVGTag::Image) { } - using INHERITED = SkSVGTransformableNode; + using INHERITED = SVGTransformableNode; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGLine.h b/include/tgfx/svg/node/SVGLine.h index 5828db1a..47362112 100644 --- a/include/tgfx/svg/node/SVGLine.h +++ b/include/tgfx/svg/node/SVGLine.h @@ -29,10 +29,10 @@ namespace tgfx { -class SkSVGLine final : public SVGShape { +class SVGLine final : public SVGShape { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGLine()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGLine()); } SVG_ATTR(X1, SVGLength, SVGLength(0)) @@ -41,21 +41,17 @@ class SkSVGLine final : public SVGShape { SVG_ATTR(Y2, SVGLength, SVGLength(0)) protected: - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; -#ifndef RENDER_SVG void onDraw(Canvas*, const SVGLengthContext&, const Paint&, PathFillType) const override; Path onAsPath(const SVGRenderContext&) const override; -#endif private: - SkSVGLine(); + SVGLine(); -#ifndef RENDER_SVG // resolve and return the two endpoints std::tuple resolve(const SVGLengthContext&) const; -#endif using INHERITED = SVGShape; }; diff --git a/include/tgfx/svg/node/SVGLinearGradient.h b/include/tgfx/svg/node/SVGLinearGradient.h index 0272d2f1..6f55242d 100644 --- a/include/tgfx/svg/node/SVGLinearGradient.h +++ b/include/tgfx/svg/node/SVGLinearGradient.h @@ -26,10 +26,10 @@ namespace tgfx { -class SkSVGLinearGradient final : public SkSVGGradient { +class SVGLinearGradient final : public SVGGradient { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGLinearGradient()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGLinearGradient()); } SVG_ATTR(X1, SVGLength, SVGLength(0, SVGLength::Unit::Percentage)) @@ -38,17 +38,15 @@ class SkSVGLinearGradient final : public SkSVGGradient { SVG_ATTR(Y2, SVGLength, SVGLength(0, SVGLength::Unit::Percentage)) protected: - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; -#ifndef RENDER_SVG std::shared_ptr onMakeShader(const SVGRenderContext&, const std::vector&, const std::vector&, TileMode, const Matrix& localMatrix) const override; -#endif private: - SkSVGLinearGradient(); + SVGLinearGradient(); - using INHERITED = SkSVGGradient; + using INHERITED = SVGGradient; }; } // namespace tgfx diff --git a/include/tgfx/svg/node/SVGMask.h b/include/tgfx/svg/node/SVGMask.h index 9b885a01..7dbf6775 100644 --- a/include/tgfx/svg/node/SVGMask.h +++ b/include/tgfx/svg/node/SVGMask.h @@ -25,10 +25,10 @@ namespace tgfx { -class SkSVGMask final : public SVGHiddenContainer { +class SVGMask final : public SVGHiddenContainer { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGMask()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGMask()); } SVG_OPTIONAL_ATTR(X, SVGLength) @@ -44,16 +44,14 @@ class SkSVGMask final : public SVGHiddenContainer { private: friend class SVGRenderContext; - SkSVGMask() : INHERITED(SVGTag::Mask) { + SVGMask() : INHERITED(SVGTag::Mask) { } - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; -#ifndef RENDER_SVG Rect bounds(const SVGRenderContext&) const; void renderMask(const SVGRenderContext&) const; -#endif using INHERITED = SVGHiddenContainer; }; diff --git a/include/tgfx/svg/node/SVGNode.h b/include/tgfx/svg/node/SVGNode.h index 777408e0..83e1b204 100644 --- a/include/tgfx/svg/node/SVGNode.h +++ b/include/tgfx/svg/node/SVGNode.h @@ -21,15 +21,16 @@ #include #include #include "tgfx/core/Matrix.h" +#include "tgfx/core/Paint.h" +#include "tgfx/core/Path.h" #include "tgfx/core/Rect.h" -#include "tgfx/svg//SVGTypes.h" #include "tgfx/svg/SVGAttribute.h" -#include "tgfx/svg/SVGAttributeParser.h" -#include "tgfx/svg/SVGRenderContext.h" +#include "tgfx/svg/SVGTypes.h" namespace tgfx { class SVGValue; +class SVGRenderContext; enum class SVGTag { Circle, @@ -79,37 +80,36 @@ enum class SVGTag { Use }; -#define SVG_PRES_ATTR(attr_name, attr_type, attr_inherited) \ - private: \ - bool set##attr_name( \ - SVGAttributeParser::ParseResult>&& pr) { \ - if (pr.has_value()) { \ - this->set##attr_name(std::move(*pr)); \ - } \ - return pr.has_value(); \ - } \ - \ - public: \ - const SVGProperty& get##attr_name() const { \ - return _presentationAttributes.attr_name; \ - } \ - void set##attr_name(const SVGProperty& v) { \ - auto* dest = &_presentationAttributes.attr_name; \ - if (!dest->isInheritable() || v.isValue()) { \ - /* TODO: If dest is not inheritable, handle v == "inherit" */ \ - *dest = v; \ - } else { \ - dest->set(SVGPropertyState::Inherit); \ - } \ - } \ - void set##attr_name(SVGProperty&& v) { \ - auto* dest = &_presentationAttributes.attr_name; \ - if (!dest->isInheritable() || v.isValue()) { \ - /* TODO: If dest is not inheritable, handle v == "inherit" */ \ - *dest = std::move(v); \ - } else { \ - dest->set(SVGPropertyState::Inherit); \ - } \ +#define SVG_PRES_ATTR(attr_name, attr_type, attr_inherited) \ + private: \ + bool set##attr_name(std::optional>&& pr) { \ + if (pr.has_value()) { \ + this->set##attr_name(std::move(*pr)); \ + } \ + return pr.has_value(); \ + } \ + \ + public: \ + const SVGProperty& get##attr_name() const { \ + return _presentationAttributes.attr_name; \ + } \ + void set##attr_name(const SVGProperty& v) { \ + auto* dest = &_presentationAttributes.attr_name; \ + if (!dest->isInheritable() || v.isValue()) { \ + /* TODO: If dest is not inheritable, handle v == "inherit" */ \ + *dest = v; \ + } else { \ + dest->set(SVGPropertyState::Inherit); \ + } \ + } \ + void set##attr_name(SVGProperty&& v) { \ + auto* dest = &_presentationAttributes.attr_name; \ + if (!dest->isInheritable() || v.isValue()) { \ + /* TODO: If dest is not inheritable, handle v == "inherit" */ \ + *dest = std::move(v); \ + } else { \ + dest->set(SVGPropertyState::Inherit); \ + } \ } class SVGNode { @@ -130,18 +130,14 @@ class SVGNode { return false; } -#ifndef RENDER_SVG - void render(const SVGRenderContext&) const; bool asPaint(const SVGRenderContext&, Paint*) const; Path asPath(const SVGRenderContext&) const; Rect objectBoundingBox(const SVGRenderContext&) const; -#endif - void setAttribute(SVGAttribute, const SVGValue&); bool setAttribute(const std::string& attributeName, const std::string& attributeValue); - virtual bool parseAndSetAttribute(const char* name, const char* value); + virtual bool parseAndSetAttribute(const std::string& name, const std::string& value); SVG_PRES_ATTR(ClipRule, SVGFillRule, true) SVG_PRES_ATTR(Color, SVGColorType, true) @@ -184,9 +180,9 @@ class SVGNode { virtual void onSetAttribute(SVGAttribute, const SVGValue&) { } -#ifndef RENDER_SVG + // Called before onRender(), to apply local attributes to the context. Unlike onRender(), - // onPrepareToRender() bubbles up the inheritance chain: overriders should always call + // onPrepareToRender() bubbles up the inheritance chain: override should always call // INHERITED::onPrepareToRender(), unless they intend to short-circuit rendering // (return false). // Implementations are expected to return true if rendering is to continue, or false if @@ -204,39 +200,37 @@ class SVGNode { virtual Rect onObjectBoundingBox(const SVGRenderContext&) const { return Rect::MakeEmpty(); } -#endif private: SVGTag _tag; - // FIXME: this should be sparse SVGPresentationAttributes _presentationAttributes; }; //NOLINTBEGIN #undef SVG_PRES_ATTR // presentation attributes are only defined for the base class -#define SVG_ATTR_SETTERS(attr_name, attr_type, attr_default, set_cp, set_mv) \ - private: \ - bool set##attr_name(const SVGAttributeParser::ParseResult& pr) { \ - if (pr.has_value()) { \ - this->set##attr_name(*pr); \ - } \ - return pr.has_value(); \ - } \ - bool set##attr_name(SVGAttributeParser::ParseResult&& pr) { \ - if (pr.has_value()) { \ - this->set##attr_name(std::move(*pr)); \ - } \ - return pr.has_value(); \ - } \ - \ - public: \ - void set##attr_name(const attr_type& a) { \ - set_cp(a); \ - } \ - void set##attr_name(attr_type&& a) { \ - set_mv(std::move(a)); \ +#define SVG_ATTR_SETTERS(attr_name, attr_type, attr_default, set_cp, set_mv) \ + private: \ + bool set##attr_name(const std::optional& pr) { \ + if (pr.has_value()) { \ + this->set##attr_name(*pr); \ + } \ + return pr.has_value(); \ + } \ + bool set##attr_name(std::optional&& pr) { \ + if (pr.has_value()) { \ + this->set##attr_name(std::move(*pr)); \ + } \ + return pr.has_value(); \ + } \ + \ + public: \ + void set##attr_name(const attr_type& a) { \ + set_cp(a); \ + } \ + void set##attr_name(attr_type&& a) { \ + set_mv(std::move(a)); \ } #define SVG_ATTR(attr_name, attr_type, attr_default) \ diff --git a/include/tgfx/svg/node/SVGPath.h b/include/tgfx/svg/node/SVGPath.h index ddf3af20..4fd47adf 100644 --- a/include/tgfx/svg/node/SVGPath.h +++ b/include/tgfx/svg/node/SVGPath.h @@ -27,27 +27,25 @@ namespace tgfx { -class SkSVGPath final : public SVGShape { +class SVGPath final : public SVGShape { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGPath()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGPath()); } SVG_ATTR(ShapePath, Path, Path()) protected: - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; -#ifndef RENDER_SVG void onDraw(Canvas*, const SVGLengthContext&, const Paint&, PathFillType) const override; Path onAsPath(const SVGRenderContext&) const override; Rect onObjectBoundingBox(const SVGRenderContext&) const override; -#endif private: - SkSVGPath(); + SVGPath(); using INHERITED = SVGShape; }; diff --git a/include/tgfx/svg/node/SVGPattern.h b/include/tgfx/svg/node/SVGPattern.h index 4ed1a1c6..6204017e 100644 --- a/include/tgfx/svg/node/SVGPattern.h +++ b/include/tgfx/svg/node/SVGPattern.h @@ -27,10 +27,10 @@ namespace tgfx { -class SkSVGPattern final : public SVGHiddenContainer { +class SVGPattern final : public SVGHiddenContainer { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGPattern()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGPattern()); } SVG_ATTR(Href, SVGIRI, SVGIRI()) @@ -45,13 +45,11 @@ class SkSVGPattern final : public SVGHiddenContainer { SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::UserSpaceOnUse)) protected: - SkSVGPattern(); + SVGPattern(); - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; -#ifndef RENDER_SVG bool onAsPaint(const SVGRenderContext&, Paint*) const override; -#endif private: struct PatternAttributes { @@ -59,10 +57,8 @@ class SkSVGPattern final : public SVGHiddenContainer { std::optional patternTransform; }; -#ifndef RENDER_SVG - const SkSVGPattern* resolveHref(const SVGRenderContext&, PatternAttributes*) const; - const SkSVGPattern* hrefTarget(const SVGRenderContext&) const; -#endif + const SVGPattern* resolveHref(const SVGRenderContext&, PatternAttributes*) const; + const SVGPattern* hrefTarget(const SVGRenderContext&) const; using INHERITED = SVGHiddenContainer; }; diff --git a/include/tgfx/svg/node/SVGPoly.h b/include/tgfx/svg/node/SVGPoly.h index dc01cc05..26d6af92 100644 --- a/include/tgfx/svg/node/SVGPoly.h +++ b/include/tgfx/svg/node/SVGPoly.h @@ -29,31 +29,29 @@ namespace tgfx { // Handles and elements. -class SkSVGPoly final : public SVGShape { +class SVGPoly final : public SVGShape { public: - static std::shared_ptr MakePolygon() { - return std::shared_ptr(new SkSVGPoly(SVGTag::Polygon)); + static std::shared_ptr MakePolygon() { + return std::shared_ptr(new SVGPoly(SVGTag::Polygon)); } - static std::shared_ptr MakePolyline() { - return std::shared_ptr(new SkSVGPoly(SVGTag::Polyline)); + static std::shared_ptr MakePolyline() { + return std::shared_ptr(new SVGPoly(SVGTag::Polyline)); } SVG_ATTR(Points, SVGPointsType, SVGPointsType()) protected: - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; -#ifndef RENDER_SVG void onDraw(Canvas*, const SVGLengthContext&, const Paint&, PathFillType) const override; Path onAsPath(const SVGRenderContext&) const override; Rect onObjectBoundingBox(const SVGRenderContext&) const override; -#endif private: - SkSVGPoly(SVGTag); + SVGPoly(SVGTag); mutable Path fPath; // mutated in onDraw(), to apply inherited fill types. diff --git a/include/tgfx/svg/node/SVGRadialGradient.h b/include/tgfx/svg/node/SVGRadialGradient.h index 56331722..87bcd06d 100644 --- a/include/tgfx/svg/node/SVGRadialGradient.h +++ b/include/tgfx/svg/node/SVGRadialGradient.h @@ -27,10 +27,10 @@ namespace tgfx { -class SkSVGRadialGradient final : public SkSVGGradient { +class SVGRadialGradient final : public SVGGradient { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGRadialGradient()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGRadialGradient()); } SVG_ATTR(Cx, SVGLength, SVGLength(50, SVGLength::Unit::Percentage)) @@ -40,17 +40,15 @@ class SkSVGRadialGradient final : public SkSVGGradient { SVG_OPTIONAL_ATTR(Fy, SVGLength) protected: - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; -#ifndef RENDER_SVG std::shared_ptr onMakeShader(const SVGRenderContext&, const std::vector&, const std::vector&, TileMode, const Matrix&) const override; -#endif private: - SkSVGRadialGradient(); + SVGRadialGradient(); - using INHERITED = SkSVGGradient; + using INHERITED = SVGGradient; }; } // namespace tgfx diff --git a/include/tgfx/svg/node/SVGRect.h b/include/tgfx/svg/node/SVGRect.h index 18a1ab90..8b827ddf 100644 --- a/include/tgfx/svg/node/SVGRect.h +++ b/include/tgfx/svg/node/SVGRect.h @@ -30,10 +30,10 @@ namespace tgfx { -class SkSVGRect final : public SVGShape { +class SVGRect final : public SVGShape { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGRect()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGRect()); } SVG_ATTR(X, SVGLength, SVGLength(0)) @@ -45,21 +45,16 @@ class SkSVGRect final : public SVGShape { SVG_OPTIONAL_ATTR(Ry, SVGLength) protected: - bool parseAndSetAttribute(const char*, const char*) override; - -#ifndef RENDER_SVG - - // void onRender(const SVGRenderContext&) const override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; void onDraw(Canvas*, const SVGLengthContext&, const Paint&, PathFillType) const override; Path onAsPath(const SVGRenderContext&) const override; Rect onObjectBoundingBox(const SVGRenderContext&) const override; -#endif private: - SkSVGRect(); + SVGRect(); RRect resolve(const SVGLengthContext&) const; diff --git a/include/tgfx/svg/node/SVGSVG.h b/include/tgfx/svg/node/SVGSVG.h index 8a7208bc..853d0eb6 100644 --- a/include/tgfx/svg/node/SVGSVG.h +++ b/include/tgfx/svg/node/SVGSVG.h @@ -27,7 +27,9 @@ namespace tgfx { -class SVGSVG : public SkSVGContainer { +class SVGLengthContext; + +class SVGSVG : public SVGContainer { public: enum class Type { kRoot, @@ -45,16 +47,12 @@ class SVGSVG : public SkSVGContainer { SVG_OPTIONAL_ATTR(ViewBox, SVGViewBoxType) -#ifndef RENDER_SVG Size intrinsicSize(const SVGLengthContext&) const; void renderNode(const SVGRenderContext&, const SVGIRI& iri) const; -#endif protected: -#ifndef RENDER_SVG bool onPrepareToRender(SVGRenderContext*) const override; -#endif void onSetAttribute(SVGAttribute, const SVGValue&) override; @@ -65,7 +63,7 @@ class SVGSVG : public SkSVGContainer { // Some attributes behave differently for the outermost svg element. const Type type; - using INHERITED = SkSVGContainer; + using INHERITED = SVGContainer; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGShape.h b/include/tgfx/svg/node/SVGShape.h index a8b83ae5..f2cfec42 100644 --- a/include/tgfx/svg/node/SVGShape.h +++ b/include/tgfx/svg/node/SVGShape.h @@ -30,21 +30,19 @@ class SVGNode; class SVGRenderContext; enum class SVGTag; -class SVGShape : public SkSVGTransformableNode { +class SVGShape : public SVGTransformableNode { public: void appendChild(std::shared_ptr) override; protected: explicit SVGShape(SVGTag); -#ifndef RENDER_SVG void onRender(const SVGRenderContext&) const override; virtual void onDraw(Canvas*, const SVGLengthContext&, const Paint&, PathFillType) const = 0; -#endif private: - using INHERITED = SkSVGTransformableNode; + using INHERITED = SVGTransformableNode; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGStop.h b/include/tgfx/svg/node/SVGStop.h index e533a4d1..d9a07508 100644 --- a/include/tgfx/svg/node/SVGStop.h +++ b/include/tgfx/svg/node/SVGStop.h @@ -25,21 +25,21 @@ namespace tgfx { -class SkSVGStop : public SVGHiddenContainer { +class SVGStop : public SVGHiddenContainer { public: static constexpr SVGTag tag = SVGTag::Stop; - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGStop()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGStop()); } SVG_ATTR(Offset, SVGLength, SVGLength(0, SVGLength::Unit::Percentage)) protected: - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; private: - SkSVGStop(); + SVGStop(); using INHERITED = SVGHiddenContainer; }; diff --git a/include/tgfx/svg/node/SVGText.h b/include/tgfx/svg/node/SVGText.h index 78fc6761..9027c278 100644 --- a/include/tgfx/svg/node/SVGText.h +++ b/include/tgfx/svg/node/SVGText.h @@ -35,12 +35,12 @@ using ShapedTextCallback = std::function&)>; // Base class for text-rendering nodes. -class SkSVGTextFragment : public SkSVGTransformableNode { +class SVGTextFragment : public SVGTransformableNode { public: void renderText(const SVGRenderContext&, const ShapedTextCallback&) const; protected: - explicit SkSVGTextFragment(SVGTag t) : INHERITED(t) { + explicit SVGTextFragment(SVGTag t) : INHERITED(t) { } virtual void onShapeText(const SVGRenderContext&, const ShapedTextCallback&) const = 0; @@ -52,11 +52,11 @@ class SkSVGTextFragment : public SkSVGTransformableNode { private: Path onAsPath(const SVGRenderContext&) const override; - using INHERITED = SkSVGTransformableNode; + using INHERITED = SVGTransformableNode; }; // Base class for nestable text containers (, , etc). -class SkSVGTextContainer : public SkSVGTextFragment { +class SVGTextContainer : public SVGTextFragment { public: SVG_ATTR(X, std::vector, {}) SVG_ATTR(Y, std::vector, {}) @@ -67,59 +67,59 @@ class SkSVGTextContainer : public SkSVGTextFragment { void appendChild(std::shared_ptr) final; protected: - explicit SkSVGTextContainer(SVGTag t) : INHERITED(t) { + explicit SVGTextContainer(SVGTag t) : INHERITED(t) { } void onShapeText(const SVGRenderContext&, const ShapedTextCallback&) const override; - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; private: - std::vector> fChildren; + std::vector> children; - using INHERITED = SkSVGTextFragment; + using INHERITED = SVGTextFragment; }; -class SkSVGText final : public SkSVGTextContainer { +class SVGText final : public SVGTextContainer { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGText()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGText()); } private: - SkSVGText() : INHERITED(SVGTag::Text) { + SVGText() : INHERITED(SVGTag::Text) { } void onRender(const SVGRenderContext&) const override; Rect onObjectBoundingBox(const SVGRenderContext&) const override; Path onAsPath(const SVGRenderContext&) const override; - using INHERITED = SkSVGTextContainer; + using INHERITED = SVGTextContainer; }; -class SkSVGTSpan final : public SkSVGTextContainer { +class SVGTSpan final : public SVGTextContainer { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGTSpan()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGTSpan()); } private: - SkSVGTSpan() : INHERITED(SVGTag::TSpan) { + SVGTSpan() : INHERITED(SVGTag::TSpan) { } - using INHERITED = SkSVGTextContainer; + using INHERITED = SVGTextContainer; }; -class SkSVGTextLiteral final : public SkSVGTextFragment { +class SVGTextLiteral final : public SVGTextFragment { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGTextLiteral()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGTextLiteral()); } SVG_ATTR(Text, SVGStringType, SVGStringType()) private: - SkSVGTextLiteral() : INHERITED(SVGTag::TextLiteral) { + SVGTextLiteral() : INHERITED(SVGTag::TextLiteral) { } void onShapeText(const SVGRenderContext&, const ShapedTextCallback&) const override; @@ -127,26 +127,26 @@ class SkSVGTextLiteral final : public SkSVGTextFragment { void appendChild(std::shared_ptr) override { } - using INHERITED = SkSVGTextFragment; + using INHERITED = SVGTextFragment; }; -class SkSVGTextPath final : public SkSVGTextContainer { +class SVGTextPath final : public SVGTextContainer { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGTextPath()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGTextPath()); } SVG_ATTR(Href, SVGIRI, {}) SVG_ATTR(StartOffset, SVGLength, SVGLength(0)) private: - SkSVGTextPath() : INHERITED(SVGTag::TextPath) { + SVGTextPath() : INHERITED(SVGTag::TextPath) { } void onShapeText(const SVGRenderContext&, const ShapedTextCallback&) const override; - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; - using INHERITED = SkSVGTextContainer; + using INHERITED = SVGTextContainer; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGTransformableNode.h b/include/tgfx/svg/node/SVGTransformableNode.h index 50ae7f30..2da2bb95 100644 --- a/include/tgfx/svg/node/SVGTransformableNode.h +++ b/include/tgfx/svg/node/SVGTransformableNode.h @@ -29,25 +29,22 @@ namespace tgfx { class SVGRenderContext; enum class SVGAttribute; -class SkSVGTransformableNode : public SVGNode { +class SVGTransformableNode : public SVGNode { public: void setTransform(const SVGTransformType& t) { fTransform = t; } protected: - SkSVGTransformableNode(SVGTag); + SVGTransformableNode(SVGTag); -#ifndef RENDER_SVG bool onPrepareToRender(SVGRenderContext*) const override; -#endif + void onSetAttribute(SVGAttribute, const SVGValue&) override; -#ifndef RENDER_SVG void mapToParent(Path*) const; void mapToParent(Rect*) const; -#endif private: // FIXME: should be sparse diff --git a/include/tgfx/svg/node/SVGUse.h b/include/tgfx/svg/node/SVGUse.h index 8ccf11c2..ce04c083 100644 --- a/include/tgfx/svg/node/SVGUse.h +++ b/include/tgfx/svg/node/SVGUse.h @@ -33,10 +33,10 @@ class SVGRenderContext; * Implements support for (reference) elements. * (https://www.w3.org/TR/SVG11/struct.html#UseElement) */ -class SkSVGUse final : public SkSVGTransformableNode { +class SVGUse final : public SVGTransformableNode { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SkSVGUse()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGUse()); } void appendChild(std::shared_ptr) override{}; @@ -46,18 +46,16 @@ class SkSVGUse final : public SkSVGTransformableNode { SVG_ATTR(Href, SVGIRI, SVGIRI()) protected: -#ifndef RENDER_SVG bool onPrepareToRender(SVGRenderContext*) const override; void onRender(const SVGRenderContext&) const override; Path onAsPath(const SVGRenderContext&) const override; Rect onObjectBoundingBox(const SVGRenderContext&) const override; -#endif private: - SkSVGUse(); + SVGUse(); - bool parseAndSetAttribute(const char*, const char*) override; + bool parseAndSetAttribute(const std::string&, const std::string&) override; - using INHERITED = SkSVGTransformableNode; + using INHERITED = SVGTransformableNode; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/xml/XMLDOM.h b/include/tgfx/svg/xml/XMLDOM.h index 4bdf81d9..70999a5a 100644 --- a/include/tgfx/svg/xml/XMLDOM.h +++ b/include/tgfx/svg/xml/XMLDOM.h @@ -33,7 +33,10 @@ struct DOMAttribute { std::string value; }; -enum class DOMNodeType { Element, Text }; +enum class DOMNodeType { + Element, + Text, +}; struct DOMNode { std::string name; diff --git a/src/svg/SVGAttributeParser.cpp b/src/svg/SVGAttributeParser.cpp index d6efd8f6..4b50ed60 100644 --- a/src/svg/SVGAttributeParser.cpp +++ b/src/svg/SVGAttributeParser.cpp @@ -16,30 +16,23 @@ // ///////////////////////////////////////////////////////////////////////////////////////////////// -#include "tgfx/svg/SVGAttributeParser.h" -#include +#include "SVGAttributeParser.h" #include #include #include #include #include -#include #include #include "core/utils/Log.h" #include "core/utils/MathExtra.h" +#include "svg/SVGParse.h" #include "tgfx/core/Color.h" -#include "tgfx/core/Matrix.h" -#include "tgfx/core/Point.h" -#include "tgfx/core/Rect.h" #include "tgfx/core/Typeface.h" #include "tgfx/core/UTF.h" -#include "tgfx/svg/SVGParse.h" #include "tgfx/svg/SVGTypes.h" namespace { -// TODO: these should be shared with SkParse.cpp - inline bool is_between(char c, char min, char max) { ASSERT(min <= max); return (unsigned)(c - min) <= (unsigned)(max - min); @@ -65,14 +58,13 @@ inline bool is_hex(char c) { namespace tgfx { -SVGAttributeParser::SVGAttributeParser(const char attributeString[]) - // TODO: need actual UTF-8 with length. - : fCurPos(attributeString), fEndPos(fCurPos + strlen(attributeString)) { +SVGAttributeParser::SVGAttributeParser(std::string attributeString) + : fCurPos(attributeString.data()), fEndPos(fCurPos + attributeString.size()) { } template inline bool SVGAttributeParser::advanceWhile(F f) { - auto initial = fCurPos; + const auto* initial = fCurPos; while (fCurPos < fEndPos && f(*fCurPos)) { fCurPos++; } @@ -356,7 +348,9 @@ bool SVGAttributeParser::parseRGBColorToken(Color* c) { return this->parseParenthesized( "rgb", [this](Color* c) -> bool { - int32_t r, g, b; + int32_t r = 0; + int32_t g = 0; + int32_t b = 0; if (this->parseColorComponentToken(&r) && this->parseSepToken() && this->parseColorComponentToken(&g) && this->parseSepToken() && this->parseColorComponentToken(&b)) { @@ -374,7 +368,10 @@ bool SVGAttributeParser::parseRGBAColorToken(Color* c) { return this->parseParenthesized( "rgba", [this](Color* c) -> bool { - int32_t r, g, b, a; + int32_t r = 0; + int32_t g = 0; + int32_t b = 0; + int32_t a = 0; if (this->parseColorComponentToken(&r) && this->parseSepToken() && this->parseColorComponentToken(&g) && this->parseSepToken() && this->parseColorComponentToken(&b) && this->parseSepToken() && @@ -523,7 +520,7 @@ bool SVGAttributeParser::parse(SVGNumberType* number) { float s; if (this->parseScalarToken(&s)) { - *number = SVGNumberType(s); + *number = static_cast(s); // consume trailing separators this->parseSepToken(); return true; @@ -570,7 +567,10 @@ bool SVGAttributeParser::parse(SVGLength* length) { // https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute bool SVGAttributeParser::parseViewBox(SVGViewBoxType* vb) { - float x, y, w, h; + float x = 0.0f; + float y = 0.0f; + float w = 0.0f; + float h = 0.0f; this->parseWSToken(); bool parsedValue = false; @@ -619,7 +619,7 @@ bool SVGAttributeParser::parseMatrixToken(Matrix* matrix) { [this](Matrix* m) -> bool { float scalars[6]; for (int i = 0; i < 6; ++i) { - if (!(this->parseScalarToken(scalars + i) && (i > 4 || this->parseSepToken()))) { + if (!this->parseScalarToken(scalars + i) || (i <= 4 && !this->parseSepToken())) { return false; } } @@ -634,7 +634,8 @@ bool SVGAttributeParser::parseTranslateToken(Matrix* matrix) { return this->parseParenthesized( "translate", [this](Matrix* m) -> bool { - float tx = 0.0, ty = 0.0; + float tx = 0.0; + float ty = 0.0; this->parseWSToken(); if (!this->parseScalarToken(&tx)) { return false; @@ -654,7 +655,8 @@ bool SVGAttributeParser::parseScaleToken(Matrix* matrix) { return this->parseParenthesized( "scale", [this](Matrix* m) -> bool { - float sx = 0.0, sy = 0.0; + float sx = 0.0; + float sy = 0.0; if (!this->parseScalarToken(&sx)) { return false; } diff --git a/include/tgfx/svg/SVGAttributeParser.h b/src/svg/SVGAttributeParser.h similarity index 83% rename from include/tgfx/svg/SVGAttributeParser.h rename to src/svg/SVGAttributeParser.h index a879acaf..8727fcf2 100644 --- a/include/tgfx/svg/SVGAttributeParser.h +++ b/src/svg/SVGAttributeParser.h @@ -32,14 +32,12 @@ namespace tgfx { class SVGAttributeParser { public: - explicit SVGAttributeParser(const char[]); + explicit SVGAttributeParser(std::string); bool parseInteger(SVGIntegerType*); bool parseViewBox(SVGViewBoxType*); bool parsePreserveAspectRatio(SVGPreserveAspectRatio*); - // TODO: Migrate all parse*() functions to this style (and delete the old version) - // so they can be used by parse(): bool parse(SVGIntegerType* v) { return parseInteger(v); } @@ -48,7 +46,7 @@ class SVGAttributeParser { using ParseResult = std::optional; template - static ParseResult parse(const char* value) { + static ParseResult parse(const std::string& value) { ParseResult result; T parsedValue; if (SVGAttributeParser(value).parse(&parsedValue)) { @@ -58,8 +56,9 @@ class SVGAttributeParser { } template - static ParseResult parse(const char* expectedName, const char* name, const char* value) { - if (!strcmp(name, expectedName)) { + static ParseResult parse(const std::string& expectedName, const std::string& name, + const std::string& value) { + if (name == expectedName) { return parse(value); } @@ -67,13 +66,13 @@ class SVGAttributeParser { } template - static ParseResult parseProperty(const char* expectedName, const char* name, - const char* value) { - if (strcmp(name, expectedName) != 0) { + static ParseResult parseProperty(const std::string& expectedName, + const std::string& name, const std::string& value) { + if (name != expectedName) { return ParseResult(); } - if (!strcmp(value, "inherit")) { + if (value == "inherit") { PropertyT result(SVGPropertyState::Inherit); return ParseResult(result); } @@ -94,22 +93,22 @@ class SVGAttributeParser { private: class RestoreCurPos { public: - explicit RestoreCurPos(SVGAttributeParser* self) : fSelf(self), fCurPos(self->fCurPos) { + explicit RestoreCurPos(SVGAttributeParser* self) : self(self), currentPos(self->fCurPos) { } ~RestoreCurPos() { - if (fSelf) { - fSelf->fCurPos = this->fCurPos; + if (self) { + self->fCurPos = this->currentPos; } } void clear() { - fSelf = nullptr; + self = nullptr; } private: - SVGAttributeParser* fSelf; - const char* fCurPos; + SVGAttributeParser* self; + const char* currentPos; public: RestoreCurPos(const RestoreCurPos&) = delete; @@ -178,7 +177,5 @@ class SVGAttributeParser { // The current position in the input string. const char* fCurPos; const char* fEndPos; - - // using INHERITED = SkNoncopyable; }; } // namespace tgfx diff --git a/src/svg/SVGDOM.cpp b/src/svg/SVGDOM.cpp index 755fa942..005ad546 100644 --- a/src/svg/SVGDOM.cpp +++ b/src/svg/SVGDOM.cpp @@ -17,23 +17,22 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/SVGDOM.h" -#include #include -#include #include #include #include #include -#include #include +#include #include #include "core/utils/Log.h" +#include "svg/SVGAttributeParser.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/Canvas.h" #include "tgfx/core/Data.h" #include "tgfx/core/Recorder.h" #include "tgfx/core/Surface.h" #include "tgfx/svg/SVGAttribute.h" -#include "tgfx/svg/SVGAttributeParser.h" #include "tgfx/svg/SVGTypes.h" #include "tgfx/svg/SVGValue.h" #include "tgfx/svg/node/SVGCircle.h" @@ -75,7 +74,7 @@ namespace tgfx { namespace { -bool SetIRIAttribute(SVGNode& node, SVGAttribute attr, const char* stringValue) { +bool SetIRIAttribute(SVGNode& node, SVGAttribute attr, const std::string& stringValue) { auto parseResult = SVGAttributeParser::parse(stringValue); if (!parseResult.has_value()) { return false; @@ -85,14 +84,13 @@ bool SetIRIAttribute(SVGNode& node, SVGAttribute attr, const char* stringValue) return true; } -bool SetStringAttribute(SVGNode& node, SVGAttribute attr, const char* stringValue) { - std::string str(stringValue, strlen(stringValue)); - SVGStringType strType = SVGStringType(str); +bool SetStringAttribute(SVGNode& node, SVGAttribute attr, const std::string& stringValue) { + SVGStringType strType = SVGStringType(stringValue); node.setAttribute(attr, SVGStringValue(strType)); return true; } -bool SetTransformAttribute(SVGNode& node, SVGAttribute attr, const char* stringValue) { +bool SetTransformAttribute(SVGNode& node, SVGAttribute attr, const std::string& stringValue) { auto parseResult = SVGAttributeParser::parse(stringValue); if (!parseResult.has_value()) { return false; @@ -102,7 +100,7 @@ bool SetTransformAttribute(SVGNode& node, SVGAttribute attr, const char* stringV return true; } -bool SetLengthAttribute(SVGNode& node, SVGAttribute attr, const char* stringValue) { +bool SetLengthAttribute(SVGNode& node, SVGAttribute attr, const std::string& stringValue) { auto parseResult = SVGAttributeParser::parse(stringValue); if (!parseResult.has_value()) { return false; @@ -112,7 +110,7 @@ bool SetLengthAttribute(SVGNode& node, SVGAttribute attr, const char* stringValu return true; } -bool SetViewBoxAttribute(SVGNode& node, SVGAttribute attr, const char* stringValue) { +bool SetViewBoxAttribute(SVGNode& node, SVGAttribute attr, const std::string& stringValue) { SVGViewBoxType viewBox; SVGAttributeParser parser(stringValue); if (!parser.parseViewBox(&viewBox)) { @@ -123,7 +121,8 @@ bool SetViewBoxAttribute(SVGNode& node, SVGAttribute attr, const char* stringVal return true; } -bool SetObjectBoundingBoxUnitsAttribute(SVGNode& node, SVGAttribute attr, const char* stringValue) { +bool SetObjectBoundingBoxUnitsAttribute(SVGNode& node, SVGAttribute attr, + const std::string& stringValue) { auto parseResult = SVGAttributeParser::parse(stringValue); if (!parseResult.has_value()) { return false; @@ -133,7 +132,8 @@ bool SetObjectBoundingBoxUnitsAttribute(SVGNode& node, SVGAttribute attr, const return true; } -bool SetPreserveAspectRatioAttribute(SVGNode& node, SVGAttribute attr, const char* stringValue) { +bool SetPreserveAspectRatioAttribute(SVGNode& node, SVGAttribute attr, + const std::string& stringValue) { SVGPreserveAspectRatio par; SVGAttributeParser parser(stringValue); if (!parser.parsePreserveAspectRatio(&par)) { @@ -163,24 +163,25 @@ std::string TrimmedString(const char* first, const char* last) { // Breaks a "foo: bar; baz: ..." string into key:value pairs. class StyleIterator { public: - explicit StyleIterator(const char* str) : fPos(str) { + explicit StyleIterator(const std::string& str) : pos(str.data()) { } std::tuple next() { std::string name; std::string value; - if (fPos) { + if (pos) { const char* sep = this->nextSeparator(); - ASSERT(*sep == ';' || *sep == '\0'); + ASSERT(*sep == ';'); + ASSERT(*sep == '\0') - const char* valueSep = strchr(fPos, ':'); + const char* valueSep = strchr(pos, ':'); if (valueSep && valueSep < sep) { - name = TrimmedString(fPos, valueSep - 1); + name = TrimmedString(pos, valueSep - 1); value = TrimmedString(valueSep + 1, sep - 1); } - fPos = *sep ? sep + 1 : nullptr; + pos = *sep ? sep + 1 : nullptr; } return std::make_tuple(name, value); @@ -188,19 +189,19 @@ class StyleIterator { private: const char* nextSeparator() const { - const char* sep = fPos; + const char* sep = pos; while (*sep != ';' && *sep != '\0') { sep++; } return sep; } - const char* fPos; + const char* pos; }; -bool set_string_attribute(SVGNode& node, const std::string& name, const std::string& value); +bool SetAttribute(SVGNode& node, const std::string& name, const std::string& value); -bool SetStyleAttributes(SVGNode& node, SVGAttribute, const char* stringValue) { +bool SetStyleAttributes(SVGNode& node, SVGAttribute, const std::string& stringValue) { std::string name; std::string value; @@ -210,26 +211,19 @@ bool SetStyleAttributes(SVGNode& node, SVGAttribute, const char* stringValue) { if (name.empty()) { break; } - set_string_attribute(node, name, value); + SetAttribute(node, name, value); } return true; } -template -struct SortedDictionaryEntry { - const std::string fKey; - const T fValue; -}; - -using AttributeSetter = std::function; - +using AttributeSetter = std::function; struct AttrParseInfo { - SVGAttribute fAttr; - AttributeSetter fSetter; + SVGAttribute attribute; + AttributeSetter setter; }; -std::vector> gAttributeParseInfo = { +std::unordered_map gAttributeParseInfo = { {"cx", {SVGAttribute::Cx, SetLengthAttribute}}, {"cy", {SVGAttribute::Cy, SetLengthAttribute}}, {"filterUnits", {SVGAttribute::FilterUnits, SetObjectBoundingBoxUnitsAttribute}}, @@ -255,101 +249,85 @@ std::vector> gAttributeParseInfo = { {"y2", {SVGAttribute::Y2, SetLengthAttribute}}, }; -std::vector (*)()>> gTagFactories = { - {"a", []() -> std::shared_ptr { return SkSVGG::Make(); }}, +using ElementFactory = std::function()>; +std::unordered_map ElementFactories = { + {"a", []() -> std::shared_ptr { return SVGG::Make(); }}, {"circle", []() -> std::shared_ptr { return SVGCircle::Make(); }}, {"clipPath", []() -> std::shared_ptr { return SVGClipPath::Make(); }}, - {"defs", []() -> std::shared_ptr { return SkSVGDefs::Make(); }}, - {"ellipse", []() -> std::shared_ptr { return SkSVGEllipse::Make(); }}, - {"feBlend", []() -> std::shared_ptr { return SkSVGFeBlend::Make(); }}, - {"feColorMatrix", []() -> std::shared_ptr { return SkSVGFeColorMatrix::Make(); }}, + {"defs", []() -> std::shared_ptr { return SVGDefs::Make(); }}, + {"ellipse", []() -> std::shared_ptr { return SVGEllipse::Make(); }}, + {"feBlend", []() -> std::shared_ptr { return SVGFeBlend::Make(); }}, + {"feColorMatrix", []() -> std::shared_ptr { return SVGFeColorMatrix::Make(); }}, {"feComponentTransfer", - []() -> std::shared_ptr { return SkSVGFeComponentTransfer::Make(); }}, - {"feComposite", []() -> std::shared_ptr { return SkSVGFeComposite::Make(); }}, + []() -> std::shared_ptr { return SVGFeComponentTransfer::Make(); }}, + {"feComposite", []() -> std::shared_ptr { return SVGFeComposite::Make(); }}, {"feDiffuseLighting", - []() -> std::shared_ptr { return SkSVGFeDiffuseLighting::Make(); }}, + []() -> std::shared_ptr { return SVGFeDiffuseLighting::Make(); }}, {"feDisplacementMap", - []() -> std::shared_ptr { return SkSVGFeDisplacementMap::Make(); }}, - {"feDistantLight", []() -> std::shared_ptr { return SkSVGFeDistantLight::Make(); }}, - {"feFlood", []() -> std::shared_ptr { return SkSVGFeFlood::Make(); }}, - {"feFuncA", []() -> std::shared_ptr { return SkSVGFeFunc::MakeFuncA(); }}, - {"feFuncB", []() -> std::shared_ptr { return SkSVGFeFunc::MakeFuncB(); }}, - {"feFuncG", []() -> std::shared_ptr { return SkSVGFeFunc::MakeFuncG(); }}, - {"feFuncR", []() -> std::shared_ptr { return SkSVGFeFunc::MakeFuncR(); }}, - {"feGaussianBlur", []() -> std::shared_ptr { return SkSVGFeGaussianBlur::Make(); }}, - {"feImage", []() -> std::shared_ptr { return SkSVGFeImage::Make(); }}, - {"feMerge", []() -> std::shared_ptr { return SkSVGFeMerge::Make(); }}, - {"feMergeNode", []() -> std::shared_ptr { return SkSVGFeMergeNode::Make(); }}, - {"feMorphology", []() -> std::shared_ptr { return SkSVGFeMorphology::Make(); }}, - {"feOffset", []() -> std::shared_ptr { return SkSVGFeOffset::Make(); }}, - {"fePointLight", []() -> std::shared_ptr { return SkSVGFePointLight::Make(); }}, + []() -> std::shared_ptr { return SVGFeDisplacementMap::Make(); }}, + {"feDistantLight", []() -> std::shared_ptr { return SVGFeDistantLight::Make(); }}, + {"feFlood", []() -> std::shared_ptr { return SVGFeFlood::Make(); }}, + {"feFuncA", []() -> std::shared_ptr { return SVGFeFunc::MakeFuncA(); }}, + {"feFuncB", []() -> std::shared_ptr { return SVGFeFunc::MakeFuncB(); }}, + {"feFuncG", []() -> std::shared_ptr { return SVGFeFunc::MakeFuncG(); }}, + {"feFuncR", []() -> std::shared_ptr { return SVGFeFunc::MakeFuncR(); }}, + {"feGaussianBlur", []() -> std::shared_ptr { return SVGFeGaussianBlur::Make(); }}, + {"feImage", []() -> std::shared_ptr { return SVGFeImage::Make(); }}, + {"feMerge", []() -> std::shared_ptr { return SVGFeMerge::Make(); }}, + {"feMergeNode", []() -> std::shared_ptr { return SVGFeMergeNode::Make(); }}, + {"feMorphology", []() -> std::shared_ptr { return SVGFeMorphology::Make(); }}, + {"feOffset", []() -> std::shared_ptr { return SVGFeOffset::Make(); }}, + {"fePointLight", []() -> std::shared_ptr { return SVGFePointLight::Make(); }}, {"feSpecularLighting", - []() -> std::shared_ptr { return SkSVGFeSpecularLighting::Make(); }}, - {"feSpotLight", []() -> std::shared_ptr { return SkSVGFeSpotLight::Make(); }}, - {"feTurbulence", []() -> std::shared_ptr { return SkSVGFeTurbulence::Make(); }}, - {"filter", []() -> std::shared_ptr { return SkSVGFilter::Make(); }}, - {"g", []() -> std::shared_ptr { return SkSVGG::Make(); }}, - {"image", []() -> std::shared_ptr { return SkSVGImage::Make(); }}, - {"line", []() -> std::shared_ptr { return SkSVGLine::Make(); }}, - {"linearGradient", []() -> std::shared_ptr { return SkSVGLinearGradient::Make(); }}, - {"mask", []() -> std::shared_ptr { return SkSVGMask::Make(); }}, - {"path", []() -> std::shared_ptr { return SkSVGPath::Make(); }}, - {"pattern", []() -> std::shared_ptr { return SkSVGPattern::Make(); }}, - {"polygon", []() -> std::shared_ptr { return SkSVGPoly::MakePolygon(); }}, - {"polyline", []() -> std::shared_ptr { return SkSVGPoly::MakePolyline(); }}, - {"radialGradient", []() -> std::shared_ptr { return SkSVGRadialGradient::Make(); }}, - {"rect", []() -> std::shared_ptr { return SkSVGRect::Make(); }}, - {"stop", []() -> std::shared_ptr { return SkSVGStop::Make(); }}, + []() -> std::shared_ptr { return SVGFeSpecularLighting::Make(); }}, + {"feSpotLight", []() -> std::shared_ptr { return SVGFeSpotLight::Make(); }}, + {"feTurbulence", []() -> std::shared_ptr { return SVGFeTurbulence::Make(); }}, + {"filter", []() -> std::shared_ptr { return SVGFilter::Make(); }}, + {"g", []() -> std::shared_ptr { return SVGG::Make(); }}, + {"image", []() -> std::shared_ptr { return SVGImage::Make(); }}, + {"line", []() -> std::shared_ptr { return SVGLine::Make(); }}, + {"linearGradient", []() -> std::shared_ptr { return SVGLinearGradient::Make(); }}, + {"mask", []() -> std::shared_ptr { return SVGMask::Make(); }}, + {"path", []() -> std::shared_ptr { return SVGPath::Make(); }}, + {"pattern", []() -> std::shared_ptr { return SVGPattern::Make(); }}, + {"polygon", []() -> std::shared_ptr { return SVGPoly::MakePolygon(); }}, + {"polyline", []() -> std::shared_ptr { return SVGPoly::MakePolyline(); }}, + {"radialGradient", []() -> std::shared_ptr { return SVGRadialGradient::Make(); }}, + {"rect", []() -> std::shared_ptr { return SVGRect::Make(); }}, + {"stop", []() -> std::shared_ptr { return SVGStop::Make(); }}, // "svg" handled explicitly - {"text", []() -> std::shared_ptr { return SkSVGText::Make(); }}, - {"textPath", []() -> std::shared_ptr { return SkSVGTextPath::Make(); }}, - {"tspan", []() -> std::shared_ptr { return SkSVGTSpan::Make(); }}, - {"use", []() -> std::shared_ptr { return SkSVGUse::Make(); }}, + {"text", []() -> std::shared_ptr { return SVGText::Make(); }}, + {"textPath", []() -> std::shared_ptr { return SVGTextPath::Make(); }}, + {"tspan", []() -> std::shared_ptr { return SVGTSpan::Make(); }}, + {"use", []() -> std::shared_ptr { return SVGUse::Make(); }}, }; -template -int StringSearch(const std::vector>& base, const std::string& target) { - if (base.empty()) { - return -1; - } - - for (uint32_t i = 0; i < base.size(); i++) { - if (base[i].fKey == target) { - return static_cast(i); - } - } - return -1; -} - struct ConstructionContext { - explicit ConstructionContext(SVGIDMapper* mapper) : fParent(nullptr), fIDMapper(mapper) { + explicit ConstructionContext(SVGIDMapper* mapper) : parentNode(nullptr), nodeIDMapper(mapper) { } ConstructionContext(const ConstructionContext& other, const std::shared_ptr& newParent) - : fParent(newParent.get()), fIDMapper(other.fIDMapper) { + : parentNode(newParent.get()), nodeIDMapper(other.nodeIDMapper) { } - SVGNode* fParent; - SVGIDMapper* fIDMapper; + SVGNode* parentNode; + SVGIDMapper* nodeIDMapper; }; -bool set_string_attribute(SVGNode& node, const std::string& name, const std::string& value) { - if (node.parseAndSetAttribute(name.c_str(), value.c_str())) { +bool SetAttribute(SVGNode& node, const std::string& name, const std::string& value) { + if (node.parseAndSetAttribute(name, value)) { // Handled by new code path return true; } - const int attrIndex = StringSearch(gAttributeParseInfo, name); - if (attrIndex < 0) { - return false; + if (auto iter = gAttributeParseInfo.find(name); iter != gAttributeParseInfo.end()) { + auto setter = iter->second.setter; + return setter(node, iter->second.attribute, value); } - - ASSERT(static_cast(attrIndex) < std::size(gAttributeParseInfo)); - const auto& attrInfo = gAttributeParseInfo[static_cast(attrIndex)].fValue; - return attrInfo.fSetter(node, attrInfo.fAttr, value.c_str()); + return true; } -void parse_node_attributes(const DOMNode* xmlNode, const std::shared_ptr& svgNode, - SVGIDMapper* mapper) { +void ParseNodeAttributes(const DOMNode* xmlNode, const std::shared_ptr& svgNode, + SVGIDMapper* mapper) { for (const auto& attr : xmlNode->attributes) { auto name = attr.name; @@ -358,56 +336,51 @@ void parse_node_attributes(const DOMNode* xmlNode, const std::shared_ptrinsert({value, svgNode}); continue; } - set_string_attribute(*svgNode, name, value); + SetAttribute(*svgNode, name, value); } } -std::shared_ptr construct_svg_node(const ConstructionContext& ctx, - const DOMNode* xmlNode) { - std::string elem = xmlNode->name; - const auto elemType = xmlNode->type; +std::shared_ptr ConstructSVGNode(const ConstructionContext& context, + const DOMNode* xmlNode) { + std::string elementName = xmlNode->name; + const auto elementType = xmlNode->type; - if (elemType == DOMNodeType::Text) { + if (elementType == DOMNodeType::Text) { // Text literals require special handling. ASSERT(xmlNode->attributes.empty()); - auto txt = SkSVGTextLiteral::Make(); - txt->setText(xmlNode->name); - ctx.fParent->appendChild(std::move(txt)); - + auto text = SVGTextLiteral::Make(); + text->setText(xmlNode->name); + context.parentNode->appendChild(std::move(text)); return nullptr; } - ASSERT(elemType == DOMNodeType::Element); + ASSERT(elementType == DOMNodeType::Element); - auto makeNode = [](const ConstructionContext& ctx, - const std::string& elem) -> std::shared_ptr { - if (elem == "svg") { + auto makeNode = [](const ConstructionContext& context, + const std::string& elementName) -> std::shared_ptr { + if (elementName == "svg") { // Outermost SVG element must be tagged as such. - return SVGSVG::Make(ctx.fParent ? SVGSVG::Type::kInner : SVGSVG::Type::kRoot); + return SVGSVG::Make(context.parentNode ? SVGSVG::Type::kInner : SVGSVG::Type::kRoot); } - // const int tagIndex = - // StringSearch(&gTagFactories[0].fKey, static_cast(std::size(gTagFactories)), - // elem.c_str(), sizeof(gTagFactories[0])); - int tagIndex = StringSearch(gTagFactories, elem); - if (tagIndex < 0) { - return nullptr; + if (auto iter = ElementFactories.find(elementName); iter != ElementFactories.end()) { + return iter->second(); } - ASSERT(static_cast(tagIndex) < std::size(gTagFactories)); - return gTagFactories[static_cast(tagIndex)].fValue(); + //can't find the element factory + ASSERT(false); }; - auto node = makeNode(ctx, elem); + auto node = makeNode(context, elementName); if (!node) { return nullptr; } - parse_node_attributes(xmlNode, node, ctx.fIDMapper); + ParseNodeAttributes(xmlNode, node, context.nodeIDMapper); - ConstructionContext localCtx(ctx, node); + ConstructionContext localCtx(context, node); std::shared_ptr child = xmlNode->firstChild; while (child) { - std::shared_ptr childNode = construct_svg_node(localCtx, child.get()); + std::shared_ptr childNode = ConstructSVGNode(localCtx, child.get()); if (childNode) { node->appendChild(std::move(childNode)); } @@ -418,28 +391,12 @@ std::shared_ptr construct_svg_node(const ConstructionContext& ctx, } // anonymous namespace -// SVGDOM::Builder& SVGDOM::Builder::setFontManager(std::shared_ptr fmgr) { -// fFontMgr = std::move(fmgr); -// return *this; -// } - -// SVGDOM::Builder& SVGDOM::Builder::setResourceProvider(std::shared_ptr rp) { -// fResourceProvider = std::move(rp); -// return *this; -// } - -// SVGDOM::Builder& SVGDOM::Builder::setTextShapingFactory(std::shared_ptr f) { -// fTextShapingFactory = f; -// return *this; -// } - -std::shared_ptr SVGDOM::Builder::make(Data& data, - std::shared_ptr fontManager) const { - // TRACE_EVENT0("skia", TRACE_FUNC); - if (data.empty()) { +std::shared_ptr SVGDOM::Make(const std::shared_ptr& data, + std::shared_ptr fontManager) { + if (!data) { return nullptr; } - auto xmlDom = DOM::MakeFromData(data); + auto xmlDom = DOM::MakeFromData(*data); if (!xmlDom) { return nullptr; } @@ -447,20 +404,11 @@ std::shared_ptr SVGDOM::Builder::make(Data& data, SVGIDMapper mapper; ConstructionContext ctx(&mapper); - auto root = construct_svg_node(ctx, xmlDom->getRootNode().get()); + auto root = ConstructSVGNode(ctx, xmlDom->getRootNode().get()); if (!root || root->tag() != SVGTag::Svg) { return nullptr; } - // class NullResourceProvider final : public skresources::ResourceProvider { - // std::shared_ptr load(const char[], const char[]) const override { return nullptr; } - // }; - - // auto resource_provider = fResourceProvider ? fResourceProvider - // : sk_make_sp(); - - // auto factory = fTextShapingFactory ? fTextShapingFactory : SkShapers::Primitive::Factory(); - return std::shared_ptr(new SVGDOM(std::static_pointer_cast(root), std::move(mapper), std::move(fontManager))); } @@ -473,8 +421,8 @@ SVGDOM::SVGDOM(std::shared_ptr root, SVGIDMapper&& mapper, void SVGDOM::render(Canvas* canvas) { if (root) { if (!renderPicture) { - SVGLengthContext lctx(_containerSize); - SkSVGPresentationContext pctx; + SVGLengthContext lctx(containerSize); + SVGPresentationContext pctx; Recorder recorder; auto* drawCanvas = recorder.beginRecording(); @@ -490,31 +438,15 @@ void SVGDOM::render(Canvas* canvas) { } } -void SVGDOM::renderNode(Canvas* canvas, SkSVGPresentationContext& pctx, const char* id) const { - if (root) { - SVGLengthContext lctx(_containerSize); - SVGRenderContext renderCtx(canvas->getSurface()->getContext(), canvas, fontManager, - _nodeIDMapper, lctx, pctx, {nullptr, nullptr}, canvas->getMatrix()); - root->renderNode(renderCtx, SVGIRI(SVGIRI::Type::Local, SVGStringType(id))); - } -} - -const Size& SVGDOM::containerSize() const { - return _containerSize; -} - -void SVGDOM::setContainerSize(const Size& containerSize) { - // TODO: inval - _containerSize = containerSize; +const Size& SVGDOM::getContainerSize() const { + return containerSize; } -std::shared_ptr SVGDOM::findNodeById(const std::string& id) { - auto iter = _nodeIDMapper.find(id); - return iter == this->_nodeIDMapper.end() ? nullptr : iter->second; +void SVGDOM::setContainerSize(const Size& size) { + containerSize = size; } -// TODO(fuego): move this to SkSVGNode or its own CU. bool SVGNode::setAttribute(const std::string& attributeName, const std::string& attributeValue) { - return set_string_attribute(*this, attributeName, attributeValue); + return SetAttribute(*this, attributeName, attributeValue); } } // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGParse.cpp b/src/svg/SVGParse.cpp index 25405669..aff483bd 100644 --- a/src/svg/SVGParse.cpp +++ b/src/svg/SVGParse.cpp @@ -16,7 +16,7 @@ // ///////////////////////////////////////////////////////////////////////////////////////////////// -#include "tgfx/svg/SVGParse.h" +#include "SVGParse.h" #include #include #include @@ -418,9 +418,6 @@ std::tuple> PathParse::FromSVGString(const char data std::string PathParse::ToSVGString(const std::shared_ptr& path, PathParse::PathEncoding encoding) { - - // SkDynamicMemoryWStream stream; - Point currentPoint = Point::Zero(); const int relSelector = encoding == PathParse::PathEncoding::Relative ? 1 : 0; diff --git a/include/tgfx/svg/SVGParse.h b/src/svg/SVGParse.h similarity index 100% rename from include/tgfx/svg/SVGParse.h rename to src/svg/SVGParse.h diff --git a/src/svg/SVGParseColor.cpp b/src/svg/SVGParseColor.cpp index 4955af28..1f9d6caf 100644 --- a/src/svg/SVGParseColor.cpp +++ b/src/svg/SVGParseColor.cpp @@ -18,11 +18,11 @@ #include #include +#include "svg/SVGParse.h" #include "tgfx/core/Color.h" -#include "tgfx/svg/SVGParse.h" namespace { -static const char* colorNames[] = { +const char* colorNames[] = { "aliceblue", "antiquewhite", "aqua", @@ -169,7 +169,7 @@ struct ColorRec { uint8_t r, g, b; }; -static constexpr ColorRec colors[] = { +constexpr ColorRec colors[] = { {0xf0, 0xf8, 0xff}, // aliceblue {0xfa, 0xeb, 0xd7}, // antiquewhite {0x00, 0xff, 0xff}, // aqua diff --git a/src/svg/SVGRenderContext.cpp b/src/svg/SVGRenderContext.cpp index ae266605..2f93651a 100644 --- a/src/svg/SVGRenderContext.cpp +++ b/src/svg/SVGRenderContext.cpp @@ -16,14 +16,12 @@ // ///////////////////////////////////////////////////////////////////////////////////////////////// -#include "tgfx/svg/SVGRenderContext.h" +#include "SVGRenderContext.h" #include #include -#include #include #include #include "core/utils/Log.h" -#include "tgfx/core/BlendMode.h" #include "tgfx/core/Canvas.h" #include "tgfx/core/Color.h" #include "tgfx/core/ImageFilter.h" @@ -41,8 +39,6 @@ #include "tgfx/svg/node/SVGMask.h" #include "tgfx/svg/node/SVGNode.h" -#ifndef RENDER_SVG - namespace tgfx { namespace { @@ -118,7 +114,7 @@ Rect SVGLengthContext::resolveRect(const SVGLength& x, const SVGLength& y, const namespace { -LineCap toSkCap(const SVGLineCap& cap) { +LineCap toCap(const SVGLineCap& cap) { switch (cap) { case SVGLineCap::Butt: return LineCap::Butt; @@ -129,7 +125,7 @@ LineCap toSkCap(const SVGLineCap& cap) { } } -LineJoin toSkJoin(const SVGLineJoin& join) { +LineJoin toJoin(const SVGLineJoin& join) { switch (join.type()) { case SVGLineJoin::Type::Miter: return LineJoin::Miter; @@ -173,14 +169,14 @@ std::unique_ptr dash_effect(const SVGPresentationAttributes& props, } // namespace -SkSVGPresentationContext::SkSVGPresentationContext() +SVGPresentationContext::SVGPresentationContext() : _inherited(SVGPresentationAttributes::MakeInitial()) { } SVGRenderContext::SVGRenderContext(Context* device, Canvas* canvas, const std::shared_ptr& fontManager, const SVGIDMapper& mapper, const SVGLengthContext& lctx, - const SkSVGPresentationContext& pctx, const OBBScope& obbs, + const SVGPresentationContext& pctx, const OBBScope& obbs, const Matrix& matrix) : fontManager(fontManager), nodeIDMapper(mapper), _lengthContext(lctx), _presentationContext(pctx), renderCanvas(canvas), recorder(), @@ -336,7 +332,7 @@ std::shared_ptr SVGRenderContext::applyFilter(const SVGFuncIRI& fil return nullptr; } - const auto* filterNode = reinterpret_cast(node.get()); + const auto* filterNode = reinterpret_cast(node.get()); return filterNode->buildFilterDAG(*this); } @@ -367,7 +363,7 @@ std::shared_ptr SVGRenderContext::applyMask(const SVGFuncIRI& mask) return nullptr; } - auto maskNode = std::static_pointer_cast(node); + auto maskNode = std::static_pointer_cast(node); auto maskBound = maskNode->bounds(*this); Recorder maskRecorder; @@ -384,56 +380,10 @@ std::shared_ptr SVGRenderContext::applyMask(const SVGFuncIRI& mask) auto bound = picture->getBounds(); maskBound.join(bound); - // if (!_deviceContext) { - // return nullptr; - // } - // auto tempSurface = Surface::Make(_deviceContext, static_cast(bound.width()), - // static_cast(bound.height())); - // if (!tempSurface) { - // return nullptr; - // } - // auto* tempCanvas = tempSurface->getCanvas(); - // tempCanvas->translate(bound.x(), bound.y()); - // // Matrix matrix = Matrix::I(); - // // Paint paint; - // // paint.setColor(Color::Green()); - // // tempCanvas->drawPicture(picture, &matrix, &paint); - // tempCanvas->drawPicture(picture); - // // Paint paint; - // // paint.setColor(Color::Black()); - // // tempCanvas->drawCircle(50, 50, 50, paint); - // _deviceContext->flushAndSubmit(); - // auto shaderImage = tempSurface->makeImageSnapshot(); - // if (!shaderImage) { - // return nullptr; - // } auto transMatrix = matrix * Matrix::MakeTrans(-maskBound.left, -maskBound.top); auto shaderImage = Image::MakeFrom(picture, static_cast(bound.width() * matrix.getScaleX()), static_cast(bound.height() * matrix.getScaleY()), &transMatrix); - // { - // auto tempSurface = Surface::Make(_deviceContext, static_cast(bound.width()) * 2, - // static_cast(bound.height()) * 2); - // tempSurface->getCanvas()->clear(); - // tempSurface->getCanvas()->drawPicture(picture); - // tgfx::Bitmap bitmap = {}; - // bitmap.allocPixels(tempSurface->width(), tempSurface->height()); - // auto* pixels = bitmap.lockPixels(); - // auto success = tempSurface->readPixels(bitmap.info(), pixels); - // bitmap.unlockPixels(); - // if (!success) { - // return nullptr; - // } - // auto imageData = bitmap.encode(); - // imageData->bytes(); - - // std::ofstream out("/Users/yg/Downloads/yg.png", std::ios::binary); - // if (!out) { - // return nullptr; - // } - // out.write(reinterpret_cast(imageData->data()), - // static_cast(imageData->size())); - // } auto shader = Shader::MakeImageShader(shaderImage, TileMode::Decal, TileMode::Decal); if (!shader) { return nullptr; @@ -462,7 +412,7 @@ std::optional SVGRenderContext::commonPaint(const SVGPaint& paint_selecto // Preserve the OBB scope because some paints use object bounding box coords // (e.g. gradient control points), which requires access to the render context // and node being rendered. - SkSVGPresentationContext pctx; + SVGPresentationContext pctx; pctx._namedColors = _presentationContext->_namedColors; SVGRenderContext local_ctx(_deviceContext, _canvas, fontManager, nodeIDMapper, *_lengthContext, pctx, scope, Matrix::I()); @@ -506,8 +456,8 @@ std::optional SVGRenderContext::strokePaint() const { p->setStrokeWidth( _lengthContext->resolve(*props.StrokeWidth, SVGLengthContext::LengthType::Other)); Stroke stroke; - stroke.cap = toSkCap(*props.StrokeLineCap); - stroke.join = toSkJoin(*props.StrokeLineJoin); + stroke.cap = toCap(*props.StrokeLineCap); + stroke.join = toJoin(*props.StrokeLineJoin); stroke.miterLimit = *props.StrokeMiterLimit; p->setStroke(stroke); @@ -563,6 +513,4 @@ Rect SVGRenderContext::resolveOBBRect(const SVGLength& x, const SVGLength& y, co return Rect::MakeXYWH(obbt.scale.x * r.x() + obbt.offset.x, obbt.scale.y * r.y() + obbt.offset.y, obbt.scale.x * r.width(), obbt.scale.y * r.height()); } -} // namespace tgfx - -#endif // RENDER_SVG \ No newline at end of file +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGRenderContext.h b/src/svg/SVGRenderContext.h similarity index 94% rename from include/tgfx/svg/SVGRenderContext.h rename to src/svg/SVGRenderContext.h index 82fc5bd4..91324c52 100644 --- a/include/tgfx/svg/SVGRenderContext.h +++ b/src/svg/SVGRenderContext.h @@ -34,8 +34,8 @@ #include "tgfx/core/Size.h" #include "tgfx/gpu/Context.h" #include "tgfx/svg/SVGAttribute.h" +#include "tgfx/svg/SVGDOM.h" #include "tgfx/svg/SVGFontManager.h" -#include "tgfx/svg/SVGIDMapper.h" #include "tgfx/svg/SVGTypes.h" namespace tgfx { @@ -147,10 +147,10 @@ class SVGLengthContext { std::optional patternUnit; }; -struct SkSVGPresentationContext { - SkSVGPresentationContext(); - SkSVGPresentationContext(const SkSVGPresentationContext&) = default; - SkSVGPresentationContext& operator=(const SkSVGPresentationContext&) = default; +struct SVGPresentationContext { + SVGPresentationContext(); + SVGPresentationContext(const SVGPresentationContext&) = default; + SVGPresentationContext& operator=(const SVGPresentationContext&) = default; const std::unordered_map* _namedColors = nullptr; // Inherited presentation attributes, computed for the current node. @@ -166,7 +166,7 @@ class SVGRenderContext { }; SVGRenderContext(Context*, Canvas*, const std::shared_ptr&, const SVGIDMapper&, - const SVGLengthContext&, const SkSVGPresentationContext&, const OBBScope&, + const SVGLengthContext&, const SVGPresentationContext&, const OBBScope&, const Matrix& matrix); SVGRenderContext(const SVGRenderContext&); SVGRenderContext(const SVGRenderContext&, Canvas*); @@ -185,7 +185,7 @@ class SVGRenderContext { return _lengthContext.writable(); } - const SkSVGPresentationContext& presentationContext() const { + const SVGPresentationContext& presentationContext() const { return *_presentationContext; } @@ -258,7 +258,7 @@ class SVGRenderContext { std::shared_ptr fontManager; const SVGIDMapper& nodeIDMapper; CopyOnWrite _lengthContext; - CopyOnWrite _presentationContext; + CopyOnWrite _presentationContext; Canvas* renderCanvas; Recorder recorder; diff --git a/src/svg/node/SVGCircle.cpp b/src/svg/node/SVGCircle.cpp index 32273b8f..e69bfa9e 100644 --- a/src/svg/node/SVGCircle.cpp +++ b/src/svg/node/SVGCircle.cpp @@ -17,25 +17,25 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGCircle.h" +#include "svg/SVGAttributeParser.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/Paint.h" #include "tgfx/core/Path.h" #include "tgfx/core/Point.h" #include "tgfx/core/Rect.h" -#include "tgfx/svg/SVGAttributeParser.h" namespace tgfx { SVGCircle::SVGCircle() : INHERITED(SVGTag::Circle) { } -bool SVGCircle::parseAndSetAttribute(const char* n, const char* v) { +bool SVGCircle::parseAndSetAttribute(const std::string& n, const std::string& v) { return INHERITED::parseAndSetAttribute(n, v) || this->setCx(SVGAttributeParser::parse("cx", n, v)) || this->setCy(SVGAttributeParser::parse("cy", n, v)) || this->setR(SVGAttributeParser::parse("r", n, v)); } -#ifndef RENDER_SVG std::tuple SVGCircle::resolve(const SVGLengthContext& lctx) const { const auto cx = lctx.resolve(Cx, SVGLengthContext::LengthType::Horizontal); const auto cy = lctx.resolve(Cy, SVGLengthContext::LengthType::Vertical); @@ -66,5 +66,4 @@ Rect SVGCircle::onObjectBoundingBox(const SVGRenderContext& ctx) const { const auto [pos, r] = this->resolve(ctx.lengthContext()); return Rect::MakeXYWH(pos.x - r, pos.y - r, 2 * r, 2 * r); } -#endif } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGClipPath.cpp b/src/svg/node/SVGClipPath.cpp index 76cad966..4730dd17 100644 --- a/src/svg/node/SVGClipPath.cpp +++ b/src/svg/node/SVGClipPath.cpp @@ -17,22 +17,22 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGClipPath.h" +#include "svg/SVGAttributeParser.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/Matrix.h" #include "tgfx/core/Path.h" -#include "tgfx/svg/SVGAttributeParser.h" namespace tgfx { SVGClipPath::SVGClipPath() : INHERITED(SVGTag::ClipPath) { } -bool SVGClipPath::parseAndSetAttribute(const char* n, const char* v) { +bool SVGClipPath::parseAndSetAttribute(const std::string& n, const std::string& v) { return INHERITED::parseAndSetAttribute(n, v) || this->setClipPathUnits( SVGAttributeParser::parse("clipPathUnits", n, v)); } -#ifndef RENDER_SVG Path SVGClipPath::resolveClip(const SVGRenderContext& ctx) const { auto clip = this->asPath(ctx); @@ -43,5 +43,5 @@ Path SVGClipPath::resolveClip(const SVGRenderContext& ctx) const { return clip; } -#endif + } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGContainer.cpp b/src/svg/node/SVGContainer.cpp index 8755e185..fca365d1 100644 --- a/src/svg/node/SVGContainer.cpp +++ b/src/svg/node/SVGContainer.cpp @@ -27,33 +27,32 @@ namespace tgfx { class SVGRenderContext; -SkSVGContainer::SkSVGContainer(SVGTag t) : INHERITED(t) { +SVGContainer::SVGContainer(SVGTag t) : INHERITED(t) { } -void SkSVGContainer::appendChild(std::shared_ptr node) { +void SVGContainer::appendChild(std::shared_ptr node) { ASSERT(node); - fChildren.push_back(std::move(node)); + children.push_back(std::move(node)); } -const std::vector>& SkSVGContainer::getChildren() const { - return fChildren; +const std::vector>& SVGContainer::getChildren() const { + return children; } -bool SkSVGContainer::hasChildren() const { - return !fChildren.empty(); +bool SVGContainer::hasChildren() const { + return !children.empty(); } -#ifndef RENDER_SVG -void SkSVGContainer::onRender(const SVGRenderContext& ctx) const { - for (const auto& i : fChildren) { +void SVGContainer::onRender(const SVGRenderContext& ctx) const { + for (const auto& i : children) { i->render(ctx); } } -Path SkSVGContainer::onAsPath(const SVGRenderContext& ctx) const { +Path SVGContainer::onAsPath(const SVGRenderContext& ctx) const { Path path; - for (const auto& i : fChildren) { + for (const auto& i : children) { const Path childPath = i->asPath(ctx); path.addPath(childPath, PathOp::Union); } @@ -62,15 +61,15 @@ Path SkSVGContainer::onAsPath(const SVGRenderContext& ctx) const { return path; } -Rect SkSVGContainer::onObjectBoundingBox(const SVGRenderContext& ctx) const { +Rect SVGContainer::onObjectBoundingBox(const SVGRenderContext& ctx) const { Rect bounds = Rect::MakeEmpty(); - for (const auto& i : fChildren) { + for (const auto& i : children) { const Rect childBounds = i->objectBoundingBox(ctx); bounds.join(childBounds); } return bounds; } -#endif + } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGEllipse.cpp b/src/svg/node/SVGEllipse.cpp index 454986f3..437d05d7 100644 --- a/src/svg/node/SVGEllipse.cpp +++ b/src/svg/node/SVGEllipse.cpp @@ -17,16 +17,17 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGEllipse.h" #include "SVGRectPriv.h" +#include "svg/SVGAttributeParser.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/Rect.h" -#include "tgfx/svg/SVGAttributeParser.h" #include "tgfx/svg/SVGTypes.h" namespace tgfx { -SkSVGEllipse::SkSVGEllipse() : INHERITED(SVGTag::Ellipse) { +SVGEllipse::SVGEllipse() : INHERITED(SVGTag::Ellipse) { } -bool SkSVGEllipse::parseAndSetAttribute(const char* n, const char* v) { +bool SVGEllipse::parseAndSetAttribute(const std::string& n, const std::string& v) { return INHERITED::parseAndSetAttribute(n, v) || this->setCx(SVGAttributeParser::parse("cx", n, v)) || this->setCy(SVGAttributeParser::parse("cy", n, v)) || @@ -36,8 +37,7 @@ bool SkSVGEllipse::parseAndSetAttribute(const char* n, const char* v) { s = 10; } -#ifndef RENDER_SVG -Rect SkSVGEllipse::resolve(const SVGLengthContext& lctx) const { +Rect SVGEllipse::resolve(const SVGLengthContext& lctx) const { const auto cx = lctx.resolve(Cx, SVGLengthContext::LengthType::Horizontal); const auto cy = lctx.resolve(Cy, SVGLengthContext::LengthType::Vertical); @@ -52,17 +52,17 @@ Rect SkSVGEllipse::resolve(const SVGLengthContext& lctx) const { return (rx > 0 && ry > 0) ? Rect::MakeXYWH(cx - rx, cy - ry, rx * 2, ry * 2) : Rect::MakeEmpty(); } -void SkSVGEllipse::onDraw(Canvas* canvas, const SVGLengthContext& lctx, const Paint& paint, - PathFillType) const { +void SVGEllipse::onDraw(Canvas* canvas, const SVGLengthContext& lctx, const Paint& paint, + PathFillType) const { canvas->drawOval(this->resolve(lctx), paint); } -Path SkSVGEllipse::onAsPath(const SVGRenderContext& ctx) const { +Path SVGEllipse::onAsPath(const SVGRenderContext& ctx) const { Path path; path.addOval(this->resolve(ctx.lengthContext())); this->mapToParent(&path); return path; } -#endif + } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFe.cpp b/src/svg/node/SVGFe.cpp index 1ba144c9..de2ca0f5 100644 --- a/src/svg/node/SVGFe.cpp +++ b/src/svg/node/SVGFe.cpp @@ -19,22 +19,20 @@ #include "tgfx/svg/node/SVGFe.h" #include #include +#include "svg/SVGAttributeParser.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/Rect.h" -#include "tgfx/svg/SVGAttributeParser.h" -#include "tgfx/svg/SVGRenderContext.h" #include "tgfx/svg/node/SVGFilterContext.h" namespace tgfx { -#ifndef RENDER_SVG - -std::shared_ptr SkSVGFe::makeImageFilter( - const SVGRenderContext& ctx, const SkSVGFilterContext& filterContext) const { +std::shared_ptr SVGFe::makeImageFilter(const SVGRenderContext& ctx, + const SVGFilterContext& filterContext) const { return this->onMakeImageFilter(ctx, filterContext); } -Rect SkSVGFe::resolveBoundaries(const SVGRenderContext& ctx, - const SkSVGFilterContext& filterContext) const { +Rect SVGFe::resolveBoundaries(const SVGRenderContext& ctx, + const SVGFilterContext& filterContext) const { const auto x = X.has_value() ? *X : SVGLength(0, SVGLength::Unit::Percentage); const auto y = Y.has_value() ? *Y : SVGLength(0, SVGLength::Unit::Percentage); const auto w = Width.has_value() ? *Width : SVGLength(100, SVGLength::Unit::Percentage); @@ -43,7 +41,7 @@ Rect SkSVGFe::resolveBoundaries(const SVGRenderContext& ctx, return ctx.resolveOBBRect(x, y, w, h, filterContext.primitiveUnits()); } -static bool AnyIsStandardInput(const SkSVGFilterContext& fctx, +static bool AnyIsStandardInput(const SVGFilterContext& fctx, const std::vector& inputs) { for (const auto& in : inputs) { switch (in.type()) { @@ -68,8 +66,8 @@ static bool AnyIsStandardInput(const SkSVGFilterContext& fctx, return false; } -Rect SkSVGFe::resolveFilterSubregion(const SVGRenderContext& ctx, - const SkSVGFilterContext& fctx) const { +Rect SVGFe::resolveFilterSubregion(const SVGRenderContext& ctx, + const SVGFilterContext& fctx) const { // From https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveSubRegion, // the default filter effect subregion is equal to the union of the subregions defined // for all "referenced nodes" (filter effect inputs). If there are no inputs, the @@ -98,19 +96,17 @@ Rect SkSVGFe::resolveFilterSubregion(const SVGRenderContext& ctx, Height.has_value() ? boundaries.height() : defaultSubregion.height()); } -SVGColorspace SkSVGFe::resolveColorspace(const SVGRenderContext& ctx, - const SkSVGFilterContext&) const { +SVGColorspace SVGFe::resolveColorspace(const SVGRenderContext& ctx, const SVGFilterContext&) const { constexpr SVGColorspace kDefaultCS = SVGColorspace::SRGB; const SVGColorspace cs = *ctx.presentationContext()._inherited.ColorInterpolationFilters; return cs == SVGColorspace::Auto ? kDefaultCS : cs; } -void SkSVGFe::applyProperties(SVGRenderContext* ctx) const { +void SVGFe::applyProperties(SVGRenderContext* ctx) const { this->onPrepareToRender(ctx); } -#endif -bool SkSVGFe::parseAndSetAttribute(const char* name, const char* value) { +bool SVGFe::parseAndSetAttribute(const std::string& name, const std::string& value) { return INHERITED::parseAndSetAttribute(name, value) || this->setIn(SVGAttributeParser::parse("in", name, value)) || this->setResult(SVGAttributeParser::parse("result", name, value)) || diff --git a/src/svg/node/SVGFeBlend.cpp b/src/svg/node/SVGFeBlend.cpp index 5e56f0d4..3034d080 100644 --- a/src/svg/node/SVGFeBlend.cpp +++ b/src/svg/node/SVGFeBlend.cpp @@ -18,57 +18,34 @@ #include "tgfx/svg/node/SVGFeBlend.h" #include -#include "tgfx/core/BlendMode.h" +#include "svg/SVGAttributeParser.h" #include "tgfx/core/ImageFilter.h" -#include "tgfx/core/Rect.h" -#include "tgfx/svg/SVGAttributeParser.h" namespace tgfx { class SVGRenderContext; -bool SkSVGFeBlend::parseAndSetAttribute(const char* name, const char* value) { +bool SVGFeBlend::parseAndSetAttribute(const std::string& name, const std::string& value) { return INHERITED::parseAndSetAttribute(name, value) || this->setIn2(SVGAttributeParser::parse("in2", name, value)) || - this->setBlendMode(SVGAttributeParser::parse("mode", name, value)); + this->setBlendMode(SVGAttributeParser::parse("mode", name, value)); } -#ifndef RENDER_SVG - -// static BlendMode GetBlendMode(SkSVGFeBlend::Mode mode) { -// switch (mode) { -// case SkSVGFeBlend::Mode::kNormal: -// return BlendMode::SrcOver; -// case SkSVGFeBlend::Mode::kMultiply: -// return BlendMode::Multiply; -// case SkSVGFeBlend::Mode::kScreen: -// return BlendMode::Screen; -// case SkSVGFeBlend::Mode::kDarken: -// return BlendMode::Darken; -// case SkSVGFeBlend::Mode::kLighten: -// return BlendMode::Lighten; -// } -// } - -std::shared_ptr SkSVGFeBlend::onMakeImageFilter(const SVGRenderContext& ctx, - const SkSVGFilterContext& fctx) const { - // const Rect cropRect = this->resolveFilterSubregion(ctx, fctx); - // const BlendMode blendMode = GetBlendMode(this->getMode()); +std::shared_ptr SVGFeBlend::onMakeImageFilter(const SVGRenderContext& ctx, + const SVGFilterContext& fctx) const { const SVGColorspace colorspace = this->resolveColorspace(ctx, fctx); const std::shared_ptr background = fctx.resolveInput(ctx, In2, colorspace); const std::shared_ptr foreground = fctx.resolveInput(ctx, this->getIn(), colorspace); return ImageFilter::Compose(background, foreground); // TODO (YG),relay on ImageFilters::Blend to implement this - // return SkImageFilters::Blend(blendMode, background, foreground, cropRect); } -#endif template <> -bool SVGAttributeParser::parse(SkSVGFeBlend::Mode* mode) { - static constexpr std::tuple gMap[] = { - {"normal", SkSVGFeBlend::Mode::kNormal}, {"multiply", SkSVGFeBlend::Mode::kMultiply}, - {"screen", SkSVGFeBlend::Mode::kScreen}, {"darken", SkSVGFeBlend::Mode::kDarken}, - {"lighten", SkSVGFeBlend::Mode::kLighten}, +bool SVGAttributeParser::parse(SVGFeBlend::Mode* mode) { + static constexpr std::tuple gMap[] = { + {"normal", SVGFeBlend::Mode::kNormal}, {"multiply", SVGFeBlend::Mode::kMultiply}, + {"screen", SVGFeBlend::Mode::kScreen}, {"darken", SVGFeBlend::Mode::kDarken}, + {"lighten", SVGFeBlend::Mode::kLighten}, }; return this->parseEnumMap(gMap, mode) && this->parseEOSToken(); diff --git a/src/svg/node/SVGFeColorMatrix.cpp b/src/svg/node/SVGFeColorMatrix.cpp index f31230d2..f7f950dc 100644 --- a/src/svg/node/SVGFeColorMatrix.cpp +++ b/src/svg/node/SVGFeColorMatrix.cpp @@ -19,15 +19,15 @@ #include "tgfx/svg/node/SVGFeColorMatrix.h" #include #include "core/utils/MathExtra.h" +#include "svg/SVGAttributeParser.h" #include "tgfx/core/ColorFilter.h" #include "tgfx/core/ImageFilter.h" -#include "tgfx/svg/SVGAttributeParser.h" class SVGRenderContext; namespace tgfx { -bool SkSVGFeColorMatrix::parseAndSetAttribute(const char* name, const char* value) { +bool SVGFeColorMatrix::parseAndSetAttribute(const std::string& name, const std::string& value) { return INHERITED::parseAndSetAttribute(name, value) || this->setType(SVGAttributeParser::parse("type", name, value)) || this->setValues(SVGAttributeParser::parse("values", name, value)); @@ -45,8 +45,7 @@ bool SVGAttributeParser::parse(SVGFeColorMatrixType* type) { return this->parseEnumMap(gTypeMap, type) && this->parseEOSToken(); } -#ifndef RENDER_SVG -ColorMatrix SkSVGFeColorMatrix::makeMatrixForType() const { +ColorMatrix SVGFeColorMatrix::makeMatrixForType() const { if (Values.empty() && Type != SVGFeColorMatrixType::LuminanceToAlpha) { return ColorMatrix(); } @@ -69,7 +68,7 @@ ColorMatrix SkSVGFeColorMatrix::makeMatrixForType() const { } } -ColorMatrix SkSVGFeColorMatrix::MakeSaturate(SVGNumberType sat) { +ColorMatrix SVGFeColorMatrix::MakeSaturate(SVGNumberType sat) { enum { kR_Scale = 0, kG_Scale = 6, @@ -106,7 +105,7 @@ ColorMatrix SkSVGFeColorMatrix::MakeSaturate(SVGNumberType sat) { return matrix; } -ColorMatrix SkSVGFeColorMatrix::MakeHueRotate(SVGNumberType degrees) { +ColorMatrix SVGFeColorMatrix::MakeHueRotate(SVGNumberType degrees) { const float theta = DegreesToRadians(degrees); const SVGNumberType c = std::cos(theta); const SVGNumberType s = std::sin(theta); @@ -140,14 +139,14 @@ constexpr float LUM_COEFF_R = 0.2126f; constexpr float LUM_COEFF_G = 0.7152f; constexpr float LUM_COEFF_B = 0.0722f; -ColorMatrix SkSVGFeColorMatrix::MakeLuminanceToAlpha() { +ColorMatrix SVGFeColorMatrix::MakeLuminanceToAlpha() { return ColorMatrix{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, LUM_COEFF_R, LUM_COEFF_G, LUM_COEFF_B, 0, 0}; } -std::shared_ptr SkSVGFeColorMatrix::onMakeImageFilter( - const SVGRenderContext&, const SkSVGFilterContext&) const { +std::shared_ptr SVGFeColorMatrix::onMakeImageFilter(const SVGRenderContext&, + const SVGFilterContext&) const { return ImageFilter::ColorFilter(ColorFilter::Matrix(makeMatrixForType())); } -#endif + } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeComponentTransfer.cpp b/src/svg/node/SVGFeComponentTransfer.cpp index 8a400e6b..fb40af80 100644 --- a/src/svg/node/SVGFeComponentTransfer.cpp +++ b/src/svg/node/SVGFeComponentTransfer.cpp @@ -23,54 +23,14 @@ #include #include #include "core/utils/Log.h" -#include "tgfx/svg/SVGAttributeParser.h" +#include "svg/SVGAttributeParser.h" #include "tgfx/svg/SVGTypes.h" namespace tgfx { class SVGRenderContext; -#ifdef RENDER_SVG -sk_sp SkSVGFeComponentTransfer::onMakeImageFilter( - const SVGRenderContext& ctx, const SkSVGFilterContext& fctx) const { - std::vector a_tbl, b_tbl, g_tbl, r_tbl; - - for (const auto& child : fChildren) { - switch (child->tag()) { - case SkSVGTag::kFeFuncA: - a_tbl = static_cast(child.get())->getTable(); - break; - case SkSVGTag::kFeFuncB: - b_tbl = static_cast(child.get())->getTable(); - break; - case SkSVGTag::kFeFuncG: - g_tbl = static_cast(child.get())->getTable(); - break; - case SkSVGTag::kFeFuncR: - r_tbl = static_cast(child.get())->getTable(); - break; - default: - break; - } - } - SkASSERT(a_tbl.empty() || a_tbl.size() == 256); - SkASSERT(b_tbl.empty() || b_tbl.size() == 256); - SkASSERT(g_tbl.empty() || g_tbl.size() == 256); - SkASSERT(r_tbl.empty() || r_tbl.size() == 256); - - const SkRect cropRect = this->resolveFilterSubregion(ctx, fctx); - const sk_sp input = - fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)); - - const auto cf = SkColorFilters::TableARGB( - a_tbl.empty() ? nullptr : a_tbl.data(), r_tbl.empty() ? nullptr : r_tbl.data(), - g_tbl.empty() ? nullptr : g_tbl.data(), b_tbl.empty() ? nullptr : b_tbl.data()); - - return SkImageFilters::ColorFilter(std::move(cf), std::move(input), cropRect); -} -#endif - -std::vector SkSVGFeFunc::getTable() const { +std::vector SVGFeFunc::getTable() const { // https://www.w3.org/TR/SVG11/filters.html#feComponentTransferTypeAttribute const auto make_linear = [this]() -> std::vector { std::vector tbl(256); @@ -152,20 +112,18 @@ std::vector SkSVGFeFunc::getTable() const { case SVGFeFuncType::Gamma: return make_gamma(); } - - // SkUNREACHABLE; } -bool SkSVGFeFunc::parseAndSetAttribute(const char* name, const char* val) { - return INHERITED::parseAndSetAttribute(name, val) || - this->setAmplitude(SVGAttributeParser::parse("amplitude", name, val)) || - this->setExponent(SVGAttributeParser::parse("exponent", name, val)) || - this->setIntercept(SVGAttributeParser::parse("intercept", name, val)) || - this->setOffset(SVGAttributeParser::parse("offset", name, val)) || - this->setSlope(SVGAttributeParser::parse("slope", name, val)) || +bool SVGFeFunc::parseAndSetAttribute(const std::string& name, const std::string& value) { + return INHERITED::parseAndSetAttribute(name, value) || + this->setAmplitude(SVGAttributeParser::parse("amplitude", name, value)) || + this->setExponent(SVGAttributeParser::parse("exponent", name, value)) || + this->setIntercept(SVGAttributeParser::parse("intercept", name, value)) || + this->setOffset(SVGAttributeParser::parse("offset", name, value)) || + this->setSlope(SVGAttributeParser::parse("slope", name, value)) || this->setTableValues( - SVGAttributeParser::parse>("tableValues", name, val)) || - this->setType(SVGAttributeParser::parse("type", name, val)); + SVGAttributeParser::parse>("tableValues", name, value)) || + this->setType(SVGAttributeParser::parse("type", name, value)); } template <> diff --git a/src/svg/node/SVGFeComposite.cpp b/src/svg/node/SVGFeComposite.cpp index 366a1554..cfce2bdd 100644 --- a/src/svg/node/SVGFeComposite.cpp +++ b/src/svg/node/SVGFeComposite.cpp @@ -19,16 +19,14 @@ #include "tgfx/svg/node/SVGFeComposite.h" #include #include "core/utils/Log.h" -#include "tgfx/core/Rect.h" -#include "tgfx/svg/SVGAttributeParser.h" +#include "svg/SVGAttributeParser.h" namespace tgfx { class SVGRenderContext; -bool SkSVGFeComposite::parseAndSetAttribute(const char* name, const char* value) { +bool SVGFeComposite::parseAndSetAttribute(const std::string& name, const std::string& value) { return INHERITED::parseAndSetAttribute(name, value) || - // SkSVGFeInputType parsing defined in SkSVGFe.cpp: this->setIn2(SVGAttributeParser::parse("in2", name, value)) || this->setK1(SVGAttributeParser::parse("k1", name, value)) || this->setK2(SVGAttributeParser::parse("k2", name, value)) || @@ -38,8 +36,7 @@ bool SkSVGFeComposite::parseAndSetAttribute(const char* name, const char* value) SVGAttributeParser::parse("operator", name, value)); } -#ifndef RENDER_SVG -BlendMode SkSVGFeComposite::BlendModeForOperator(SVGFeCompositeOperator op) { +BlendMode SVGFeComposite::BlendModeForOperator(SVGFeCompositeOperator op) { switch (op) { case SVGFeCompositeOperator::Over: return BlendMode::SrcOver; @@ -58,8 +55,8 @@ BlendMode SkSVGFeComposite::BlendModeForOperator(SVGFeCompositeOperator op) { } } -std::shared_ptr SkSVGFeComposite::onMakeImageFilter( - const SVGRenderContext& /*ctx*/, const SkSVGFilterContext& /*fctx*/) const { +std::shared_ptr SVGFeComposite::onMakeImageFilter( + const SVGRenderContext& /*ctx*/, const SVGFilterContext& /*fctx*/) const { // const Rect cropRect = this->resolveFilterSubregion(ctx, fctx); // const SVGColorspace colorspace = this->resolveColorspace(ctx, fctx); // const std::shared_ptr background = fctx.resolveInput(ctx, fIn2, colorspace); @@ -75,7 +72,6 @@ std::shared_ptr SkSVGFeComposite::onMakeImageFilter( //TODO (YG) return nullptr; } -#endif // RENDER_SVG template <> bool SVGAttributeParser::parse(SVGFeCompositeOperator* op) { diff --git a/src/svg/node/SVGFeDisplacementMap.cpp b/src/svg/node/SVGFeDisplacementMap.cpp index 3e7144fe..e996416f 100644 --- a/src/svg/node/SVGFeDisplacementMap.cpp +++ b/src/svg/node/SVGFeDisplacementMap.cpp @@ -18,27 +18,24 @@ #include "tgfx/svg/node/SVGFeDisplacementMap.h" #include -#include "tgfx/core/Color.h" +#include "svg/SVGAttributeParser.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/Rect.h" -#include "tgfx/svg/SVGAttributeParser.h" namespace tgfx { -bool SkSVGFeDisplacementMap::parseAndSetAttribute(const char* name, const char* value) { +bool SVGFeDisplacementMap::parseAndSetAttribute(const std::string& name, const std::string& value) { return INHERITED::parseAndSetAttribute(name, value) || this->setIn2(SVGAttributeParser::parse("in2", name, value)) || - this->setXChannelSelector( - SVGAttributeParser::parse("xChannelSelector", - name, value)) || - this->setYChannelSelector( - SVGAttributeParser::parse("yChannelSelector", - name, value)) || + this->setXChannelSelector(SVGAttributeParser::parse( + "xChannelSelector", name, value)) || + this->setYChannelSelector(SVGAttributeParser::parse( + "yChannelSelector", name, value)) || this->setScale(SVGAttributeParser::parse("scale", name, value)); } -#ifndef RENDER_SVG -std::shared_ptr SkSVGFeDisplacementMap::onMakeImageFilter( - const SVGRenderContext& ctx, const SkSVGFilterContext& fctx) const { +std::shared_ptr SVGFeDisplacementMap::onMakeImageFilter( + const SVGRenderContext& ctx, const SVGFilterContext& fctx) const { const Rect cropRect = this->resolveFilterSubregion(ctx, fctx); cropRect.centerX(); const SVGColorspace colorspace = this->resolveColorspace(ctx, fctx); @@ -57,27 +54,24 @@ std::shared_ptr SkSVGFeDisplacementMap::onMakeImageFilter( } return nullptr; - // return SkImageFilters::DisplacementMap(fXChannelSelector, fYChannelSelector, scale, in2, in, - // cropRect); } -SVGColorspace SkSVGFeDisplacementMap::resolveColorspace(const SVGRenderContext& ctx, - const SkSVGFilterContext& fctx) const { +SVGColorspace SVGFeDisplacementMap::resolveColorspace(const SVGRenderContext& ctx, + const SVGFilterContext& fctx) const { // According to spec https://www.w3.org/TR/SVG11/filters.html#feDisplacementMapElement, // the 'in' source image must remain in its current colorspace, which means the colorspace of // this FE node is the same as the input. return fctx.resolveInputColorspace(ctx, this->getIn()); } -#endif template <> -bool SVGAttributeParser::parse( - SkSVGFeDisplacementMap::ChannelSelector* channel) { - static constexpr std::tuple gMap[] = { - {"R", SkSVGFeDisplacementMap::ChannelSelector::R}, - {"G", SkSVGFeDisplacementMap::ChannelSelector::G}, - {"B", SkSVGFeDisplacementMap::ChannelSelector::B}, - {"A", SkSVGFeDisplacementMap::ChannelSelector::A}, +bool SVGAttributeParser::parse( + SVGFeDisplacementMap::ChannelSelector* channel) { + static constexpr std::tuple gMap[] = { + {"R", SVGFeDisplacementMap::ChannelSelector::R}, + {"G", SVGFeDisplacementMap::ChannelSelector::G}, + {"B", SVGFeDisplacementMap::ChannelSelector::B}, + {"A", SVGFeDisplacementMap::ChannelSelector::A}, }; return this->parseEnumMap(gMap, channel) && this->parseEOSToken(); diff --git a/src/svg/node/SVGFeFlood.cpp b/src/svg/node/SVGFeFlood.cpp deleted file mode 100644 index 603b5a73..00000000 --- a/src/svg/node/SVGFeFlood.cpp +++ /dev/null @@ -1,42 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making tgfx available. -// -// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// unless required by applicable law or agreed to in writing, software distributed under the -// license is distributed on an "as is" basis, without warranties or conditions of any kind, -// either express or implied. see the license for the specific language governing permissions -// and limitations under the license. -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -// #include "tgfx/svg/SVGFeFlood.h" - -// class SkSVGFilterContext; - -#ifdef RENDER_SVG -SkColor SkSVGFeFlood::resolveFloodColor(const SVGRenderContext& ctx) const { - const auto floodColor = this->getFloodColor(); - const auto floodOpacity = this->getFloodOpacity(); - // Uninherited presentation attributes should have a concrete value by now. - if (!floodColor.isValue() || !floodOpacity.isValue()) { - SkDebugf("unhandled: flood-color or flood-opacity has no value\n"); - return SK_ColorBLACK; - } - - const SkColor color = ctx.resolveSvgColor(*floodColor); - return SkColorSetA(color, SkScalarRoundToInt(*floodOpacity * 255)); -} - -sk_sp SkSVGFeFlood::onMakeImageFilter(const SVGRenderContext& ctx, - const SkSVGFilterContext& fctx) const { - return SkImageFilters::Shader(SkShaders::Color(resolveFloodColor(ctx)), - this->resolveFilterSubregion(ctx, fctx)); -} -#endif \ No newline at end of file diff --git a/src/svg/node/SVGFeGaussianBlur.cpp b/src/svg/node/SVGFeGaussianBlur.cpp index ca5287e9..64481dfa 100644 --- a/src/svg/node/SVGFeGaussianBlur.cpp +++ b/src/svg/node/SVGFeGaussianBlur.cpp @@ -17,31 +17,30 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGFeGaussianBlur.h" +#include "svg/SVGAttributeParser.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/ImageFilter.h" #include "tgfx/core/Point.h" -#include "tgfx/svg/SVGAttributeParser.h" namespace tgfx { -bool SkSVGFeGaussianBlur::parseAndSetAttribute(const char* name, const char* value) { +bool SVGFeGaussianBlur::parseAndSetAttribute(const std::string& name, const std::string& value) { return INHERITED::parseAndSetAttribute(name, value) || - this->setstdDeviation(SVGAttributeParser::parse( + this->setstdDeviation(SVGAttributeParser::parse( "stdDeviation", name, value)); } -#ifndef RENDER_SVG -std::shared_ptr SkSVGFeGaussianBlur::onMakeImageFilter( - const SVGRenderContext& ctx, const SkSVGFilterContext& fctx) const { +std::shared_ptr SVGFeGaussianBlur::onMakeImageFilter( + const SVGRenderContext& ctx, const SVGFilterContext& fctx) const { auto scale = ctx.transformForCurrentOBB(fctx.primitiveUnits()).scale; const auto sigmaX = stdDeviation.fX * scale.x * 4; const auto sigmaY = stdDeviation.fY * scale.y * 4; return ImageFilter::Blur(sigmaX, sigmaY); } -#endif template <> -bool SVGAttributeParser::parse( - SkSVGFeGaussianBlur::StdDeviation* stdDeviation) { +bool SVGAttributeParser::parse( + SVGFeGaussianBlur::StdDeviation* stdDeviation) { std::vector values; if (!this->parse(&values)) { return false; diff --git a/src/svg/node/SVGFeImage.cpp b/src/svg/node/SVGFeImage.cpp index 8efe5349..f67a9c99 100644 --- a/src/svg/node/SVGFeImage.cpp +++ b/src/svg/node/SVGFeImage.cpp @@ -17,41 +17,20 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGFeImage.h" -#include "tgfx/core/Rect.h" -#include "tgfx/svg/SVGAttributeParser.h" +#include "svg/SVGAttributeParser.h" namespace tgfx { -bool SkSVGFeImage::parseAndSetAttribute(const char* n, const char* v) { - return INHERITED::parseAndSetAttribute(n, v) || - this->setHref(SVGAttributeParser::parse("xlink:href", n, v)) || +bool SVGFeImage::parseAndSetAttribute(const std::string& name, const std::string& value) { + return INHERITED::parseAndSetAttribute(name, value) || + this->setHref(SVGAttributeParser::parse("xlink:href", name, value)) || this->setPreserveAspectRatio( - SVGAttributeParser::parse("preserveAspectRatio", n, v)); + SVGAttributeParser::parse("preserveAspectRatio", name, value)); } -#ifndef RENDER_SVG -std::shared_ptr SkSVGFeImage::onMakeImageFilter( - const SVGRenderContext& /*ctx*/, const SkSVGFilterContext& /*fctx*/) const { - // // Load image and map viewbox (image bounds) to viewport (filter effects subregion). - // const Rect viewport = this->resolveFilterSubregion(ctx, fctx); - // const auto imgInfo = - // SVGImage::LoadImage(ctx.resourceProvider(), fHref, viewport, fPreserveAspectRatio); - // if (!imgInfo.fImage) { - // return nullptr; - // } - - // // Create the image filter mapped according to aspect ratio - // const Rect srcRect = Rect::Make(imgInfo.fImage->bounds()); - // const Rect& dstRect = imgInfo.fDst; - // // TODO: image-rendering property - // auto imgfilt = - // ImageFilter:: ::Image(imgInfo.fImage, srcRect, dstRect, - // SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNearest)); - - // // Aspect ratio mapping may end up drawing content outside of the filter effects region, - // // so perform an explicit crop. - // return SkImageFilters::Merge(&imgfilt, 1, fctx.filterEffectsRegion()); +std::shared_ptr SVGFeImage::onMakeImageFilter(const SVGRenderContext& /*ctx*/, + const SVGFilterContext& /*fctx*/) const { return nullptr; } -#endif + } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeLightSource.cpp b/src/svg/node/SVGFeLightSource.cpp index b697879c..9ac761d9 100644 --- a/src/svg/node/SVGFeLightSource.cpp +++ b/src/svg/node/SVGFeLightSource.cpp @@ -17,37 +17,24 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGFeLightSource.h" -#include -#include "tgfx/svg/SVGAttributeParser.h" +#include "svg/SVGAttributeParser.h" namespace tgfx { -// SkPoint3 SkSVGFeDistantLight::computeDirection() const { -// // Computing direction from azimuth+elevation is two 3D rotations: -// // - Rotate [1,0,0] about y axis first (elevation) -// // - Rotate result about z axis (azimuth) -// // Which is just the first column vector in the 3x3 matrix Rz*Ry. -// const float azimuthRad = SkDegreesToRadians(fAzimuth); -// const float elevationRad = SkDegreesToRadians(fElevation); -// const float sinAzimuth = sinf(azimuthRad), cosAzimuth = cosf(azimuthRad); -// const float sinElevation = sinf(elevationRad), cosElevation = cosf(elevationRad); -// return SkPoint3::Make(cosAzimuth * cosElevation, sinAzimuth * cosElevation, sinElevation); -// } - -bool SkSVGFeDistantLight::parseAndSetAttribute(const char* n, const char* v) { +bool SVGFeDistantLight::parseAndSetAttribute(const std::string& n, const std::string& v) { return INHERITED::parseAndSetAttribute(n, v) || this->setAzimuth(SVGAttributeParser::parse("azimuth", n, v)) || this->setElevation(SVGAttributeParser::parse("elevation", n, v)); } -bool SkSVGFePointLight::parseAndSetAttribute(const char* n, const char* v) { +bool SVGFePointLight::parseAndSetAttribute(const std::string& n, const std::string& v) { return INHERITED::parseAndSetAttribute(n, v) || this->setX(SVGAttributeParser::parse("x", n, v)) || this->setY(SVGAttributeParser::parse("y", n, v)) || this->setZ(SVGAttributeParser::parse("z", n, v)); } -bool SkSVGFeSpotLight::parseAndSetAttribute(const char* n, const char* v) { +bool SVGFeSpotLight::parseAndSetAttribute(const std::string& n, const std::string& v) { return INHERITED::parseAndSetAttribute(n, v) || this->setX(SVGAttributeParser::parse("x", n, v)) || this->setY(SVGAttributeParser::parse("y", n, v)) || diff --git a/src/svg/node/SVGFeLighting.cpp b/src/svg/node/SVGFeLighting.cpp index aefe2396..d0966c58 100644 --- a/src/svg/node/SVGFeLighting.cpp +++ b/src/svg/node/SVGFeLighting.cpp @@ -17,20 +17,21 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGFeLighting.h" -#include "tgfx/svg/SVGAttributeParser.h" +#include "svg/SVGAttributeParser.h" namespace tgfx { -bool SkSVGFeLighting::parseAndSetAttribute(const char* n, const char* v) { - return INHERITED::parseAndSetAttribute(n, v) || - this->setSurfaceScale(SVGAttributeParser::parse("surfaceScale", n, v)) || - this->setUnitLength(SVGAttributeParser::parse( - "kernelUnitLength", n, v)); +bool SVGFeLighting::parseAndSetAttribute(const std::string& name, const std::string& value) { + return INHERITED::parseAndSetAttribute(name, value) || + this->setSurfaceScale( + SVGAttributeParser::parse("surfaceScale", name, value)) || + this->setUnitLength(SVGAttributeParser::parse( + "kernelUnitLength", name, value)); } template <> -bool SVGAttributeParser::parse( - SkSVGFeLighting::KernelUnitLength* kernelUnitLength) { +bool SVGAttributeParser::parse( + SVGFeLighting::KernelUnitLength* kernelUnitLength) { std::vector values; if (!this->parse(&values)) { return false; @@ -41,140 +42,19 @@ bool SVGAttributeParser::parse( return true; } -#ifdef RENDER_SVG -sk_sp SkSVGFeLighting::onMakeImageFilter(const SVGRenderContext& ctx, - const SkSVGFilterContext& fctx) const { - for (const auto& child : fChildren) { - switch (child->tag()) { - case SkSVGTag::kFeDistantLight: - return this->makeDistantLight(ctx, fctx, - static_cast(child.get())); - case SkSVGTag::kFePointLight: - return this->makePointLight(ctx, fctx, static_cast(child.get())); - case SkSVGTag::kFeSpotLight: - return this->makeSpotLight(ctx, fctx, static_cast(child.get())); - default: - // Ignore unknown children, such as elements - break; - } - } - - SkDebugf("lighting filter effect needs exactly one light source\n"); - return nullptr; -} - -SkColor SkSVGFeLighting::resolveLightingColor(const SVGRenderContext& ctx) const { - const auto color = this->getLightingColor(); - if (!color.isValue()) { - // Uninherited presentation attributes should have a concrete value by now. - SkDebugf("unhandled: lighting-color has no value\n"); - return SK_ColorWHITE; - } - - return ctx.resolveSvgColor(*color); -} - -SkPoint3 SkSVGFeLighting::resolveXYZ(const SVGRenderContext& ctx, const SkSVGFilterContext& fctx, - SkSVGNumberType x, SkSVGNumberType y, - SkSVGNumberType z) const { - const auto obbt = ctx.transformForCurrentOBB(fctx.primitiveUnits()); - const auto xy = SkV2{x, y} * obbt.scale + obbt.offset; - z = SkSVGLengthContext({obbt.scale.x, obbt.scale.y}) - .resolve(SkSVGLength(z * 100.f, SkSVGLength::Unit::kPercentage), - SkSVGLengthContext::LengthType::kOther); - return SkPoint3::Make(xy.x, xy.y, z); -} -#endif - -bool SkSVGFeSpecularLighting::parseAndSetAttribute(const char* n, const char* v) { - return INHERITED::parseAndSetAttribute(n, v) || +bool SVGFeSpecularLighting::parseAndSetAttribute(const std::string& name, + const std::string& value) { + return INHERITED::parseAndSetAttribute(name, value) || this->setSpecularConstant( - SVGAttributeParser::parse("specularConstant", n, v)) || + SVGAttributeParser::parse("specularConstant", name, value)) || this->setSpecularExponent( - SVGAttributeParser::parse("specularExponent", n, v)); -} - -#ifdef RENDER_SVG -sk_sp SkSVGFeSpecularLighting::makeDistantLight( - const SVGRenderContext& ctx, const SkSVGFilterContext& fctx, - const SkSVGFeDistantLight* light) const { - const SkPoint3 dir = light->computeDirection(); - return SkImageFilters::DistantLitSpecular( - this->resolveXYZ(ctx, fctx, dir.fX, dir.fY, dir.fZ), this->resolveLightingColor(ctx), - this->getSurfaceScale(), fSpecularConstant, fSpecularExponent, - fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)), - this->resolveFilterSubregion(ctx, fctx)); + SVGAttributeParser::parse("specularExponent", name, value)); } -sk_sp SkSVGFeSpecularLighting::makePointLight(const SVGRenderContext& ctx, - const SkSVGFilterContext& fctx, - const SkSVGFePointLight* light) const { - return SkImageFilters::PointLitSpecular( - this->resolveXYZ(ctx, fctx, light->getX(), light->getY(), light->getZ()), - this->resolveLightingColor(ctx), this->getSurfaceScale(), fSpecularConstant, - fSpecularExponent, fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)), - this->resolveFilterSubregion(ctx, fctx)); -} - -sk_sp SkSVGFeSpecularLighting::makeSpotLight(const SVGRenderContext& ctx, - const SkSVGFilterContext& fctx, - const SkSVGFeSpotLight* light) const { - const auto& limitingConeAngle = light->getLimitingConeAngle(); - const float cutoffAngle = limitingConeAngle.isValid() ? *limitingConeAngle : 180.f; - - return SkImageFilters::SpotLitSpecular( - this->resolveXYZ(ctx, fctx, light->getX(), light->getY(), light->getZ()), - this->resolveXYZ(ctx, fctx, light->getPointsAtX(), light->getPointsAtY(), - light->getPointsAtZ()), - light->getSpecularExponent(), cutoffAngle, this->resolveLightingColor(ctx), - this->getSurfaceScale(), fSpecularConstant, fSpecularExponent, - fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)), - this->resolveFilterSubregion(ctx, fctx)); -} -#endif - -bool SkSVGFeDiffuseLighting::parseAndSetAttribute(const char* n, const char* v) { - return INHERITED::parseAndSetAttribute(n, v) || +bool SVGFeDiffuseLighting::parseAndSetAttribute(const std::string& name, const std::string& value) { + return INHERITED::parseAndSetAttribute(name, value) || this->setDiffuseConstant( - SVGAttributeParser::parse("diffuseConstant", n, v)); + SVGAttributeParser::parse("diffuseConstant", name, value)); } -#ifdef RENDER_SVG -sk_sp SkSVGFeDiffuseLighting::makeDistantLight( - const SVGRenderContext& ctx, const SkSVGFilterContext& fctx, - const SkSVGFeDistantLight* light) const { - const SkPoint3 dir = light->computeDirection(); - return SkImageFilters::DistantLitDiffuse( - this->resolveXYZ(ctx, fctx, dir.fX, dir.fY, dir.fZ), this->resolveLightingColor(ctx), - this->getSurfaceScale(), this->getDiffuseConstant(), - fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)), - this->resolveFilterSubregion(ctx, fctx)); -} - -sk_sp SkSVGFeDiffuseLighting::makePointLight(const SVGRenderContext& ctx, - const SkSVGFilterContext& fctx, - const SkSVGFePointLight* light) const { - return SkImageFilters::PointLitDiffuse( - this->resolveXYZ(ctx, fctx, light->getX(), light->getY(), light->getZ()), - this->resolveLightingColor(ctx), this->getSurfaceScale(), this->getDiffuseConstant(), - fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)), - this->resolveFilterSubregion(ctx, fctx)); -} - -sk_sp SkSVGFeDiffuseLighting::makeSpotLight(const SVGRenderContext& ctx, - const SkSVGFilterContext& fctx, - const SkSVGFeSpotLight* light) const { - const auto& limitingConeAngle = light->getLimitingConeAngle(); - const float cutoffAngle = limitingConeAngle.isValid() ? *limitingConeAngle : 180.f; - - return SkImageFilters::SpotLitDiffuse( - this->resolveXYZ(ctx, fctx, light->getX(), light->getY(), light->getZ()), - this->resolveXYZ(ctx, fctx, light->getPointsAtX(), light->getPointsAtY(), - light->getPointsAtZ()), - light->getSpecularExponent(), cutoffAngle, this->resolveLightingColor(ctx), - this->getSurfaceScale(), this->getDiffuseConstant(), - fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)), - this->resolveFilterSubregion(ctx, fctx)); -} -#endif } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeMerge.cpp b/src/svg/node/SVGFeMerge.cpp index 94952a64..11ed4800 100644 --- a/src/svg/node/SVGFeMerge.cpp +++ b/src/svg/node/SVGFeMerge.cpp @@ -17,42 +17,30 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGFeMerge.h" +#include "svg/SVGAttributeParser.h" #include "tgfx/core/ImageFilter.h" -#include "tgfx/svg/SVGAttributeParser.h" namespace tgfx { // class SVGRenderContext; -bool SkSVGFeMergeNode::parseAndSetAttribute(const char* name, const char* value) { +bool SVGFeMergeNode::parseAndSetAttribute(const std::string& name, const std::string& value) { return INHERITED::parseAndSetAttribute(name, value) || this->setIn(SVGAttributeParser::parse("in", name, value)); } -#ifndef RENDER_SVG -std::shared_ptr SkSVGFeMerge::onMakeImageFilter( - const SVGRenderContext& /*ctx*/, const SkSVGFilterContext& /*fctx*/) const { - // const SkSVGColorspace colorspace = this->resolveColorspace(ctx, fctx); - - // skia_private::STArray<8, sk_sp> merge_node_filters; - // merge_node_filters.reserve(fChildren.size()); - - // this->forEachChild([&](const SkSVGFeMergeNode* child) { - // merge_node_filters.push_back(fctx.resolveInput(ctx, child->getIn(), colorspace)); - // }); - - // return SkImageFilters::Merge(merge_node_filters.data(), merge_node_filters.size(), - // this->resolveFilterSubregion(ctx, fctx)); +std::shared_ptr SVGFeMerge::onMakeImageFilter(const SVGRenderContext& /*ctx*/, + const SVGFilterContext& /*fctx*/) const { + //TODO (YGAurora): Implement this return nullptr; } -#endif -std::vector SkSVGFeMerge::getInputs() const { +std::vector SVGFeMerge::getInputs() const { std::vector inputs; - inputs.reserve(fChildren.size()); + inputs.reserve(children.size()); - this->forEachChild( - [&](const SkSVGFeMergeNode* child) { inputs.push_back(child->getIn()); }); + this->forEachChild( + [&](const SVGFeMergeNode* child) { inputs.push_back(child->getIn()); }); return inputs; } diff --git a/src/svg/node/SVGFeMorphology.cpp b/src/svg/node/SVGFeMorphology.cpp index a42e2844..ecc88977 100644 --- a/src/svg/node/SVGFeMorphology.cpp +++ b/src/svg/node/SVGFeMorphology.cpp @@ -18,49 +18,35 @@ #include "tgfx/svg/node/SVGFeMorphology.h" #include -#include "tgfx/svg/SVGAttributeParser.h" +#include "svg/SVGAttributeParser.h" namespace tgfx { -bool SkSVGFeMorphology::parseAndSetAttribute(const char* name, const char* value) { +bool SVGFeMorphology::parseAndSetAttribute(const std::string& name, const std::string& value) { return INHERITED::parseAndSetAttribute(name, value) || this->setMorphOperator( - SVGAttributeParser::parse("operator", name, value)) || + SVGAttributeParser::parse("operator", name, value)) || this->setMorphRadius( - SVGAttributeParser::parse("radius", name, value)); + SVGAttributeParser::parse("radius", name, value)); } -#ifndef RENDER_SVG -std::shared_ptr SkSVGFeMorphology::onMakeImageFilter( - const SVGRenderContext& /*ctx*/, const SkSVGFilterContext& /*fctx*/) const { - // const SkRect cropRect = this->resolveFilterSubregion(ctx, fctx); - // const SkSVGColorspace colorspace = this->resolveColorspace(ctx, fctx); - // sk_sp input = fctx.resolveInput(ctx, this->getIn(), colorspace); - - // const auto r = - // SkV2{fRadius.fX, fRadius.fY} * ctx.transformForCurrentOBB(fctx.primitiveUnits()).scale; - // switch (fOperator) { - // case Operator::kErode: - // return SkImageFilters::Erode(r.x, r.y, input, cropRect); - // case Operator::kDilate: - // return SkImageFilters::Dilate(r.x, r.y, input, cropRect); - // } +std::shared_ptr SVGFeMorphology::onMakeImageFilter( + const SVGRenderContext& /*ctx*/, const SVGFilterContext& /*fctx*/) const { return nullptr; } -#endif template <> -bool SVGAttributeParser::parse(SkSVGFeMorphology::Operator* op) { - static constexpr std::tuple gMap[] = { - {"dilate", SkSVGFeMorphology::Operator::kDilate}, - {"erode", SkSVGFeMorphology::Operator::kErode}, +bool SVGAttributeParser::parse(SVGFeMorphology::Operator* op) { + static constexpr std::tuple gMap[] = { + {"dilate", SVGFeMorphology::Operator::kDilate}, + {"erode", SVGFeMorphology::Operator::kErode}, }; return this->parseEnumMap(gMap, op) && this->parseEOSToken(); } template <> -bool SVGAttributeParser::parse(SkSVGFeMorphology::Radius* radius) { +bool SVGAttributeParser::parse(SVGFeMorphology::Radius* radius) { std::vector values; if (!this->parse(&values)) { return false; diff --git a/src/svg/node/SVGFeOffset.cpp b/src/svg/node/SVGFeOffset.cpp index baecffbe..1326d315 100644 --- a/src/svg/node/SVGFeOffset.cpp +++ b/src/svg/node/SVGFeOffset.cpp @@ -17,29 +17,20 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGFeOffset.h" +#include "svg/SVGAttributeParser.h" #include "tgfx/core/ImageFilter.h" -#include "tgfx/svg/SVGAttributeParser.h" namespace tgfx { -bool SkSVGFeOffset::parseAndSetAttribute(const char* name, const char* value) { +bool SVGFeOffset::parseAndSetAttribute(const std::string& name, const std::string& value) { return INHERITED::parseAndSetAttribute(name, value) || this->setDx(SVGAttributeParser::parse("dx", name, value)) || this->setDy(SVGAttributeParser::parse("dy", name, value)); } -#ifndef RENDER_SVG -std::shared_ptr SkSVGFeOffset::onMakeImageFilter( - const SVGRenderContext& /*ctx*/, const SkSVGFilterContext& /*fctx*/) const { - // const auto d = - // SkV2{this->getDx(), this->getDy()} * ctx.transformForCurrentOBB(fctx.primitiveUnits()).scale; - - // sk_sp in = - // fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)); - // return SkImageFilters::Offset(d.x, d.y, std::move(in), this->resolveFilterSubregion(ctx, fctx)); - - //TODO (YG) +std::shared_ptr SVGFeOffset::onMakeImageFilter( + const SVGRenderContext& /*ctx*/, const SVGFilterContext& /*fctx*/) const { return nullptr; } -#endif + } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeTurbulence.cpp b/src/svg/node/SVGFeTurbulence.cpp index 65ca684a..b4e97f69 100644 --- a/src/svg/node/SVGFeTurbulence.cpp +++ b/src/svg/node/SVGFeTurbulence.cpp @@ -6,12 +6,12 @@ */ #include "tgfx/svg/node/SVGFeTurbulence.h" +#include "svg/SVGAttributeParser.h" #include "tgfx/core/ImageFilter.h" -#include "tgfx/svg/SVGAttributeParser.h" namespace tgfx { -bool SkSVGFeTurbulence::parseAndSetAttribute(const char* name, const char* value) { +bool SVGFeTurbulence::parseAndSetAttribute(const std::string& name, const std::string& value) { return INHERITED::parseAndSetAttribute(name, value) || this->setNumOctaves( SVGAttributeParser::parse("numOctaves", name, value)) || @@ -55,27 +55,9 @@ bool SVGAttributeParser::parse(SVGFeTurbulenceType* type) { return parsedValue && this->parseEOSToken(); } -#ifndef RENDER_SVG -std::shared_ptr SkSVGFeTurbulence::onMakeImageFilter( - const SVGRenderContext& /*ctx*/, const SkSVGFilterContext& /*fctx*/) const { - // const SkISize* tileSize = nullptr; // TODO: needs filter element subregion properties - - // sk_sp shader; - // switch (fTurbulenceType.fType) { - // case SkSVGFeTurbulenceType::Type::kTurbulence: - // shader = SkShaders::MakeTurbulence(fBaseFrequency.freqX(), fBaseFrequency.freqY(), - // fNumOctaves, fSeed, tileSize); - // break; - // case SkSVGFeTurbulenceType::Type::kFractalNoise: - // shader = SkShaders::MakeFractalNoise(fBaseFrequency.freqX(), fBaseFrequency.freqY(), - // fNumOctaves, fSeed, tileSize); - // break; - // } - - // return SkImageFilters::Shader(shader, this->resolveFilterSubregion(ctx, fctx)); - - //TODO (YG) +std::shared_ptr SVGFeTurbulence::onMakeImageFilter( + const SVGRenderContext& /*ctx*/, const SVGFilterContext& /*fctx*/) const { return nullptr; } -#endif + } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFilter.cpp b/src/svg/node/SVGFilter.cpp index b7f38562..f293cf06 100644 --- a/src/svg/node/SVGFilter.cpp +++ b/src/svg/node/SVGFilter.cpp @@ -17,17 +17,16 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGFilter.h" -#include "tgfx/core/ColorFilter.h" +#include "svg/SVGAttributeParser.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/ImageFilter.h" #include "tgfx/core/Rect.h" -#include "tgfx/svg/SVGAttributeParser.h" -#include "tgfx/svg/SVGRenderContext.h" #include "tgfx/svg/node/SVGFe.h" #include "tgfx/svg/node/SVGFilterContext.h" namespace tgfx { -bool SkSVGFilter::parseAndSetAttribute(const char* name, const char* value) { +bool SVGFilter::parseAndSetAttribute(const std::string& name, const std::string& value) { return INHERITED::parseAndSetAttribute(name, value) || this->setX(SVGAttributeParser::parse("x", name, value)) || this->setY(SVGAttributeParser::parse("y", name, value)) || @@ -39,26 +38,26 @@ bool SkSVGFilter::parseAndSetAttribute(const char* name, const char* value) { SVGAttributeParser::parse("primitiveUnits", name, value)); } -void SkSVGFilter::applyProperties(SVGRenderContext* ctx) const { +void SVGFilter::applyProperties(SVGRenderContext* ctx) const { this->onPrepareToRender(ctx); } -std::shared_ptr SkSVGFilter::buildFilterDAG(const SVGRenderContext& ctx) const { +std::shared_ptr SVGFilter::buildFilterDAG(const SVGRenderContext& ctx) const { std::shared_ptr filter; - SkSVGFilterContext fctx(ctx.resolveOBBRect(X, Y, Width, Height, FilterUnits), PrimitiveUnits); + SVGFilterContext fctx(ctx.resolveOBBRect(X, Y, Width, Height, FilterUnits), PrimitiveUnits); SVGRenderContext localCtx(ctx); this->applyProperties(&localCtx); SVGColorspace cs = SVGColorspace::SRGB; - for (const auto& child : fChildren) { - if (!SkSVGFe::IsFilterEffect(child)) { + for (const auto& child : children) { + if (!SVGFe::IsFilterEffect(child)) { continue; } - const auto& feNode = static_cast(*child); + const auto& feNode = static_cast(*child); const auto& feResultType = feNode.getResult(); // Propagate any inherited properties that may impact filter effect behavior (e.g. - // color-interpolation-filters). We call this explicitly here because the SkSVGFe + // color-interpolation-filters). We call this explicitly here because the SVGFe // nodes do not participate in the normal onRender path, which is when property // propagation currently occurs. SVGRenderContext localChildCtx(localCtx); @@ -78,9 +77,7 @@ std::shared_ptr SkSVGFilter::buildFilterDAG(const SVGRenderContext& // Convert to final destination colorspace if (cs != SVGColorspace::SRGB) { - // filter = SkImageFilters::ColorFilter(SkColorFilters::LinearToSRGBGamma(), filter); - - //TODO (YG) + //TODO (YGAurora): Implement color space conversion filter = nullptr; } diff --git a/src/svg/node/SVGFilterContext.cpp b/src/svg/node/SVGFilterContext.cpp index ff38aed3..3a242729 100644 --- a/src/svg/node/SVGFilterContext.cpp +++ b/src/svg/node/SVGFilterContext.cpp @@ -19,91 +19,53 @@ #include "tgfx/svg/node/SVGFilterContext.h" #include #include "core/utils/Log.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/ColorFilter.h" #include "tgfx/core/ImageFilter.h" #include "tgfx/core/Rect.h" -#include "tgfx/core/Shader.h" #include "tgfx/svg/SVGTypes.h" namespace tgfx { -namespace { -#ifndef RENDER_SVG -std::shared_ptr ConvertFilterColorspace(std::shared_ptr&& input, - SVGColorspace src, SVGColorspace dst) { - if (src == dst) { - return std::move(input); - } - // TODO (YG) - Implement this - // else if (src == SVGColorspace::kSRGB && dst == SVGColorspace::kLinearRGB) { - // return ImageFilter::ColorFilter(SkColorFilters::SRGBToLinearGamma(), input); - // } else { - // ASSERT(src == SVGColorspace::kLinearRGB && dst == SVGColorspace::kSRGB); - // return ImageFilter::ColorFilter(ColorFilters::LinearToSRGBGamma(), input); - // } - return std::move(input); -} - -// std::shared_ptr paint_as_shader(const Paint& paint) { -// std::shared_ptr shader = paint.getShader(); -// auto color = paint.getColor(); -// if (shader && color.alpha < 1.f) { -// // Multiply by paint alpha -// shader = shader->makeWithColorFilter(ColorFilter::Blend(color, BlendMode::DstIn)); -// } else if (!shader) { -// shader = Shader::MakeColorShader(color); -// } -// if (paint.getColorFilter()) { -// shader = shader->makeWithColorFilter(paint.getColorFilter()); -// } -// return shader; -// } - -#endif -} // namespace -const SkSVGFilterContext::Result* SkSVGFilterContext::findResultById( - const SVGStringType& id) const { - auto iter = fResults.find(id); - return iter != fResults.end() ? &iter->second : nullptr; +const SVGFilterContext::Result* SVGFilterContext::findResultById(const SVGStringType& id) const { + auto iter = results.find(id); + return iter != results.end() ? &iter->second : nullptr; } -const Rect& SkSVGFilterContext::filterPrimitiveSubregion(const SVGFeInputType& input) const { +const Rect& SVGFilterContext::filterPrimitiveSubregion(const SVGFeInputType& input) const { const Result* res = nullptr; if (input.type() == SVGFeInputType::Type::FilterPrimitiveReference) { - auto iter = fResults.find(input.id()); - res = iter != fResults.end() ? &iter->second : nullptr; + auto iter = results.find(input.id()); + res = iter != results.end() ? &iter->second : nullptr; } else if (input.type() == SVGFeInputType::Type::Unspecified) { - res = &fPreviousResult; + res = &previousResult; } - return res ? res->fFilterSubregion : fFilterEffectsRegion; + return res ? res->filterSubregion : _filterEffectsRegion; } -void SkSVGFilterContext::registerResult(const SVGStringType& id, - const std::shared_ptr& result, - const Rect& subregion, SVGColorspace resultColorspace) { +void SVGFilterContext::registerResult(const SVGStringType& id, + const std::shared_ptr& result, + const Rect& subregion, SVGColorspace resultColorspace) { ASSERT(!id.empty()); - fResults[id] = {result, subregion, resultColorspace}; + results[id] = {result, subregion, resultColorspace}; } -void SkSVGFilterContext::setPreviousResult(const std::shared_ptr& result, - const Rect& subregion, SVGColorspace resultColorspace) { - fPreviousResult = {result, subregion, resultColorspace}; +void SVGFilterContext::setPreviousResult(const std::shared_ptr& result, + const Rect& subregion, SVGColorspace resultColorspace) { + previousResult = {result, subregion, resultColorspace}; } -bool SkSVGFilterContext::previousResultIsSourceGraphic() const { - return fPreviousResult.fImageFilter == nullptr; +bool SVGFilterContext::previousResultIsSourceGraphic() const { + return previousResult.imageFilter == nullptr; } -#ifndef RENDER_SVG // https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveInAttribute -std::tuple, SVGColorspace> SkSVGFilterContext::getInput( +std::tuple, SVGColorspace> SVGFilterContext::getInput( const SVGRenderContext& ctx, const SVGFeInputType& inputType) const { SVGColorspace inputCS = SVGColorspace::SRGB; std::shared_ptr result; switch (inputType.type()) { case SVGFeInputType::Type::SourceAlpha: { - - //TODO (YG) - Implement this with class ColorMatrix std::array colorMatrix{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}; auto colorFilter = ColorFilter::Matrix(colorMatrix); result = ImageFilter::ColorFilter(colorFilter); @@ -115,36 +77,28 @@ std::tuple, SVGColorspace> SkSVGFilterContext::getI case SVGFeInputType::Type::FillPaint: { const auto& fillPaint = ctx.fillPaint(); if (fillPaint.has_value()) { - //TODO (YG) - Implement this by dither and shader image filter - // auto dither = - // fillPaint->isDither() ? SkImageFilters::Dither::kYes : SkImageFilters::Dither::kNo; - // result = SkImageFilters::Shader(paint_as_shader(*fillPaint), dither); + //TODO (YGAurora) - Implement shader image filter by paint } break; } case SVGFeInputType::Type::StrokePaint: { - // The paint filter doesn't apply fill/stroke styling, but use the paint settings - // defined for strokes. const auto& strokePaint = ctx.strokePaint(); if (strokePaint.has_value()) { - //TODO (YG) - Implement this by dither and shader image filter - // auto dither = - // strokePaint->isDither() ? SkImageFilters::Dither::kYes : SkImageFilters::Dither::kNo; - // result = SkImageFilters::Shader(paint_as_shader(*strokePaint), dither); + //TODO (YGAurora) - Implement shader image filter by paint } break; } case SVGFeInputType::Type::FilterPrimitiveReference: { const Result* res = findResultById(inputType.id()); if (res) { - result = res->fImageFilter; - inputCS = res->fColorspace; + result = res->imageFilter; + inputCS = res->colorspace; } break; } case SVGFeInputType::Type::Unspecified: { - result = fPreviousResult.fImageFilter; - inputCS = fPreviousResult.fColorspace; + result = previousResult.imageFilter; + inputCS = previousResult.colorspace; break; } default: @@ -154,21 +108,21 @@ std::tuple, SVGColorspace> SkSVGFilterContext::getI return {result, inputCS}; } -SVGColorspace SkSVGFilterContext::resolveInputColorspace(const SVGRenderContext& ctx, - const SVGFeInputType& inputType) const { - return std::get<1>(this->getInput(ctx, inputType)); +SVGColorspace SVGFilterContext::resolveInputColorspace(const SVGRenderContext& context, + const SVGFeInputType& inputType) const { + return std::get<1>(this->getInput(context, inputType)); } -std::shared_ptr SkSVGFilterContext::resolveInput( - const SVGRenderContext& ctx, const SVGFeInputType& inputType) const { - return std::get<0>(this->getInput(ctx, inputType)); +std::shared_ptr SVGFilterContext::resolveInput(const SVGRenderContext& context, + const SVGFeInputType& inputType) const { + return std::get<0>(this->getInput(context, inputType)); } -std::shared_ptr SkSVGFilterContext::resolveInput(const SVGRenderContext& ctx, - const SVGFeInputType& inputType, - SVGColorspace colorspace) const { - auto [result, inputCS] = this->getInput(ctx, inputType); - return ConvertFilterColorspace(std::move(result), inputCS, colorspace); +std::shared_ptr SVGFilterContext::resolveInput(const SVGRenderContext& /*ctx*/, + const SVGFeInputType& /*inputType*/, + SVGColorspace /*colorspace*/) const { + //TODO (YGAurora) - ConvertFilterColorspace + return nullptr; } -#endif + } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGGradient.cpp b/src/svg/node/SVGGradient.cpp index 94629f70..1fc20416 100644 --- a/src/svg/node/SVGGradient.cpp +++ b/src/svg/node/SVGGradient.cpp @@ -18,16 +18,16 @@ #include "tgfx/svg/node/SVGGradient.h" #include -#include #include #include "core/utils/Log.h" +#include "svg/SVGAttributeParser.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/Color.h" #include "tgfx/core/Matrix.h" -#include "tgfx/svg/SVGAttributeParser.h" namespace tgfx { -bool SkSVGGradient::parseAndSetAttribute(const char* name, const char* value) { +bool SVGGradient::parseAndSetAttribute(const std::string& name, const std::string& value) { return INHERITED::parseAndSetAttribute(name, value) || this->setGradientTransform( SVGAttributeParser::parse("gradientTransform", name, value)) || @@ -38,14 +38,13 @@ bool SkSVGGradient::parseAndSetAttribute(const char* name, const char* value) { SVGAttributeParser::parse("gradientUnits", name, value)); } -#ifndef RENDER_SVG // https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementHrefAttribute -void SkSVGGradient::collectColorStops(const SVGRenderContext& ctx, std::vector& colors, - std::vector& positions) const { +void SVGGradient::collectColorStops(const SVGRenderContext& ctx, std::vector& colors, + std::vector& positions) const { // Used to resolve percentage offsets. const SVGLengthContext ltx(Size::Make(1, 1)); - this->forEachChild([&](const SkSVGStop* stop) { + this->forEachChild([&](const SVGStop* stop) { colors.push_back(this->resolveStopColor(ctx, *stop)); positions.push_back( std::clamp(ltx.resolve(stop->getOffset(), SVGLengthContext::LengthType::Other), 0.f, 1.f)); @@ -56,12 +55,12 @@ void SkSVGGradient::collectColorStops(const SVGRenderContext& ctx, std::vectortag() == SVGTag::LinearGradient || ref->tag() == SVGTag::RadialGradient)) { - static_cast(ref.get())->collectColorStops(ctx, colors, positions); + static_cast(ref.get())->collectColorStops(ctx, colors, positions); } } } -Color SkSVGGradient::resolveStopColor(const SVGRenderContext& ctx, const SkSVGStop& stop) const { +Color SVGGradient::resolveStopColor(const SVGRenderContext& ctx, const SVGStop& stop) const { const auto& stopColor = stop.getStopColor(); const auto& stopOpacity = stop.getStopOpacity(); // Uninherited presentation attrs should have a concrete value at this point. @@ -74,7 +73,7 @@ Color SkSVGGradient::resolveStopColor(const SVGRenderContext& ctx, const SkSVGSt return {color.red, color.green, color.blue, *stopOpacity * color.alpha}; } -bool SkSVGGradient::onAsPaint(const SVGRenderContext& ctx, Paint* paint) const { +bool SVGGradient::onAsPaint(const SVGRenderContext& ctx, Paint* paint) const { std::vector colors; std::vector positions; @@ -101,7 +100,6 @@ bool SkSVGGradient::onAsPaint(const SVGRenderContext& ctx, Paint* paint) const { paint->setShader(this->onMakeShader(ctx, colors, positions, tileMode, localMatrix)); return true; } -#endif // https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementSpreadMethodAttribute template <> diff --git a/src/svg/node/SVGImage.cpp b/src/svg/node/SVGImage.cpp index 81eb94a3..935e2af8 100644 --- a/src/svg/node/SVGImage.cpp +++ b/src/svg/node/SVGImage.cpp @@ -18,19 +18,17 @@ #include "tgfx/svg/node/SVGImage.h" #include -#include "core/ImageDecoder.h" #include "core/utils/Log.h" -#include "fstream" -#include "gpu/ResourceProvider.h" +#include "svg/SVGAttributeParser.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/Data.h" #include "tgfx/core/Image.h" #include "tgfx/core/Matrix.h" #include "tgfx/core/Rect.h" -#include "tgfx/svg/SVGAttributeParser.h" namespace tgfx { -bool SkSVGImage::parseAndSetAttribute(const char* n, const char* v) { +bool SVGImage::parseAndSetAttribute(const std::string& n, const std::string& v) { return INHERITED::parseAndSetAttribute(n, v) || this->setX(SVGAttributeParser::parse("x", n, v)) || this->setY(SVGAttributeParser::parse("y", n, v)) || @@ -41,8 +39,7 @@ bool SkSVGImage::parseAndSetAttribute(const char* n, const char* v) { SVGAttributeParser::parse("preserveAspectRatio", n, v)); } -#ifndef RENDER_SVG -bool SkSVGImage::onPrepareToRender(SVGRenderContext* ctx) const { +bool SVGImage::onPrepareToRender(SVGRenderContext* ctx) const { // Width or height of 0 disables rendering per spec: // https://www.w3.org/TR/SVG11/struct.html#ImageElement return !Href.iri().empty() && Width.value() > 0 && Height.value() > 0 && @@ -103,8 +100,8 @@ std::shared_ptr LoadImage(const SVGIRI& href) { return Image::MakeFromEncoded(data); } -SkSVGImage::ImageInfo SkSVGImage::LoadImage(const SVGIRI& iri, const Rect& viewPort, - SVGPreserveAspectRatio /*par*/) { +SVGImage::ImageInfo SVGImage::LoadImage(const SVGIRI& iri, const Rect& viewPort, + SVGPreserveAspectRatio /*par*/) { std::shared_ptr image = ::tgfx::LoadImage(iri); if (!image) { return {}; @@ -121,12 +118,11 @@ SkSVGImage::ImageInfo SkSVGImage::LoadImage(const SVGIRI& iri, const Rect& viewP return {std::move(image), dst}; } -void SkSVGImage::onRender(const SVGRenderContext& ctx) const { +void SVGImage::onRender(const SVGRenderContext& ctx) const { // Per spec: x, w, width, height attributes establish the new viewport. const SVGLengthContext& lctx = ctx.lengthContext(); const Rect viewPort = lctx.resolveRect(X, Y, Width, Height); - //TODO (YG) ImageInfo image; const auto imgInfo = LoadImage(Href, viewPort, PreserveAspectRatio); if (!imgInfo.fImage) { @@ -134,7 +130,6 @@ void SkSVGImage::onRender(const SVGRenderContext& ctx) const { return; } - // TODO: image-rendering property auto matrix = Matrix::MakeScale(viewPort.width() / imgInfo.fDst.width(), viewPort.height() / imgInfo.fDst.height()); matrix.preTranslate(imgInfo.fDst.x(), imgInfo.fDst.y()); @@ -144,13 +139,13 @@ void SkSVGImage::onRender(const SVGRenderContext& ctx) const { // drawImageRect(imgInfo.fImage, imgInfo.fDst, SkSamplingOptions(SkFilterMode::kLinear)); } -Path SkSVGImage::onAsPath(const SVGRenderContext&) const { +Path SVGImage::onAsPath(const SVGRenderContext&) const { return {}; } -Rect SkSVGImage::onObjectBoundingBox(const SVGRenderContext& ctx) const { +Rect SVGImage::onObjectBoundingBox(const SVGRenderContext& ctx) const { const SVGLengthContext& lctx = ctx.lengthContext(); return lctx.resolveRect(X, Y, Width, Height); } -#endif + } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGLine.cpp b/src/svg/node/SVGLine.cpp index b95167a2..15657af0 100644 --- a/src/svg/node/SVGLine.cpp +++ b/src/svg/node/SVGLine.cpp @@ -17,17 +17,18 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGLine.h" +#include "svg/SVGAttributeParser.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/Canvas.h" #include "tgfx/core/Path.h" #include "tgfx/core/Point.h" -#include "tgfx/svg/SVGAttributeParser.h" namespace tgfx { -SkSVGLine::SkSVGLine() : INHERITED(SVGTag::Line) { +SVGLine::SVGLine() : INHERITED(SVGTag::Line) { } -bool SkSVGLine::parseAndSetAttribute(const char* n, const char* v) { +bool SVGLine::parseAndSetAttribute(const std::string& n, const std::string& v) { return INHERITED::parseAndSetAttribute(n, v) || this->setX1(SVGAttributeParser::parse("x1", n, v)) || this->setY1(SVGAttributeParser::parse("y1", n, v)) || @@ -35,21 +36,20 @@ bool SkSVGLine::parseAndSetAttribute(const char* n, const char* v) { this->setY2(SVGAttributeParser::parse("y2", n, v)); } -#ifndef RENDER_SVG -std::tuple SkSVGLine::resolve(const SVGLengthContext& lctx) const { +std::tuple SVGLine::resolve(const SVGLengthContext& lctx) const { return std::make_tuple(Point::Make(lctx.resolve(X1, SVGLengthContext::LengthType::Horizontal), lctx.resolve(Y1, SVGLengthContext::LengthType::Vertical)), Point::Make(lctx.resolve(X2, SVGLengthContext::LengthType::Horizontal), lctx.resolve(Y2, SVGLengthContext::LengthType::Vertical))); } -void SkSVGLine::onDraw(Canvas* canvas, const SVGLengthContext& lctx, const Paint& paint, - PathFillType) const { +void SVGLine::onDraw(Canvas* canvas, const SVGLengthContext& lctx, const Paint& paint, + PathFillType) const { auto [p0, p1] = this->resolve(lctx); canvas->drawLine(p0, p1, paint); } -Path SkSVGLine::onAsPath(const SVGRenderContext& ctx) const { +Path SVGLine::onAsPath(const SVGRenderContext& ctx) const { auto [p0, p1] = this->resolve(ctx.lengthContext()); //TODO (YG) path add methods to support line @@ -60,5 +60,5 @@ Path SkSVGLine::onAsPath(const SVGRenderContext& ctx) const { return path; } -#endif + } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGLinearGradient.cpp b/src/svg/node/SVGLinearGradient.cpp index 502d4b3d..f0d8da72 100644 --- a/src/svg/node/SVGLinearGradient.cpp +++ b/src/svg/node/SVGLinearGradient.cpp @@ -17,18 +17,19 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGLinearGradient.h" +#include "svg/SVGAttributeParser.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/Matrix.h" #include "tgfx/core/Point.h" #include "tgfx/core/Shader.h" #include "tgfx/core/TileMode.h" -#include "tgfx/svg/SVGAttributeParser.h" namespace tgfx { -SkSVGLinearGradient::SkSVGLinearGradient() : INHERITED(SVGTag::LinearGradient) { +SVGLinearGradient::SVGLinearGradient() : INHERITED(SVGTag::LinearGradient) { } -bool SkSVGLinearGradient::parseAndSetAttribute(const char* name, const char* value) { +bool SVGLinearGradient::parseAndSetAttribute(const std::string& name, const std::string& value) { return INHERITED::parseAndSetAttribute(name, value) || this->setX1(SVGAttributeParser::parse("x1", name, value)) || this->setY1(SVGAttributeParser::parse("y1", name, value)) || @@ -36,11 +37,10 @@ bool SkSVGLinearGradient::parseAndSetAttribute(const char* name, const char* val this->setY2(SVGAttributeParser::parse("y2", name, value)); } -#ifndef RENDER_SVG -std::shared_ptr SkSVGLinearGradient::onMakeShader(const SVGRenderContext& ctx, - const std::vector& colors, - const std::vector& positions, - TileMode, const Matrix&) const { +std::shared_ptr SVGLinearGradient::onMakeShader(const SVGRenderContext& ctx, + const std::vector& colors, + const std::vector& positions, + TileMode, const Matrix&) const { SVGLengthContext lctx = ctx.lengthContext(); lctx.setPatternUnits(getGradientUnits()); @@ -51,5 +51,5 @@ std::shared_ptr SkSVGLinearGradient::onMakeShader(const SVGRenderContext return Shader::MakeLinearGradient(startPoint, endPoint, colors, positions); } -#endif + } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGMask.cpp b/src/svg/node/SVGMask.cpp index 41e96700..7fda9b71 100644 --- a/src/svg/node/SVGMask.cpp +++ b/src/svg/node/SVGMask.cpp @@ -17,19 +17,17 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGMask.h" -#include "tgfx/core/Color.h" +#include "svg/SVGAttributeParser.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/ColorFilter.h" -#include "tgfx/core/ImageFilter.h" -#include "tgfx/core/MaskFilter.h" #include "tgfx/core/Paint.h" #include "tgfx/core/Recorder.h" #include "tgfx/core/Rect.h" -#include "tgfx/svg/SVGAttributeParser.h" #include "tgfx/svg/SVGTypes.h" namespace tgfx { -bool SkSVGMask::parseAndSetAttribute(const char* n, const char* v) { +bool SVGMask::parseAndSetAttribute(const std::string& n, const std::string& v) { return INHERITED::parseAndSetAttribute(n, v) || this->setX(SVGAttributeParser::parse("x", n, v)) || this->setY(SVGAttributeParser::parse("y", n, v)) || @@ -41,8 +39,7 @@ bool SkSVGMask::parseAndSetAttribute(const char* n, const char* v) { SVGAttributeParser::parse("maskContentUnits", n, v)); } -#ifndef RENDER_SVG -Rect SkSVGMask::bounds(const SVGRenderContext& context) const { +Rect SVGMask::bounds(const SVGRenderContext& context) const { auto lengthContext = context.lengthContext(); lengthContext.setPatternUnits(MaskUnits); SVGRenderContext resolveContext(context, lengthContext); @@ -64,7 +61,7 @@ std::array MakeLuminanceToAlpha() { 0, 0, 0, 0, 0, LUM_COEFF_R, LUM_COEFF_G, LUM_COEFF_B, 0, 0}; } -void SkSVGMask::renderMask(const SVGRenderContext& context) const { +void SVGMask::renderMask(const SVGRenderContext& context) const { // https://www.w3.org/TR/SVG11/masking.html#Masking // Propagate any inherited properties that may impact mask effect behavior (e.g. // color-interpolation). We call this explicitly here because the SkSVGMask @@ -78,7 +75,7 @@ void SkSVGMask::renderMask(const SVGRenderContext& context) const { // Ensure the render context is destructed, drawing to the canvas upon destruction SVGRenderContext localContext(context, canvas); this->onPrepareToRender(&localContext); - for (const auto& child : fChildren) { + for (const auto& child : children) { child->render(localContext); } } @@ -91,5 +88,5 @@ void SkSVGMask::renderMask(const SVGRenderContext& context) const { luminancePaint.setColorFilter(luminanceFilter); context.canvas()->drawPicture(picture, nullptr, &luminancePaint); } -#endif + } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGNode.cpp b/src/svg/node/SVGNode.cpp index 1df7d990..00f97c05 100644 --- a/src/svg/node/SVGNode.cpp +++ b/src/svg/node/SVGNode.cpp @@ -20,6 +20,8 @@ #include #include #include +#include "svg/SVGAttributeParser.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/Color.h" #include "tgfx/core/Matrix.h" #include "tgfx/core/Paint.h" @@ -27,7 +29,6 @@ #include "tgfx/core/PathTypes.h" #include "tgfx/core/Point.h" #include "tgfx/core/Rect.h" -#include "tgfx/svg/SVGRenderContext.h" #include "tgfx/svg/SVGTypes.h" namespace tgfx { @@ -44,7 +45,6 @@ SVGNode::SVGNode(SVGTag t) : _tag(t) { SVGNode::~SVGNode() { } -#ifndef RENDER_SVG void SVGNode::render(const SVGRenderContext& ctx) const { SVGRenderContext localContext(ctx, this); @@ -91,7 +91,6 @@ bool SVGNode::onPrepareToRender(SVGRenderContext* ctx) const { return visibility != SVGVisibility::Type::Hidden && (!display.isValue() || *display != SVGDisplay::None); } -#endif void SVGNode::setAttribute(SVGAttribute attr, const SVGValue& v) { this->onSetAttribute(attr, v); @@ -108,7 +107,7 @@ void SetInheritedByDefault(std::optional& presentation_attribute, const T& va } } -bool SVGNode::parseAndSetAttribute(const char* n, const char* v) { +bool SVGNode::parseAndSetAttribute(const std::string& n, const std::string& v) { #define PARSE_AND_SET(svgName, attrName) \ this->set##attrName( \ SVGAttributeParser::parseProperty(svgName, n, \ diff --git a/src/svg/node/SVGPath.cpp b/src/svg/node/SVGPath.cpp index 1b694874..9ede367e 100644 --- a/src/svg/node/SVGPath.cpp +++ b/src/svg/node/SVGPath.cpp @@ -18,18 +18,19 @@ #include "tgfx/svg/node/SVGPath.h" #include +#include "svg/SVGAttributeParser.h" +#include "svg/SVGParse.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/Canvas.h" #include "tgfx/core/Path.h" #include "tgfx/core/Rect.h" -#include "tgfx/svg/SVGAttributeParser.h" -#include "tgfx/svg/SVGParse.h" namespace tgfx { -SkSVGPath::SkSVGPath() : INHERITED(SVGTag::Path) { +SVGPath::SVGPath() : INHERITED(SVGTag::Path) { } -bool SkSVGPath::parseAndSetAttribute(const char* n, const char* v) { +bool SVGPath::parseAndSetAttribute(const std::string& n, const std::string& v) { return INHERITED::parseAndSetAttribute(n, v) || this->setShapePath(SVGAttributeParser::parse("d", n, v)); } @@ -43,16 +44,15 @@ bool SVGAttributeParser::parse(Path* path) { return success; } -#ifndef RENDER_SVG -void SkSVGPath::onDraw(Canvas* canvas, const SVGLengthContext&, const Paint& paint, - PathFillType fillType) const { +void SVGPath::onDraw(Canvas* canvas, const SVGLengthContext&, const Paint& paint, + PathFillType fillType) const { // the passed fillType follows inheritance rules and needs to be applied at draw time. Path path = ShapePath; // Note: point and verb data are CoW path.setFillType(fillType); canvas->drawPath(path, paint); } -Path SkSVGPath::onAsPath(const SVGRenderContext& ctx) const { +Path SVGPath::onAsPath(const SVGRenderContext& ctx) const { Path path = ShapePath; // clip-rule can be inherited and needs to be applied at clip time. path.setFillType(ctx.presentationContext()._inherited.ClipRule->asFillType()); @@ -60,10 +60,8 @@ Path SkSVGPath::onAsPath(const SVGRenderContext& ctx) const { return path; } -Rect SkSVGPath::onObjectBoundingBox(const SVGRenderContext&) const { +Rect SVGPath::onObjectBoundingBox(const SVGRenderContext&) const { return ShapePath.getBounds(); - //TODO (YG): Implement this - // return fPath.computeTightBounds(); + //TODO (YGAurora): Implement thigh bounds computation } -#endif } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGPattern.cpp b/src/svg/node/SVGPattern.cpp index e6b64d6a..3f0c61ac 100644 --- a/src/svg/node/SVGPattern.cpp +++ b/src/svg/node/SVGPattern.cpp @@ -18,23 +18,23 @@ #include "tgfx/svg/node/SVGPattern.h" #include -#include #include +#include "svg/SVGAttributeParser.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/Matrix.h" #include "tgfx/core/Paint.h" #include "tgfx/core/Rect.h" #include "tgfx/core/Shader.h" #include "tgfx/core/Surface.h" #include "tgfx/core/TileMode.h" -#include "tgfx/svg/SVGAttributeParser.h" #include "tgfx/svg/SVGTypes.h" namespace tgfx { -SkSVGPattern::SkSVGPattern() : INHERITED(SVGTag::Pattern) { +SVGPattern::SVGPattern() : INHERITED(SVGTag::Pattern) { } -bool SkSVGPattern::parseAndSetAttribute(const char* name, const char* value) { +bool SVGPattern::parseAndSetAttribute(const std::string& name, const std::string& value) { return INHERITED::parseAndSetAttribute(name, value) || this->setX(SVGAttributeParser::parse("x", name, value)) || this->setY(SVGAttributeParser::parse("y", name, value)) || @@ -49,8 +49,7 @@ bool SkSVGPattern::parseAndSetAttribute(const char* name, const char* value) { "patternContentUnits", name, value)); } -#ifndef RENDER_SVG -const SkSVGPattern* SkSVGPattern::hrefTarget(const SVGRenderContext& ctx) const { +const SVGPattern* SVGPattern::hrefTarget(const SVGRenderContext& ctx) const { if (Href.iri().empty()) { return nullptr; } @@ -60,7 +59,7 @@ const SkSVGPattern* SkSVGPattern::hrefTarget(const SVGRenderContext& ctx) const return nullptr; } - return static_cast(href.get()); + return static_cast(href.get()); } template @@ -82,10 +81,10 @@ int inherit_if_needed(const std::optional& src, std::optional& dst) { * referenced element inherits attributes or children due to its own ‘xlink:href’ attribute, then * the current element can inherit those attributes or children. */ -const SkSVGPattern* SkSVGPattern::resolveHref(const SVGRenderContext& ctx, - PatternAttributes* attrs) const { - const SkSVGPattern* currentNode = this; - const SkSVGPattern* contentNode = this; +const SVGPattern* SVGPattern::resolveHref(const SVGRenderContext& ctx, + PatternAttributes* attrs) const { + const SVGPattern* currentNode = this; + const SVGPattern* contentNode = this; do { // Bitwise OR to avoid short-circuiting. @@ -129,7 +128,7 @@ const SkSVGPattern* SkSVGPattern::resolveHref(const SVGRenderContext& ctx, return contentNode; } -bool SkSVGPattern::onAsPaint(const SVGRenderContext& ctx, Paint* paint) const { +bool SVGPattern::onAsPaint(const SVGRenderContext& ctx, Paint* paint) const { PatternAttributes attrs; const auto* contentNode = this->resolveHref(ctx, &attrs); auto lengthContext = ctx.lengthContext(); @@ -149,7 +148,7 @@ bool SkSVGPattern::onAsPaint(const SVGRenderContext& ctx, Paint* paint) const { canvas->concat(patternMatrix); { SVGRenderContext recordingContext(ctx, canvas, lengthContext); - contentNode->SkSVGContainer::onRender(recordingContext); + contentNode->SVGContainer::onRender(recordingContext); } auto picture = patternRecorder.finishRecordingAsPicture(); auto shaderImage = @@ -158,5 +157,5 @@ bool SkSVGPattern::onAsPaint(const SVGRenderContext& ctx, Paint* paint) const { paint->setShader(shader); return true; } -#endif + } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGPoly.cpp b/src/svg/node/SVGPoly.cpp index a808415e..8ac515d6 100644 --- a/src/svg/node/SVGPoly.cpp +++ b/src/svg/node/SVGPoly.cpp @@ -17,15 +17,16 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGPoly.h" +#include "svg/SVGAttributeParser.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/Canvas.h" -#include "tgfx/svg/SVGAttributeParser.h" namespace tgfx { -SkSVGPoly::SkSVGPoly(SVGTag t) : INHERITED(t) { +SVGPoly::SVGPoly(SVGTag t) : INHERITED(t) { } -bool SkSVGPoly::parseAndSetAttribute(const char* n, const char* v) { +bool SVGPoly::parseAndSetAttribute(const std::string& n, const std::string& v) { if (INHERITED::parseAndSetAttribute(n, v)) { return true; } @@ -41,15 +42,14 @@ bool SkSVGPoly::parseAndSetAttribute(const char* n, const char* v) { return false; } -#ifndef RENDER_SVG -void SkSVGPoly::onDraw(Canvas* canvas, const SVGLengthContext&, const Paint& paint, - PathFillType fillType) const { +void SVGPoly::onDraw(Canvas* canvas, const SVGLengthContext&, const Paint& paint, + PathFillType fillType) const { // the passed fillType follows inheritance rules and needs to be applied at draw time. fPath.setFillType(fillType); canvas->drawPath(fPath, paint); } -Path SkSVGPoly::onAsPath(const SVGRenderContext& ctx) const { +Path SVGPoly::onAsPath(const SVGRenderContext& ctx) const { Path path = fPath; // clip-rule can be inherited and needs to be applied at clip time. @@ -59,8 +59,8 @@ Path SkSVGPoly::onAsPath(const SVGRenderContext& ctx) const { return path; } -Rect SkSVGPoly::onObjectBoundingBox(const SVGRenderContext&) const { +Rect SVGPoly::onObjectBoundingBox(const SVGRenderContext&) const { return fPath.getBounds(); } -#endif + } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGRadialGradient.cpp b/src/svg/node/SVGRadialGradient.cpp index 1825c345..c5792d99 100644 --- a/src/svg/node/SVGRadialGradient.cpp +++ b/src/svg/node/SVGRadialGradient.cpp @@ -18,18 +18,19 @@ #include "tgfx/svg/node/SVGRadialGradient.h" #include +#include "svg/SVGAttributeParser.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/Color.h" #include "tgfx/core/Matrix.h" #include "tgfx/core/Shader.h" #include "tgfx/core/TileMode.h" -#include "tgfx/svg/SVGAttributeParser.h" namespace tgfx { -SkSVGRadialGradient::SkSVGRadialGradient() : INHERITED(SVGTag::RadialGradient) { +SVGRadialGradient::SVGRadialGradient() : INHERITED(SVGTag::RadialGradient) { } -bool SkSVGRadialGradient::parseAndSetAttribute(const char* name, const char* value) { +bool SVGRadialGradient::parseAndSetAttribute(const std::string& name, const std::string& value) { return INHERITED::parseAndSetAttribute(name, value) || this->setCx(SVGAttributeParser::parse("cx", name, value)) || this->setCy(SVGAttributeParser::parse("cy", name, value)) || @@ -38,11 +39,10 @@ bool SkSVGRadialGradient::parseAndSetAttribute(const char* name, const char* val this->setFy(SVGAttributeParser::parse("fy", name, value)); } -#ifndef RENDER_SVG -std::shared_ptr SkSVGRadialGradient::onMakeShader(const SVGRenderContext& ctx, - const std::vector& colors, - const std::vector& position, - TileMode, const Matrix& matrix) const { +std::shared_ptr SVGRadialGradient::onMakeShader(const SVGRenderContext& ctx, + const std::vector& colors, + const std::vector& position, + TileMode, const Matrix& matrix) const { SVGLengthContext lctx = ctx.lengthContext(); lctx.setPatternUnits(getGradientUnits()); @@ -59,5 +59,5 @@ std::shared_ptr SkSVGRadialGradient::onMakeShader(const SVGRenderContext radius *= matrix.getAxisScales().x; return Shader::MakeRadialGradient(center, radius, colors, position); } -#endif + } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGRect.cpp b/src/svg/node/SVGRect.cpp index ce7a0370..33de14d9 100644 --- a/src/svg/node/SVGRect.cpp +++ b/src/svg/node/SVGRect.cpp @@ -19,20 +19,19 @@ #include "tgfx/svg/node/SVGRect.h" #include #include "SVGRectPriv.h" +#include "svg/SVGAttributeParser.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/Canvas.h" #include "tgfx/core/Point.h" #include "tgfx/core/RRect.h" #include "tgfx/core/Rect.h" -#include "tgfx/core/Size.h" -#include "tgfx/svg/SVGAttributeParser.h" -#include "tgfx/svg/SVGRenderContext.h" namespace tgfx { -SkSVGRect::SkSVGRect() : INHERITED(SVGTag::Rect) { +SVGRect::SVGRect() : INHERITED(SVGTag::Rect) { } -bool SkSVGRect::parseAndSetAttribute(const char* n, const char* v) { +bool SVGRect::parseAndSetAttribute(const std::string& n, const std::string& v) { return INHERITED::parseAndSetAttribute(n, v) || this->setX(SVGAttributeParser::parse("x", n, v)) || this->setY(SVGAttributeParser::parse("y", n, v)) || @@ -42,7 +41,6 @@ bool SkSVGRect::parseAndSetAttribute(const char* n, const char* v) { this->setRy(SVGAttributeParser::parse("ry", n, v)); } -#ifndef RENDER_SVG std::tuple ResolveOptionalRadii(const std::optional& opt_rx, const std::optional& opt_ry, const SVGLengthContext& lctx) { @@ -73,7 +71,7 @@ std::tuple ResolveOptionalRadii(const std::optional& op return {opt_rx.has_value() ? rx : ry, opt_ry.has_value() ? ry : rx}; } -RRect SkSVGRect::resolve(const SVGLengthContext& lctx) const { +RRect SVGRect::resolve(const SVGLengthContext& lctx) const { const auto rect = lctx.resolveRect(X, Y, Width, Height); const auto [rx, ry] = ResolveOptionalRadii(Rx, Ry, lctx); @@ -91,29 +89,8 @@ RRect SkSVGRect::resolve(const SVGLengthContext& lctx) const { return rrect; } -// void SkSVGRect::onRender(const SVGRenderContext& ctx) const { -// const auto fillType = ctx.presentationContext().fInherited.fFillRule->asFillType(); - -// auto lengthCtx = ctx.lengthContext(); -// Size rectSize = Size::Make(lengthCtx.resolve(fWidth, SkSVGLengthContext::LengthType::kHorizontal), -// lengthCtx.resolve(fHeight, SkSVGLengthContext::LengthType::kVertical)); -// lengthCtx.setViewPort(rectSize); -// SVGRenderContext paintCtx(ctx, ctx.canvas(), lengthCtx); -// const auto fillPaint = paintCtx.fillPaint(); -// const auto strokePaint = paintCtx.strokePaint(); - -// // TODO: this approach forces duplicate geometry resolution in onDraw(); refactor to avoid. -// if (fillPaint.has_value()) { -// this->onDraw(ctx.canvas(), ctx.lengthContext(), fillPaint.value(), fillType); -// } - -// if (strokePaint.has_value()) { -// this->onDraw(ctx.canvas(), ctx.lengthContext(), strokePaint.value(), fillType); -// } -// } - -void SkSVGRect::onDraw(Canvas* canvas, const SVGLengthContext& lctx, const Paint& paint, - PathFillType) const { +void SVGRect::onDraw(Canvas* canvas, const SVGLengthContext& lctx, const Paint& paint, + PathFillType) const { auto rect = this->resolve(lctx); auto offset = Point::Make(rect.rect.left, rect.rect.top); rect.rect = rect.rect.makeOffset(-offset.x, -offset.y); @@ -123,7 +100,7 @@ void SkSVGRect::onDraw(Canvas* canvas, const SVGLengthContext& lctx, const Paint canvas->restore(); } -Path SkSVGRect::onAsPath(const SVGRenderContext& ctx) const { +Path SVGRect::onAsPath(const SVGRenderContext& ctx) const { Path path; path.addRRect(this->resolve(ctx.lengthContext())); this->mapToParent(&path); @@ -131,8 +108,8 @@ Path SkSVGRect::onAsPath(const SVGRenderContext& ctx) const { return path; } -Rect SkSVGRect::onObjectBoundingBox(const SVGRenderContext& ctx) const { +Rect SVGRect::onObjectBoundingBox(const SVGRenderContext& ctx) const { return ctx.lengthContext().resolveRect(X, Y, Width, Height); } -#endif + } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGSVG.cpp b/src/svg/node/SVGSVG.cpp index 0fc7f3d9..78a697dc 100644 --- a/src/svg/node/SVGSVG.cpp +++ b/src/svg/node/SVGSVG.cpp @@ -17,6 +17,7 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGSVG.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/Size.h" #include "tgfx/svg/SVGAttribute.h" #include "tgfx/svg/SVGTypes.h" @@ -24,7 +25,6 @@ namespace tgfx { -#ifndef RENDER_SVG void SVGSVG::renderNode(const SVGRenderContext& ctx, const SVGIRI& iri) const { SVGRenderContext localContext(ctx, this); auto node = localContext.findNodeById(iri); @@ -86,7 +86,6 @@ Size SVGSVG::intrinsicSize(const SVGLengthContext& lctx) const { return Size::Make(lctx.resolve(Width, SVGLengthContext::LengthType::Horizontal), lctx.resolve(Height, SVGLengthContext::LengthType::Vertical)); } -#endif void SVGSVG::onSetAttribute(SVGAttribute attr, const SVGValue& v) { if (type != Type::kInner && type != Type::kRoot) return; diff --git a/src/svg/node/SVGShape.cpp b/src/svg/node/SVGShape.cpp index 47b428ac..8aea7879 100644 --- a/src/svg/node/SVGShape.cpp +++ b/src/svg/node/SVGShape.cpp @@ -19,8 +19,8 @@ #include "tgfx/svg/node/SVGShape.h" #include #include "core/utils/Log.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/Size.h" -#include "tgfx/svg/SVGRenderContext.h" class SkSVGNode; enum class SkSVGTag; @@ -30,7 +30,6 @@ namespace tgfx { SVGShape::SVGShape(SVGTag t) : INHERITED(t) { } -#ifndef RENDER_SVG void SVGShape::onRender(const SVGRenderContext& ctx) const { const auto fillType = ctx.presentationContext()._inherited.FillRule->asFillType(); @@ -42,7 +41,6 @@ void SVGShape::onRender(const SVGRenderContext& ctx) const { const auto fillPaint = paintCtx.fillPaint(); const auto strokePaint = paintCtx.strokePaint(); - // TODO: this approach forces duplicate geometry resolution in onDraw(); refactor to avoid. if (fillPaint.has_value()) { this->onDraw(ctx.canvas(), ctx.lengthContext(), fillPaint.value(), fillType); } @@ -51,7 +49,6 @@ void SVGShape::onRender(const SVGRenderContext& ctx) const { this->onDraw(ctx.canvas(), ctx.lengthContext(), strokePaint.value(), fillType); } } -#endif void SVGShape::appendChild(std::shared_ptr) { LOGE("cannot append child nodes to an SVG shape.\n"); diff --git a/src/svg/node/SVGStop.cpp b/src/svg/node/SVGStop.cpp index 3356e4f5..8cd624d5 100644 --- a/src/svg/node/SVGStop.cpp +++ b/src/svg/node/SVGStop.cpp @@ -17,14 +17,14 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGStop.h" -#include "tgfx/svg/SVGAttributeParser.h" +#include "svg/SVGAttributeParser.h" namespace tgfx { -SkSVGStop::SkSVGStop() : INHERITED(SVGTag::Stop) { +SVGStop::SVGStop() : INHERITED(SVGTag::Stop) { } -bool SkSVGStop::parseAndSetAttribute(const char* n, const char* v) { +bool SVGStop::parseAndSetAttribute(const std::string& n, const std::string& v) { return INHERITED::parseAndSetAttribute(n, v) || this->setOffset(SVGAttributeParser::parse("offset", n, v)); } diff --git a/src/svg/node/SVGText.cpp b/src/svg/node/SVGText.cpp index 5ce2dd02..e65f53b8 100644 --- a/src/svg/node/SVGText.cpp +++ b/src/svg/node/SVGText.cpp @@ -22,10 +22,11 @@ #include #include #include "core/utils/Log.h" +#include "svg/SVGAttributeParser.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/Font.h" #include "tgfx/core/Path.h" #include "tgfx/core/TextBlob.h" -#include "tgfx/svg/SVGAttributeParser.h" #include "tgfx/svg/SVGFontManager.h" namespace tgfx { @@ -113,7 +114,7 @@ std::vector ResolveLengths(const SVGLengthContext& lengthCtx, return resolved; } -float ComputeAlignmentFactor(const SkSVGPresentationContext& context) { +float ComputeAlignmentFactor(const SVGPresentationContext& context) { switch (context._inherited.TextAnchor->type()) { case SVGTextAnchor::Type::Start: return 0.0f; @@ -128,37 +129,34 @@ float ComputeAlignmentFactor(const SkSVGPresentationContext& context) { } } // namespace -void SkSVGTextContainer::appendChild(std::shared_ptr child) { +void SVGTextContainer::appendChild(std::shared_ptr child) { // Only allow text content child nodes. switch (child->tag()) { case SVGTag::TextLiteral: case SVGTag::TextPath: case SVGTag::TSpan: - fChildren.push_back(std::static_pointer_cast(child)); + children.push_back(std::static_pointer_cast(child)); break; default: break; } } -void SkSVGTextFragment::renderText(const SVGRenderContext& context, - const ShapedTextCallback& function) const { - // N.B.: unlike regular elements, text fragments do not establish a new OBB scope -- they - // always defer to the root element for OBB resolution. +void SVGTextFragment::renderText(const SVGRenderContext& context, + const ShapedTextCallback& function) const { SVGRenderContext localContext(context); - if (this->onPrepareToRender(&localContext)) { this->onShapeText(localContext, function); } } -Path SkSVGTextFragment::onAsPath(const SVGRenderContext&) const { +Path SVGTextFragment::onAsPath(const SVGRenderContext&) const { // TODO (YGAurora) return Path(); } -void SkSVGTextContainer::onShapeText(const SVGRenderContext& context, - const ShapedTextCallback& function) const { +void SVGTextContainer::onShapeText(const SVGRenderContext& context, + const ShapedTextCallback& function) const { auto x = ResolveLengths(context.lengthContext(), X, SVGLengthContext::LengthType::Horizontal); auto y = ResolveLengths(context.lengthContext(), Y, SVGLengthContext::LengthType::Vertical); @@ -166,8 +164,8 @@ void SkSVGTextContainer::onShapeText(const SVGRenderContext& context, auto dy = ResolveLengths(context.lengthContext(), Dy, SVGLengthContext::LengthType::Vertical); // TODO (YGAurora) : Handle rotate - for (uint32_t i = 0; i < fChildren.size(); i++) { - auto child = fChildren[i]; + for (uint32_t i = 0; i < children.size(); i++) { + auto child = children[i]; context.canvas()->save(); float offsetX = x[i] + (i < dx.size() ? dx[i] : 0); float offsetY = y[i] + (i < dy.size() ? dy[i] : 0); @@ -177,7 +175,7 @@ void SkSVGTextContainer::onShapeText(const SVGRenderContext& context, } } -bool SkSVGTextContainer::parseAndSetAttribute(const char* name, const char* value) { +bool SVGTextContainer::parseAndSetAttribute(const std::string& name, const std::string& value) { return INHERITED::parseAndSetAttribute(name, value) || this->setX(SVGAttributeParser::parse>("x", name, value)) || this->setY(SVGAttributeParser::parse>("y", name, value)) || @@ -187,8 +185,8 @@ bool SkSVGTextContainer::parseAndSetAttribute(const char* name, const char* valu SVGAttributeParser::parse>("rotate", name, value)); } -void SkSVGTextLiteral::onShapeText(const SVGRenderContext& context, - const ShapedTextCallback& function) const { +void SVGTextLiteral::onShapeText(const SVGRenderContext& context, + const ShapedTextCallback& function) const { auto [success, font] = ResolveFont(context); if (!success) { @@ -198,7 +196,7 @@ void SkSVGTextLiteral::onShapeText(const SVGRenderContext& context, function(context, textBlob); } -void SkSVGText::onRender(const SVGRenderContext& context) const { +void SVGText::onRender(const SVGRenderContext& context) const { auto renderer = [](const SVGRenderContext& renderCtx, const std::shared_ptr& textBlob) -> void { @@ -227,7 +225,7 @@ void SkSVGText::onRender(const SVGRenderContext& context) const { this->onShapeText(context, renderer); } -Rect SkSVGText::onObjectBoundingBox(const SVGRenderContext& ctx) const { +Rect SVGText::onObjectBoundingBox(const SVGRenderContext& ctx) const { Rect bounds = Rect::MakeEmpty(); auto boundCollector = [&bounds](const SVGRenderContext&, @@ -243,17 +241,17 @@ Rect SkSVGText::onObjectBoundingBox(const SVGRenderContext& ctx) const { return bounds; } -Path SkSVGText::onAsPath(const SVGRenderContext&) const { +Path SVGText::onAsPath(const SVGRenderContext&) const { // TODO (YGAurora) return Path(); } -void SkSVGTextPath::onShapeText(const SVGRenderContext& ctx, - const ShapedTextCallback& function) const { +void SVGTextPath::onShapeText(const SVGRenderContext& ctx, + const ShapedTextCallback& function) const { this->INHERITED::onShapeText(ctx, function); } -bool SkSVGTextPath::parseAndSetAttribute(const char* name, const char* value) { +bool SVGTextPath::parseAndSetAttribute(const std::string& name, const std::string& value) { return INHERITED::parseAndSetAttribute(name, value) || this->setHref(SVGAttributeParser::parse("xlink:href", name, value)) || this->setStartOffset(SVGAttributeParser::parse("startOffset", name, value)); diff --git a/src/svg/node/SVGTransformableNode.cpp b/src/svg/node/SVGTransformableNode.cpp index 297388bc..d1ea1a50 100644 --- a/src/svg/node/SVGTransformableNode.cpp +++ b/src/svg/node/SVGTransformableNode.cpp @@ -17,6 +17,7 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGTransformableNode.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/Matrix.h" #include "tgfx/core/Path.h" #include "tgfx/core/Rect.h" @@ -26,12 +27,10 @@ namespace tgfx { -SkSVGTransformableNode::SkSVGTransformableNode(SVGTag tag) - : INHERITED(tag), fTransform(Matrix::I()) { +SVGTransformableNode::SVGTransformableNode(SVGTag tag) : INHERITED(tag), fTransform(Matrix::I()) { } -#ifndef RENDER_SVG -bool SkSVGTransformableNode::onPrepareToRender(SVGRenderContext* ctx) const { +bool SVGTransformableNode::onPrepareToRender(SVGRenderContext* ctx) const { if (!fTransform.isIdentity()) { auto transform = fTransform; if (auto unit = ctx->lengthContext().getPatternUnits(); @@ -47,9 +46,8 @@ bool SkSVGTransformableNode::onPrepareToRender(SVGRenderContext* ctx) const { return this->INHERITED::onPrepareToRender(ctx); } -#endif -void SkSVGTransformableNode::onSetAttribute(SVGAttribute attr, const SVGValue& v) { +void SVGTransformableNode::onSetAttribute(SVGAttribute attr, const SVGValue& v) { switch (attr) { case SVGAttribute::Transform: if (const auto* transform = v.as()) { @@ -62,14 +60,13 @@ void SkSVGTransformableNode::onSetAttribute(SVGAttribute attr, const SVGValue& v } } -#ifndef RENDER_SVG -void SkSVGTransformableNode::mapToParent(Path* path) const { +void SVGTransformableNode::mapToParent(Path* path) const { // transforms the path to parent node coordinates. path->transform(fTransform); } -void SkSVGTransformableNode::mapToParent(Rect* rect) const { +void SVGTransformableNode::mapToParent(Rect* rect) const { *rect = fTransform.mapRect(*rect); } -#endif + } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGUse.cpp b/src/svg/node/SVGUse.cpp index 0e22e282..2ce42a18 100644 --- a/src/svg/node/SVGUse.cpp +++ b/src/svg/node/SVGUse.cpp @@ -17,27 +17,25 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGUse.h" -#include #include "core/utils/MathExtra.h" +#include "svg/SVGAttributeParser.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/Path.h" #include "tgfx/core/Rect.h" -#include "tgfx/svg/SVGAttributeParser.h" -#include "tgfx/svg/SVGRenderContext.h" namespace tgfx { -SkSVGUse::SkSVGUse() : INHERITED(SVGTag::Use) { +SVGUse::SVGUse() : INHERITED(SVGTag::Use) { } -bool SkSVGUse::parseAndSetAttribute(const char* n, const char* v) { +bool SVGUse::parseAndSetAttribute(const std::string& n, const std::string& v) { return INHERITED::parseAndSetAttribute(n, v) || this->setX(SVGAttributeParser::parse("x", n, v)) || this->setY(SVGAttributeParser::parse("y", n, v)) || this->setHref(SVGAttributeParser::parse("xlink:href", n, v)); } -#ifndef RENDER_SVG -bool SkSVGUse::onPrepareToRender(SVGRenderContext* ctx) const { +bool SVGUse::onPrepareToRender(SVGRenderContext* ctx) const { if (Href.iri().empty() || !INHERITED::onPrepareToRender(ctx)) { return false; } @@ -48,12 +46,10 @@ bool SkSVGUse::onPrepareToRender(SVGRenderContext* ctx) const { ctx->canvas()->translate(X.value(), Y.value()); } - // TODO: width/height override for targets. - return true; } -void SkSVGUse::onRender(const SVGRenderContext& ctx) const { +void SVGUse::onRender(const SVGRenderContext& ctx) const { const auto ref = ctx.findNodeById(Href); if (!ref) { return; @@ -65,7 +61,7 @@ void SkSVGUse::onRender(const SVGRenderContext& ctx) const { ref->render(localContext); } -Path SkSVGUse::onAsPath(const SVGRenderContext& ctx) const { +Path SVGUse::onAsPath(const SVGRenderContext& ctx) const { const auto ref = ctx.findNodeById(Href); if (!ref) { return Path(); @@ -77,7 +73,7 @@ Path SkSVGUse::onAsPath(const SVGRenderContext& ctx) const { return ref->asPath(localContext); } -Rect SkSVGUse::onObjectBoundingBox(const SVGRenderContext& ctx) const { +Rect SVGUse::onObjectBoundingBox(const SVGRenderContext& ctx) const { const auto ref = ctx.findNodeById(Href); if (!ref) { return Rect::MakeEmpty(); @@ -93,5 +89,5 @@ Rect SkSVGUse::onObjectBoundingBox(const SVGRenderContext& ctx) const { return bounds; } -#endif + } // namespace tgfx \ No newline at end of file diff --git a/test/src/SVGRenderTest.cpp b/test/src/SVGRenderTest.cpp index 8461d9cc..37a269d3 100644 --- a/test/src/SVGRenderTest.cpp +++ b/test/src/SVGRenderTest.cpp @@ -25,7 +25,7 @@ TGFX_TEST(SVGRenderTest, PathSVG) { ASSERT_TRUE(data != nullptr); auto fontManager = std::make_shared(); ASSERT_TRUE(fontManager != nullptr); - auto SVGDom = SVGDOM::Builder().make(*data, fontManager); + auto SVGDom = SVGDOM::Make(data, fontManager); auto device = DevicePool::Make(); ASSERT_TRUE(device != nullptr); @@ -48,7 +48,7 @@ TGFX_TEST(SVGRenderTest, PNGImageSVG) { ASSERT_TRUE(data != nullptr); auto fontManager = std::make_shared(); ASSERT_TRUE(fontManager != nullptr); - auto SVGDom = SVGDOM::Builder().make(*data, fontManager); + auto SVGDom = SVGDOM::Make(data, fontManager); auto device = DevicePool::Make(); ASSERT_TRUE(device != nullptr); @@ -71,7 +71,7 @@ TGFX_TEST(SVGRenderTest, JPGImageSVG) { ASSERT_TRUE(data != nullptr); auto fontManager = std::make_shared(); ASSERT_TRUE(fontManager != nullptr); - auto SVGDom = SVGDOM::Builder().make(*data, fontManager); + auto SVGDom = SVGDOM::Make(data, fontManager); auto device = DevicePool::Make(); ASSERT_TRUE(device != nullptr); @@ -94,7 +94,7 @@ TGFX_TEST(SVGRenderTest, MaskSVG) { ASSERT_TRUE(data != nullptr); auto fontManager = std::make_shared(); ASSERT_TRUE(fontManager != nullptr); - auto SVGDom = SVGDOM::Builder().make(*data, fontManager); + auto SVGDom = SVGDOM::Make(data, fontManager); auto device = DevicePool::Make(); ASSERT_TRUE(device != nullptr); @@ -117,7 +117,7 @@ TGFX_TEST(SVGRenderTest, GradientSVG) { ASSERT_TRUE(data != nullptr); auto fontManager = std::make_shared(); ASSERT_TRUE(fontManager != nullptr); - auto SVGDom = SVGDOM::Builder().make(*data, fontManager); + auto SVGDom = SVGDOM::Make(data, fontManager); auto device = DevicePool::Make(); ASSERT_TRUE(device != nullptr); @@ -140,7 +140,7 @@ TGFX_TEST(SVGRenderTest, BlurSVG) { ASSERT_TRUE(data != nullptr); auto fontManager = std::make_shared(); ASSERT_TRUE(fontManager != nullptr); - auto SVGDom = SVGDOM::Builder().make(*data, fontManager); + auto SVGDom = SVGDOM::Make(data, fontManager); auto device = DevicePool::Make(); ASSERT_TRUE(device != nullptr); @@ -167,7 +167,7 @@ TGFX_TEST(SVGRenderTest, TextSVG) { ASSERT_TRUE(typeface != nullptr); fontManager->setDefaultTypeface(typeface); - auto SVGDom = SVGDOM::Builder().make(*data, fontManager); + auto SVGDom = SVGDOM::Make(data, fontManager); auto device = DevicePool::Make(); ASSERT_TRUE(device != nullptr); From a482430f8a9374f4b77546f242396a6c1e3cd578 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Tue, 17 Dec 2024 20:42:41 +0800 Subject: [PATCH 13/31] format --- include/tgfx/svg/SVGFontManager.h | 35 +- include/tgfx/svg/SVGTypes.h | 8 - include/tgfx/svg/SVGValue.h | 8 +- include/tgfx/svg/node/SVGFe.h | 7 +- include/tgfx/svg/node/SVGFeBlend.h | 1 - include/tgfx/svg/node/SVGNode.h | 54 +-- include/tgfx/svg/node/SVGPoly.h | 2 +- src/svg/SVGAttributeParser.cpp | 137 +++---- src/svg/SVGAttributeParser.h | 8 +- src/svg/SVGDOM.cpp | 368 +----------------- src/svg/{node => }/SVGFilterContext.cpp | 14 +- .../svg/node => src/svg}/SVGFilterContext.h | 2 +- src/svg/SVGLengthContext.cpp | 124 ++++++ src/svg/SVGLengthContext.h | 70 ++++ src/svg/SVGNodeConstructor.cpp | 365 +++++++++++++++++ src/svg/SVGNodeConstructor.h | 76 ++++ src/svg/SVGRenderContext.cpp | 164 +++----- src/svg/SVGRenderContext.h | 45 +-- src/svg/node/SVGCircle.cpp | 20 +- src/svg/node/SVGClipPath.cpp | 10 +- src/svg/node/SVGContainer.cpp | 12 +- src/svg/node/SVGEllipse.cpp | 21 +- src/svg/node/SVGFe.cpp | 56 +-- src/svg/node/SVGFeBlend.cpp | 16 +- src/svg/node/SVGFeComposite.cpp | 22 +- src/svg/node/SVGFeDisplacementMap.cpp | 27 +- src/svg/node/SVGFeGaussianBlur.cpp | 5 +- src/svg/node/SVGFeImage.cpp | 4 +- src/svg/node/SVGFeMerge.cpp | 6 +- src/svg/node/SVGFeMorphology.cpp | 5 +- src/svg/node/SVGFeOffset.cpp | 5 +- src/svg/node/SVGFeTurbulence.cpp | 5 +- src/svg/node/SVGFilter.cpp | 27 +- src/svg/node/SVGGradient.cpp | 45 +-- src/svg/node/SVGImage.cpp | 20 +- src/svg/node/SVGLine.cpp | 19 +- src/svg/node/SVGLinearGradient.cpp | 14 +- src/svg/node/SVGNode.cpp | 48 ++- src/svg/node/SVGPath.cpp | 6 +- src/svg/node/SVGPattern.cpp | 16 +- src/svg/node/SVGPoly.cpp | 24 +- src/svg/node/SVGRadialGradient.cpp | 14 +- src/svg/node/SVGRect.cpp | 50 +-- src/svg/node/SVGRectPriv.h | 30 -- src/svg/node/SVGSVG.cpp | 26 +- src/svg/node/SVGShape.cpp | 17 +- src/svg/node/SVGText.cpp | 8 +- src/svg/node/SVGTransformableNode.cpp | 16 +- src/svg/node/SVGUse.cpp | 32 +- 49 files changed, 1097 insertions(+), 1017 deletions(-) rename src/svg/{node => }/SVGFilterContext.cpp (93%) rename {include/tgfx/svg/node => src/svg}/SVGFilterContext.h (98%) create mode 100644 src/svg/SVGLengthContext.cpp create mode 100644 src/svg/SVGLengthContext.h create mode 100644 src/svg/SVGNodeConstructor.cpp create mode 100644 src/svg/SVGNodeConstructor.h delete mode 100644 src/svg/node/SVGRectPriv.h diff --git a/include/tgfx/svg/SVGFontManager.h b/include/tgfx/svg/SVGFontManager.h index b906d5c9..4e79be9a 100644 --- a/include/tgfx/svg/SVGFontManager.h +++ b/include/tgfx/svg/SVGFontManager.h @@ -29,12 +29,13 @@ class FontStyle { public: struct Hash { std::size_t operator()(const FontStyle& style) const { - return std::hash()(style.weight()) ^ std::hash()(style.width()) ^ - std::hash()(style.slant()); + return std::hash()(static_cast(style.weight())) ^ + std::hash()(static_cast(style.width())) ^ + std::hash()(static_cast(style.slant())); } }; - enum Weight { + enum class Weight { Invisible_Weight = 0, Thin_Weight = 100, ExtraLight_Weight = 200, @@ -48,7 +49,7 @@ class FontStyle { ExtraBlack_Weight = 1000, }; - enum Width { + enum class Width { UltraCondensed_Width = 1, ExtraCondensed_Width = 2, Condensed_Width = 3, @@ -60,28 +61,30 @@ class FontStyle { UltraExpanded_Width = 9, }; - enum Slant { + enum class Slant { Upright_Slant, Italic_Slant, Oblique_Slant, }; constexpr FontStyle(Weight weight, Width width, Slant slant) - : value(weight + (width << 16) + (slant << 24)) { + : value(static_cast(weight) + (static_cast(width) << 16) + + (static_cast(slant) << 24)) { } - constexpr FontStyle() : FontStyle{Normal_Weight, Normal_Width, Upright_Slant} { + constexpr FontStyle() + : FontStyle{Weight::Normal_Weight, Width::Normal_Width, Slant::Upright_Slant} { } bool operator==(const FontStyle& rhs) const { return value == rhs.value; } - int weight() const { - return value & 0xFFFF; + Weight weight() const { + return static_cast(value & 0xFFFF); } - int width() const { - return (value >> 16) & 0xFF; + Width width() const { + return static_cast((value >> 16) & 0xFF); } Slant slant() const { return static_cast((value >> 24) & 0xFF); @@ -96,16 +99,26 @@ class SVGFontManager { SVGFontManager() = default; ~SVGFontManager() = default; + /** + * Set the Default Typeface object + * @param typeface + * @return true + * @return false + */ bool setDefaultTypeface(const std::shared_ptr& typeface); void addFontStyle(const std::string& fontFamily, FontStyle style); + void setTypeface(const std::string& fontFamily, FontStyle style, const std::shared_ptr& typeface); + std::vector getFontFamilies() const; + std::vector getFontStyles(const std::string& fontFamily) const; std::shared_ptr getTypefaceForConfig(const std::string& fontFamily, FontStyle style) const; + std::shared_ptr getTypefaceForRender(const std::string& fontFamily, FontStyle style) const; diff --git a/include/tgfx/svg/SVGTypes.h b/include/tgfx/svg/SVGTypes.h index 2325ed60..daa71b97 100644 --- a/include/tgfx/svg/SVGTypes.h +++ b/include/tgfx/svg/SVGTypes.h @@ -101,26 +101,18 @@ class SVGProperty { } T* operator->() { - // ASSERT(fState == SVGPropertyState::kValue); - // ASSERT(fValue.has_value()); return &_value.value(); } const T* operator->() const { - // ASSERT(fState == SVGPropertyState::kValue); - // ASSERT(fValue.has_value()); return &_value.value(); } T& operator*() { - // ASSERT(fState == SVGPropertyState::kValue); - // ASSERT(fValue.has_value()); return *_value; } const T& operator*() const { - // ASSERT(fState == SVGPropertyState::kValue); - // ASSERT(fValue.has_value()); return *_value; } diff --git a/include/tgfx/svg/SVGValue.h b/include/tgfx/svg/SVGValue.h index 794fa246..fc2376e0 100644 --- a/include/tgfx/svg/SVGValue.h +++ b/include/tgfx/svg/SVGValue.h @@ -59,18 +59,18 @@ class SVGWrapperValue final : public SVGValue { public: static constexpr Type _type = ValueType; - explicit SVGWrapperValue(const T& v) : INHERITED(ValueType), _wrappedValue(v) { + explicit SVGWrapperValue(const T& v) : INHERITED(ValueType), wrappedValue(v) { } // NOLINTBEGIN // Allow implicit conversion to the wrapped type operator. operator const T&() const { - return _wrappedValue; + return wrappedValue; } // NOLINTEND const T* operator->() const { - return &_wrappedValue; + return &wrappedValue; } // Stack-only @@ -78,7 +78,7 @@ class SVGWrapperValue final : public SVGValue { void* operator new(size_t, void*) = delete; private: - const T& _wrappedValue; + const T& wrappedValue; using INHERITED = SVGValue; }; diff --git a/include/tgfx/svg/node/SVGFe.h b/include/tgfx/svg/node/SVGFe.h index f97c322a..48c03fc2 100644 --- a/include/tgfx/svg/node/SVGFe.h +++ b/include/tgfx/svg/node/SVGFe.h @@ -23,15 +23,12 @@ #include "tgfx/core/ImageFilter.h" #include "tgfx/core/Rect.h" #include "tgfx/svg/SVGTypes.h" -#include "tgfx/svg/node/SVGFilterContext.h" #include "tgfx/svg/node/SVGHiddenContainer.h" #include "tgfx/svg/node/SVGNode.h" -class SVGFilterContext; -class SVGRenderContext; - namespace tgfx { +class SVGFilterContext; class SVGFe : public SVGHiddenContainer { public: static bool IsFilterEffect(const std::shared_ptr& node) { @@ -56,7 +53,7 @@ class SVGFe : public SVGHiddenContainer { } } - std::shared_ptr makeImageFilter(const SVGRenderContext& ctx, + std::shared_ptr makeImageFilter(const SVGRenderContext& context, const SVGFilterContext& filterContext) const; // https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveSubRegion diff --git a/include/tgfx/svg/node/SVGFeBlend.h b/include/tgfx/svg/node/SVGFeBlend.h index f19adcc0..03c3aadf 100644 --- a/include/tgfx/svg/node/SVGFeBlend.h +++ b/include/tgfx/svg/node/SVGFeBlend.h @@ -24,7 +24,6 @@ #include "tgfx/svg/node/SVGFe.h" #include "tgfx/svg/node/SVGNode.h" -class SVGFilterContext; class SVGRenderContext; namespace tgfx { diff --git a/include/tgfx/svg/node/SVGNode.h b/include/tgfx/svg/node/SVGNode.h index 83e1b204..1a01ea9f 100644 --- a/include/tgfx/svg/node/SVGNode.h +++ b/include/tgfx/svg/node/SVGNode.h @@ -91,10 +91,10 @@ enum class SVGTag { \ public: \ const SVGProperty& get##attr_name() const { \ - return _presentationAttributes.attr_name; \ + return presentationAttributes.attr_name; \ } \ void set##attr_name(const SVGProperty& v) { \ - auto* dest = &_presentationAttributes.attr_name; \ + auto* dest = &presentationAttributes.attr_name; \ if (!dest->isInheritable() || v.isValue()) { \ /* TODO: If dest is not inheritable, handle v == "inherit" */ \ *dest = v; \ @@ -103,7 +103,7 @@ enum class SVGTag { } \ } \ void set##attr_name(SVGProperty&& v) { \ - auto* dest = &_presentationAttributes.attr_name; \ + auto* dest = &presentationAttributes.attr_name; \ if (!dest->isInheritable() || v.isValue()) { \ /* TODO: If dest is not inheritable, handle v == "inherit" */ \ *dest = std::move(v); \ @@ -120,25 +120,6 @@ class SVGNode { return _tag; } - const SVGPresentationAttributes* presentationAttributes() const { - return &_presentationAttributes; - } - - virtual void appendChild(std::shared_ptr) = 0; - - virtual bool hasChildren() const { - return false; - } - - void render(const SVGRenderContext&) const; - bool asPaint(const SVGRenderContext&, Paint*) const; - Path asPath(const SVGRenderContext&) const; - Rect objectBoundingBox(const SVGRenderContext&) const; - - void setAttribute(SVGAttribute, const SVGValue&); - bool setAttribute(const std::string& attributeName, const std::string& attributeValue); - virtual bool parseAndSetAttribute(const std::string& name, const std::string& value); - SVG_PRES_ATTR(ClipRule, SVGFillRule, true) SVG_PRES_ATTR(Color, SVGColorType, true) SVG_PRES_ATTR(ColorInterpolation, SVGColorspace, true) @@ -173,9 +154,29 @@ class SVGNode { SVG_PRES_ATTR(FloodOpacity, SVGNumberType, false) SVG_PRES_ATTR(LightingColor, SVGColor, false) + virtual bool hasChildren() const { + return false; + } + protected: explicit SVGNode(SVGTag); + virtual void appendChild(std::shared_ptr) = 0; + + void render(const SVGRenderContext&) const; + + bool asPaint(const SVGRenderContext&, Paint*) const; + + Path asPath(const SVGRenderContext&) const; + + Rect objectBoundingBox(const SVGRenderContext&) const; + + void setAttribute(SVGAttribute, const SVGValue&); + + bool setAttribute(const std::string& attributeName, const std::string& attributeValue); + + virtual bool parseAndSetAttribute(const std::string& name, const std::string& value); + static Matrix ComputeViewboxMatrix(const Rect&, const Rect&, SVGPreserveAspectRatio); virtual void onSetAttribute(SVGAttribute, const SVGValue&) { @@ -203,8 +204,15 @@ class SVGNode { private: SVGTag _tag; + SVGPresentationAttributes presentationAttributes; - SVGPresentationAttributes _presentationAttributes; + friend class SVGDOM; + friend class SVGMask; + friend class SVGUse; + friend class SVGSVG; + friend class SVGContainer; + friend class SVGNodeConstructor; + friend class SVGRenderContext; }; //NOLINTBEGIN diff --git a/include/tgfx/svg/node/SVGPoly.h b/include/tgfx/svg/node/SVGPoly.h index 26d6af92..db09ca81 100644 --- a/include/tgfx/svg/node/SVGPoly.h +++ b/include/tgfx/svg/node/SVGPoly.h @@ -53,7 +53,7 @@ class SVGPoly final : public SVGShape { private: SVGPoly(SVGTag); - mutable Path fPath; // mutated in onDraw(), to apply inherited fill types. + mutable Path path; // mutated in onDraw(), to apply inherited fill types. using INHERITED = SVGShape; }; diff --git a/src/svg/SVGAttributeParser.cpp b/src/svg/SVGAttributeParser.cpp index 4b50ed60..0a15a2e1 100644 --- a/src/svg/SVGAttributeParser.cpp +++ b/src/svg/SVGAttributeParser.cpp @@ -35,7 +35,7 @@ namespace { inline bool is_between(char c, char min, char max) { ASSERT(min <= max); - return (unsigned)(c - min) <= (unsigned)(max - min); + return static_cast(c - min) <= static_cast(max - min); } inline bool is_ws(char c) { @@ -59,22 +59,22 @@ inline bool is_hex(char c) { namespace tgfx { SVGAttributeParser::SVGAttributeParser(std::string attributeString) - : fCurPos(attributeString.data()), fEndPos(fCurPos + attributeString.size()) { + : currentPos(attributeString.data()), endPos(currentPos + attributeString.size()) { } template inline bool SVGAttributeParser::advanceWhile(F f) { - const auto* initial = fCurPos; - while (fCurPos < fEndPos && f(*fCurPos)) { - fCurPos++; + const auto* initial = currentPos; + while (currentPos < endPos && f(*currentPos)) { + currentPos++; } - return fCurPos != initial; + return currentPos != initial; } bool SVGAttributeParser::matchStringToken(const char* token, const char** newPos) const { - const char* c = fCurPos; + const char* c = currentPos; - while (c < fEndPos && *token && *c == *token) { + while (c < endPos && *token && *c == *token) { c++; token++; } @@ -91,7 +91,7 @@ bool SVGAttributeParser::matchStringToken(const char* token, const char** newPos } bool SVGAttributeParser::parseEOSToken() { - return fCurPos == fEndPos; + return currentPos == endPos; } bool SVGAttributeParser::parseSepToken() { @@ -114,32 +114,32 @@ bool SVGAttributeParser::parseExpectedStringToken(const char* expected) { return false; } - fCurPos = newPos; + currentPos = newPos; return true; } bool SVGAttributeParser::parseScalarToken(float* res) { - if (const char* next = SVGParse::FindScalar(fCurPos, res)) { - fCurPos = next; + if (const char* next = SVGParse::FindScalar(currentPos, res)) { + currentPos = next; return true; } return false; } bool SVGAttributeParser::parseInt32Token(int32_t* res) { - if (const char* next = SVGParse::FindS32(fCurPos, res)) { - fCurPos = next; + if (const char* next = SVGParse::FindS32(currentPos, res)) { + currentPos = next; return true; } return false; } bool SVGAttributeParser::matchHexToken(const char** newPos) const { - *newPos = fCurPos; - while (*newPos < fEndPos && is_hex(**newPos)) { + *newPos = currentPos; + while (*newPos < endPos && is_hex(**newPos)) { ++*newPos; } - return *newPos != fCurPos; + return *newPos != currentPos; } bool SVGAttributeParser::parseEscape(Unichar* c) { @@ -151,12 +151,12 @@ bool SVGAttributeParser::parseEscape(Unichar* c) { } const char* hexEnd; if (this->matchHexToken(&hexEnd)) { - if (hexEnd - fCurPos > 6) { - hexEnd = fCurPos + 6; + if (hexEnd - currentPos > 6) { + hexEnd = currentPos + 6; } char hexString[7]; - auto hexSize = static_cast(hexEnd - fCurPos); - memcpy(hexString, fCurPos, hexSize); + auto hexSize = static_cast(hexEnd - currentPos); + memcpy(hexString, currentPos, hexSize); hexString[hexSize] = '\0'; uint32_t cp; const char* hexFound = SVGParse::FindHex(hexString, &cp); @@ -164,13 +164,13 @@ bool SVGAttributeParser::parseEscape(Unichar* c) { cp = 0xFFFD; } *c = static_cast(cp); - fCurPos = hexEnd; + currentPos = hexEnd; this->parseWSToken(); - } else if (this->parseEOSToken() || is_nl(*fCurPos)) { + } else if (this->parseEOSToken() || is_nl(*currentPos)) { *c = 0xFFFD; return false; } else { - if (*c = UTF::NextUTF8(&fCurPos, fEndPos); *c < 0) { + if (*c = UTF::NextUTF8(¤tPos, endPos); *c < 0) { return false; } } @@ -194,7 +194,7 @@ bool SVGAttributeParser::parseIdentToken(std::string* ident) { if (this->parseEscape(&c)) { // ident->appendUnichar(c); } else { - if (c = UTF::NextUTF8(&fCurPos, fEndPos); c < 0) { + if (c = UTF::NextUTF8(¤tPos, endPos); c < 0) { return false; } if ((c < 'a' || 'z' < c) && (c < 'A' || 'Z' < c) && (c != '_') && @@ -204,13 +204,13 @@ bool SVGAttributeParser::parseIdentToken(std::string* ident) { ident->append(UTF::ToUTF8(c)); } } - while (fCurPos < fEndPos) { + while (currentPos < endPos) { if (this->parseEscape(&c)) { ident->append(UTF::ToUTF8(c)); continue; } - const char* next = fCurPos; - if (c = UTF::NextUTF8(&next, fEndPos); c < 0) { + const char* next = currentPos; + if (c = UTF::NextUTF8(&next, endPos); c < 0) { break; } if ((c < 'a' || 'z' < c) && (c < 'A' || 'Z' < c) && (c < '0' || '9' < c) && (c != '_') && @@ -218,7 +218,7 @@ bool SVGAttributeParser::parseIdentToken(std::string* ident) { break; } ident->append(UTF::ToUTF8(c)); - fCurPos = next; + currentPos = next; } restoreCurPos.clear(); @@ -274,7 +274,7 @@ bool SVGAttributeParser::parseHexColorToken(Color* c) { } uint32_t v; - std::string hexString(fCurPos, static_cast(hexEnd - fCurPos)); + std::string hexString(currentPos, static_cast(hexEnd - currentPos)); SVGParse::FindHex(hexString.c_str(), &v); switch (hexString.size()) { @@ -291,14 +291,14 @@ bool SVGAttributeParser::parseHexColorToken(Color* c) { } *c = SVGParse::Uint32ToColor(v | 0xff000000); - fCurPos = hexEnd; + currentPos = hexEnd; restoreCurPos.clear(); return true; } bool SVGAttributeParser::parseColorComponentIntegralToken(int32_t* c) { - const char* p = SVGParse::FindS32(fCurPos, c); + const char* p = SVGParse::FindS32(currentPos, c); if (!p || *p == '.') { // No value parsed, or fractional value. return false; @@ -310,13 +310,13 @@ bool SVGAttributeParser::parseColorComponentIntegralToken(int32_t* c) { p++; } - fCurPos = p; + currentPos = p; return true; } bool SVGAttributeParser::parseColorComponentFractionalToken(int32_t* c) { float s; - const char* p = SVGParse::FindScalar(fCurPos, &s); + const char* p = SVGParse::FindScalar(currentPos, &s); if (!p || *p != '%') { // Floating point must be a percentage (CSS2 rgb-percent syntax). return false; @@ -325,16 +325,16 @@ bool SVGAttributeParser::parseColorComponentFractionalToken(int32_t* c) { *c = static_cast(std::round(s * 255.0f / 100)); *c = std::clamp(*c, 0, 255); - fCurPos = p; + currentPos = p; return true; } bool SVGAttributeParser::parseColorComponentScalarToken(int32_t* c) { float s; - if (const char* p = SVGParse::FindScalar(fCurPos, &s)) { + if (const char* p = SVGParse::FindScalar(currentPos, &s)) { *c = static_cast(std::round(s * 255.0f)); *c = std::clamp(*c, 0, 255); - fCurPos = p; + currentPos = p; return true; } return false; @@ -479,11 +479,11 @@ bool SVGAttributeParser::parse(SVGIRI* iri) { iriType = SVGIRI::Type::Nonlocal; } - const auto* start = fCurPos; + const auto* start = currentPos; if (!this->advanceWhile([](char c) -> bool { return c != ')'; })) { return false; } - *iri = SVGIRI(iriType, std::string(start, static_cast(fCurPos - start))); + *iri = SVGIRI(iriType, std::string(start, static_cast(currentPos - start))); return true; } @@ -507,8 +507,8 @@ bool SVGAttributeParser::parse(SVGStringType* result) { if (this->parseEOSToken()) { return false; } - *result = SVGStringType(fCurPos); - fCurPos += result->size(); + *result = SVGStringType(currentPos); + currentPos += result->size(); return this->parseEOSToken(); } @@ -578,7 +578,7 @@ bool SVGAttributeParser::parseViewBox(SVGViewBoxType* vb) { this->parseSepToken() && this->parseScalarToken(&w) && this->parseSepToken() && this->parseScalarToken(&h)) { - *vb = SVGViewBoxType(Rect::MakeXYWH(x, y, w, h)); + *vb = static_cast(Rect::MakeXYWH(x, y, w, h)); parsedValue = true; // consume trailing whitespace this->parseWSToken(); @@ -782,14 +782,14 @@ bool SVGAttributeParser::parse(SVGPaint* paint) { // https://www.w3.org/TR/SVG11/masking.html#MaskProperty // https://www.w3.org/TR/SVG11/filters.html#FilterProperty template <> -bool SVGAttributeParser::parse(SVGFuncIRI* firi) { +bool SVGAttributeParser::parse(SVGFuncIRI* funcIRI) { SVGStringType iri; bool parsedValue = false; if (this->parseExpectedStringToken("none")) { - *firi = SVGFuncIRI(); + *funcIRI = SVGFuncIRI(); parsedValue = true; - } else if (this->parseFuncIRI(firi)) { + } else if (this->parseFuncIRI(funcIRI)) { parsedValue = true; } @@ -809,9 +809,9 @@ bool SVGAttributeParser::parse(SVGLineCap* cap) { }; bool parsedValue = false; - for (size_t i = 0; i < std::size(gCapInfo); ++i) { - if (this->parseExpectedStringToken(gCapInfo[i].fName)) { - *cap = SVGLineCap(gCapInfo[i].fType); + for (auto i : gCapInfo) { + if (this->parseExpectedStringToken(i.fName)) { + *cap = (i.fType); parsedValue = true; break; } @@ -881,7 +881,8 @@ bool SVGAttributeParser::parse(SVGPointsType* points) { break; } - float x, y; + float x; + float y; if (!this->parseScalarToken(&x)) { break; } @@ -890,7 +891,7 @@ bool SVGAttributeParser::parse(SVGPointsType* points) { // coordinate-pair: // coordinate comma-wsp coordinate // | coordinate negative-coordinate - if (!this->parseCommaWspToken() && !this->parseEOSToken() && *fCurPos != '-') { + if (!this->parseCommaWspToken() && !this->parseEOSToken() && *currentPos != '-') { break; } @@ -1000,11 +1001,11 @@ bool SVGAttributeParser::parse(SVGFontFamily* family) { } else { // The spec allows specifying a comma-separated list for explicit fallback order. // For now, we only use the first entry and rely on the font manager to handle fallback. - const auto* comma = strchr(fCurPos, ','); - auto family_name = - comma ? std::string(fCurPos, static_cast(comma - fCurPos)) : std::string(fCurPos); - *family = SVGFontFamily(family_name.c_str()); - fCurPos += strlen(fCurPos); + const auto* comma = strchr(currentPos, ','); + auto family_name = comma ? std::string(currentPos, static_cast(comma - currentPos)) + : std::string(currentPos); + *family = SVGFontFamily(family_name); + currentPos += strlen(currentPos); parsedValue = true; } @@ -1032,7 +1033,7 @@ bool SVGAttributeParser::parse(SVGFontSize* size) { // https://www.w3.org/TR/SVG11/text.html#FontStyleProperty template <> bool SVGAttributeParser::parse(SVGFontStyle* style) { - static constexpr std::tuple gStyleMap[] = { + static constexpr std::tuple styleMap[] = { {"normal", SVGFontStyle::Type::Normal}, {"italic", SVGFontStyle::Type::Italic}, {"oblique", SVGFontStyle::Type::Oblique}, @@ -1042,7 +1043,7 @@ bool SVGAttributeParser::parse(SVGFontStyle* style) { bool parsedValue = false; SVGFontStyle::Type type; - if (this->parseEnumMap(gStyleMap, &type)) { + if (this->parseEnumMap(styleMap, &type)) { *style = SVGFontStyle(type); parsedValue = true; } @@ -1053,7 +1054,7 @@ bool SVGAttributeParser::parse(SVGFontStyle* style) { // https://www.w3.org/TR/SVG11/text.html#FontWeightProperty template <> bool SVGAttributeParser::parse(SVGFontWeight* weight) { - static constexpr std::tuple gWeightMap[] = { + static constexpr std::tuple weightMap[] = { {"normal", SVGFontWeight::Type::Normal}, {"bold", SVGFontWeight::Type::Bold}, {"bolder", SVGFontWeight::Type::Bolder}, {"lighter", SVGFontWeight::Type::Lighter}, {"100", SVGFontWeight::Type::W100}, {"200", SVGFontWeight::Type::W200}, @@ -1066,7 +1067,7 @@ bool SVGAttributeParser::parse(SVGFontWeight* weight) { bool parsedValue = false; SVGFontWeight::Type type; - if (this->parseEnumMap(gWeightMap, &type)) { + if (this->parseEnumMap(weightMap, &type)) { *weight = SVGFontWeight(type); parsedValue = true; } @@ -1077,7 +1078,7 @@ bool SVGAttributeParser::parse(SVGFontWeight* weight) { // https://www.w3.org/TR/SVG11/text.html#TextAnchorProperty template <> bool SVGAttributeParser::parse(SVGTextAnchor* anchor) { - static constexpr std::tuple gAnchorMap[] = { + static constexpr std::tuple anchorMap[] = { {"start", SVGTextAnchor::Type::Start}, {"middle", SVGTextAnchor::Type::Middle}, {"end", SVGTextAnchor::Type::End}, @@ -1087,7 +1088,7 @@ bool SVGAttributeParser::parse(SVGTextAnchor* anchor) { bool parsedValue = false; SVGTextAnchor::Type type; - if (this->parseEnumMap(gAnchorMap, &type)) { + if (this->parseEnumMap(anchorMap, &type)) { *anchor = SVGTextAnchor(type); parsedValue = true; } @@ -1097,7 +1098,7 @@ bool SVGAttributeParser::parse(SVGTextAnchor* anchor) { // https://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute bool SVGAttributeParser::parsePreserveAspectRatio(SVGPreserveAspectRatio* par) { - static constexpr std::tuple gAlignMap[] = { + static constexpr std::tuple alignMap[] = { {"none", SVGPreserveAspectRatio::None}, {"xMinYMin", SVGPreserveAspectRatio::XMinYMin}, {"xMidYMin", SVGPreserveAspectRatio::XMidYMin}, @@ -1110,7 +1111,7 @@ bool SVGAttributeParser::parsePreserveAspectRatio(SVGPreserveAspectRatio* par) { {"xMaxYMax", SVGPreserveAspectRatio::XMaxYMax}, }; - static constexpr std::tuple gScaleMap[] = { + static constexpr std::tuple scaleMap[] = { {"meet", SVGPreserveAspectRatio::Meet}, {"slice", SVGPreserveAspectRatio::Slice}, }; @@ -1121,12 +1122,12 @@ bool SVGAttributeParser::parsePreserveAspectRatio(SVGPreserveAspectRatio* par) { this->parseExpectedStringToken("defer"); this->parseWSToken(); - if (this->parseEnumMap(gAlignMap, &par->align)) { + if (this->parseEnumMap(alignMap, &par->align)) { parsedValue = true; // optional scaling selector this->parseWSToken(); - this->parseEnumMap(gScaleMap, &par->scale); + this->parseEnumMap(scaleMap, &par->scale); } return parsedValue && this->parseEOSToken(); @@ -1168,13 +1169,13 @@ bool SVGAttributeParser::parse(std::vector* numbers) { template <> bool SVGAttributeParser::parse(SVGColorspace* colorspace) { - static constexpr std::tuple gColorspaceMap[] = { + static constexpr std::tuple colorspaceMap[] = { {"auto", SVGColorspace::Auto}, {"sRGB", SVGColorspace::SRGB}, {"linearRGB", SVGColorspace::LinearRGB}, }; - return this->parseEnumMap(gColorspaceMap, colorspace) && this->parseEOSToken(); + return this->parseEnumMap(colorspaceMap, colorspace) && this->parseEOSToken(); } // https://www.w3.org/TR/SVG11/painting.html#DisplayProperty @@ -1191,7 +1192,7 @@ bool SVGAttributeParser::parse(SVGDisplay* display) { bool parsedValue = false; for (const auto& parseInfo : gDisplayInfo) { if (this->parseExpectedStringToken(parseInfo.fName)) { - *display = SVGDisplay(parseInfo.fType); + *display = parseInfo.fType; parsedValue = true; break; } diff --git a/src/svg/SVGAttributeParser.h b/src/svg/SVGAttributeParser.h index 8727fcf2..9e7a81ca 100644 --- a/src/svg/SVGAttributeParser.h +++ b/src/svg/SVGAttributeParser.h @@ -93,12 +93,12 @@ class SVGAttributeParser { private: class RestoreCurPos { public: - explicit RestoreCurPos(SVGAttributeParser* self) : self(self), currentPos(self->fCurPos) { + explicit RestoreCurPos(SVGAttributeParser* self) : self(self), currentPos(self->currentPos) { } ~RestoreCurPos() { if (self) { - self->fCurPos = this->currentPos; + self->currentPos = this->currentPos; } } @@ -175,7 +175,7 @@ class SVGAttributeParser { } // The current position in the input string. - const char* fCurPos; - const char* fEndPos; + const char* currentPos; + const char* endPos; }; } // namespace tgfx diff --git a/src/svg/SVGDOM.cpp b/src/svg/SVGDOM.cpp index 005ad546..0ee2cbfb 100644 --- a/src/svg/SVGDOM.cpp +++ b/src/svg/SVGDOM.cpp @@ -27,6 +27,7 @@ #include #include "core/utils/Log.h" #include "svg/SVGAttributeParser.h" +#include "svg/SVGNodeConstructor.h" #include "svg/SVGRenderContext.h" #include "tgfx/core/Canvas.h" #include "tgfx/core/Data.h" @@ -35,361 +36,9 @@ #include "tgfx/svg/SVGAttribute.h" #include "tgfx/svg/SVGTypes.h" #include "tgfx/svg/SVGValue.h" -#include "tgfx/svg/node/SVGCircle.h" -#include "tgfx/svg/node/SVGClipPath.h" -#include "tgfx/svg/node/SVGDefs.h" -#include "tgfx/svg/node/SVGEllipse.h" -#include "tgfx/svg/node/SVGFeBlend.h" -#include "tgfx/svg/node/SVGFeColorMatrix.h" -#include "tgfx/svg/node/SVGFeComponentTransfer.h" -#include "tgfx/svg/node/SVGFeComposite.h" -#include "tgfx/svg/node/SVGFeDisplacementMap.h" -#include "tgfx/svg/node/SVGFeFlood.h" -#include "tgfx/svg/node/SVGFeGaussianBlur.h" -#include "tgfx/svg/node/SVGFeImage.h" -#include "tgfx/svg/node/SVGFeLightSource.h" -#include "tgfx/svg/node/SVGFeLighting.h" -#include "tgfx/svg/node/SVGFeMerge.h" -#include "tgfx/svg/node/SVGFeMorphology.h" -#include "tgfx/svg/node/SVGFeOffset.h" -#include "tgfx/svg/node/SVGFeTurbulence.h" -#include "tgfx/svg/node/SVGFilter.h" -#include "tgfx/svg/node/SVGG.h" -#include "tgfx/svg/node/SVGImage.h" -#include "tgfx/svg/node/SVGLine.h" -#include "tgfx/svg/node/SVGLinearGradient.h" -#include "tgfx/svg/node/SVGMask.h" -#include "tgfx/svg/node/SVGNode.h" -#include "tgfx/svg/node/SVGPath.h" -#include "tgfx/svg/node/SVGPattern.h" -#include "tgfx/svg/node/SVGPoly.h" -#include "tgfx/svg/node/SVGRadialGradient.h" -#include "tgfx/svg/node/SVGRect.h" -#include "tgfx/svg/node/SVGSVG.h" -#include "tgfx/svg/node/SVGStop.h" -#include "tgfx/svg/node/SVGText.h" -#include "tgfx/svg/node/SVGUse.h" #include "tgfx/svg/xml/XMLDOM.h" namespace tgfx { -namespace { - -bool SetIRIAttribute(SVGNode& node, SVGAttribute attr, const std::string& stringValue) { - auto parseResult = SVGAttributeParser::parse(stringValue); - if (!parseResult.has_value()) { - return false; - } - - node.setAttribute(attr, SVGStringValue(parseResult->iri())); - return true; -} - -bool SetStringAttribute(SVGNode& node, SVGAttribute attr, const std::string& stringValue) { - SVGStringType strType = SVGStringType(stringValue); - node.setAttribute(attr, SVGStringValue(strType)); - return true; -} - -bool SetTransformAttribute(SVGNode& node, SVGAttribute attr, const std::string& stringValue) { - auto parseResult = SVGAttributeParser::parse(stringValue); - if (!parseResult.has_value()) { - return false; - } - - node.setAttribute(attr, SVGTransformValue(*parseResult)); - return true; -} - -bool SetLengthAttribute(SVGNode& node, SVGAttribute attr, const std::string& stringValue) { - auto parseResult = SVGAttributeParser::parse(stringValue); - if (!parseResult.has_value()) { - return false; - } - - node.setAttribute(attr, SVGLengthValue(*parseResult)); - return true; -} - -bool SetViewBoxAttribute(SVGNode& node, SVGAttribute attr, const std::string& stringValue) { - SVGViewBoxType viewBox; - SVGAttributeParser parser(stringValue); - if (!parser.parseViewBox(&viewBox)) { - return false; - } - - node.setAttribute(attr, SVGViewBoxValue(viewBox)); - return true; -} - -bool SetObjectBoundingBoxUnitsAttribute(SVGNode& node, SVGAttribute attr, - const std::string& stringValue) { - auto parseResult = SVGAttributeParser::parse(stringValue); - if (!parseResult.has_value()) { - return false; - } - - node.setAttribute(attr, SVGObjectBoundingBoxUnitsValue(*parseResult)); - return true; -} - -bool SetPreserveAspectRatioAttribute(SVGNode& node, SVGAttribute attr, - const std::string& stringValue) { - SVGPreserveAspectRatio par; - SVGAttributeParser parser(stringValue); - if (!parser.parsePreserveAspectRatio(&par)) { - return false; - } - - node.setAttribute(attr, SVGPreserveAspectRatioValue(par)); - return true; -} - -std::string TrimmedString(const char* first, const char* last) { - ASSERT(first); - ASSERT(last); - ASSERT(first <= last); - - while (first <= last && *first <= ' ') { - first++; - } - while (first <= last && *last <= ' ') { - last--; - } - - ASSERT(last - first + 1 >= 0); - return std::string(first, static_cast(last - first + 1)); -} - -// Breaks a "foo: bar; baz: ..." string into key:value pairs. -class StyleIterator { - public: - explicit StyleIterator(const std::string& str) : pos(str.data()) { - } - - std::tuple next() { - std::string name; - std::string value; - - if (pos) { - const char* sep = this->nextSeparator(); - ASSERT(*sep == ';'); - ASSERT(*sep == '\0') - - const char* valueSep = strchr(pos, ':'); - if (valueSep && valueSep < sep) { - name = TrimmedString(pos, valueSep - 1); - value = TrimmedString(valueSep + 1, sep - 1); - } - - pos = *sep ? sep + 1 : nullptr; - } - - return std::make_tuple(name, value); - } - - private: - const char* nextSeparator() const { - const char* sep = pos; - while (*sep != ';' && *sep != '\0') { - sep++; - } - return sep; - } - - const char* pos; -}; - -bool SetAttribute(SVGNode& node, const std::string& name, const std::string& value); - -bool SetStyleAttributes(SVGNode& node, SVGAttribute, const std::string& stringValue) { - - std::string name; - std::string value; - StyleIterator iter(stringValue); - for (;;) { - std::tie(name, value) = iter.next(); - if (name.empty()) { - break; - } - SetAttribute(node, name, value); - } - - return true; -} - -using AttributeSetter = std::function; -struct AttrParseInfo { - SVGAttribute attribute; - AttributeSetter setter; -}; - -std::unordered_map gAttributeParseInfo = { - {"cx", {SVGAttribute::Cx, SetLengthAttribute}}, - {"cy", {SVGAttribute::Cy, SetLengthAttribute}}, - {"filterUnits", {SVGAttribute::FilterUnits, SetObjectBoundingBoxUnitsAttribute}}, - // focal point x & y - {"fx", {SVGAttribute::Fx, SetLengthAttribute}}, - {"fy", {SVGAttribute::Fy, SetLengthAttribute}}, - {"height", {SVGAttribute::Height, SetLengthAttribute}}, - {"preserveAspectRatio", {SVGAttribute::PreserveAspectRatio, SetPreserveAspectRatioAttribute}}, - {"r", {SVGAttribute::R, SetLengthAttribute}}, - {"rx", {SVGAttribute::Rx, SetLengthAttribute}}, - {"ry", {SVGAttribute::Ry, SetLengthAttribute}}, - {"style", {SVGAttribute::Unknown, SetStyleAttributes}}, - {"text", {SVGAttribute::Text, SetStringAttribute}}, - {"transform", {SVGAttribute::Transform, SetTransformAttribute}}, - {"viewBox", {SVGAttribute::ViewBox, SetViewBoxAttribute}}, - {"width", {SVGAttribute::Width, SetLengthAttribute}}, - {"x", {SVGAttribute::X, SetLengthAttribute}}, - {"x1", {SVGAttribute::X1, SetLengthAttribute}}, - {"x2", {SVGAttribute::X2, SetLengthAttribute}}, - {"xlink:href", {SVGAttribute::Href, SetIRIAttribute}}, - {"y", {SVGAttribute::Y, SetLengthAttribute}}, - {"y1", {SVGAttribute::Y1, SetLengthAttribute}}, - {"y2", {SVGAttribute::Y2, SetLengthAttribute}}, -}; - -using ElementFactory = std::function()>; -std::unordered_map ElementFactories = { - {"a", []() -> std::shared_ptr { return SVGG::Make(); }}, - {"circle", []() -> std::shared_ptr { return SVGCircle::Make(); }}, - {"clipPath", []() -> std::shared_ptr { return SVGClipPath::Make(); }}, - {"defs", []() -> std::shared_ptr { return SVGDefs::Make(); }}, - {"ellipse", []() -> std::shared_ptr { return SVGEllipse::Make(); }}, - {"feBlend", []() -> std::shared_ptr { return SVGFeBlend::Make(); }}, - {"feColorMatrix", []() -> std::shared_ptr { return SVGFeColorMatrix::Make(); }}, - {"feComponentTransfer", - []() -> std::shared_ptr { return SVGFeComponentTransfer::Make(); }}, - {"feComposite", []() -> std::shared_ptr { return SVGFeComposite::Make(); }}, - {"feDiffuseLighting", - []() -> std::shared_ptr { return SVGFeDiffuseLighting::Make(); }}, - {"feDisplacementMap", - []() -> std::shared_ptr { return SVGFeDisplacementMap::Make(); }}, - {"feDistantLight", []() -> std::shared_ptr { return SVGFeDistantLight::Make(); }}, - {"feFlood", []() -> std::shared_ptr { return SVGFeFlood::Make(); }}, - {"feFuncA", []() -> std::shared_ptr { return SVGFeFunc::MakeFuncA(); }}, - {"feFuncB", []() -> std::shared_ptr { return SVGFeFunc::MakeFuncB(); }}, - {"feFuncG", []() -> std::shared_ptr { return SVGFeFunc::MakeFuncG(); }}, - {"feFuncR", []() -> std::shared_ptr { return SVGFeFunc::MakeFuncR(); }}, - {"feGaussianBlur", []() -> std::shared_ptr { return SVGFeGaussianBlur::Make(); }}, - {"feImage", []() -> std::shared_ptr { return SVGFeImage::Make(); }}, - {"feMerge", []() -> std::shared_ptr { return SVGFeMerge::Make(); }}, - {"feMergeNode", []() -> std::shared_ptr { return SVGFeMergeNode::Make(); }}, - {"feMorphology", []() -> std::shared_ptr { return SVGFeMorphology::Make(); }}, - {"feOffset", []() -> std::shared_ptr { return SVGFeOffset::Make(); }}, - {"fePointLight", []() -> std::shared_ptr { return SVGFePointLight::Make(); }}, - {"feSpecularLighting", - []() -> std::shared_ptr { return SVGFeSpecularLighting::Make(); }}, - {"feSpotLight", []() -> std::shared_ptr { return SVGFeSpotLight::Make(); }}, - {"feTurbulence", []() -> std::shared_ptr { return SVGFeTurbulence::Make(); }}, - {"filter", []() -> std::shared_ptr { return SVGFilter::Make(); }}, - {"g", []() -> std::shared_ptr { return SVGG::Make(); }}, - {"image", []() -> std::shared_ptr { return SVGImage::Make(); }}, - {"line", []() -> std::shared_ptr { return SVGLine::Make(); }}, - {"linearGradient", []() -> std::shared_ptr { return SVGLinearGradient::Make(); }}, - {"mask", []() -> std::shared_ptr { return SVGMask::Make(); }}, - {"path", []() -> std::shared_ptr { return SVGPath::Make(); }}, - {"pattern", []() -> std::shared_ptr { return SVGPattern::Make(); }}, - {"polygon", []() -> std::shared_ptr { return SVGPoly::MakePolygon(); }}, - {"polyline", []() -> std::shared_ptr { return SVGPoly::MakePolyline(); }}, - {"radialGradient", []() -> std::shared_ptr { return SVGRadialGradient::Make(); }}, - {"rect", []() -> std::shared_ptr { return SVGRect::Make(); }}, - {"stop", []() -> std::shared_ptr { return SVGStop::Make(); }}, - // "svg" handled explicitly - {"text", []() -> std::shared_ptr { return SVGText::Make(); }}, - {"textPath", []() -> std::shared_ptr { return SVGTextPath::Make(); }}, - {"tspan", []() -> std::shared_ptr { return SVGTSpan::Make(); }}, - {"use", []() -> std::shared_ptr { return SVGUse::Make(); }}, -}; - -struct ConstructionContext { - explicit ConstructionContext(SVGIDMapper* mapper) : parentNode(nullptr), nodeIDMapper(mapper) { - } - ConstructionContext(const ConstructionContext& other, const std::shared_ptr& newParent) - : parentNode(newParent.get()), nodeIDMapper(other.nodeIDMapper) { - } - - SVGNode* parentNode; - SVGIDMapper* nodeIDMapper; -}; - -bool SetAttribute(SVGNode& node, const std::string& name, const std::string& value) { - if (node.parseAndSetAttribute(name, value)) { - // Handled by new code path - return true; - } - - if (auto iter = gAttributeParseInfo.find(name); iter != gAttributeParseInfo.end()) { - auto setter = iter->second.setter; - return setter(node, iter->second.attribute, value); - } - return true; -} - -void ParseNodeAttributes(const DOMNode* xmlNode, const std::shared_ptr& svgNode, - SVGIDMapper* mapper) { - - for (const auto& attr : xmlNode->attributes) { - auto name = attr.name; - auto value = attr.value; - if (name == "id") { - mapper->insert({value, svgNode}); - continue; - } - SetAttribute(*svgNode, name, value); - } -} - -std::shared_ptr ConstructSVGNode(const ConstructionContext& context, - const DOMNode* xmlNode) { - std::string elementName = xmlNode->name; - const auto elementType = xmlNode->type; - - if (elementType == DOMNodeType::Text) { - // Text literals require special handling. - ASSERT(xmlNode->attributes.empty()); - auto text = SVGTextLiteral::Make(); - text->setText(xmlNode->name); - context.parentNode->appendChild(std::move(text)); - return nullptr; - } - - ASSERT(elementType == DOMNodeType::Element); - - auto makeNode = [](const ConstructionContext& context, - const std::string& elementName) -> std::shared_ptr { - if (elementName == "svg") { - // Outermost SVG element must be tagged as such. - return SVGSVG::Make(context.parentNode ? SVGSVG::Type::kInner : SVGSVG::Type::kRoot); - } - - if (auto iter = ElementFactories.find(elementName); iter != ElementFactories.end()) { - return iter->second(); - } - //can't find the element factory - ASSERT(false); - }; - - auto node = makeNode(context, elementName); - if (!node) { - return nullptr; - } - - ParseNodeAttributes(xmlNode, node, context.nodeIDMapper); - - ConstructionContext localCtx(context, node); - std::shared_ptr child = xmlNode->firstChild; - while (child) { - std::shared_ptr childNode = ConstructSVGNode(localCtx, child.get()); - if (childNode) { - node->appendChild(std::move(childNode)); - } - child = child->nextSibling; - } - return node; -} - -} // anonymous namespace std::shared_ptr SVGDOM::Make(const std::shared_ptr& data, std::shared_ptr fontManager) { @@ -402,9 +51,10 @@ std::shared_ptr SVGDOM::Make(const std::shared_ptr& data, } SVGIDMapper mapper; - ConstructionContext ctx(&mapper); + ConstructionContext constructionContext(&mapper); - auto root = ConstructSVGNode(ctx, xmlDom->getRootNode().get()); + auto root = + SVGNodeConstructor::ConstructSVGNode(constructionContext, xmlDom->getRootNode().get()); if (!root || root->tag() != SVGTag::Svg) { return nullptr; } @@ -421,15 +71,15 @@ SVGDOM::SVGDOM(std::shared_ptr root, SVGIDMapper&& mapper, void SVGDOM::render(Canvas* canvas) { if (root) { if (!renderPicture) { - SVGLengthContext lctx(containerSize); - SVGPresentationContext pctx; + SVGLengthContext lengthContext(containerSize); + SVGPresentationContext presentationContext; Recorder recorder; auto* drawCanvas = recorder.beginRecording(); { SVGRenderContext renderCtx(canvas->getSurface()->getContext(), drawCanvas, fontManager, - _nodeIDMapper, lctx, pctx, {nullptr, nullptr}, - canvas->getMatrix()); + _nodeIDMapper, lengthContext, presentationContext, + {nullptr, nullptr}, canvas->getMatrix()); root->render(renderCtx); } renderPicture = recorder.finishRecordingAsPicture(); @@ -447,6 +97,6 @@ void SVGDOM::setContainerSize(const Size& size) { } bool SVGNode::setAttribute(const std::string& attributeName, const std::string& attributeValue) { - return SetAttribute(*this, attributeName, attributeValue); + return SVGNodeConstructor::SetAttribute(*this, attributeName, attributeValue); } } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFilterContext.cpp b/src/svg/SVGFilterContext.cpp similarity index 93% rename from src/svg/node/SVGFilterContext.cpp rename to src/svg/SVGFilterContext.cpp index 3a242729..f53cba62 100644 --- a/src/svg/node/SVGFilterContext.cpp +++ b/src/svg/SVGFilterContext.cpp @@ -16,7 +16,7 @@ // ///////////////////////////////////////////////////////////////////////////////////////////////// -#include "tgfx/svg/node/SVGFilterContext.h" +#include "SVGFilterContext.h" #include #include "core/utils/Log.h" #include "svg/SVGRenderContext.h" @@ -61,7 +61,7 @@ bool SVGFilterContext::previousResultIsSourceGraphic() const { // https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveInAttribute std::tuple, SVGColorspace> SVGFilterContext::getInput( - const SVGRenderContext& ctx, const SVGFeInputType& inputType) const { + const SVGRenderContext& context, const SVGFeInputType& inputType) const { SVGColorspace inputCS = SVGColorspace::SRGB; std::shared_ptr result; switch (inputType.type()) { @@ -75,14 +75,14 @@ std::tuple, SVGColorspace> SVGFilterContext::getInp // Do nothing. break; case SVGFeInputType::Type::FillPaint: { - const auto& fillPaint = ctx.fillPaint(); + const auto& fillPaint = context.fillPaint(); if (fillPaint.has_value()) { //TODO (YGAurora) - Implement shader image filter by paint } break; } case SVGFeInputType::Type::StrokePaint: { - const auto& strokePaint = ctx.strokePaint(); + const auto& strokePaint = context.strokePaint(); if (strokePaint.has_value()) { //TODO (YGAurora) - Implement shader image filter by paint } @@ -118,9 +118,9 @@ std::shared_ptr SVGFilterContext::resolveInput(const SVGRenderConte return std::get<0>(this->getInput(context, inputType)); } -std::shared_ptr SVGFilterContext::resolveInput(const SVGRenderContext& /*ctx*/, - const SVGFeInputType& /*inputType*/, - SVGColorspace /*colorspace*/) const { +std::shared_ptr SVGFilterContext::resolveInput(const SVGRenderContext&, + const SVGFeInputType&, + SVGColorspace) const { //TODO (YGAurora) - ConvertFilterColorspace return nullptr; } diff --git a/include/tgfx/svg/node/SVGFilterContext.h b/src/svg/SVGFilterContext.h similarity index 98% rename from include/tgfx/svg/node/SVGFilterContext.h rename to src/svg/SVGFilterContext.h index 7a69b527..3ba1d042 100644 --- a/include/tgfx/svg/node/SVGFilterContext.h +++ b/src/svg/SVGFilterContext.h @@ -20,13 +20,13 @@ #include #include +#include "svg/SVGRenderContext.h" #include "tgfx/core/ImageFilter.h" #include "tgfx/core/Rect.h" #include "tgfx/svg/SVGTypes.h" namespace tgfx { -class SVGRenderContext; class SVGFilterContext { public: SVGFilterContext(const Rect& filterEffectsRegion, const SVGObjectBoundingBoxUnits& primitiveUnits) diff --git a/src/svg/SVGLengthContext.cpp b/src/svg/SVGLengthContext.cpp new file mode 100644 index 00000000..01136d24 --- /dev/null +++ b/src/svg/SVGLengthContext.cpp @@ -0,0 +1,124 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SVGLengthContext.h" +#include "core/utils/Log.h" + +namespace tgfx { + +namespace { + +float length_size_for_type(const Size& viewport, SVGLengthContext::LengthType t) { + switch (t) { + case SVGLengthContext::LengthType::Horizontal: + return viewport.width; + case SVGLengthContext::LengthType::Vertical: + return viewport.height; + case SVGLengthContext::LengthType::Other: { + // https://www.w3.org/TR/SVG11/coords.html#Units_viewport_percentage + const float rsqrt2 = 1.0f / std::sqrt(2.0f); + const float w = viewport.width; + const float h = viewport.height; + return rsqrt2 * std::sqrt(w * w + h * h); + } + } + ASSERT(false); // Not reached. + return 0; +} + +// Multipliers for DPI-relative units. +constexpr float INMultiplier = 1.00f; +constexpr float PTMultiplier = INMultiplier / 72.272f; +constexpr float PCMultiplier = PTMultiplier * 12; +constexpr float MMMultiplier = INMultiplier / 25.4f; +constexpr float CMMultiplier = MMMultiplier * 10; + +} // namespace + +float SVGLengthContext::resolve(const SVGLength& l, LengthType t) const { + switch (l.unit()) { + case SVGLength::Unit::Number: { + if (patternUnit.has_value()) { + if (patternUnit.value().type() == SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox) { + return l.value() * length_size_for_type(_viewPort, t); + } else { + return l.value(); + } + } else { + return l.value(); + } + } + case SVGLength::Unit::PX: + return l.value(); + case SVGLength::Unit::Percentage: + return l.value() * length_size_for_type(_viewPort, t) / 100; + case SVGLength::Unit::CM: + return l.value() * dpi * CMMultiplier; + case SVGLength::Unit::MM: + return l.value() * dpi * MMMultiplier; + case SVGLength::Unit::IN: + return l.value() * dpi * INMultiplier; + case SVGLength::Unit::PT: + return l.value() * dpi * PTMultiplier; + case SVGLength::Unit::PC: + return l.value() * dpi * PCMultiplier; + default: + //unsupported unit type + ASSERT(false); + return 0; + } +} + +Rect SVGLengthContext::resolveRect(const SVGLength& x, const SVGLength& y, const SVGLength& width, + const SVGLength& height) const { + return Rect::MakeXYWH(this->resolve(x, SVGLengthContext::LengthType::Horizontal), + this->resolve(y, SVGLengthContext::LengthType::Vertical), + this->resolve(width, SVGLengthContext::LengthType::Horizontal), + this->resolve(height, SVGLengthContext::LengthType::Vertical)); +} + +std::tuple SVGLengthContext::resolveOptionalRadii( + const std::optional& optionalRx, const std::optional& optionalRy) const { + // https://www.w3.org/TR/SVG2/shapes.html#RectElement + // + // The used values for rx and ry are determined from the computed values by following these + // steps in order: + // + // 1. If both rx and ry have a computed value of auto (since auto is the initial value for both + // properties, this will also occur if neither are specified by the author or if all + // author-supplied values are invalid), then the used value of both rx and ry is 0. + // (This will result in square corners.) + // 2. Otherwise, convert specified values to absolute values as follows: + // 1. If rx is set to a length value or a percentage, but ry is auto, calculate an absolute + // length equivalent for rx, resolving percentages against the used width of the + // rectangle; the absolute value for ry is the same. + // 2. If ry is set to a length value or a percentage, but rx is auto, calculate the absolute + // length equivalent for ry, resolving percentages against the used height of the + // rectangle; the absolute value for rx is the same. + // 3. If both rx and ry were set to lengths or percentages, absolute values are generated + // individually, resolving rx percentages against the used width, and resolving ry + // percentages against the used height. + const float rx = + optionalRx.has_value() ? resolve(*optionalRx, SVGLengthContext::LengthType::Horizontal) : 0; + const float ry = + optionalRy.has_value() ? resolve(*optionalRy, SVGLengthContext::LengthType::Vertical) : 0; + + return {optionalRx.has_value() ? rx : ry, optionalRy.has_value() ? ry : rx}; +} + +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGLengthContext.h b/src/svg/SVGLengthContext.h new file mode 100644 index 00000000..358a64f1 --- /dev/null +++ b/src/svg/SVGLengthContext.h @@ -0,0 +1,70 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "tgfx/core/Size.h" +#include "tgfx/svg/SVGTypes.h" + +namespace tgfx { + +class SVGLengthContext { + public: + explicit SVGLengthContext(const Size& viewport, float dpi = 90) : _viewPort(viewport), dpi(dpi) { + } + + enum class LengthType { + Horizontal, + Vertical, + Other, + }; + + const Size& viewPort() const { + return _viewPort; + } + void setViewPort(const Size& viewPort) { + _viewPort = viewPort; + } + + float resolve(const SVGLength&, LengthType) const; + + Rect resolveRect(const SVGLength& x, const SVGLength& y, const SVGLength& w, + const SVGLength& h) const; + + std::tuple resolveOptionalRadii(const std::optional& optionalRx, + const std::optional& optionalRy) const; + + void setPatternUnits(SVGObjectBoundingBoxUnits unit) { + patternUnit = unit; + } + + void clearPatternUnits() { + patternUnit.reset(); + } + + std::optional getPatternUnits() const { + return patternUnit; + } + + private: + Size _viewPort; + float dpi; + std::optional patternUnit; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGNodeConstructor.cpp b/src/svg/SVGNodeConstructor.cpp new file mode 100644 index 00000000..c06800a0 --- /dev/null +++ b/src/svg/SVGNodeConstructor.cpp @@ -0,0 +1,365 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SVGNodeConstructor.h" +#include "core/utils/Log.h" +#include "svg/SVGAttributeParser.h" +#include "tgfx/svg/node/SVGCircle.h" +#include "tgfx/svg/node/SVGClipPath.h" +#include "tgfx/svg/node/SVGDefs.h" +#include "tgfx/svg/node/SVGEllipse.h" +#include "tgfx/svg/node/SVGFeBlend.h" +#include "tgfx/svg/node/SVGFeColorMatrix.h" +#include "tgfx/svg/node/SVGFeComponentTransfer.h" +#include "tgfx/svg/node/SVGFeComposite.h" +#include "tgfx/svg/node/SVGFeDisplacementMap.h" +#include "tgfx/svg/node/SVGFeFlood.h" +#include "tgfx/svg/node/SVGFeGaussianBlur.h" +#include "tgfx/svg/node/SVGFeImage.h" +#include "tgfx/svg/node/SVGFeLightSource.h" +#include "tgfx/svg/node/SVGFeLighting.h" +#include "tgfx/svg/node/SVGFeMerge.h" +#include "tgfx/svg/node/SVGFeMorphology.h" +#include "tgfx/svg/node/SVGFeOffset.h" +#include "tgfx/svg/node/SVGFeTurbulence.h" +#include "tgfx/svg/node/SVGFilter.h" +#include "tgfx/svg/node/SVGG.h" +#include "tgfx/svg/node/SVGImage.h" +#include "tgfx/svg/node/SVGLine.h" +#include "tgfx/svg/node/SVGLinearGradient.h" +#include "tgfx/svg/node/SVGMask.h" +#include "tgfx/svg/node/SVGNode.h" +#include "tgfx/svg/node/SVGPath.h" +#include "tgfx/svg/node/SVGPattern.h" +#include "tgfx/svg/node/SVGPoly.h" +#include "tgfx/svg/node/SVGRadialGradient.h" +#include "tgfx/svg/node/SVGRect.h" +#include "tgfx/svg/node/SVGSVG.h" +#include "tgfx/svg/node/SVGStop.h" +#include "tgfx/svg/node/SVGText.h" +#include "tgfx/svg/node/SVGUse.h" + +namespace tgfx { + +bool SVGNodeConstructor::SetIRIAttribute(SVGNode& node, SVGAttribute attr, + const std::string& stringValue) { + auto parseResult = SVGAttributeParser::parse(stringValue); + if (!parseResult.has_value()) { + return false; + } + + node.setAttribute(attr, SVGStringValue(parseResult->iri())); + return true; +} + +bool SVGNodeConstructor::SetStringAttribute(SVGNode& node, SVGAttribute attr, + const std::string& stringValue) { + SVGStringType strType = SVGStringType(stringValue); + node.setAttribute(attr, SVGStringValue(strType)); + return true; +} + +bool SVGNodeConstructor::SetTransformAttribute(SVGNode& node, SVGAttribute attr, + const std::string& stringValue) { + auto parseResult = SVGAttributeParser::parse(stringValue); + if (!parseResult.has_value()) { + return false; + } + + node.setAttribute(attr, SVGTransformValue(*parseResult)); + return true; +} + +bool SVGNodeConstructor::SetLengthAttribute(SVGNode& node, SVGAttribute attr, + const std::string& stringValue) { + auto parseResult = SVGAttributeParser::parse(stringValue); + if (!parseResult.has_value()) { + return false; + } + + node.setAttribute(attr, SVGLengthValue(*parseResult)); + return true; +} + +bool SVGNodeConstructor::SetViewBoxAttribute(SVGNode& node, SVGAttribute attr, + const std::string& stringValue) { + SVGViewBoxType viewBox; + SVGAttributeParser parser(stringValue); + if (!parser.parseViewBox(&viewBox)) { + return false; + } + + node.setAttribute(attr, SVGViewBoxValue(viewBox)); + return true; +} + +bool SVGNodeConstructor::SetObjectBoundingBoxUnitsAttribute(SVGNode& node, SVGAttribute attr, + const std::string& stringValue) { + auto parseResult = SVGAttributeParser::parse(stringValue); + if (!parseResult.has_value()) { + return false; + } + + node.setAttribute(attr, SVGObjectBoundingBoxUnitsValue(*parseResult)); + return true; +} + +bool SVGNodeConstructor::SetPreserveAspectRatioAttribute(SVGNode& node, SVGAttribute attr, + const std::string& stringValue) { + SVGPreserveAspectRatio par; + SVGAttributeParser parser(stringValue); + if (!parser.parsePreserveAspectRatio(&par)) { + return false; + } + + node.setAttribute(attr, SVGPreserveAspectRatioValue(par)); + return true; +} + +std::string TrimmedString(const char* first, const char* last) { + ASSERT(first); + ASSERT(last); + ASSERT(first <= last); + + while (first <= last && *first <= ' ') { + first++; + } + while (first <= last && *last <= ' ') { + last--; + } + + ASSERT(last - first + 1 >= 0); + return std::string(first, static_cast(last - first + 1)); +} + +// Breaks a "foo: bar; baz: ..." string into key:value pairs. +class StyleIterator { + public: + explicit StyleIterator(const std::string& str) : pos(str.data()) { + } + + std::tuple next() { + std::string name; + std::string value; + + if (pos) { + const char* sep = this->nextSeparator(); + ASSERT(*sep == ';'); + ASSERT(*sep == '\0') + + const char* valueSep = strchr(pos, ':'); + if (valueSep && valueSep < sep) { + name = TrimmedString(pos, valueSep - 1); + value = TrimmedString(valueSep + 1, sep - 1); + } + + pos = *sep ? sep + 1 : nullptr; + } + + return std::make_tuple(name, value); + } + + private: + const char* nextSeparator() const { + const char* sep = pos; + while (*sep != ';' && *sep != '\0') { + sep++; + } + return sep; + } + + const char* pos; +}; + +bool SVGNodeConstructor::SetStyleAttributes(SVGNode& node, SVGAttribute, + const std::string& stringValue) { + + std::string name; + std::string value; + StyleIterator iter(stringValue); + for (;;) { + std::tie(name, value) = iter.next(); + if (name.empty()) { + break; + } + SetAttribute(node, name, value); + } + + return true; +} + +std::unordered_map SVGNodeConstructor::attributeParseInfo = { + {"cx", {SVGAttribute::Cx, SVGNodeConstructor::SetLengthAttribute}}, + {"cy", {SVGAttribute::Cy, SVGNodeConstructor::SetLengthAttribute}}, + {"filterUnits", + {SVGAttribute::FilterUnits, SVGNodeConstructor::SetObjectBoundingBoxUnitsAttribute}}, + // focal point x & y + {"fx", {SVGAttribute::Fx, SVGNodeConstructor::SetLengthAttribute}}, + {"fy", {SVGAttribute::Fy, SVGNodeConstructor::SetLengthAttribute}}, + {"height", {SVGAttribute::Height, SVGNodeConstructor::SetLengthAttribute}}, + {"preserveAspectRatio", + {SVGAttribute::PreserveAspectRatio, SVGNodeConstructor::SetPreserveAspectRatioAttribute}}, + {"r", {SVGAttribute::R, SVGNodeConstructor::SetLengthAttribute}}, + {"rx", {SVGAttribute::Rx, SVGNodeConstructor::SetLengthAttribute}}, + {"ry", {SVGAttribute::Ry, SVGNodeConstructor::SetLengthAttribute}}, + {"style", {SVGAttribute::Unknown, SVGNodeConstructor::SetStyleAttributes}}, + {"text", {SVGAttribute::Text, SVGNodeConstructor::SetStringAttribute}}, + {"transform", {SVGAttribute::Transform, SVGNodeConstructor::SetTransformAttribute}}, + {"viewBox", {SVGAttribute::ViewBox, SVGNodeConstructor::SetViewBoxAttribute}}, + {"width", {SVGAttribute::Width, SVGNodeConstructor::SetLengthAttribute}}, + {"x", {SVGAttribute::X, SVGNodeConstructor::SetLengthAttribute}}, + {"x1", {SVGAttribute::X1, SVGNodeConstructor::SetLengthAttribute}}, + {"x2", {SVGAttribute::X2, SVGNodeConstructor::SetLengthAttribute}}, + {"xlink:href", {SVGAttribute::Href, SVGNodeConstructor::SetIRIAttribute}}, + {"y", {SVGAttribute::Y, SVGNodeConstructor::SetLengthAttribute}}, + {"y1", {SVGAttribute::Y1, SVGNodeConstructor::SetLengthAttribute}}, + {"y2", {SVGAttribute::Y2, SVGNodeConstructor::SetLengthAttribute}}, +}; + +using ElementFactory = std::function()>; +std::unordered_map ElementFactories = { + {"a", []() -> std::shared_ptr { return SVGG::Make(); }}, + {"circle", []() -> std::shared_ptr { return SVGCircle::Make(); }}, + {"clipPath", []() -> std::shared_ptr { return SVGClipPath::Make(); }}, + {"defs", []() -> std::shared_ptr { return SVGDefs::Make(); }}, + {"ellipse", []() -> std::shared_ptr { return SVGEllipse::Make(); }}, + {"feBlend", []() -> std::shared_ptr { return SVGFeBlend::Make(); }}, + {"feColorMatrix", []() -> std::shared_ptr { return SVGFeColorMatrix::Make(); }}, + {"feComponentTransfer", + []() -> std::shared_ptr { return SVGFeComponentTransfer::Make(); }}, + {"feComposite", []() -> std::shared_ptr { return SVGFeComposite::Make(); }}, + {"feDiffuseLighting", + []() -> std::shared_ptr { return SVGFeDiffuseLighting::Make(); }}, + {"feDisplacementMap", + []() -> std::shared_ptr { return SVGFeDisplacementMap::Make(); }}, + {"feDistantLight", []() -> std::shared_ptr { return SVGFeDistantLight::Make(); }}, + {"feFlood", []() -> std::shared_ptr { return SVGFeFlood::Make(); }}, + {"feFuncA", []() -> std::shared_ptr { return SVGFeFunc::MakeFuncA(); }}, + {"feFuncB", []() -> std::shared_ptr { return SVGFeFunc::MakeFuncB(); }}, + {"feFuncG", []() -> std::shared_ptr { return SVGFeFunc::MakeFuncG(); }}, + {"feFuncR", []() -> std::shared_ptr { return SVGFeFunc::MakeFuncR(); }}, + {"feGaussianBlur", []() -> std::shared_ptr { return SVGFeGaussianBlur::Make(); }}, + {"feImage", []() -> std::shared_ptr { return SVGFeImage::Make(); }}, + {"feMerge", []() -> std::shared_ptr { return SVGFeMerge::Make(); }}, + {"feMergeNode", []() -> std::shared_ptr { return SVGFeMergeNode::Make(); }}, + {"feMorphology", []() -> std::shared_ptr { return SVGFeMorphology::Make(); }}, + {"feOffset", []() -> std::shared_ptr { return SVGFeOffset::Make(); }}, + {"fePointLight", []() -> std::shared_ptr { return SVGFePointLight::Make(); }}, + {"feSpecularLighting", + []() -> std::shared_ptr { return SVGFeSpecularLighting::Make(); }}, + {"feSpotLight", []() -> std::shared_ptr { return SVGFeSpotLight::Make(); }}, + {"feTurbulence", []() -> std::shared_ptr { return SVGFeTurbulence::Make(); }}, + {"filter", []() -> std::shared_ptr { return SVGFilter::Make(); }}, + {"g", []() -> std::shared_ptr { return SVGG::Make(); }}, + {"image", []() -> std::shared_ptr { return SVGImage::Make(); }}, + {"line", []() -> std::shared_ptr { return SVGLine::Make(); }}, + {"linearGradient", []() -> std::shared_ptr { return SVGLinearGradient::Make(); }}, + {"mask", []() -> std::shared_ptr { return SVGMask::Make(); }}, + {"path", []() -> std::shared_ptr { return SVGPath::Make(); }}, + {"pattern", []() -> std::shared_ptr { return SVGPattern::Make(); }}, + {"polygon", []() -> std::shared_ptr { return SVGPoly::MakePolygon(); }}, + {"polyline", []() -> std::shared_ptr { return SVGPoly::MakePolyline(); }}, + {"radialGradient", []() -> std::shared_ptr { return SVGRadialGradient::Make(); }}, + {"rect", []() -> std::shared_ptr { return SVGRect::Make(); }}, + {"stop", []() -> std::shared_ptr { return SVGStop::Make(); }}, + // "svg" handled explicitly + {"text", []() -> std::shared_ptr { return SVGText::Make(); }}, + {"textPath", []() -> std::shared_ptr { return SVGTextPath::Make(); }}, + {"tspan", []() -> std::shared_ptr { return SVGTSpan::Make(); }}, + {"use", []() -> std::shared_ptr { return SVGUse::Make(); }}, +}; + +bool SVGNodeConstructor::SetAttribute(SVGNode& node, const std::string& name, + const std::string& value) { + if (node.parseAndSetAttribute(name, value)) { + // Handled by new code path + return true; + } + + if (auto iter = attributeParseInfo.find(name); iter != attributeParseInfo.end()) { + auto setter = iter->second.setter; + return setter(node, iter->second.attribute, value); + } + return true; +} + +void SVGNodeConstructor::ParseNodeAttributes(const DOMNode* xmlNode, + const std::shared_ptr& svgNode, + SVGIDMapper* mapper) { + + for (const auto& attr : xmlNode->attributes) { + auto name = attr.name; + auto value = attr.value; + if (name == "id") { + mapper->insert({value, svgNode}); + continue; + } + SetAttribute(*svgNode, name, value); + } +} + +std::shared_ptr SVGNodeConstructor::ConstructSVGNode(const ConstructionContext& context, + const DOMNode* xmlNode) { + std::string elementName = xmlNode->name; + const auto elementType = xmlNode->type; + + if (elementType == DOMNodeType::Text) { + // Text literals require special handling. + ASSERT(xmlNode->attributes.empty()); + auto text = SVGTextLiteral::Make(); + text->setText(xmlNode->name); + context.parentNode->appendChild(std::move(text)); + return nullptr; + } + + ASSERT(elementType == DOMNodeType::Element); + + auto makeNode = [](const ConstructionContext& context, + const std::string& elementName) -> std::shared_ptr { + if (elementName == "svg") { + // Outermost SVG element must be tagged as such. + return SVGSVG::Make(context.parentNode ? SVGSVG::Type::kInner : SVGSVG::Type::kRoot); + } + + if (auto iter = ElementFactories.find(elementName); iter != ElementFactories.end()) { + return iter->second(); + } + //can't find the element factory + ASSERT(false); + }; + + auto node = makeNode(context, elementName); + if (!node) { + return nullptr; + } + + ParseNodeAttributes(xmlNode, node, context.nodeIDMapper); + + ConstructionContext localCtx(context, node); + std::shared_ptr child = xmlNode->firstChild; + while (child) { + std::shared_ptr childNode = ConstructSVGNode(localCtx, child.get()); + if (childNode) { + node->appendChild(std::move(childNode)); + } + child = child->nextSibling; + } + return node; +} + +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGNodeConstructor.h b/src/svg/SVGNodeConstructor.h new file mode 100644 index 00000000..ebdb5ce3 --- /dev/null +++ b/src/svg/SVGNodeConstructor.h @@ -0,0 +1,76 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "tgfx/svg/SVGDOM.h" +#include "tgfx/svg/xml/XMLDOM.h" + +namespace tgfx { + +struct ConstructionContext { + explicit ConstructionContext(SVGIDMapper* mapper) : parentNode(nullptr), nodeIDMapper(mapper) { + } + ConstructionContext(const ConstructionContext& other, const std::shared_ptr& newParent) + : parentNode(newParent.get()), nodeIDMapper(other.nodeIDMapper) { + } + + SVGNode* parentNode; + SVGIDMapper* nodeIDMapper; +}; + +using AttributeSetter = std::function; +struct AttrParseInfo { + SVGAttribute attribute; + AttributeSetter setter; +}; + +class SVGNodeConstructor { + public: + static std::shared_ptr ConstructSVGNode(const ConstructionContext& context, + const DOMNode* xmlNode); + + static bool SetAttribute(SVGNode& node, const std::string& name, const std::string& value); + + private: + static void ParseNodeAttributes(const DOMNode* xmlNode, const std::shared_ptr& svgNode, + SVGIDMapper* mapper); + + static bool SetStyleAttributes(SVGNode& node, SVGAttribute, const std::string& stringValue); + + static bool SetPreserveAspectRatioAttribute(SVGNode& node, SVGAttribute attr, + const std::string& stringValue); + + static bool SetObjectBoundingBoxUnitsAttribute(SVGNode& node, SVGAttribute attr, + const std::string& stringValue); + + static bool SetViewBoxAttribute(SVGNode& node, SVGAttribute attr, const std::string& stringValue); + + static bool SetLengthAttribute(SVGNode& node, SVGAttribute attr, const std::string& stringValue); + + static bool SetTransformAttribute(SVGNode& node, SVGAttribute attr, + const std::string& stringValue); + + static bool SetStringAttribute(SVGNode& node, SVGAttribute attr, const std::string& stringValue); + + static bool SetIRIAttribute(SVGNode& node, SVGAttribute attr, const std::string& stringValue); + + static std::unordered_map attributeParseInfo; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGRenderContext.cpp b/src/svg/SVGRenderContext.cpp index 2f93651a..ea795e9e 100644 --- a/src/svg/SVGRenderContext.cpp +++ b/src/svg/SVGRenderContext.cpp @@ -40,78 +40,6 @@ #include "tgfx/svg/node/SVGNode.h" namespace tgfx { - -namespace { - -float length_size_for_type(const Size& viewport, SVGLengthContext::LengthType t) { - switch (t) { - case SVGLengthContext::LengthType::Horizontal: - return viewport.width; - case SVGLengthContext::LengthType::Vertical: - return viewport.height; - case SVGLengthContext::LengthType::Other: { - // https://www.w3.org/TR/SVG11/coords.html#Units_viewport_percentage - const float rsqrt2 = 1.0f / std::sqrt(2.0f); - const float w = viewport.width; - const float h = viewport.height; - return rsqrt2 * std::sqrt(w * w + h * h); - } - } - ASSERT(false); // Not reached. - return 0; -} - -// Multipliers for DPI-relative units. -constexpr float kINMultiplier = 1.00f; -constexpr float kPTMultiplier = kINMultiplier / 72.272f; -constexpr float kPCMultiplier = kPTMultiplier * 12; -constexpr float kMMMultiplier = kINMultiplier / 25.4f; -constexpr float kCMMultiplier = kMMMultiplier * 10; - -} // namespace - -float SVGLengthContext::resolve(const SVGLength& l, LengthType t) const { - switch (l.unit()) { - case SVGLength::Unit::Number: { - if (patternUnit.has_value()) { - if (patternUnit.value().type() == SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox) { - return l.value() * length_size_for_type(_viewPort, t); - } else { - return l.value(); - } - } else { - return l.value(); - } - } - case SVGLength::Unit::PX: - return l.value(); - case SVGLength::Unit::Percentage: - return l.value() * length_size_for_type(_viewPort, t) / 100; - case SVGLength::Unit::CM: - return l.value() * dpi * kCMMultiplier; - case SVGLength::Unit::MM: - return l.value() * dpi * kMMMultiplier; - case SVGLength::Unit::IN: - return l.value() * dpi * kINMultiplier; - case SVGLength::Unit::PT: - return l.value() * dpi * kPTMultiplier; - case SVGLength::Unit::PC: - return l.value() * dpi * kPCMultiplier; - default: - //unsupported unit type - ASSERT(false); - return 0; - } -} - -Rect SVGLengthContext::resolveRect(const SVGLength& x, const SVGLength& y, const SVGLength& width, - const SVGLength& height) const { - return Rect::MakeXYWH(this->resolve(x, SVGLengthContext::LengthType::Horizontal), - this->resolve(y, SVGLengthContext::LengthType::Vertical), - this->resolve(width, SVGLengthContext::LengthType::Horizontal), - this->resolve(height, SVGLengthContext::LengthType::Vertical)); -} - namespace { LineCap toCap(const SVGLineCap& cap) { @@ -140,7 +68,7 @@ LineJoin toJoin(const SVGLineJoin& join) { } std::unique_ptr dash_effect(const SVGPresentationAttributes& props, - const SVGLengthContext& lctx) { + const SVGLengthContext& lengthContext) { if (props.StrokeDashArray->type() != SVGDashArray::Type::DashArray) { return nullptr; } @@ -150,7 +78,7 @@ std::unique_ptr dash_effect(const SVGPresentationAttributes& props, std::vector intervals; intervals.reserve(count); for (const auto& dash : da.dashArray()) { - intervals.push_back(lctx.resolve(dash, SVGLengthContext::LengthType::Other)); + intervals.push_back(lengthContext.resolve(dash, SVGLengthContext::LengthType::Other)); } if (count & 1) { @@ -162,7 +90,8 @@ std::unique_ptr dash_effect(const SVGPresentationAttributes& props, ASSERT((intervals.size() & 1) == 0); - const auto phase = lctx.resolve(*props.StrokeDashOffset, SVGLengthContext::LengthType::Other); + const auto phase = + lengthContext.resolve(*props.StrokeDashOffset, SVGLengthContext::LengthType::Other); return PathEffect::MakeDash(intervals.data(), static_cast(intervals.size()), phase); } @@ -175,11 +104,11 @@ SVGPresentationContext::SVGPresentationContext() SVGRenderContext::SVGRenderContext(Context* device, Canvas* canvas, const std::shared_ptr& fontManager, - const SVGIDMapper& mapper, const SVGLengthContext& lctx, - const SVGPresentationContext& pctx, const OBBScope& obbs, - const Matrix& matrix) - : fontManager(fontManager), nodeIDMapper(mapper), _lengthContext(lctx), - _presentationContext(pctx), renderCanvas(canvas), recorder(), + const SVGIDMapper& mapper, const SVGLengthContext& lengthContext, + const SVGPresentationContext& presentContext, + const OBBScope& obbs, const Matrix& matrix) + : fontManager(fontManager), nodeIDMapper(mapper), _lengthContext(lengthContext), + _presentationContext(presentContext), renderCanvas(canvas), recorder(), _canvas(recorder.beginRecording()), scope(obbs), _deviceContext(device), matrix(matrix) { } @@ -392,16 +321,15 @@ std::shared_ptr SVGRenderContext::applyMask(const SVGFuncIRI& mask) return MaskFilter::MakeShader(shader); } -std::optional SVGRenderContext::commonPaint(const SVGPaint& paint_selector, - float paint_opacity) const { - if (paint_selector.type() == SVGPaint::Type::None) { +std::optional SVGRenderContext::commonPaint(const SVGPaint& svgPaint, float opacity) const { + if (svgPaint.type() == SVGPaint::Type::None) { return std::optional(); } - std::optional p = Paint(); - switch (paint_selector.type()) { + std::optional paint = Paint(); + switch (svgPaint.type()) { case SVGPaint::Type::Color: - p->setColor(this->resolveSvgColor(paint_selector.color())); + paint->setColor(this->resolveSVGColor(svgPaint.color())); break; case SVGPaint::Type::IRI: { // Our property inheritance is borked as it follows the render path and not the tree @@ -412,64 +340,63 @@ std::optional SVGRenderContext::commonPaint(const SVGPaint& paint_selecto // Preserve the OBB scope because some paints use object bounding box coords // (e.g. gradient control points), which requires access to the render context // and node being rendered. - SVGPresentationContext pctx; - pctx._namedColors = _presentationContext->_namedColors; - SVGRenderContext local_ctx(_deviceContext, _canvas, fontManager, nodeIDMapper, - *_lengthContext, pctx, scope, Matrix::I()); + SVGPresentationContext presentContext; + presentContext._namedColors = _presentationContext->_namedColors; + SVGRenderContext localContext(_deviceContext, _canvas, fontManager, nodeIDMapper, + *_lengthContext, presentContext, scope, Matrix::I()); - const auto node = this->findNodeById(paint_selector.iri()); - if (!node || !node->asPaint(local_ctx, &(p.value()))) { + const auto node = this->findNodeById(svgPaint.iri()); + if (!node || !node->asPaint(localContext, &(paint.value()))) { // Use the fallback color. - p->setColor(this->resolveSvgColor(paint_selector.color())); + paint->setColor(this->resolveSVGColor(svgPaint.color())); } } break; default: break; } - p->setAntiAlias(true); + paint->setAntiAlias(true); // We observe 3 opacity components: // - initial paint server opacity (e.g. color stop opacity) // - paint-specific opacity (e.g. 'fill-opacity', 'stroke-opacity') // - deferred opacity override (optimization for leaf nodes 'opacity') - p->setAlpha(std::clamp(p->getAlpha() * paint_opacity * deferredPaintOpacity, 0.0f, 1.0f)); - return p; + paint->setAlpha(std::clamp(paint->getAlpha() * opacity * deferredPaintOpacity, 0.0f, 1.0f)); + return paint; } std::optional SVGRenderContext::fillPaint() const { const auto& props = _presentationContext->_inherited; - auto p = this->commonPaint(*props.Fill, *props.FillOpacity); + auto paint = this->commonPaint(*props.Fill, *props.FillOpacity); - if (p.has_value()) { - p->setStyle(PaintStyle::Fill); + if (paint.has_value()) { + paint->setStyle(PaintStyle::Fill); } - return p; + return paint; } std::optional SVGRenderContext::strokePaint() const { const auto& props = _presentationContext->_inherited; - auto p = this->commonPaint(*props.Stroke, *props.StrokeOpacity); + auto paint = this->commonPaint(*props.Stroke, *props.StrokeOpacity); - if (p.has_value()) { - p->setStyle(PaintStyle::Stroke); - p->setStrokeWidth( + if (paint.has_value()) { + paint->setStyle(PaintStyle::Stroke); + paint->setStrokeWidth( _lengthContext->resolve(*props.StrokeWidth, SVGLengthContext::LengthType::Other)); Stroke stroke; stroke.cap = toCap(*props.StrokeLineCap); stroke.join = toJoin(*props.StrokeLineJoin); stroke.miterLimit = *props.StrokeMiterLimit; - p->setStroke(stroke); + paint->setStroke(stroke); - //TODO (YG) - // p->setPathEffect(dash_effect(props, *fLengthContext)); + //TODO (YGAurora): Implement stroke dash array use PathEffect dash_effect(props, *_lengthContext); } - return p; + return paint; } -SVGColorType SVGRenderContext::resolveSvgColor(const SVGColor& color) const { +SVGColorType SVGRenderContext::resolveSVGColor(const SVGColor& color) const { if (_presentationContext->_namedColors) { for (auto&& ident : *color.vars()) { auto iter = _presentationContext->_namedColors->find(ident); @@ -488,9 +415,9 @@ SVGColorType SVGRenderContext::resolveSvgColor(const SVGColor& color) const { } } -SVGRenderContext::OBBTransform SVGRenderContext::transformForCurrentOBB( - SVGObjectBoundingBoxUnits u) const { - if (!scope.node || u.type() == SVGObjectBoundingBoxUnits::Type::UserSpaceOnUse) { +SVGRenderContext::OBBTransform SVGRenderContext::transformForCurrentBoundBox( + SVGObjectBoundingBoxUnits unit) const { + if (!scope.node || unit.type() == SVGObjectBoundingBoxUnits::Type::UserSpaceOnUse) { return {{0, 0}, {1, 1}}; } ASSERT(scope.context); @@ -501,16 +428,17 @@ SVGRenderContext::OBBTransform SVGRenderContext::transformForCurrentOBB( Rect SVGRenderContext::resolveOBBRect(const SVGLength& x, const SVGLength& y, const SVGLength& w, const SVGLength& h, SVGObjectBoundingBoxUnits unit) const { - CopyOnWrite lctx(_lengthContext); + CopyOnWrite lengthContext(_lengthContext); if (unit.type() == SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox) { - *lctx.writable() = SVGLengthContext({1, 1}); + *lengthContext.writable() = SVGLengthContext({1, 1}); } - auto r = lctx->resolveRect(x, y, w, h); - const auto obbt = this->transformForCurrentOBB(unit); + auto r = lengthContext->resolveRect(x, y, w, h); + const auto transform = this->transformForCurrentBoundBox(unit); - return Rect::MakeXYWH(obbt.scale.x * r.x() + obbt.offset.x, obbt.scale.y * r.y() + obbt.offset.y, - obbt.scale.x * r.width(), obbt.scale.y * r.height()); + return Rect::MakeXYWH(transform.scale.x * r.x() + transform.offset.x, + transform.scale.y * r.y() + transform.offset.y, + transform.scale.x * r.width(), transform.scale.y * r.height()); } } // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGRenderContext.h b/src/svg/SVGRenderContext.h index 91324c52..2bd47469 100644 --- a/src/svg/SVGRenderContext.h +++ b/src/svg/SVGRenderContext.h @@ -23,6 +23,7 @@ #include #include #include +#include "svg/SVGLengthContext.h" #include "tgfx/core/Canvas.h" #include "tgfx/core/MaskFilter.h" #include "tgfx/core/Matrix.h" @@ -107,46 +108,6 @@ class CopyOnWrite { std::optional optional; }; -class SVGLengthContext { - public: - explicit SVGLengthContext(const Size& viewport, float dpi = 90) : _viewPort(viewport), dpi(dpi) { - } - - enum class LengthType { - Horizontal, - Vertical, - Other, - }; - - const Size& viewPort() const { - return _viewPort; - } - void setViewPort(const Size& viewPort) { - _viewPort = viewPort; - } - - float resolve(const SVGLength&, LengthType) const; - Rect resolveRect(const SVGLength& x, const SVGLength& y, const SVGLength& w, - const SVGLength& h) const; - - void setPatternUnits(SVGObjectBoundingBoxUnits unit) { - patternUnit = unit; - } - - void clearPatternUnits() { - patternUnit.reset(); - } - - std::optional getPatternUnits() const { - return patternUnit; - } - - private: - Size _viewPort; - float dpi; - std::optional patternUnit; -}; - struct SVGPresentationContext { SVGPresentationContext(); SVGPresentationContext(const SVGPresentationContext&) = default; @@ -214,7 +175,7 @@ class SVGRenderContext { std::optional fillPaint() const; std::optional strokePaint() const; - SVGColorType resolveSvgColor(const SVGColor&) const; + SVGColorType resolveSVGColor(const SVGColor&) const; // The local computed clip path (not inherited). Path clipPath() const { @@ -237,7 +198,7 @@ class SVGRenderContext { Point offset, scale; }; - OBBTransform transformForCurrentOBB(SVGObjectBoundingBoxUnits) const; + OBBTransform transformForCurrentBoundBox(SVGObjectBoundingBoxUnits) const; Rect resolveOBBRect(const SVGLength& x, const SVGLength& y, const SVGLength& w, const SVGLength& h, SVGObjectBoundingBoxUnits unit) const; diff --git a/src/svg/node/SVGCircle.cpp b/src/svg/node/SVGCircle.cpp index e69bfa9e..a094ab5a 100644 --- a/src/svg/node/SVGCircle.cpp +++ b/src/svg/node/SVGCircle.cpp @@ -36,25 +36,25 @@ bool SVGCircle::parseAndSetAttribute(const std::string& n, const std::string& v) this->setR(SVGAttributeParser::parse("r", n, v)); } -std::tuple SVGCircle::resolve(const SVGLengthContext& lctx) const { - const auto cx = lctx.resolve(Cx, SVGLengthContext::LengthType::Horizontal); - const auto cy = lctx.resolve(Cy, SVGLengthContext::LengthType::Vertical); - const auto r = lctx.resolve(R, SVGLengthContext::LengthType::Other); +std::tuple SVGCircle::resolve(const SVGLengthContext& lengthContext) const { + const auto cx = lengthContext.resolve(Cx, SVGLengthContext::LengthType::Horizontal); + const auto cy = lengthContext.resolve(Cy, SVGLengthContext::LengthType::Vertical); + const auto r = lengthContext.resolve(R, SVGLengthContext::LengthType::Other); return std::make_tuple(Point::Make(cx, cy), r); } -void SVGCircle::onDraw(Canvas* canvas, const SVGLengthContext& lctx, const Paint& paint, +void SVGCircle::onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, PathFillType) const { - auto [pos, r] = this->resolve(lctx); + auto [pos, r] = this->resolve(lengthContext); if (r > 0) { canvas->drawCircle(pos.x, pos.y, r, paint); } } -Path SVGCircle::onAsPath(const SVGRenderContext& ctx) const { - auto [pos, r] = this->resolve(ctx.lengthContext()); +Path SVGCircle::onAsPath(const SVGRenderContext& context) const { + auto [pos, r] = this->resolve(context.lengthContext()); Path path; path.addOval(Rect::MakeXYWH(pos.x - r, pos.y - r, 2 * r, 2 * r)); @@ -62,8 +62,8 @@ Path SVGCircle::onAsPath(const SVGRenderContext& ctx) const { return path; } -Rect SVGCircle::onObjectBoundingBox(const SVGRenderContext& ctx) const { - const auto [pos, r] = this->resolve(ctx.lengthContext()); +Rect SVGCircle::onObjectBoundingBox(const SVGRenderContext& context) const { + const auto [pos, r] = this->resolve(context.lengthContext()); return Rect::MakeXYWH(pos.x - r, pos.y - r, 2 * r, 2 * r); } } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGClipPath.cpp b/src/svg/node/SVGClipPath.cpp index 4730dd17..85c8de49 100644 --- a/src/svg/node/SVGClipPath.cpp +++ b/src/svg/node/SVGClipPath.cpp @@ -33,12 +33,12 @@ bool SVGClipPath::parseAndSetAttribute(const std::string& n, const std::string& SVGAttributeParser::parse("clipPathUnits", n, v)); } -Path SVGClipPath::resolveClip(const SVGRenderContext& ctx) const { - auto clip = this->asPath(ctx); +Path SVGClipPath::resolveClip(const SVGRenderContext& context) const { + auto clip = this->asPath(context); - const auto obbt = ctx.transformForCurrentOBB(ClipPathUnits); - const auto m = Matrix::MakeTrans(obbt.offset.x, obbt.offset.y) * - Matrix::MakeScale(obbt.scale.x, obbt.scale.y); + const auto transform = context.transformForCurrentBoundBox(ClipPathUnits); + const auto m = Matrix::MakeTrans(transform.offset.x, transform.offset.y) * + Matrix::MakeScale(transform.scale.x, transform.scale.y); clip.transform(m); return clip; diff --git a/src/svg/node/SVGContainer.cpp b/src/svg/node/SVGContainer.cpp index fca365d1..6c973c9d 100644 --- a/src/svg/node/SVGContainer.cpp +++ b/src/svg/node/SVGContainer.cpp @@ -43,17 +43,17 @@ bool SVGContainer::hasChildren() const { return !children.empty(); } -void SVGContainer::onRender(const SVGRenderContext& ctx) const { +void SVGContainer::onRender(const SVGRenderContext& context) const { for (const auto& i : children) { - i->render(ctx); + i->render(context); } } -Path SVGContainer::onAsPath(const SVGRenderContext& ctx) const { +Path SVGContainer::onAsPath(const SVGRenderContext& context) const { Path path; for (const auto& i : children) { - const Path childPath = i->asPath(ctx); + const Path childPath = i->asPath(context); path.addPath(childPath, PathOp::Union); } @@ -61,11 +61,11 @@ Path SVGContainer::onAsPath(const SVGRenderContext& ctx) const { return path; } -Rect SVGContainer::onObjectBoundingBox(const SVGRenderContext& ctx) const { +Rect SVGContainer::onObjectBoundingBox(const SVGRenderContext& context) const { Rect bounds = Rect::MakeEmpty(); for (const auto& i : children) { - const Rect childBounds = i->objectBoundingBox(ctx); + const Rect childBounds = i->objectBoundingBox(context); bounds.join(childBounds); } diff --git a/src/svg/node/SVGEllipse.cpp b/src/svg/node/SVGEllipse.cpp index 437d05d7..a092bd83 100644 --- a/src/svg/node/SVGEllipse.cpp +++ b/src/svg/node/SVGEllipse.cpp @@ -16,7 +16,7 @@ // ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGEllipse.h" -#include "SVGRectPriv.h" +// #include "SVGRectPriv.h" #include "svg/SVGAttributeParser.h" #include "svg/SVGRenderContext.h" #include "tgfx/core/Rect.h" @@ -33,35 +33,32 @@ bool SVGEllipse::parseAndSetAttribute(const std::string& n, const std::string& v this->setCy(SVGAttributeParser::parse("cy", n, v)) || this->setRx(SVGAttributeParser::parse("rx", n, v)) || this->setRy(SVGAttributeParser::parse("ry", n, v)); - std::optional s; - s = 10; } -Rect SVGEllipse::resolve(const SVGLengthContext& lctx) const { - const auto cx = lctx.resolve(Cx, SVGLengthContext::LengthType::Horizontal); - const auto cy = lctx.resolve(Cy, SVGLengthContext::LengthType::Vertical); +Rect SVGEllipse::resolve(const SVGLengthContext& lengthContext) const { + const auto cx = lengthContext.resolve(Cx, SVGLengthContext::LengthType::Horizontal); + const auto cy = lengthContext.resolve(Cy, SVGLengthContext::LengthType::Vertical); // https://www.w3.org/TR/SVG2/shapes.html#EllipseElement // // An auto value for either rx or ry is converted to a used value, following the rules given // above for rectangles (but without any clamping based on width or height). - const auto [rx, ry] = ResolveOptionalRadii(Rx, Ry, lctx); + const auto [rx, ry] = lengthContext.resolveOptionalRadii(Rx, Ry); // A computed value of zero for either dimension, or a computed value of auto for both // dimensions, disables rendering of the element. return (rx > 0 && ry > 0) ? Rect::MakeXYWH(cx - rx, cy - ry, rx * 2, ry * 2) : Rect::MakeEmpty(); } -void SVGEllipse::onDraw(Canvas* canvas, const SVGLengthContext& lctx, const Paint& paint, +void SVGEllipse::onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, PathFillType) const { - canvas->drawOval(this->resolve(lctx), paint); + canvas->drawOval(this->resolve(lengthContext), paint); } -Path SVGEllipse::onAsPath(const SVGRenderContext& ctx) const { +Path SVGEllipse::onAsPath(const SVGRenderContext& context) const { Path path; - path.addOval(this->resolve(ctx.lengthContext())); + path.addOval(this->resolve(context.lengthContext())); this->mapToParent(&path); - return path; } diff --git a/src/svg/node/SVGFe.cpp b/src/svg/node/SVGFe.cpp index de2ca0f5..aff64990 100644 --- a/src/svg/node/SVGFe.cpp +++ b/src/svg/node/SVGFe.cpp @@ -20,28 +20,28 @@ #include #include #include "svg/SVGAttributeParser.h" +#include "svg/SVGFilterContext.h" #include "svg/SVGRenderContext.h" #include "tgfx/core/Rect.h" -#include "tgfx/svg/node/SVGFilterContext.h" namespace tgfx { -std::shared_ptr SVGFe::makeImageFilter(const SVGRenderContext& ctx, +std::shared_ptr SVGFe::makeImageFilter(const SVGRenderContext& context, const SVGFilterContext& filterContext) const { - return this->onMakeImageFilter(ctx, filterContext); -} + return this->onMakeImageFilter(context, filterContext); +}; -Rect SVGFe::resolveBoundaries(const SVGRenderContext& ctx, +Rect SVGFe::resolveBoundaries(const SVGRenderContext& context, const SVGFilterContext& filterContext) const { const auto x = X.has_value() ? *X : SVGLength(0, SVGLength::Unit::Percentage); const auto y = Y.has_value() ? *Y : SVGLength(0, SVGLength::Unit::Percentage); const auto w = Width.has_value() ? *Width : SVGLength(100, SVGLength::Unit::Percentage); const auto h = Height.has_value() ? *Height : SVGLength(100, SVGLength::Unit::Percentage); - return ctx.resolveOBBRect(x, y, w, h, filterContext.primitiveUnits()); + return context.resolveOBBRect(x, y, w, h, filterContext.primitiveUnits()); } -static bool AnyIsStandardInput(const SVGFilterContext& fctx, +static bool AnyIsStandardInput(const SVGFilterContext& filterContext, const std::vector& inputs) { for (const auto& in : inputs) { switch (in.type()) { @@ -56,7 +56,7 @@ static bool AnyIsStandardInput(const SVGFilterContext& fctx, return true; case SVGFeInputType::Type::Unspecified: // Unspecified means previous result (which may be SourceGraphic). - if (fctx.previousResultIsSourceGraphic()) { + if (filterContext.previousResultIsSourceGraphic()) { return true; } break; @@ -66,8 +66,8 @@ static bool AnyIsStandardInput(const SVGFilterContext& fctx, return false; } -Rect SVGFe::resolveFilterSubregion(const SVGRenderContext& ctx, - const SVGFilterContext& fctx) const { +Rect SVGFe::resolveFilterSubregion(const SVGRenderContext& context, + const SVGFilterContext& filterContext) const { // From https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveSubRegion, // the default filter effect subregion is equal to the union of the subregions defined // for all "referenced nodes" (filter effect inputs). If there are no inputs, the @@ -75,19 +75,19 @@ Rect SVGFe::resolveFilterSubregion(const SVGRenderContext& ctx, // (https://www.w3.org/TR/SVG11/filters.html#FilterEffectsRegion). const std::vector inputs = this->getInputs(); Rect defaultSubregion; - if (inputs.empty() || AnyIsStandardInput(fctx, inputs)) { - defaultSubregion = fctx.filterEffectsRegion(); + if (inputs.empty() || AnyIsStandardInput(filterContext, inputs)) { + defaultSubregion = filterContext.filterEffectsRegion(); } else { - defaultSubregion = fctx.filterPrimitiveSubregion(inputs[0]); + defaultSubregion = filterContext.filterPrimitiveSubregion(inputs[0]); for (size_t i = 1; i < inputs.size(); i++) { - defaultSubregion.join(fctx.filterPrimitiveSubregion(inputs[i])); + defaultSubregion.join(filterContext.filterPrimitiveSubregion(inputs[i])); } } // Next resolve the rect specified by the x, y, width, height attributes on this filter effect. // If those attributes were given, they override the corresponding attribute of the default // filter effect subregion calculated above. - const Rect boundaries = this->resolveBoundaries(ctx, fctx); + const Rect boundaries = this->resolveBoundaries(context, filterContext); // Compute and return the fully resolved subregion. return Rect::MakeXYWH(X.has_value() ? boundaries.left : defaultSubregion.left, @@ -96,14 +96,16 @@ Rect SVGFe::resolveFilterSubregion(const SVGRenderContext& ctx, Height.has_value() ? boundaries.height() : defaultSubregion.height()); } -SVGColorspace SVGFe::resolveColorspace(const SVGRenderContext& ctx, const SVGFilterContext&) const { - constexpr SVGColorspace kDefaultCS = SVGColorspace::SRGB; - const SVGColorspace cs = *ctx.presentationContext()._inherited.ColorInterpolationFilters; - return cs == SVGColorspace::Auto ? kDefaultCS : cs; +SVGColorspace SVGFe::resolveColorspace(const SVGRenderContext& context, + const SVGFilterContext&) const { + constexpr SVGColorspace DEFAULT_COLOR_SPACE = SVGColorspace::SRGB; + const SVGColorspace colorspace = + *context.presentationContext()._inherited.ColorInterpolationFilters; + return colorspace == SVGColorspace::Auto ? DEFAULT_COLOR_SPACE : colorspace; } -void SVGFe::applyProperties(SVGRenderContext* ctx) const { - this->onPrepareToRender(ctx); +void SVGFe::applyProperties(SVGRenderContext* context) const { + this->onPrepareToRender(context); } bool SVGFe::parseAndSetAttribute(const std::string& name, const std::string& value) { @@ -128,14 +130,14 @@ bool SVGAttributeParser::parse(SVGFeInputType* type) { }; SVGStringType resultId; - SVGFeInputType::Type t; + SVGFeInputType::Type tempType; bool parsedValue = false; - if (this->parseEnumMap(gTypeMap, &t)) { - *type = SVGFeInputType(t); - parsedValue = true; + if (this->parseEnumMap(gTypeMap, &tempType)) { + *type = SVGFeInputType(tempType); + parsedValue = true; } else if (parse(&resultId)) { - *type = SVGFeInputType(resultId); - parsedValue = true; + *type = SVGFeInputType(resultId); + parsedValue = true; } return parsedValue && this->parseEOSToken(); diff --git a/src/svg/node/SVGFeBlend.cpp b/src/svg/node/SVGFeBlend.cpp index 3034d080..b7ea1852 100644 --- a/src/svg/node/SVGFeBlend.cpp +++ b/src/svg/node/SVGFeBlend.cpp @@ -19,23 +19,25 @@ #include "tgfx/svg/node/SVGFeBlend.h" #include #include "svg/SVGAttributeParser.h" +#include "svg/SVGFilterContext.h" +#include "svg/SVGRenderContext.h" #include "tgfx/core/ImageFilter.h" namespace tgfx { -class SVGRenderContext; - bool SVGFeBlend::parseAndSetAttribute(const std::string& name, const std::string& value) { return INHERITED::parseAndSetAttribute(name, value) || this->setIn2(SVGAttributeParser::parse("in2", name, value)) || this->setBlendMode(SVGAttributeParser::parse("mode", name, value)); } -std::shared_ptr SVGFeBlend::onMakeImageFilter(const SVGRenderContext& ctx, - const SVGFilterContext& fctx) const { - const SVGColorspace colorspace = this->resolveColorspace(ctx, fctx); - const std::shared_ptr background = fctx.resolveInput(ctx, In2, colorspace); - const std::shared_ptr foreground = fctx.resolveInput(ctx, this->getIn(), colorspace); +std::shared_ptr SVGFeBlend::onMakeImageFilter( + const SVGRenderContext& context, const SVGFilterContext& filterContext) const { + const SVGColorspace colorspace = this->resolveColorspace(context, filterContext); + const std::shared_ptr background = + filterContext.resolveInput(context, In2, colorspace); + const std::shared_ptr foreground = + filterContext.resolveInput(context, this->getIn(), colorspace); return ImageFilter::Compose(background, foreground); // TODO (YG),relay on ImageFilters::Blend to implement this } diff --git a/src/svg/node/SVGFeComposite.cpp b/src/svg/node/SVGFeComposite.cpp index cfce2bdd..fc70e387 100644 --- a/src/svg/node/SVGFeComposite.cpp +++ b/src/svg/node/SVGFeComposite.cpp @@ -55,32 +55,20 @@ BlendMode SVGFeComposite::BlendModeForOperator(SVGFeCompositeOperator op) { } } -std::shared_ptr SVGFeComposite::onMakeImageFilter( - const SVGRenderContext& /*ctx*/, const SVGFilterContext& /*fctx*/) const { - // const Rect cropRect = this->resolveFilterSubregion(ctx, fctx); - // const SVGColorspace colorspace = this->resolveColorspace(ctx, fctx); - // const std::shared_ptr background = fctx.resolveInput(ctx, fIn2, colorspace); - // const std::shared_ptr foreground = fctx.resolveInput(ctx, this->getIn(), colorspace); - // if (fOperator == SVGFeCompositeOperator::kArithmetic) { - // constexpr bool enforcePMColor = true; - // return ImageFilter::Arithmetic(fK1, fK2, fK3, fK4, enforcePMColor, background, foreground, - // cropRect); - // } else { - // return ImageFilter::Blend(BlendModeForOperator(fOperator), background, foreground, cropRect); - // } - - //TODO (YG) +std::shared_ptr SVGFeComposite::onMakeImageFilter(const SVGRenderContext&, + const SVGFilterContext&) const { + //TODO (YGAurora) Implement this by filter blending return nullptr; } template <> bool SVGAttributeParser::parse(SVGFeCompositeOperator* op) { - static constexpr std::tuple gOpMap[] = { + static constexpr std::tuple opMap[] = { {"over", SVGFeCompositeOperator::Over}, {"in", SVGFeCompositeOperator::In}, {"out", SVGFeCompositeOperator::Out}, {"atop", SVGFeCompositeOperator::Atop}, {"xor", SVGFeCompositeOperator::Xor}, {"arithmetic", SVGFeCompositeOperator::Arithmetic}, }; - return this->parseEnumMap(gOpMap, op) && this->parseEOSToken(); + return this->parseEnumMap(opMap, op) && this->parseEOSToken(); } } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeDisplacementMap.cpp b/src/svg/node/SVGFeDisplacementMap.cpp index e996416f..8039c665 100644 --- a/src/svg/node/SVGFeDisplacementMap.cpp +++ b/src/svg/node/SVGFeDisplacementMap.cpp @@ -19,6 +19,7 @@ #include "tgfx/svg/node/SVGFeDisplacementMap.h" #include #include "svg/SVGAttributeParser.h" +#include "svg/SVGFilterContext.h" #include "svg/SVGRenderContext.h" #include "tgfx/core/Rect.h" @@ -35,33 +36,17 @@ bool SVGFeDisplacementMap::parseAndSetAttribute(const std::string& name, const s } std::shared_ptr SVGFeDisplacementMap::onMakeImageFilter( - const SVGRenderContext& ctx, const SVGFilterContext& fctx) const { - const Rect cropRect = this->resolveFilterSubregion(ctx, fctx); - cropRect.centerX(); - const SVGColorspace colorspace = this->resolveColorspace(ctx, fctx); - - // According to spec https://www.w3.org/TR/SVG11/filters.html#feDisplacementMapElement, - // the 'in' source image must remain in its current colorspace. - std::shared_ptr in = fctx.resolveInput(ctx, this->getIn()); - std::shared_ptr in2 = fctx.resolveInput(ctx, this->getIn2(), colorspace); - - float scale = Scale; - if (fctx.primitiveUnits().type() == SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox) { - const auto obbt = ctx.transformForCurrentOBB(fctx.primitiveUnits()); - scale = SVGLengthContext({obbt.scale.x, obbt.scale.y}) - .resolve(SVGLength(scale, SVGLength::Unit::Percentage), - SVGLengthContext::LengthType::Other); - } - + const SVGRenderContext&, const SVGFilterContext&) const { + //TODO (YGAurora): Implement DisplacementMap image filter. return nullptr; } -SVGColorspace SVGFeDisplacementMap::resolveColorspace(const SVGRenderContext& ctx, - const SVGFilterContext& fctx) const { +SVGColorspace SVGFeDisplacementMap::resolveColorspace(const SVGRenderContext& context, + const SVGFilterContext& filterContext) const { // According to spec https://www.w3.org/TR/SVG11/filters.html#feDisplacementMapElement, // the 'in' source image must remain in its current colorspace, which means the colorspace of // this FE node is the same as the input. - return fctx.resolveInputColorspace(ctx, this->getIn()); + return filterContext.resolveInputColorspace(context, this->getIn()); } template <> diff --git a/src/svg/node/SVGFeGaussianBlur.cpp b/src/svg/node/SVGFeGaussianBlur.cpp index 64481dfa..a95dd98c 100644 --- a/src/svg/node/SVGFeGaussianBlur.cpp +++ b/src/svg/node/SVGFeGaussianBlur.cpp @@ -18,6 +18,7 @@ #include "tgfx/svg/node/SVGFeGaussianBlur.h" #include "svg/SVGAttributeParser.h" +#include "svg/SVGFilterContext.h" #include "svg/SVGRenderContext.h" #include "tgfx/core/ImageFilter.h" #include "tgfx/core/Point.h" @@ -31,8 +32,8 @@ bool SVGFeGaussianBlur::parseAndSetAttribute(const std::string& name, const std: } std::shared_ptr SVGFeGaussianBlur::onMakeImageFilter( - const SVGRenderContext& ctx, const SVGFilterContext& fctx) const { - auto scale = ctx.transformForCurrentOBB(fctx.primitiveUnits()).scale; + const SVGRenderContext& context, const SVGFilterContext& filterContext) const { + auto scale = context.transformForCurrentBoundBox(filterContext.primitiveUnits()).scale; const auto sigmaX = stdDeviation.fX * scale.x * 4; const auto sigmaY = stdDeviation.fY * scale.y * 4; return ImageFilter::Blur(sigmaX, sigmaY); diff --git a/src/svg/node/SVGFeImage.cpp b/src/svg/node/SVGFeImage.cpp index f67a9c99..264a2e1c 100644 --- a/src/svg/node/SVGFeImage.cpp +++ b/src/svg/node/SVGFeImage.cpp @@ -28,8 +28,8 @@ bool SVGFeImage::parseAndSetAttribute(const std::string& name, const std::string SVGAttributeParser::parse("preserveAspectRatio", name, value)); } -std::shared_ptr SVGFeImage::onMakeImageFilter(const SVGRenderContext& /*ctx*/, - const SVGFilterContext& /*fctx*/) const { +std::shared_ptr SVGFeImage::onMakeImageFilter(const SVGRenderContext&, + const SVGFilterContext&) const { return nullptr; } diff --git a/src/svg/node/SVGFeMerge.cpp b/src/svg/node/SVGFeMerge.cpp index 11ed4800..8752a7d5 100644 --- a/src/svg/node/SVGFeMerge.cpp +++ b/src/svg/node/SVGFeMerge.cpp @@ -29,9 +29,9 @@ bool SVGFeMergeNode::parseAndSetAttribute(const std::string& name, const std::st this->setIn(SVGAttributeParser::parse("in", name, value)); } -std::shared_ptr SVGFeMerge::onMakeImageFilter(const SVGRenderContext& /*ctx*/, - const SVGFilterContext& /*fctx*/) const { - //TODO (YGAurora): Implement this +std::shared_ptr SVGFeMerge::onMakeImageFilter(const SVGRenderContext&, + const SVGFilterContext&) const { + //TODO (YGAurora): Implement relying on ImageFilters::Merge to implement this return nullptr; } diff --git a/src/svg/node/SVGFeMorphology.cpp b/src/svg/node/SVGFeMorphology.cpp index ecc88977..de3a7940 100644 --- a/src/svg/node/SVGFeMorphology.cpp +++ b/src/svg/node/SVGFeMorphology.cpp @@ -30,8 +30,9 @@ bool SVGFeMorphology::parseAndSetAttribute(const std::string& name, const std::s SVGAttributeParser::parse("radius", name, value)); } -std::shared_ptr SVGFeMorphology::onMakeImageFilter( - const SVGRenderContext& /*ctx*/, const SVGFilterContext& /*fctx*/) const { +std::shared_ptr SVGFeMorphology::onMakeImageFilter(const SVGRenderContext&, + const SVGFilterContext&) const { + //TODO (YGAurora) return nullptr; } diff --git a/src/svg/node/SVGFeOffset.cpp b/src/svg/node/SVGFeOffset.cpp index 1326d315..06d2c6bd 100644 --- a/src/svg/node/SVGFeOffset.cpp +++ b/src/svg/node/SVGFeOffset.cpp @@ -28,8 +28,9 @@ bool SVGFeOffset::parseAndSetAttribute(const std::string& name, const std::strin this->setDy(SVGAttributeParser::parse("dy", name, value)); } -std::shared_ptr SVGFeOffset::onMakeImageFilter( - const SVGRenderContext& /*ctx*/, const SVGFilterContext& /*fctx*/) const { +std::shared_ptr SVGFeOffset::onMakeImageFilter(const SVGRenderContext&, + const SVGFilterContext&) const { + //TODO (YGAurora) return nullptr; } diff --git a/src/svg/node/SVGFeTurbulence.cpp b/src/svg/node/SVGFeTurbulence.cpp index b4e97f69..dfef1502 100644 --- a/src/svg/node/SVGFeTurbulence.cpp +++ b/src/svg/node/SVGFeTurbulence.cpp @@ -55,8 +55,9 @@ bool SVGAttributeParser::parse(SVGFeTurbulenceType* type) { return parsedValue && this->parseEOSToken(); } -std::shared_ptr SVGFeTurbulence::onMakeImageFilter( - const SVGRenderContext& /*ctx*/, const SVGFilterContext& /*fctx*/) const { +std::shared_ptr SVGFeTurbulence::onMakeImageFilter(const SVGRenderContext&, + const SVGFilterContext&) const { + //TODO (YGAurora) return nullptr; } diff --git a/src/svg/node/SVGFilter.cpp b/src/svg/node/SVGFilter.cpp index f293cf06..f5944f43 100644 --- a/src/svg/node/SVGFilter.cpp +++ b/src/svg/node/SVGFilter.cpp @@ -18,11 +18,11 @@ #include "tgfx/svg/node/SVGFilter.h" #include "svg/SVGAttributeParser.h" +#include "svg/SVGFilterContext.h" #include "svg/SVGRenderContext.h" #include "tgfx/core/ImageFilter.h" #include "tgfx/core/Rect.h" #include "tgfx/svg/node/SVGFe.h" -#include "tgfx/svg/node/SVGFilterContext.h" namespace tgfx { @@ -38,15 +38,16 @@ bool SVGFilter::parseAndSetAttribute(const std::string& name, const std::string& SVGAttributeParser::parse("primitiveUnits", name, value)); } -void SVGFilter::applyProperties(SVGRenderContext* ctx) const { - this->onPrepareToRender(ctx); +void SVGFilter::applyProperties(SVGRenderContext* context) const { + this->onPrepareToRender(context); } -std::shared_ptr SVGFilter::buildFilterDAG(const SVGRenderContext& ctx) const { +std::shared_ptr SVGFilter::buildFilterDAG(const SVGRenderContext& context) const { std::shared_ptr filter; - SVGFilterContext fctx(ctx.resolveOBBRect(X, Y, Width, Height, FilterUnits), PrimitiveUnits); - SVGRenderContext localCtx(ctx); - this->applyProperties(&localCtx); + SVGFilterContext filterContext(context.resolveOBBRect(X, Y, Width, Height, FilterUnits), + PrimitiveUnits); + SVGRenderContext localContext(context); + this->applyProperties(&localContext); SVGColorspace cs = SVGColorspace::SRGB; for (const auto& child : children) { if (!SVGFe::IsFilterEffect(child)) { @@ -60,19 +61,19 @@ std::shared_ptr SVGFilter::buildFilterDAG(const SVGRenderContext& c // color-interpolation-filters). We call this explicitly here because the SVGFe // nodes do not participate in the normal onRender path, which is when property // propagation currently occurs. - SVGRenderContext localChildCtx(localCtx); + SVGRenderContext localChildCtx(localContext); feNode.applyProperties(&localChildCtx); - const Rect filterSubregion = feNode.resolveFilterSubregion(localChildCtx, fctx); - cs = feNode.resolveColorspace(localChildCtx, fctx); - filter = feNode.makeImageFilter(localChildCtx, fctx); + const Rect filterSubregion = feNode.resolveFilterSubregion(localChildCtx, filterContext); + cs = feNode.resolveColorspace(localChildCtx, filterContext); + filter = feNode.makeImageFilter(localChildCtx, filterContext); if (!feResultType.empty()) { - fctx.registerResult(feResultType, filter, filterSubregion, cs); + filterContext.registerResult(feResultType, filter, filterSubregion, cs); } // Unspecified 'in' and 'in2' inputs implicitly resolve to the previous filter's result. - fctx.setPreviousResult(filter, filterSubregion, cs); + filterContext.setPreviousResult(filter, filterSubregion, cs); } // Convert to final destination colorspace diff --git a/src/svg/node/SVGGradient.cpp b/src/svg/node/SVGGradient.cpp index 1fc20416..5c54bbde 100644 --- a/src/svg/node/SVGGradient.cpp +++ b/src/svg/node/SVGGradient.cpp @@ -39,13 +39,13 @@ bool SVGGradient::parseAndSetAttribute(const std::string& name, const std::strin } // https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementHrefAttribute -void SVGGradient::collectColorStops(const SVGRenderContext& ctx, std::vector& colors, +void SVGGradient::collectColorStops(const SVGRenderContext& context, std::vector& colors, std::vector& positions) const { // Used to resolve percentage offsets. const SVGLengthContext ltx(Size::Make(1, 1)); this->forEachChild([&](const SVGStop* stop) { - colors.push_back(this->resolveStopColor(ctx, *stop)); + colors.push_back(this->resolveStopColor(context, *stop)); positions.push_back( std::clamp(ltx.resolve(stop->getOffset(), SVGLengthContext::LengthType::Other), 0.f, 1.f)); }); @@ -53,14 +53,14 @@ void SVGGradient::collectColorStops(const SVGRenderContext& ctx, std::vectortag() == SVGTag::LinearGradient || ref->tag() == SVGTag::RadialGradient)) { - static_cast(ref.get())->collectColorStops(ctx, colors, positions); + static_cast(ref.get())->collectColorStops(context, colors, positions); } } } -Color SVGGradient::resolveStopColor(const SVGRenderContext& ctx, const SVGStop& stop) const { +Color SVGGradient::resolveStopColor(const SVGRenderContext& context, const SVGStop& stop) const { const auto& stopColor = stop.getStopColor(); const auto& stopOpacity = stop.getStopOpacity(); // Uninherited presentation attrs should have a concrete value at this point. @@ -68,36 +68,25 @@ Color SVGGradient::resolveStopColor(const SVGRenderContext& ctx, const SVGStop& return Color::Black(); } - const auto color = ctx.resolveSvgColor(*stopColor); + const auto color = context.resolveSVGColor(*stopColor); return {color.red, color.green, color.blue, *stopOpacity * color.alpha}; } -bool SVGGradient::onAsPaint(const SVGRenderContext& ctx, Paint* paint) const { +bool SVGGradient::onAsPaint(const SVGRenderContext& context, Paint* paint) const { std::vector colors; std::vector positions; - this->collectColorStops(ctx, colors, positions); + this->collectColorStops(context, colors, positions); - // TODO: - // * stop (lazy?) sorting - // * href loop detection - // * href attribute inheritance (not just color stops) - // * objectBoundingBox units support - - static_assert(static_cast(SVGSpreadMethod::Type::Pad) == TileMode::Clamp, - "SkSVGSpreadMethod::Type is out of sync"); - static_assert(static_cast(SVGSpreadMethod::Type::Repeat) == TileMode::Repeat, - "SkSVGSpreadMethod::Type is out of sync"); - static_assert(static_cast(SVGSpreadMethod::Type::Reflect) == TileMode::Mirror, - "SkSVGSpreadMethod::Type is out of sync"); const auto tileMode = static_cast(SpreadMethod.type()); - const auto obbt = ctx.transformForCurrentOBB(GradientUnits); - const auto localMatrix = Matrix::MakeTrans(obbt.offset.x, obbt.offset.y) * - Matrix::MakeScale(obbt.scale.x, obbt.scale.y) * GradientTransform; + const auto transform = context.transformForCurrentBoundBox(GradientUnits); + const auto localMatrix = Matrix::MakeTrans(transform.offset.x, transform.offset.y) * + Matrix::MakeScale(transform.scale.x, transform.scale.y) * + GradientTransform; - paint->setShader(this->onMakeShader(ctx, colors, positions, tileMode, localMatrix)); + paint->setShader(this->onMakeShader(context, colors, positions, tileMode, localMatrix)); return true; } @@ -105,8 +94,8 @@ bool SVGGradient::onAsPaint(const SVGRenderContext& ctx, Paint* paint) const { template <> bool SVGAttributeParser::parse(SVGSpreadMethod* spread) { struct TileInfo { - SVGSpreadMethod::Type fType; - const char* fName; + SVGSpreadMethod::Type type; + const char* name; }; static const TileInfo spreadInfoSet[] = { {SVGSpreadMethod::Type::Pad, "pad"}, @@ -116,8 +105,8 @@ bool SVGAttributeParser::parse(SVGSpreadMethod* spread) { bool parsedValue = false; for (auto info : spreadInfoSet) { - if (this->parseExpectedStringToken(info.fName)) { - *spread = SVGSpreadMethod(info.fType); + if (this->parseExpectedStringToken(info.name)) { + *spread = SVGSpreadMethod(info.type); parsedValue = true; break; } diff --git a/src/svg/node/SVGImage.cpp b/src/svg/node/SVGImage.cpp index 935e2af8..b33acea4 100644 --- a/src/svg/node/SVGImage.cpp +++ b/src/svg/node/SVGImage.cpp @@ -39,11 +39,11 @@ bool SVGImage::parseAndSetAttribute(const std::string& n, const std::string& v) SVGAttributeParser::parse("preserveAspectRatio", n, v)); } -bool SVGImage::onPrepareToRender(SVGRenderContext* ctx) const { +bool SVGImage::onPrepareToRender(SVGRenderContext* context) const { // Width or height of 0 disables rendering per spec: // https://www.w3.org/TR/SVG11/struct.html#ImageElement return !Href.iri().empty() && Width.value() > 0 && Height.value() > 0 && - INHERITED::onPrepareToRender(ctx); + INHERITED::onPrepareToRender(context); } std::vector base64_decode(const std::string& encoded_string) { @@ -118,10 +118,10 @@ SVGImage::ImageInfo SVGImage::LoadImage(const SVGIRI& iri, const Rect& viewPort, return {std::move(image), dst}; } -void SVGImage::onRender(const SVGRenderContext& ctx) const { +void SVGImage::onRender(const SVGRenderContext& context) const { // Per spec: x, w, width, height attributes establish the new viewport. - const SVGLengthContext& lctx = ctx.lengthContext(); - const Rect viewPort = lctx.resolveRect(X, Y, Width, Height); + const SVGLengthContext& lengthContext = context.lengthContext(); + const Rect viewPort = lengthContext.resolveRect(X, Y, Width, Height); ImageInfo image; const auto imgInfo = LoadImage(Href, viewPort, PreserveAspectRatio); @@ -134,18 +134,16 @@ void SVGImage::onRender(const SVGRenderContext& ctx) const { viewPort.height() / imgInfo.fDst.height()); matrix.preTranslate(imgInfo.fDst.x(), imgInfo.fDst.y()); - ctx.canvas()->drawImage(imgInfo.fImage, matrix); - - // drawImageRect(imgInfo.fImage, imgInfo.fDst, SkSamplingOptions(SkFilterMode::kLinear)); + context.canvas()->drawImage(imgInfo.fImage, matrix); } Path SVGImage::onAsPath(const SVGRenderContext&) const { return {}; } -Rect SVGImage::onObjectBoundingBox(const SVGRenderContext& ctx) const { - const SVGLengthContext& lctx = ctx.lengthContext(); - return lctx.resolveRect(X, Y, Width, Height); +Rect SVGImage::onObjectBoundingBox(const SVGRenderContext& context) const { + const SVGLengthContext& lengthContext = context.lengthContext(); + return lengthContext.resolveRect(X, Y, Width, Height); } } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGLine.cpp b/src/svg/node/SVGLine.cpp index 15657af0..163db8df 100644 --- a/src/svg/node/SVGLine.cpp +++ b/src/svg/node/SVGLine.cpp @@ -36,21 +36,22 @@ bool SVGLine::parseAndSetAttribute(const std::string& n, const std::string& v) { this->setY2(SVGAttributeParser::parse("y2", n, v)); } -std::tuple SVGLine::resolve(const SVGLengthContext& lctx) const { - return std::make_tuple(Point::Make(lctx.resolve(X1, SVGLengthContext::LengthType::Horizontal), - lctx.resolve(Y1, SVGLengthContext::LengthType::Vertical)), - Point::Make(lctx.resolve(X2, SVGLengthContext::LengthType::Horizontal), - lctx.resolve(Y2, SVGLengthContext::LengthType::Vertical))); +std::tuple SVGLine::resolve(const SVGLengthContext& lengthContext) const { + return std::make_tuple( + Point::Make(lengthContext.resolve(X1, SVGLengthContext::LengthType::Horizontal), + lengthContext.resolve(Y1, SVGLengthContext::LengthType::Vertical)), + Point::Make(lengthContext.resolve(X2, SVGLengthContext::LengthType::Horizontal), + lengthContext.resolve(Y2, SVGLengthContext::LengthType::Vertical))); } -void SVGLine::onDraw(Canvas* canvas, const SVGLengthContext& lctx, const Paint& paint, +void SVGLine::onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, PathFillType) const { - auto [p0, p1] = this->resolve(lctx); + auto [p0, p1] = this->resolve(lengthContext); canvas->drawLine(p0, p1, paint); } -Path SVGLine::onAsPath(const SVGRenderContext& ctx) const { - auto [p0, p1] = this->resolve(ctx.lengthContext()); +Path SVGLine::onAsPath(const SVGRenderContext& context) const { + auto [p0, p1] = this->resolve(context.lengthContext()); //TODO (YG) path add methods to support line Path path; diff --git a/src/svg/node/SVGLinearGradient.cpp b/src/svg/node/SVGLinearGradient.cpp index f0d8da72..a8aa2d54 100644 --- a/src/svg/node/SVGLinearGradient.cpp +++ b/src/svg/node/SVGLinearGradient.cpp @@ -37,17 +37,17 @@ bool SVGLinearGradient::parseAndSetAttribute(const std::string& name, const std: this->setY2(SVGAttributeParser::parse("y2", name, value)); } -std::shared_ptr SVGLinearGradient::onMakeShader(const SVGRenderContext& ctx, +std::shared_ptr SVGLinearGradient::onMakeShader(const SVGRenderContext& context, const std::vector& colors, const std::vector& positions, TileMode, const Matrix&) const { - SVGLengthContext lctx = ctx.lengthContext(); - lctx.setPatternUnits(getGradientUnits()); + SVGLengthContext lengthContext = context.lengthContext(); + lengthContext.setPatternUnits(getGradientUnits()); - auto startPoint = Point::Make(lctx.resolve(X1, SVGLengthContext::LengthType::Horizontal), - lctx.resolve(Y1, SVGLengthContext::LengthType::Vertical)); - auto endPoint = Point::Make(lctx.resolve(X2, SVGLengthContext::LengthType::Horizontal), - lctx.resolve(Y2, SVGLengthContext::LengthType::Vertical)); + auto startPoint = Point::Make(lengthContext.resolve(X1, SVGLengthContext::LengthType::Horizontal), + lengthContext.resolve(Y1, SVGLengthContext::LengthType::Vertical)); + auto endPoint = Point::Make(lengthContext.resolve(X2, SVGLengthContext::LengthType::Horizontal), + lengthContext.resolve(Y2, SVGLengthContext::LengthType::Vertical)); return Shader::MakeLinearGradient(startPoint, endPoint, colors, positions); } diff --git a/src/svg/node/SVGNode.cpp b/src/svg/node/SVGNode.cpp index 00f97c05..82b85616 100644 --- a/src/svg/node/SVGNode.cpp +++ b/src/svg/node/SVGNode.cpp @@ -35,32 +35,31 @@ namespace tgfx { SVGNode::SVGNode(SVGTag t) : _tag(t) { // Uninherited presentation attributes need a non-null default value. - _presentationAttributes.StopColor.set(SVGColor(Color::Black())); - _presentationAttributes.StopOpacity.set(static_cast(1.0f)); - _presentationAttributes.FloodColor.set(SVGColor(Color::Black())); - _presentationAttributes.FloodOpacity.set(static_cast(1.0f)); - _presentationAttributes.LightingColor.set(SVGColor(Color::White())); + presentationAttributes.StopColor.set(SVGColor(Color::Black())); + presentationAttributes.StopOpacity.set(static_cast(1.0f)); + presentationAttributes.FloodColor.set(SVGColor(Color::Black())); + presentationAttributes.FloodOpacity.set(static_cast(1.0f)); + presentationAttributes.LightingColor.set(SVGColor(Color::White())); } -SVGNode::~SVGNode() { -} +SVGNode::~SVGNode() = default; -void SVGNode::render(const SVGRenderContext& ctx) const { - SVGRenderContext localContext(ctx, this); +void SVGNode::render(const SVGRenderContext& context) const { + SVGRenderContext localContext(context, this); if (this->onPrepareToRender(&localContext)) { this->onRender(localContext); } } -bool SVGNode::asPaint(const SVGRenderContext& ctx, Paint* paint) const { - SVGRenderContext localContext(ctx); +bool SVGNode::asPaint(const SVGRenderContext& context, Paint* paint) const { + SVGRenderContext localContext(context); return this->onPrepareToRender(&localContext) && this->onAsPaint(localContext, paint); } -Path SVGNode::asPath(const SVGRenderContext& ctx) const { - SVGRenderContext localContext(ctx); +Path SVGNode::asPath(const SVGRenderContext& context) const { + SVGRenderContext localContext(context); if (!this->onPrepareToRender(&localContext)) { return Path(); } @@ -75,19 +74,17 @@ Path SVGNode::asPath(const SVGRenderContext& ctx) const { return path; } -Rect SVGNode::objectBoundingBox(const SVGRenderContext& ctx) const { - return this->onObjectBoundingBox(ctx); +Rect SVGNode::objectBoundingBox(const SVGRenderContext& context) const { + return this->onObjectBoundingBox(context); } -bool SVGNode::onPrepareToRender(SVGRenderContext* ctx) const { - ctx->applyPresentationAttributes(_presentationAttributes, - this->hasChildren() ? 0 : SVGRenderContext::kLeaf); +bool SVGNode::onPrepareToRender(SVGRenderContext* context) const { + context->applyPresentationAttributes(presentationAttributes, + this->hasChildren() ? 0 : SVGRenderContext::kLeaf); // visibility:hidden and display:none disable rendering. - // TODO: if display is not a value (true when display="inherit"), we currently - // ignore it. Eventually we should be able to add SkASSERT(display.isValue()). - const auto visibility = ctx->presentationContext()._inherited.Visibility->type(); - const auto display = _presentationAttributes.Display; // display is uninherited + const auto visibility = context->presentationContext()._inherited.Visibility->type(); + const auto display = presentationAttributes.Display; // display is uninherited return visibility != SVGVisibility::Type::Hidden && (!display.isValue() || *display != SVGDisplay::None); } @@ -108,10 +105,9 @@ void SetInheritedByDefault(std::optional& presentation_attribute, const T& va } bool SVGNode::parseAndSetAttribute(const std::string& n, const std::string& v) { -#define PARSE_AND_SET(svgName, attrName) \ - this->set##attrName( \ - SVGAttributeParser::parseProperty(svgName, n, \ - v)) +#define PARSE_AND_SET(svgName, attrName) \ + this->set##attrName( \ + SVGAttributeParser::parseProperty(svgName, n, v)) return PARSE_AND_SET("clip-path", ClipPath) || PARSE_AND_SET("clip-rule", ClipRule) || PARSE_AND_SET("color", Color) || diff --git a/src/svg/node/SVGPath.cpp b/src/svg/node/SVGPath.cpp index 9ede367e..611a332e 100644 --- a/src/svg/node/SVGPath.cpp +++ b/src/svg/node/SVGPath.cpp @@ -37,7 +37,7 @@ bool SVGPath::parseAndSetAttribute(const std::string& n, const std::string& v) { template <> bool SVGAttributeParser::parse(Path* path) { - auto [success, parsePath] = PathParse::FromSVGString(fCurPos); + auto [success, parsePath] = PathParse::FromSVGString(currentPos); if (success) { *path = *parsePath; } @@ -52,10 +52,10 @@ void SVGPath::onDraw(Canvas* canvas, const SVGLengthContext&, const Paint& paint canvas->drawPath(path, paint); } -Path SVGPath::onAsPath(const SVGRenderContext& ctx) const { +Path SVGPath::onAsPath(const SVGRenderContext& context) const { Path path = ShapePath; // clip-rule can be inherited and needs to be applied at clip time. - path.setFillType(ctx.presentationContext()._inherited.ClipRule->asFillType()); + path.setFillType(context.presentationContext()._inherited.ClipRule->asFillType()); this->mapToParent(&path); return path; } diff --git a/src/svg/node/SVGPattern.cpp b/src/svg/node/SVGPattern.cpp index 3f0c61ac..35f7382b 100644 --- a/src/svg/node/SVGPattern.cpp +++ b/src/svg/node/SVGPattern.cpp @@ -49,12 +49,12 @@ bool SVGPattern::parseAndSetAttribute(const std::string& name, const std::string "patternContentUnits", name, value)); } -const SVGPattern* SVGPattern::hrefTarget(const SVGRenderContext& ctx) const { +const SVGPattern* SVGPattern::hrefTarget(const SVGRenderContext& context) const { if (Href.iri().empty()) { return nullptr; } - const auto href = ctx.findNodeById(Href); + const auto href = context.findNodeById(Href); if (!href || href->tag() != SVGTag::Pattern) { return nullptr; } @@ -81,7 +81,7 @@ int inherit_if_needed(const std::optional& src, std::optional& dst) { * referenced element inherits attributes or children due to its own ‘xlink:href’ attribute, then * the current element can inherit those attributes or children. */ -const SVGPattern* SVGPattern::resolveHref(const SVGRenderContext& ctx, +const SVGPattern* SVGPattern::resolveHref(const SVGRenderContext& context, PatternAttributes* attrs) const { const SVGPattern* currentNode = this; const SVGPattern* contentNode = this; @@ -104,7 +104,7 @@ const SVGPattern* SVGPattern::resolveHref(const SVGRenderContext& ctx, break; } - currentNode = currentNode->hrefTarget(ctx); + currentNode = currentNode->hrefTarget(context); } while (currentNode); // To unify with Chrome and macOS preview, the width and height attributes here need to be @@ -128,10 +128,10 @@ const SVGPattern* SVGPattern::resolveHref(const SVGRenderContext& ctx, return contentNode; } -bool SVGPattern::onAsPaint(const SVGRenderContext& ctx, Paint* paint) const { +bool SVGPattern::onAsPaint(const SVGRenderContext& context, Paint* paint) const { PatternAttributes attrs; - const auto* contentNode = this->resolveHref(ctx, &attrs); - auto lengthContext = ctx.lengthContext(); + const auto* contentNode = this->resolveHref(context, &attrs); + auto lengthContext = context.lengthContext(); lengthContext.setPatternUnits(PatternUnits); Rect tile = lengthContext.resolveRect(attrs.x.has_value() ? *attrs.x : SVGLength(0), attrs.y.has_value() ? *attrs.y : SVGLength(0), @@ -147,7 +147,7 @@ bool SVGPattern::onAsPaint(const SVGRenderContext& ctx, Paint* paint) const { auto patternMatrix = attrs.patternTransform.value_or(Matrix::I()); canvas->concat(patternMatrix); { - SVGRenderContext recordingContext(ctx, canvas, lengthContext); + SVGRenderContext recordingContext(context, canvas, lengthContext); contentNode->SVGContainer::onRender(recordingContext); } auto picture = patternRecorder.finishRecordingAsPicture(); diff --git a/src/svg/node/SVGPoly.cpp b/src/svg/node/SVGPoly.cpp index 8ac515d6..dc34db9b 100644 --- a/src/svg/node/SVGPoly.cpp +++ b/src/svg/node/SVGPoly.cpp @@ -20,6 +20,7 @@ #include "svg/SVGAttributeParser.h" #include "svg/SVGRenderContext.h" #include "tgfx/core/Canvas.h" +#include "tgfx/core/Path.h" namespace tgfx { @@ -32,35 +33,30 @@ bool SVGPoly::parseAndSetAttribute(const std::string& n, const std::string& v) { } if (this->setPoints(SVGAttributeParser::parse("points", n, v))) { - // TODO: we can likely just keep the points array and create the SkPath when needed. - // fPath = SkPath::Polygon( - // fPoints.data(), fPoints.size(), - // this->tag() == SkSVGTag::kPolygon); // only polygons are auto-closed + // TODO (YGAurora): construct the polygon path by points. } - - // No other attributes on this node return false; } void SVGPoly::onDraw(Canvas* canvas, const SVGLengthContext&, const Paint& paint, PathFillType fillType) const { // the passed fillType follows inheritance rules and needs to be applied at draw time. - fPath.setFillType(fillType); - canvas->drawPath(fPath, paint); + path.setFillType(fillType); + canvas->drawPath(path, paint); } -Path SVGPoly::onAsPath(const SVGRenderContext& ctx) const { - Path path = fPath; +Path SVGPoly::onAsPath(const SVGRenderContext& context) const { + Path resultPath = path; // clip-rule can be inherited and needs to be applied at clip time. - path.setFillType(ctx.presentationContext()._inherited.ClipRule->asFillType()); + resultPath.setFillType(context.presentationContext()._inherited.ClipRule->asFillType()); - this->mapToParent(&path); - return path; + this->mapToParent(&resultPath); + return resultPath; } Rect SVGPoly::onObjectBoundingBox(const SVGRenderContext&) const { - return fPath.getBounds(); + return path.getBounds(); } } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGRadialGradient.cpp b/src/svg/node/SVGRadialGradient.cpp index c5792d99..21b57052 100644 --- a/src/svg/node/SVGRadialGradient.cpp +++ b/src/svg/node/SVGRadialGradient.cpp @@ -39,20 +39,20 @@ bool SVGRadialGradient::parseAndSetAttribute(const std::string& name, const std: this->setFy(SVGAttributeParser::parse("fy", name, value)); } -std::shared_ptr SVGRadialGradient::onMakeShader(const SVGRenderContext& ctx, +std::shared_ptr SVGRadialGradient::onMakeShader(const SVGRenderContext& context, const std::vector& colors, const std::vector& position, TileMode, const Matrix& matrix) const { - SVGLengthContext lctx = ctx.lengthContext(); - lctx.setPatternUnits(getGradientUnits()); + SVGLengthContext lengthContext = context.lengthContext(); + lengthContext.setPatternUnits(getGradientUnits()); - auto radius = lctx.resolve(R, SVGLengthContext::LengthType::Other); - auto center = Point::Make(lctx.resolve(Cx, SVGLengthContext::LengthType::Horizontal), - lctx.resolve(Cy, SVGLengthContext::LengthType::Vertical)); + auto radius = lengthContext.resolve(R, SVGLengthContext::LengthType::Other); + auto center = Point::Make(lengthContext.resolve(Cx, SVGLengthContext::LengthType::Horizontal), + lengthContext.resolve(Cy, SVGLengthContext::LengthType::Vertical)); // TODO(YGAurora): MakeTwoPointConical are unimplemented in tgfx if (radius == 0) { - const auto lastColor = colors.size() > 0 ? *colors.end() : Color::Black(); + const auto lastColor = !colors.empty() ? *colors.end() : Color::Black(); return Shader::MakeColorShader(lastColor); } matrix.mapPoints(¢er, 1); diff --git a/src/svg/node/SVGRect.cpp b/src/svg/node/SVGRect.cpp index 33de14d9..b176c520 100644 --- a/src/svg/node/SVGRect.cpp +++ b/src/svg/node/SVGRect.cpp @@ -18,7 +18,7 @@ #include "tgfx/svg/node/SVGRect.h" #include -#include "SVGRectPriv.h" +// #include "SVGRectPriv.h" #include "svg/SVGAttributeParser.h" #include "svg/SVGRenderContext.h" #include "tgfx/core/Canvas.h" @@ -41,39 +41,9 @@ bool SVGRect::parseAndSetAttribute(const std::string& n, const std::string& v) { this->setRy(SVGAttributeParser::parse("ry", n, v)); } -std::tuple ResolveOptionalRadii(const std::optional& opt_rx, - const std::optional& opt_ry, - const SVGLengthContext& lctx) { - // https://www.w3.org/TR/SVG2/shapes.html#RectElement - // - // The used values for rx and ry are determined from the computed values by following these - // steps in order: - // - // 1. If both rx and ry have a computed value of auto (since auto is the initial value for both - // properties, this will also occur if neither are specified by the author or if all - // author-supplied values are invalid), then the used value of both rx and ry is 0. - // (This will result in square corners.) - // 2. Otherwise, convert specified values to absolute values as follows: - // 1. If rx is set to a length value or a percentage, but ry is auto, calculate an absolute - // length equivalent for rx, resolving percentages against the used width of the - // rectangle; the absolute value for ry is the same. - // 2. If ry is set to a length value or a percentage, but rx is auto, calculate the absolute - // length equivalent for ry, resolving percentages against the used height of the - // rectangle; the absolute value for rx is the same. - // 3. If both rx and ry were set to lengths or percentages, absolute values are generated - // individually, resolving rx percentages against the used width, and resolving ry - // percentages against the used height. - const float rx = - opt_rx.has_value() ? lctx.resolve(*opt_rx, SVGLengthContext::LengthType::Horizontal) : 0; - const float ry = - opt_ry.has_value() ? lctx.resolve(*opt_ry, SVGLengthContext::LengthType::Vertical) : 0; - - return {opt_rx.has_value() ? rx : ry, opt_ry.has_value() ? ry : rx}; -} - -RRect SVGRect::resolve(const SVGLengthContext& lctx) const { - const auto rect = lctx.resolveRect(X, Y, Width, Height); - const auto [rx, ry] = ResolveOptionalRadii(Rx, Ry, lctx); +RRect SVGRect::resolve(const SVGLengthContext& lengthContext) const { + const auto rect = lengthContext.resolveRect(X, Y, Width, Height); + const auto [rx, ry] = lengthContext.resolveOptionalRadii(Rx, Ry); // https://www.w3.org/TR/SVG2/shapes.html#RectElement // ... @@ -89,9 +59,9 @@ RRect SVGRect::resolve(const SVGLengthContext& lctx) const { return rrect; } -void SVGRect::onDraw(Canvas* canvas, const SVGLengthContext& lctx, const Paint& paint, +void SVGRect::onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, PathFillType) const { - auto rect = this->resolve(lctx); + auto rect = this->resolve(lengthContext); auto offset = Point::Make(rect.rect.left, rect.rect.top); rect.rect = rect.rect.makeOffset(-offset.x, -offset.y); canvas->save(); @@ -100,16 +70,16 @@ void SVGRect::onDraw(Canvas* canvas, const SVGLengthContext& lctx, const Paint& canvas->restore(); } -Path SVGRect::onAsPath(const SVGRenderContext& ctx) const { +Path SVGRect::onAsPath(const SVGRenderContext& context) const { Path path; - path.addRRect(this->resolve(ctx.lengthContext())); + path.addRRect(this->resolve(context.lengthContext())); this->mapToParent(&path); return path; } -Rect SVGRect::onObjectBoundingBox(const SVGRenderContext& ctx) const { - return ctx.lengthContext().resolveRect(X, Y, Width, Height); +Rect SVGRect::onObjectBoundingBox(const SVGRenderContext& context) const { + return context.lengthContext().resolveRect(X, Y, Width, Height); } } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGRectPriv.h b/src/svg/node/SVGRectPriv.h deleted file mode 100644 index 00083fb9..00000000 --- a/src/svg/node/SVGRectPriv.h +++ /dev/null @@ -1,30 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making tgfx available. -// -// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// unless required by applicable law or agreed to in writing, software distributed under the -// license is distributed on an "as is" basis, without warranties or conditions of any kind, -// either express or implied. see the license for the specific language governing permissions -// and limitations under the license. -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -#include -#include - -class SkSVGLength; -class SkSVGLengthContext; - -namespace tgfx { - -std::tuple ResolveOptionalRadii(const std::optional& rx, - const std::optional& ry, - const SVGLengthContext&); -} diff --git a/src/svg/node/SVGSVG.cpp b/src/svg/node/SVGSVG.cpp index 78a697dc..4ad7298e 100644 --- a/src/svg/node/SVGSVG.cpp +++ b/src/svg/node/SVGSVG.cpp @@ -25,8 +25,8 @@ namespace tgfx { -void SVGSVG::renderNode(const SVGRenderContext& ctx, const SVGIRI& iri) const { - SVGRenderContext localContext(ctx, this); +void SVGSVG::renderNode(const SVGRenderContext& context, const SVGIRI& iri) const { + SVGRenderContext localContext(context, this); auto node = localContext.findNodeById(iri); if (!node) { return; @@ -34,19 +34,19 @@ void SVGSVG::renderNode(const SVGRenderContext& ctx, const SVGIRI& iri) const { if (this->onPrepareToRender(&localContext)) { if (this == node.get()) { - this->onRender(ctx); + this->onRender(context); } else { node->render(localContext); } } } -bool SVGSVG::onPrepareToRender(SVGRenderContext* ctx) const { +bool SVGSVG::onPrepareToRender(SVGRenderContext* context) const { // x/y are ignored for outermost svg elements const auto x = type == Type::kInner ? X : SVGLength(0); const auto y = type == Type::kInner ? Y : SVGLength(0); - auto viewPortRect = ctx->lengthContext().resolveRect(x, y, Width, Height); + auto viewPortRect = context->lengthContext().resolveRect(x, y, Width, Height); auto contentMatrix = Matrix::MakeTrans(viewPortRect.x(), viewPortRect.y()); auto viewPort = Size::Make(viewPortRect.width(), viewPortRect.height()); @@ -65,26 +65,26 @@ bool SVGSVG::onPrepareToRender(SVGRenderContext* ctx) const { } if (!contentMatrix.isIdentity()) { - ctx->saveOnce(); - ctx->canvas()->concat(contentMatrix); + context->saveOnce(); + context->canvas()->concat(contentMatrix); } - if (viewPort != ctx->lengthContext().viewPort()) { - ctx->writableLengthContext()->setViewPort(viewPort); + if (viewPort != context->lengthContext().viewPort()) { + context->writableLengthContext()->setViewPort(viewPort); } - return this->INHERITED::onPrepareToRender(ctx); + return this->INHERITED::onPrepareToRender(context); } // https://www.w3.org/TR/SVG11/coords.html#IntrinsicSizing -Size SVGSVG::intrinsicSize(const SVGLengthContext& lctx) const { +Size SVGSVG::intrinsicSize(const SVGLengthContext& lengthContext) const { // Percentage values do not provide an intrinsic size. if (Width.unit() == SVGLength::Unit::Percentage || Height.unit() == SVGLength::Unit::Percentage) { return Size::Make(0, 0); } - return Size::Make(lctx.resolve(Width, SVGLengthContext::LengthType::Horizontal), - lctx.resolve(Height, SVGLengthContext::LengthType::Vertical)); + return Size::Make(lengthContext.resolve(Width, SVGLengthContext::LengthType::Horizontal), + lengthContext.resolve(Height, SVGLengthContext::LengthType::Vertical)); } void SVGSVG::onSetAttribute(SVGAttribute attr, const SVGValue& v) { diff --git a/src/svg/node/SVGShape.cpp b/src/svg/node/SVGShape.cpp index 8aea7879..4986eec4 100644 --- a/src/svg/node/SVGShape.cpp +++ b/src/svg/node/SVGShape.cpp @@ -22,31 +22,28 @@ #include "svg/SVGRenderContext.h" #include "tgfx/core/Size.h" -class SkSVGNode; -enum class SkSVGTag; - namespace tgfx { SVGShape::SVGShape(SVGTag t) : INHERITED(t) { } -void SVGShape::onRender(const SVGRenderContext& ctx) const { - const auto fillType = ctx.presentationContext()._inherited.FillRule->asFillType(); +void SVGShape::onRender(const SVGRenderContext& context) const { + const auto fillType = context.presentationContext()._inherited.FillRule->asFillType(); - auto selfRect = onObjectBoundingBox(ctx); - auto lengthCtx = ctx.lengthContext(); + auto selfRect = onObjectBoundingBox(context); + auto lengthCtx = context.lengthContext(); lengthCtx.setViewPort(Size::Make(selfRect.width(), selfRect.height())); - auto paintCtx = SVGRenderContext::CopyForPaint(ctx, ctx.canvas(), lengthCtx); + auto paintCtx = SVGRenderContext::CopyForPaint(context, context.canvas(), lengthCtx); const auto fillPaint = paintCtx.fillPaint(); const auto strokePaint = paintCtx.strokePaint(); if (fillPaint.has_value()) { - this->onDraw(ctx.canvas(), ctx.lengthContext(), fillPaint.value(), fillType); + this->onDraw(context.canvas(), context.lengthContext(), fillPaint.value(), fillType); } if (strokePaint.has_value()) { - this->onDraw(ctx.canvas(), ctx.lengthContext(), strokePaint.value(), fillType); + this->onDraw(context.canvas(), context.lengthContext(), strokePaint.value(), fillType); } } diff --git a/src/svg/node/SVGText.cpp b/src/svg/node/SVGText.cpp index e65f53b8..dd0b1743 100644 --- a/src/svg/node/SVGText.cpp +++ b/src/svg/node/SVGText.cpp @@ -225,7 +225,7 @@ void SVGText::onRender(const SVGRenderContext& context) const { this->onShapeText(context, renderer); } -Rect SVGText::onObjectBoundingBox(const SVGRenderContext& ctx) const { +Rect SVGText::onObjectBoundingBox(const SVGRenderContext& context) const { Rect bounds = Rect::MakeEmpty(); auto boundCollector = [&bounds](const SVGRenderContext&, @@ -237,7 +237,7 @@ Rect SVGText::onObjectBoundingBox(const SVGRenderContext& ctx) const { bounds.join(textBound); }; - this->onShapeText(ctx, boundCollector); + this->onShapeText(context, boundCollector); return bounds; } @@ -246,9 +246,9 @@ Path SVGText::onAsPath(const SVGRenderContext&) const { return Path(); } -void SVGTextPath::onShapeText(const SVGRenderContext& ctx, +void SVGTextPath::onShapeText(const SVGRenderContext& context, const ShapedTextCallback& function) const { - this->INHERITED::onShapeText(ctx, function); + this->INHERITED::onShapeText(context, function); } bool SVGTextPath::parseAndSetAttribute(const std::string& name, const std::string& value) { diff --git a/src/svg/node/SVGTransformableNode.cpp b/src/svg/node/SVGTransformableNode.cpp index d1ea1a50..b6dac013 100644 --- a/src/svg/node/SVGTransformableNode.cpp +++ b/src/svg/node/SVGTransformableNode.cpp @@ -30,21 +30,21 @@ namespace tgfx { SVGTransformableNode::SVGTransformableNode(SVGTag tag) : INHERITED(tag), fTransform(Matrix::I()) { } -bool SVGTransformableNode::onPrepareToRender(SVGRenderContext* ctx) const { +bool SVGTransformableNode::onPrepareToRender(SVGRenderContext* context) const { if (!fTransform.isIdentity()) { auto transform = fTransform; - if (auto unit = ctx->lengthContext().getPatternUnits(); + if (auto unit = context->lengthContext().getPatternUnits(); unit.has_value() && unit.value().type() == SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox) { - transform.postScale(ctx->lengthContext().viewPort().width, - ctx->lengthContext().viewPort().height); + transform.postScale(context->lengthContext().viewPort().width, + context->lengthContext().viewPort().height); } - ctx->saveOnce(); - ctx->canvas()->concat(transform); - ctx->concat(transform); + context->saveOnce(); + context->canvas()->concat(transform); + context->concat(transform); } - return this->INHERITED::onPrepareToRender(ctx); + return this->INHERITED::onPrepareToRender(context); } void SVGTransformableNode::onSetAttribute(SVGAttribute attr, const SVGValue& v) { diff --git a/src/svg/node/SVGUse.cpp b/src/svg/node/SVGUse.cpp index 2ce42a18..1758a4f5 100644 --- a/src/svg/node/SVGUse.cpp +++ b/src/svg/node/SVGUse.cpp @@ -35,56 +35,56 @@ bool SVGUse::parseAndSetAttribute(const std::string& n, const std::string& v) { this->setHref(SVGAttributeParser::parse("xlink:href", n, v)); } -bool SVGUse::onPrepareToRender(SVGRenderContext* ctx) const { - if (Href.iri().empty() || !INHERITED::onPrepareToRender(ctx)) { +bool SVGUse::onPrepareToRender(SVGRenderContext* context) const { + if (Href.iri().empty() || !INHERITED::onPrepareToRender(context)) { return false; } if (!FloatNearlyZero(X.value()) || !FloatNearlyZero(Y.value())) { // Restored when the local SVGRenderContext leaves scope. - ctx->saveOnce(); - ctx->canvas()->translate(X.value(), Y.value()); + context->saveOnce(); + context->canvas()->translate(X.value(), Y.value()); } return true; } -void SVGUse::onRender(const SVGRenderContext& ctx) const { - const auto ref = ctx.findNodeById(Href); +void SVGUse::onRender(const SVGRenderContext& context) const { + const auto ref = context.findNodeById(Href); if (!ref) { return; } - auto lengthContext = ctx.lengthContext(); + auto lengthContext = context.lengthContext(); lengthContext.clearPatternUnits(); - SVGRenderContext localContext(ctx, lengthContext); + SVGRenderContext localContext(context, lengthContext); ref->render(localContext); } -Path SVGUse::onAsPath(const SVGRenderContext& ctx) const { - const auto ref = ctx.findNodeById(Href); +Path SVGUse::onAsPath(const SVGRenderContext& context) const { + const auto ref = context.findNodeById(Href); if (!ref) { return Path(); } - auto lengthContext = ctx.lengthContext(); + auto lengthContext = context.lengthContext(); lengthContext.clearPatternUnits(); - SVGRenderContext localContext(ctx, lengthContext); + SVGRenderContext localContext(context, lengthContext); return ref->asPath(localContext); } -Rect SVGUse::onObjectBoundingBox(const SVGRenderContext& ctx) const { - const auto ref = ctx.findNodeById(Href); +Rect SVGUse::onObjectBoundingBox(const SVGRenderContext& context) const { + const auto ref = context.findNodeById(Href); if (!ref) { return Rect::MakeEmpty(); } - auto lengthContext = ctx.lengthContext(); + auto lengthContext = context.lengthContext(); lengthContext.clearPatternUnits(); float x = lengthContext.resolve(X, SVGLengthContext::LengthType::Horizontal); float y = lengthContext.resolve(Y, SVGLengthContext::LengthType::Vertical); - Rect bounds = ref->objectBoundingBox(ctx); + Rect bounds = ref->objectBoundingBox(context); bounds.offset(x, y); return bounds; From fff9fa51080569b080f5c98331973b28b4b8d0a8 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Wed, 18 Dec 2024 14:59:10 +0800 Subject: [PATCH 14/31] ... --- include/tgfx/svg/SVGDOM.h | 9 ++- include/tgfx/svg/SVGFontManager.h | 100 +++++++++--------------------- include/tgfx/svg/node/SVGNode.h | 21 +++---- src/svg/SVGDOM.cpp | 71 ++++++++++++++------- src/svg/SVGFontManager.cpp | 38 ++++-------- src/svg/SVGRenderContext.cpp | 47 +++++++++----- src/svg/SVGRenderContext.h | 23 +++---- src/svg/node/SVGText.cpp | 71 +-------------------- 8 files changed, 140 insertions(+), 240 deletions(-) diff --git a/include/tgfx/svg/SVGDOM.h b/include/tgfx/svg/SVGDOM.h index 4711472c..3fd8d1e9 100644 --- a/include/tgfx/svg/SVGDOM.h +++ b/include/tgfx/svg/SVGDOM.h @@ -32,8 +32,7 @@ class SVGNode; using SVGIDMapper = std::unordered_map>; class SVGDOM { public: - static std::shared_ptr Make(const std::shared_ptr&, - std::shared_ptr); + static std::shared_ptr Make(const std::shared_ptr&); /** * Returns the root SVG node. @@ -42,10 +41,11 @@ class SVGDOM { return root; } + void collectRenderFonts(const std::shared_ptr&); /** * Renders the SVG to the provided canvas. */ - void render(Canvas*); + void render(Canvas*, const std::shared_ptr&); /** * Specify a "container size" for the SVG dom. @@ -61,10 +61,9 @@ class SVGDOM { const Size& getContainerSize() const; private: - SVGDOM(std::shared_ptr, SVGIDMapper&&, std::shared_ptr fontManager); + SVGDOM(std::shared_ptr, SVGIDMapper&&); const std::shared_ptr root; - const std::shared_ptr fontManager; const SVGIDMapper _nodeIDMapper; Size containerSize; std::shared_ptr renderPicture; diff --git a/include/tgfx/svg/SVGFontManager.h b/include/tgfx/svg/SVGFontManager.h index 4e79be9a..c72c44d9 100644 --- a/include/tgfx/svg/SVGFontManager.h +++ b/include/tgfx/svg/SVGFontManager.h @@ -23,110 +23,68 @@ #include #include #include "tgfx/core/Typeface.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGNode.h" namespace tgfx { -class FontStyle { +class SVGFontInfo { public: struct Hash { - std::size_t operator()(const FontStyle& style) const { - return std::hash()(static_cast(style.weight())) ^ - std::hash()(static_cast(style.width())) ^ - std::hash()(static_cast(style.slant())); + std::size_t operator()(const SVGFontInfo& info) const { + return std::hash()(static_cast(info._weight)) ^ + std::hash()(static_cast(info._style)); } }; - enum class Weight { - Invisible_Weight = 0, - Thin_Weight = 100, - ExtraLight_Weight = 200, - Light_Weight = 300, - Normal_Weight = 400, - Medium_Weight = 500, - SemiBold_Weight = 600, - Bold_Weight = 700, - ExtraBold_Weight = 800, - Black_Weight = 900, - ExtraBlack_Weight = 1000, - }; - - enum class Width { - UltraCondensed_Width = 1, - ExtraCondensed_Width = 2, - Condensed_Width = 3, - SemiCondensed_Width = 4, - Normal_Width = 5, - SemiExpanded_Width = 6, - Expanded_Width = 7, - ExtraExpanded_Width = 8, - UltraExpanded_Width = 9, - }; - - enum class Slant { - Upright_Slant, - Italic_Slant, - Oblique_Slant, - }; - - constexpr FontStyle(Weight weight, Width width, Slant slant) - : value(static_cast(weight) + (static_cast(width) << 16) + - (static_cast(slant) << 24)) { - } - - constexpr FontStyle() - : FontStyle{Weight::Normal_Weight, Width::Normal_Width, Slant::Upright_Slant} { + SVGFontInfo(SVGFontWeight::Type weight, SVGFontStyle::Type style) + : _weight(weight), _style(style) { } - bool operator==(const FontStyle& rhs) const { - return value == rhs.value; + bool operator==(const SVGFontInfo& other) const { + return _weight == other._weight && _style == other._style; } - Weight weight() const { - return static_cast(value & 0xFFFF); - } - Width width() const { - return static_cast((value >> 16) & 0xFF); + SVGFontWeight::Type weight() const { + return _weight; } - Slant slant() const { - return static_cast((value >> 24) & 0xFF); + SVGFontStyle::Type style() const { + return _style; } private: - int value; + SVGFontWeight::Type _weight; + SVGFontStyle::Type _style; }; class SVGFontManager { public: - SVGFontManager() = default; - ~SVGFontManager() = default; - - /** - * Set the Default Typeface object - * @param typeface - * @return true - * @return false - */ - bool setDefaultTypeface(const std::shared_ptr& typeface); + static std::shared_ptr Make(const std::shared_ptr& defaultTypeface); - void addFontStyle(const std::string& fontFamily, FontStyle style); + ~SVGFontManager() = default; - void setTypeface(const std::string& fontFamily, FontStyle style, + void setTypeface(const std::string& fontFamily, SVGFontInfo style, const std::shared_ptr& typeface); std::vector getFontFamilies() const; - std::vector getFontStyles(const std::string& fontFamily) const; + std::vector getFontStyles(const std::string& fontFamily) const; - std::shared_ptr getTypefaceForConfig(const std::string& fontFamily, - FontStyle style) const; + protected: + void addFontStyle(const std::string& fontFamily, SVGFontInfo style); std::shared_ptr getTypefaceForRender(const std::string& fontFamily, - FontStyle style) const; + SVGFontInfo style) const; private: + explicit SVGFontManager(const std::shared_ptr& typeface); + std::unordered_map, FontStyle::Hash>> + std::unordered_map, SVGFontInfo::Hash>> typefaceMap; std::shared_ptr defaultTypeface; + + friend class SVGRenderContext; + friend class SVGDOM; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGNode.h b/include/tgfx/svg/node/SVGNode.h index 1a01ea9f..680fd5c7 100644 --- a/include/tgfx/svg/node/SVGNode.h +++ b/include/tgfx/svg/node/SVGNode.h @@ -154,6 +154,14 @@ class SVGNode { SVG_PRES_ATTR(FloodOpacity, SVGNumberType, false) SVG_PRES_ATTR(LightingColor, SVGColor, false) + void render(const SVGRenderContext&) const; + + bool asPaint(const SVGRenderContext&, Paint*) const; + + Path asPath(const SVGRenderContext&) const; + + Rect objectBoundingBox(const SVGRenderContext&) const; + virtual bool hasChildren() const { return false; } @@ -163,14 +171,6 @@ class SVGNode { virtual void appendChild(std::shared_ptr) = 0; - void render(const SVGRenderContext&) const; - - bool asPaint(const SVGRenderContext&, Paint*) const; - - Path asPath(const SVGRenderContext&) const; - - Rect objectBoundingBox(const SVGRenderContext&) const; - void setAttribute(SVGAttribute, const SVGValue&); bool setAttribute(const std::string& attributeName, const std::string& attributeValue); @@ -206,11 +206,6 @@ class SVGNode { SVGTag _tag; SVGPresentationAttributes presentationAttributes; - friend class SVGDOM; - friend class SVGMask; - friend class SVGUse; - friend class SVGSVG; - friend class SVGContainer; friend class SVGNodeConstructor; friend class SVGRenderContext; }; diff --git a/src/svg/SVGDOM.cpp b/src/svg/SVGDOM.cpp index 0ee2cbfb..a0ea69dc 100644 --- a/src/svg/SVGDOM.cpp +++ b/src/svg/SVGDOM.cpp @@ -36,12 +36,13 @@ #include "tgfx/svg/SVGAttribute.h" #include "tgfx/svg/SVGTypes.h" #include "tgfx/svg/SVGValue.h" +#include "tgfx/svg/node/SVGContainer.h" +#include "tgfx/svg/node/SVGNode.h" #include "tgfx/svg/xml/XMLDOM.h" namespace tgfx { -std::shared_ptr SVGDOM::Make(const std::shared_ptr& data, - std::shared_ptr fontManager) { +std::shared_ptr SVGDOM::Make(const std::shared_ptr& data) { if (!data) { return nullptr; } @@ -52,40 +53,66 @@ std::shared_ptr SVGDOM::Make(const std::shared_ptr& data, SVGIDMapper mapper; ConstructionContext constructionContext(&mapper); - auto root = SVGNodeConstructor::ConstructSVGNode(constructionContext, xmlDom->getRootNode().get()); if (!root || root->tag() != SVGTag::Svg) { return nullptr; } - return std::shared_ptr(new SVGDOM(std::static_pointer_cast(root), - std::move(mapper), std::move(fontManager))); + return std::shared_ptr( + new SVGDOM(std::static_pointer_cast(root), std::move(mapper))); } -SVGDOM::SVGDOM(std::shared_ptr root, SVGIDMapper&& mapper, - std::shared_ptr fontManager) - : root(std::move(root)), fontManager(std::move(fontManager)), _nodeIDMapper(std::move(mapper)) { +SVGDOM::SVGDOM(std::shared_ptr root, SVGIDMapper&& mapper) + : root(std::move(root)), _nodeIDMapper(std::move(mapper)) { } -void SVGDOM::render(Canvas* canvas) { - if (root) { - if (!renderPicture) { - SVGLengthContext lengthContext(containerSize); - SVGPresentationContext presentationContext; +void SVGDOM::collectRenderFonts(const std::shared_ptr& fontManager) { + if (!root) { + return; + } - Recorder recorder; - auto* drawCanvas = recorder.beginRecording(); - { - SVGRenderContext renderCtx(canvas->getSurface()->getContext(), drawCanvas, fontManager, - _nodeIDMapper, lengthContext, presentationContext, - {nullptr, nullptr}, canvas->getMatrix()); - root->render(renderCtx); + auto fontCollector = [](auto collector, const std::shared_ptr& node, + const std::shared_ptr& fontManager) -> void { + if (!node) { + return; + } + if (node->tag() <= SVGTag::Text && node->tag() >= SVGTag::TSpan) { + if (node->getFontFamily()->type() == SVGFontFamily::Type::Family) { + fontManager->addFontStyle( + node->getFontFamily()->family(), + SVGFontInfo(node->getFontWeight()->type(), node->getFontStyle()->type())); } - renderPicture = recorder.finishRecordingAsPicture(); + } else if (node->hasChildren()) { + if (auto container = std::static_pointer_cast(node)) { + for (const auto& child : container->getChildren()) { + collector(collector, child, fontManager); + } + } + } + }; + + fontCollector(fontCollector, std::static_pointer_cast(root), fontManager); +} + +void SVGDOM::render(Canvas* canvas, const std::shared_ptr& fontManager) { + if (!root) { + return; + } + if (!renderPicture) { + SVGLengthContext lengthContext(containerSize); + SVGPresentationContext presentationContext; + + Recorder recorder; + auto* drawCanvas = recorder.beginRecording(); + { + SVGRenderContext renderContext(drawCanvas, fontManager, _nodeIDMapper, lengthContext, + presentationContext, {nullptr, nullptr}, canvas->getMatrix()); + root->render(renderContext); } - canvas->drawPicture(renderPicture); + renderPicture = recorder.finishRecordingAsPicture(); } + canvas->drawPicture(renderPicture); } const Size& SVGDOM::getContainerSize() const { diff --git a/src/svg/SVGFontManager.cpp b/src/svg/SVGFontManager.cpp index cfbee1c9..b4bc5b45 100644 --- a/src/svg/SVGFontManager.cpp +++ b/src/svg/SVGFontManager.cpp @@ -20,21 +20,21 @@ namespace tgfx { -bool SVGFontManager::setDefaultTypeface(const std::shared_ptr& typeface) { - if (typeface) { - defaultTypeface = typeface; - return true; - } - return false; -}; +SVGFontManager::SVGFontManager(const std::shared_ptr& defaultTypeface) + : defaultTypeface(defaultTypeface) { +} + +std::shared_ptr SVGFontManager::Make(const std::shared_ptr& typeface) { + return std::shared_ptr(new SVGFontManager(typeface)); +} -void SVGFontManager::addFontStyle(const std::string& fontFamily, FontStyle style) { +void SVGFontManager::addFontStyle(const std::string& fontFamily, SVGFontInfo style) { if (typefaceMap.at(fontFamily).find(style) == typefaceMap.at(fontFamily).end()) { typefaceMap[fontFamily][style] = nullptr; } } -void SVGFontManager::setTypeface(const std::string& fontFamily, FontStyle style, +void SVGFontManager::setTypeface(const std::string& fontFamily, SVGFontInfo style, const std::shared_ptr& typeface) { typefaceMap[fontFamily][style] = typeface; } @@ -48,9 +48,9 @@ std::vector SVGFontManager::getFontFamilies() const { return families; } -std::vector SVGFontManager::getFontStyles(const std::string& fontFamily) const { +std::vector SVGFontManager::getFontStyles(const std::string& fontFamily) const { if (auto iter = typefaceMap.find(fontFamily); iter != typefaceMap.end()) { - std::vector styles; + std::vector styles; styles.reserve(iter->second.size()); for (const auto& [style, _] : iter->second) { styles.push_back(style); @@ -61,22 +61,8 @@ std::vector SVGFontManager::getFontStyles(const std::string& fontFami } } -std::shared_ptr SVGFontManager::getTypefaceForConfig(const std::string& fontFamily, - FontStyle style) const { - auto familyIter = typefaceMap.find(fontFamily); - if (familyIter == typefaceMap.end()) { - return nullptr; - } - auto styleIter = familyIter->second.find(style); - if (styleIter == familyIter->second.end()) { - return nullptr; - } else { - return styleIter->second; - } -} - std::shared_ptr SVGFontManager::getTypefaceForRender(const std::string& fontFamily, - FontStyle style) const { + SVGFontInfo style) const { auto familyIter = typefaceMap.find(fontFamily); if (familyIter == typefaceMap.end()) { return defaultTypeface; diff --git a/src/svg/SVGRenderContext.cpp b/src/svg/SVGRenderContext.cpp index ea795e9e..b8800a66 100644 --- a/src/svg/SVGRenderContext.cpp +++ b/src/svg/SVGRenderContext.cpp @@ -102,43 +102,40 @@ SVGPresentationContext::SVGPresentationContext() : _inherited(SVGPresentationAttributes::MakeInitial()) { } -SVGRenderContext::SVGRenderContext(Context* device, Canvas* canvas, +SVGRenderContext::SVGRenderContext(Canvas* canvas, const std::shared_ptr& fontManager, const SVGIDMapper& mapper, const SVGLengthContext& lengthContext, const SVGPresentationContext& presentContext, const OBBScope& obbs, const Matrix& matrix) : fontManager(fontManager), nodeIDMapper(mapper), _lengthContext(lengthContext), _presentationContext(presentContext), renderCanvas(canvas), recorder(), - _canvas(recorder.beginRecording()), scope(obbs), _deviceContext(device), matrix(matrix) { + _canvas(recorder.beginRecording()), scope(obbs), matrix(matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other) - : SVGRenderContext(other._deviceContext, other._canvas, other.fontManager, other.nodeIDMapper, - *other._lengthContext, *other._presentationContext, other.scope, - other.matrix) { + : SVGRenderContext(other._canvas, other.fontManager, other.nodeIDMapper, *other._lengthContext, + *other._presentationContext, other.scope, other.matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, Canvas* canvas) - : SVGRenderContext(other._deviceContext, canvas, other.fontManager, other.nodeIDMapper, - *other._lengthContext, *other._presentationContext, other.scope, - other.matrix) { + : SVGRenderContext(canvas, other.fontManager, other.nodeIDMapper, *other._lengthContext, + *other._presentationContext, other.scope, other.matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, const SVGLengthContext& lengthCtx) - : SVGRenderContext(other._deviceContext, other._canvas, other.fontManager, other.nodeIDMapper, - lengthCtx, *other._presentationContext, other.scope, other.matrix) { + : SVGRenderContext(other._canvas, other.fontManager, other.nodeIDMapper, lengthCtx, + *other._presentationContext, other.scope, other.matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, Canvas* canvas, const SVGLengthContext& lengthCtx) - : SVGRenderContext(other._deviceContext, canvas, other.fontManager, other.nodeIDMapper, - lengthCtx, *other._presentationContext, other.scope, other.matrix) { + : SVGRenderContext(canvas, other.fontManager, other.nodeIDMapper, lengthCtx, + *other._presentationContext, other.scope, other.matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, const SVGNode* node) - : SVGRenderContext(other._deviceContext, other._canvas, other.fontManager, other.nodeIDMapper, - *other._lengthContext, *other._presentationContext, OBBScope{node, this}, - other.matrix) { + : SVGRenderContext(other._canvas, other.fontManager, other.nodeIDMapper, *other._lengthContext, + *other._presentationContext, OBBScope{node, this}, other.matrix) { } SVGRenderContext::~SVGRenderContext() { @@ -342,8 +339,8 @@ std::optional SVGRenderContext::commonPaint(const SVGPaint& svgPaint, flo // and node being rendered. SVGPresentationContext presentContext; presentContext._namedColors = _presentationContext->_namedColors; - SVGRenderContext localContext(_deviceContext, _canvas, fontManager, nodeIDMapper, - *_lengthContext, presentContext, scope, Matrix::I()); + SVGRenderContext localContext(_canvas, fontManager, nodeIDMapper, *_lengthContext, + presentContext, scope, Matrix::I()); const auto node = this->findNodeById(svgPaint.iri()); if (!node || !node->asPaint(localContext, &(paint.value()))) { @@ -415,6 +412,22 @@ SVGColorType SVGRenderContext::resolveSVGColor(const SVGColor& color) const { } } +std::tuple SVGRenderContext::ResolveFont() const { + const auto& family = _presentationContext->_inherited.FontFamily->family(); + auto fontWeight = _presentationContext->_inherited.FontWeight->type(); + auto fontStyle = _presentationContext->_inherited.FontStyle->type(); + + SVGFontInfo info(fontWeight, fontStyle); + auto typeface = fontManager->getTypefaceForRender(family, info); + if (!typeface) { + return {false, Font()}; + } + + float size = _lengthContext->resolve(_presentationContext->_inherited.FontSize->size(), + SVGLengthContext::LengthType::Vertical); + return {true, Font(typeface, size)}; +} + SVGRenderContext::OBBTransform SVGRenderContext::transformForCurrentBoundBox( SVGObjectBoundingBoxUnits unit) const { if (!scope.node || unit.type() == SVGObjectBoundingBoxUnits::Type::UserSpaceOnUse) { diff --git a/src/svg/SVGRenderContext.h b/src/svg/SVGRenderContext.h index 2bd47469..35d907d6 100644 --- a/src/svg/SVGRenderContext.h +++ b/src/svg/SVGRenderContext.h @@ -126,7 +126,7 @@ class SVGRenderContext { const SVGRenderContext* context; }; - SVGRenderContext(Context*, Canvas*, const std::shared_ptr&, const SVGIDMapper&, + SVGRenderContext(Canvas*, const std::shared_ptr&, const SVGIDMapper&, const SVGLengthContext&, const SVGPresentationContext&, const OBBScope&, const Matrix& matrix); SVGRenderContext(const SVGRenderContext&); @@ -142,6 +142,7 @@ class SVGRenderContext { const SVGLengthContext& lengthContext() const { return *_lengthContext; } + SVGLengthContext* writableLengthContext() { return _lengthContext.writable(); } @@ -150,13 +151,10 @@ class SVGRenderContext { return *_presentationContext; } - Context* deviceContext() const { - return _deviceContext; - } - Canvas* canvas() const { return _canvas; } + void saveOnce(); void concat(const Matrix& inputMatrix) { @@ -166,6 +164,7 @@ class SVGRenderContext { enum ApplyFlags { kLeaf = 1 << 0, // the target node doesn't have descendants }; + void applyPresentationAttributes(const SVGPresentationAttributes& attrs, uint32_t flags); // Note: the id->node association is cleared for the lifetime of the returned value @@ -173,25 +172,18 @@ class SVGRenderContext { std::shared_ptr findNodeById(const SVGIRI&) const; std::optional fillPaint() const; + std::optional strokePaint() const; SVGColorType resolveSVGColor(const SVGColor&) const; + std::tuple ResolveFont() const; + // The local computed clip path (not inherited). Path clipPath() const { return _clipPath.value_or(Path()); }; - const std::shared_ptr& fontMgr() const { - return fontManager; - } - - std::shared_ptr& fontMgr() { - // It is probably an oversight to try to render without having set the SVGFontManager. - // We will assert this in debug mode, but fallback to an empty _fontMgr in release builds. - return fontManager; - } - // Returns the translate/scale transformation required to map into the current OBB scope, // with the specified units. struct OBBTransform { @@ -234,7 +226,6 @@ class SVGRenderContext { // Current object bounding box scope. const OBBScope scope; - Context* _deviceContext; Paint picturePaint; Matrix matrix = Matrix::I(); diff --git a/src/svg/node/SVGText.cpp b/src/svg/node/SVGText.cpp index dd0b1743..475b80e8 100644 --- a/src/svg/node/SVGText.cpp +++ b/src/svg/node/SVGText.cpp @@ -31,75 +31,6 @@ namespace tgfx { namespace { -std::tuple ResolveFont(const SVGRenderContext& context) { - auto weight = [](const SVGFontWeight& w) { - switch (w.type()) { - case SVGFontWeight::Type::W100: - return FontStyle::Weight::Thin_Weight; - case SVGFontWeight::Type::W200: - return FontStyle::Weight::ExtraLight_Weight; - case SVGFontWeight::Type::W300: - return FontStyle::Weight::Light_Weight; - case SVGFontWeight::Type::W400: - return FontStyle::Weight::Normal_Weight; - case SVGFontWeight::Type::W500: - return FontStyle::Weight::Medium_Weight; - case SVGFontWeight::Type::W600: - return FontStyle::Weight::SemiBold_Weight; - case SVGFontWeight::Type::W700: - return FontStyle::Weight::Bold_Weight; - case SVGFontWeight::Type::W800: - return FontStyle::Weight::ExtraBold_Weight; - case SVGFontWeight::Type::W900: - return FontStyle::Weight::Black_Weight; - case SVGFontWeight::Type::Normal: - return FontStyle::Weight::Normal_Weight; - case SVGFontWeight::Type::Bold: - return FontStyle::Weight::Bold_Weight; - case SVGFontWeight::Type::Bolder: - return FontStyle::Weight::ExtraBold_Weight; - case SVGFontWeight::Type::Lighter: - return FontStyle::Weight::Light_Weight; - case SVGFontWeight::Type::Inherit: { - ASSERT(false); - return FontStyle::Weight::Normal_Weight; - } - } - }; - - auto slant = [](const SVGFontStyle& style) { - switch (style.type()) { - case SVGFontStyle::Type::Normal: - return FontStyle::Slant::Upright_Slant; - case SVGFontStyle::Type::Italic: - return FontStyle::Slant::Italic_Slant; - case SVGFontStyle::Type::Oblique: - return FontStyle::Slant::Oblique_Slant; - case SVGFontStyle::Type::Inherit: { - ASSERT(false); - return FontStyle::Slant::Upright_Slant; - } - } - }; - - const std::string& family = context.presentationContext()._inherited.FontFamily->family(); - - auto fontWeight = weight(*context.presentationContext()._inherited.FontWeight); - auto fontWidth = FontStyle::Width::Normal_Width; - auto fontSlant = slant(*context.presentationContext()._inherited.FontStyle); - FontStyle style(fontWeight, fontWidth, fontSlant); - - auto typeface = context.fontMgr()->getTypefaceForRender(family, style); - ASSERT(typeface); - if (!typeface) { - return {false, Font()}; - } - - float size = - context.lengthContext().resolve(context.presentationContext()._inherited.FontSize->size(), - SVGLengthContext::LengthType::Vertical); - return {true, Font(typeface, size)}; -} std::vector ResolveLengths(const SVGLengthContext& lengthCtx, const std::vector& lengths, @@ -188,7 +119,7 @@ bool SVGTextContainer::parseAndSetAttribute(const std::string& name, const std:: void SVGTextLiteral::onShapeText(const SVGRenderContext& context, const ShapedTextCallback& function) const { - auto [success, font] = ResolveFont(context); + auto [success, font] = context.ResolveFont(); if (!success) { return; } From 0d86fbb7c2548eab7e2b60efc3874772fad607fd Mon Sep 17 00:00:00 2001 From: YGauroa Date: Wed, 18 Dec 2024 16:43:18 +0800 Subject: [PATCH 15/31] ... --- include/tgfx/svg/SVGDOM.h | 2 +- include/tgfx/svg/SVGFontManager.h | 6 +- include/tgfx/svg/SVGTypes.h | 2 +- include/tgfx/svg/node/SVGNode.h | 3 +- resources/apitest/SVG/textFont.svg | 9 ++ src/svg/SVGDOM.cpp | 16 ++- src/svg/SVGFontManager.cpp | 14 ++- src/svg/SVGRenderContext.cpp | 11 +- src/svg/SVGRenderContext.h | 2 +- src/svg/node/SVGText.cpp | 2 +- test/baseline/version.json | 3 +- test/src/SVGRenderTest.cpp | 171 +++++++++++++---------------- 12 files changed, 123 insertions(+), 118 deletions(-) create mode 100644 resources/apitest/SVG/textFont.svg diff --git a/include/tgfx/svg/SVGDOM.h b/include/tgfx/svg/SVGDOM.h index 3fd8d1e9..bb682aa7 100644 --- a/include/tgfx/svg/SVGDOM.h +++ b/include/tgfx/svg/SVGDOM.h @@ -45,7 +45,7 @@ class SVGDOM { /** * Renders the SVG to the provided canvas. */ - void render(Canvas*, const std::shared_ptr&); + void render(Canvas*, const std::shared_ptr& fontManager = nullptr); /** * Specify a "container size" for the SVG dom. diff --git a/include/tgfx/svg/SVGFontManager.h b/include/tgfx/svg/SVGFontManager.h index c72c44d9..390055c5 100644 --- a/include/tgfx/svg/SVGFontManager.h +++ b/include/tgfx/svg/SVGFontManager.h @@ -67,13 +67,13 @@ class SVGFontManager { std::vector getFontFamilies() const; - std::vector getFontStyles(const std::string& fontFamily) const; + std::vector getFontInfos(const std::string& fontFamily) const; protected: void addFontStyle(const std::string& fontFamily, SVGFontInfo style); - std::shared_ptr getTypefaceForRender(const std::string& fontFamily, - SVGFontInfo style) const; + std::shared_ptr getTypefaceForRendering(const std::string& fontFamily, + SVGFontInfo style) const; private: explicit SVGFontManager(const std::shared_ptr& typeface); diff --git a/include/tgfx/svg/SVGTypes.h b/include/tgfx/svg/SVGTypes.h index daa71b97..964982b1 100644 --- a/include/tgfx/svg/SVGTypes.h +++ b/include/tgfx/svg/SVGTypes.h @@ -80,7 +80,7 @@ class SVGProperty { } T* getMaybeNull() const { - return _value.has_value() ? &_value.value() : nullptr; + return _value.value_or(nullptr); } void set(SVGPropertyState state) { diff --git a/include/tgfx/svg/node/SVGNode.h b/include/tgfx/svg/node/SVGNode.h index 680fd5c7..ae79eb3a 100644 --- a/include/tgfx/svg/node/SVGNode.h +++ b/include/tgfx/svg/node/SVGNode.h @@ -179,8 +179,7 @@ class SVGNode { static Matrix ComputeViewboxMatrix(const Rect&, const Rect&, SVGPreserveAspectRatio); - virtual void onSetAttribute(SVGAttribute, const SVGValue&) { - } + virtual void onSetAttribute(SVGAttribute, const SVGValue&){}; // Called before onRender(), to apply local attributes to the context. Unlike onRender(), // onPrepareToRender() bubbles up the inheritance chain: override should always call diff --git a/resources/apitest/SVG/textFont.svg b/resources/apitest/SVG/textFont.svg new file mode 100644 index 00000000..32c6f043 --- /dev/null +++ b/resources/apitest/SVG/textFont.svg @@ -0,0 +1,9 @@ + + + + SVG GVS + SVG GVS + diff --git a/src/svg/SVGDOM.cpp b/src/svg/SVGDOM.cpp index a0ea69dc..1576a266 100644 --- a/src/svg/SVGDOM.cpp +++ b/src/svg/SVGDOM.cpp @@ -34,6 +34,7 @@ #include "tgfx/core/Recorder.h" #include "tgfx/core/Surface.h" #include "tgfx/svg/SVGAttribute.h" +#include "tgfx/svg/SVGFontManager.h" #include "tgfx/svg/SVGTypes.h" #include "tgfx/svg/SVGValue.h" #include "tgfx/svg/node/SVGContainer.h" @@ -77,11 +78,15 @@ void SVGDOM::collectRenderFonts(const std::shared_ptr& fontManag if (!node) { return; } - if (node->tag() <= SVGTag::Text && node->tag() >= SVGTag::TSpan) { - if (node->getFontFamily()->type() == SVGFontFamily::Type::Family) { - fontManager->addFontStyle( - node->getFontFamily()->family(), - SVGFontInfo(node->getFontWeight()->type(), node->getFontStyle()->type())); + if (node->tag() >= SVGTag::Text && node->tag() <= SVGTag::TSpan) { + if (node->getFontFamily().isValue()) { + auto family = node->getFontFamily()->family(); + SVGFontWeight::Type weight = node->getFontWeight().isValue() ? node->getFontWeight()->type() + : SVGFontWeight::Type::Normal; + SVGFontStyle::Type style = node->getFontStyle().isValue() ? node->getFontStyle()->type() + : SVGFontStyle::Type::Normal; + SVGFontInfo fontStyle(weight, style); + fontManager->addFontStyle(family, fontStyle); } } else if (node->hasChildren()) { if (auto container = std::static_pointer_cast(node)) { @@ -91,7 +96,6 @@ void SVGDOM::collectRenderFonts(const std::shared_ptr& fontManag } } }; - fontCollector(fontCollector, std::static_pointer_cast(root), fontManager); } diff --git a/src/svg/SVGFontManager.cpp b/src/svg/SVGFontManager.cpp index b4bc5b45..eba2330b 100644 --- a/src/svg/SVGFontManager.cpp +++ b/src/svg/SVGFontManager.cpp @@ -29,9 +29,13 @@ std::shared_ptr SVGFontManager::Make(const std::shared_ptr SVGFontManager::getFontFamilies() const { return families; } -std::vector SVGFontManager::getFontStyles(const std::string& fontFamily) const { +std::vector SVGFontManager::getFontInfos(const std::string& fontFamily) const { if (auto iter = typefaceMap.find(fontFamily); iter != typefaceMap.end()) { std::vector styles; styles.reserve(iter->second.size()); @@ -61,8 +65,8 @@ std::vector SVGFontManager::getFontStyles(const std::string& fontFa } } -std::shared_ptr SVGFontManager::getTypefaceForRender(const std::string& fontFamily, - SVGFontInfo style) const { +std::shared_ptr SVGFontManager::getTypefaceForRendering(const std::string& fontFamily, + SVGFontInfo style) const { auto familyIter = typefaceMap.find(fontFamily); if (familyIter == typefaceMap.end()) { return defaultTypeface; diff --git a/src/svg/SVGRenderContext.cpp b/src/svg/SVGRenderContext.cpp index b8800a66..3ce8176f 100644 --- a/src/svg/SVGRenderContext.cpp +++ b/src/svg/SVGRenderContext.cpp @@ -67,7 +67,7 @@ LineJoin toJoin(const SVGLineJoin& join) { } } -std::unique_ptr dash_effect(const SVGPresentationAttributes& props, +std::shared_ptr dash_effect(const SVGPresentationAttributes& props, const SVGLengthContext& lengthContext) { if (props.StrokeDashArray->type() != SVGDashArray::Type::DashArray) { return nullptr; @@ -412,13 +412,18 @@ SVGColorType SVGRenderContext::resolveSVGColor(const SVGColor& color) const { } } -std::tuple SVGRenderContext::ResolveFont() const { +std::tuple SVGRenderContext::resolveFont() const { + // Since there may be SVGs without any text, we accept the absence of a font manager. + // However, this will result in the inability to render text. + if (!fontManager) { + return {false, Font()}; + } const auto& family = _presentationContext->_inherited.FontFamily->family(); auto fontWeight = _presentationContext->_inherited.FontWeight->type(); auto fontStyle = _presentationContext->_inherited.FontStyle->type(); SVGFontInfo info(fontWeight, fontStyle); - auto typeface = fontManager->getTypefaceForRender(family, info); + auto typeface = fontManager->getTypefaceForRendering(family, info); if (!typeface) { return {false, Font()}; } diff --git a/src/svg/SVGRenderContext.h b/src/svg/SVGRenderContext.h index 35d907d6..9f96fdd5 100644 --- a/src/svg/SVGRenderContext.h +++ b/src/svg/SVGRenderContext.h @@ -177,7 +177,7 @@ class SVGRenderContext { SVGColorType resolveSVGColor(const SVGColor&) const; - std::tuple ResolveFont() const; + std::tuple resolveFont() const; // The local computed clip path (not inherited). Path clipPath() const { diff --git a/src/svg/node/SVGText.cpp b/src/svg/node/SVGText.cpp index 475b80e8..0db02779 100644 --- a/src/svg/node/SVGText.cpp +++ b/src/svg/node/SVGText.cpp @@ -119,7 +119,7 @@ bool SVGTextContainer::parseAndSetAttribute(const std::string& name, const std:: void SVGTextLiteral::onShapeText(const SVGRenderContext& context, const ShapedTextCallback& function) const { - auto [success, font] = context.ResolveFont(); + auto [success, font] = context.resolveFont(); if (!success) { return; } diff --git a/test/baseline/version.json b/test/baseline/version.json index 8e00d5ba..5c9ab440 100644 --- a/test/baseline/version.json +++ b/test/baseline/version.json @@ -180,7 +180,8 @@ "path": "b1db872", "png_image": "b1db872", "radialGradient": "b1db872", - "text": "b1db872" + "text": "b1db872", + "textFont": "348f70e" }, "SurfaceTest": { "ImageSnapshot1": "d010fb8", diff --git a/test/src/SVGRenderTest.cpp b/test/src/SVGRenderTest.cpp index 1a5a820c..76933804 100644 --- a/test/src/SVGRenderTest.cpp +++ b/test/src/SVGRenderTest.cpp @@ -18,6 +18,8 @@ #include "gtest/gtest.h" #include "tgfx/core/Data.h" +#include "tgfx/svg/SVGDOM.h" +#include "tgfx/svg/SVGFontManager.h" #include "tgfx/svg/xml/XMLDOM.h" #include "utils/TestUtils.h" @@ -77,191 +79,172 @@ TGFX_TEST(SVGRenderTest, XMLParse) { EXPECT_EQ(copyDOM->getRootNode()->name, xmlDOM->getRootNode()->name); } -} // namespace tgfx///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making tgfx available. -// -// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// unless required by applicable law or agreed to in writing, software distributed under the -// license is distributed on an "as is" basis, without warranties or conditions of any kind, -// either express or implied. see the license for the specific language governing permissions -// and limitations under the license. -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -#include "tgfx/svg/SVGDOM.h" -#include "utils/TestUtils.h" - -namespace tgfx { TGFX_TEST(SVGRenderTest, PathSVG) { auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/path.svg")); ASSERT_TRUE(data != nullptr); - auto fontManager = std::make_shared(); - ASSERT_TRUE(fontManager != nullptr); - auto SVGDom = SVGDOM::Make(data, fontManager); - - auto device = DevicePool::Make(); - ASSERT_TRUE(device != nullptr); - auto* context = device->lockContext(); - ASSERT_TRUE(context != nullptr); + auto SVGDom = SVGDOM::Make(data); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); + + ContextScope scope; + auto* context = scope.getContext(); + ASSERT_TRUE(context != nullptr); auto surface = Surface::Make(context, static_cast(rootNode->getWidth().value()), static_cast(rootNode->getHeight().value())); - ASSERT_TRUE(device != nullptr); auto* canvas = surface->getCanvas(); SVGDom->render(canvas); EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/path")); - device->unlock(); } TGFX_TEST(SVGRenderTest, PNGImageSVG) { auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/png.svg")); ASSERT_TRUE(data != nullptr); - auto fontManager = std::make_shared(); - ASSERT_TRUE(fontManager != nullptr); - auto SVGDom = SVGDOM::Make(data, fontManager); - - auto device = DevicePool::Make(); - ASSERT_TRUE(device != nullptr); - auto* context = device->lockContext(); - ASSERT_TRUE(context != nullptr); + auto SVGDom = SVGDOM::Make(data); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); + + ContextScope scope; + auto* context = scope.getContext(); + ASSERT_TRUE(context != nullptr); auto surface = Surface::Make(context, static_cast(rootNode->getWidth().value()), static_cast(rootNode->getHeight().value())); - ASSERT_TRUE(device != nullptr); auto* canvas = surface->getCanvas(); SVGDom->render(canvas); EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/png_image")); - device->unlock(); } TGFX_TEST(SVGRenderTest, JPGImageSVG) { auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/jpg.svg")); ASSERT_TRUE(data != nullptr); - auto fontManager = std::make_shared(); - ASSERT_TRUE(fontManager != nullptr); - auto SVGDom = SVGDOM::Make(data, fontManager); - - auto device = DevicePool::Make(); - ASSERT_TRUE(device != nullptr); - auto* context = device->lockContext(); - ASSERT_TRUE(context != nullptr); + auto SVGDom = SVGDOM::Make(data); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); + + ContextScope scope; + auto* context = scope.getContext(); + ASSERT_TRUE(context != nullptr); auto surface = Surface::Make(context, static_cast(rootNode->getWidth().value()), static_cast(rootNode->getHeight().value())); - ASSERT_TRUE(device != nullptr); auto* canvas = surface->getCanvas(); SVGDom->render(canvas); EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/jpg_image")); - device->unlock(); } TGFX_TEST(SVGRenderTest, MaskSVG) { auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/mask.svg")); ASSERT_TRUE(data != nullptr); - auto fontManager = std::make_shared(); - ASSERT_TRUE(fontManager != nullptr); - auto SVGDom = SVGDOM::Make(data, fontManager); - - auto device = DevicePool::Make(); - ASSERT_TRUE(device != nullptr); - auto* context = device->lockContext(); - ASSERT_TRUE(context != nullptr); + auto SVGDom = SVGDOM::Make(data); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); + + ContextScope scope; + auto* context = scope.getContext(); + ASSERT_TRUE(context != nullptr); auto surface = Surface::Make(context, static_cast(rootNode->getWidth().value()), static_cast(rootNode->getHeight().value())); - ASSERT_TRUE(device != nullptr); auto* canvas = surface->getCanvas(); SVGDom->render(canvas); EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/mask")); - device->unlock(); } TGFX_TEST(SVGRenderTest, GradientSVG) { auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/radialGradient.svg")); ASSERT_TRUE(data != nullptr); - auto fontManager = std::make_shared(); - ASSERT_TRUE(fontManager != nullptr); - auto SVGDom = SVGDOM::Make(data, fontManager); - - auto device = DevicePool::Make(); - ASSERT_TRUE(device != nullptr); - auto* context = device->lockContext(); - ASSERT_TRUE(context != nullptr); + auto SVGDom = SVGDOM::Make(data); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); + + ContextScope scope; + auto* context = scope.getContext(); + ASSERT_TRUE(context != nullptr); auto surface = Surface::Make(context, static_cast(rootNode->getWidth().value()), static_cast(rootNode->getHeight().value())); - ASSERT_TRUE(device != nullptr); auto* canvas = surface->getCanvas(); SVGDom->render(canvas); EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/radialGradient")); - device->unlock(); } TGFX_TEST(SVGRenderTest, BlurSVG) { auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/blur.svg")); ASSERT_TRUE(data != nullptr); - auto fontManager = std::make_shared(); - ASSERT_TRUE(fontManager != nullptr); - auto SVGDom = SVGDOM::Make(data, fontManager); - - auto device = DevicePool::Make(); - ASSERT_TRUE(device != nullptr); - auto* context = device->lockContext(); - ASSERT_TRUE(context != nullptr); + auto SVGDom = SVGDOM::Make(data); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); + + ContextScope scope; + auto* context = scope.getContext(); + ASSERT_TRUE(context != nullptr); auto surface = Surface::Make(context, static_cast(rootNode->getWidth().value()), static_cast(rootNode->getHeight().value())); - ASSERT_TRUE(device != nullptr); auto* canvas = surface->getCanvas(); SVGDom->render(canvas); EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/blur")); - device->unlock(); } TGFX_TEST(SVGRenderTest, TextSVG) { auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/text.svg")); ASSERT_TRUE(data != nullptr); - auto fontManager = std::make_shared(); - ASSERT_TRUE(fontManager != nullptr); + auto SVGDom = SVGDOM::Make(data); + auto rootNode = SVGDom->getRoot(); + ASSERT_TRUE(rootNode != nullptr); + + ContextScope scope; + auto* context = scope.getContext(); + ASSERT_TRUE(context != nullptr); + auto surface = Surface::Make(context, static_cast(rootNode->getWidth().value()), + static_cast(rootNode->getHeight().value())); + auto* canvas = surface->getCanvas(); + auto typeface = MakeTypeface("resources/font/NotoSansSC-Regular.otf"); ASSERT_TRUE(typeface != nullptr); - fontManager->setDefaultTypeface(typeface); + auto fontManager = SVGFontManager::Make(typeface); + ASSERT_TRUE(fontManager != nullptr); - auto SVGDom = SVGDOM::Make(data, fontManager); + SVGDom->render(canvas, fontManager); + EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/text")); +} - auto device = DevicePool::Make(); - ASSERT_TRUE(device != nullptr); - auto* context = device->lockContext(); - ASSERT_TRUE(context != nullptr); +TGFX_TEST(SVGRenderTest, TextFontSVG) { + auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/textFont.svg")); + ASSERT_TRUE(data != nullptr); + auto SVGDom = SVGDOM::Make(data); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); + + ContextScope scope; + auto* context = scope.getContext(); + ASSERT_TRUE(context != nullptr); auto surface = Surface::Make(context, static_cast(rootNode->getWidth().value()), static_cast(rootNode->getHeight().value())); - ASSERT_TRUE(device != nullptr); auto* canvas = surface->getCanvas(); - SVGDom->render(canvas); - EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/text")); - device->unlock(); + auto defaultTypeface = MakeTypeface("resources/font/NotoSansSC-Regular.otf"); + ASSERT_TRUE(defaultTypeface != nullptr); + auto fontManager = SVGFontManager::Make(defaultTypeface); + ASSERT_TRUE(fontManager != nullptr); + + SVGDom->collectRenderFonts(fontManager); + auto families = fontManager->getFontFamilies(); + ASSERT_TRUE(families.size() == 1); + auto family = fontManager->getFontFamilies()[0]; + ASSERT_TRUE(family == "Noto Serif SC"); + auto infos = fontManager->getFontInfos(family); + ASSERT_TRUE(infos.size() == 1); + auto info = infos[0]; + ASSERT_TRUE(info.weight() == SVGFontWeight::Type::Normal); + ASSERT_TRUE(info.style() == SVGFontStyle::Type::Normal); + + auto typeface = MakeTypeface("resources/font/NotoSerifSC-Regular.otf"); + fontManager->setTypeface(family, info, typeface); + + SVGDom->render(canvas, fontManager); + EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/textFont")); } } // namespace tgfx \ No newline at end of file From e4e18a8e8157d333307274f97d2c475b9ed9776f Mon Sep 17 00:00:00 2001 From: YGauroa Date: Tue, 24 Dec 2024 09:49:44 +0800 Subject: [PATCH 16/31] save --- include/tgfx/svg/SVGDOM.h | 30 +++++++++++------------ include/tgfx/svg/node/SVGNode.h | 2 +- src/svg/SVGDOM.cpp | 42 ++++++++++++++++++--------------- src/svg/node/SVGNode.cpp | 9 +++++-- 4 files changed, 45 insertions(+), 38 deletions(-) diff --git a/include/tgfx/svg/SVGDOM.h b/include/tgfx/svg/SVGDOM.h index bb682aa7..9a15886a 100644 --- a/include/tgfx/svg/SVGDOM.h +++ b/include/tgfx/svg/SVGDOM.h @@ -30,6 +30,10 @@ namespace tgfx { class SVGNode; using SVGIDMapper = std::unordered_map>; + +/** + * + */ class SVGDOM { public: static std::shared_ptr Make(const std::shared_ptr&); @@ -37,24 +41,20 @@ class SVGDOM { /** * Returns the root SVG node. */ - const std::shared_ptr& getRoot() const { - return root; - } + const std::shared_ptr& getRoot() const; void collectRenderFonts(const std::shared_ptr&); + /** * Renders the SVG to the provided canvas. + * @param canvas The canvas to render to. + * @param fontManager The font manager for rendering SVG text. If no text rendering is needed, + * this can be nullptr, and text will not be rendered. */ - void render(Canvas*, const std::shared_ptr& fontManager = nullptr); + void render(Canvas* canvas, const std::shared_ptr& fontManager = nullptr); /** - * Specify a "container size" for the SVG dom. - * - * This is used to resolve the initial viewport when the root SVG width/height are specified - * in relative units. - * - * If the root dimensions are in absolute units, then the container size has no effect since - * the initial viewport is fixed. + * Sets the size of the container that the SVG will be rendered into. */ void setContainerSize(const Size&); @@ -62,10 +62,8 @@ class SVGDOM { private: SVGDOM(std::shared_ptr, SVGIDMapper&&); - - const std::shared_ptr root; - const SVGIDMapper _nodeIDMapper; - Size containerSize; - std::shared_ptr renderPicture; + const std::shared_ptr root = nullptr; + const SVGIDMapper nodeIDMapper = {}; + Size containerSize = Size::MakeEmpty(); }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGNode.h b/include/tgfx/svg/node/SVGNode.h index ae79eb3a..4cd0cb53 100644 --- a/include/tgfx/svg/node/SVGNode.h +++ b/include/tgfx/svg/node/SVGNode.h @@ -171,7 +171,7 @@ class SVGNode { virtual void appendChild(std::shared_ptr) = 0; - void setAttribute(SVGAttribute, const SVGValue&); + void setAttribute(SVGAttribute attribute, const SVGValue& value); bool setAttribute(const std::string& attributeName, const std::string& attributeValue); diff --git a/src/svg/SVGDOM.cpp b/src/svg/SVGDOM.cpp index 1576a266..fe3fe2b3 100644 --- a/src/svg/SVGDOM.cpp +++ b/src/svg/SVGDOM.cpp @@ -32,6 +32,7 @@ #include "tgfx/core/Canvas.h" #include "tgfx/core/Data.h" #include "tgfx/core/Recorder.h" +#include "tgfx/core/Size.h" #include "tgfx/core/Surface.h" #include "tgfx/svg/SVGAttribute.h" #include "tgfx/svg/SVGFontManager.h" @@ -65,11 +66,15 @@ std::shared_ptr SVGDOM::Make(const std::shared_ptr& data) { } SVGDOM::SVGDOM(std::shared_ptr root, SVGIDMapper&& mapper) - : root(std::move(root)), _nodeIDMapper(std::move(mapper)) { + : root(std::move(root)), nodeIDMapper(std::move(mapper)) { +} + +const std::shared_ptr& SVGDOM::getRoot() const { + return root; } void SVGDOM::collectRenderFonts(const std::shared_ptr& fontManager) { - if (!root) { + if (!root || !fontManager) { return; } @@ -100,23 +105,25 @@ void SVGDOM::collectRenderFonts(const std::shared_ptr& fontManag } void SVGDOM::render(Canvas* canvas, const std::shared_ptr& fontManager) { - if (!root) { + // If the container size is not set, use the size of the root SVG element. + auto drawSize = containerSize; + if (drawSize.isEmpty()) { + if (root->getViewBox().has_value()) { + drawSize = root->getViewBox()->size(); + } else { + drawSize = Size::Make(root->getWidth().value(), root->getHeight().value()); + } + } + if (!canvas || !root || drawSize.isEmpty()) { return; } - if (!renderPicture) { - SVGLengthContext lengthContext(containerSize); - SVGPresentationContext presentationContext; - Recorder recorder; - auto* drawCanvas = recorder.beginRecording(); - { - SVGRenderContext renderContext(drawCanvas, fontManager, _nodeIDMapper, lengthContext, - presentationContext, {nullptr, nullptr}, canvas->getMatrix()); - root->render(renderContext); - } - renderPicture = recorder.finishRecordingAsPicture(); - } - canvas->drawPicture(renderPicture); + SVGLengthContext lengthContext(containerSize); + SVGPresentationContext presentationContext; + SVGRenderContext renderContext(canvas, fontManager, nodeIDMapper, lengthContext, + presentationContext, {nullptr, nullptr}, canvas->getMatrix()); + + root->render(renderContext); } const Size& SVGDOM::getContainerSize() const { @@ -127,7 +134,4 @@ void SVGDOM::setContainerSize(const Size& size) { containerSize = size; } -bool SVGNode::setAttribute(const std::string& attributeName, const std::string& attributeValue) { - return SVGNodeConstructor::SetAttribute(*this, attributeName, attributeValue); -} } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGNode.cpp b/src/svg/node/SVGNode.cpp index 82b85616..50735d30 100644 --- a/src/svg/node/SVGNode.cpp +++ b/src/svg/node/SVGNode.cpp @@ -21,6 +21,7 @@ #include #include #include "svg/SVGAttributeParser.h" +#include "svg/SVGNodeConstructor.h" #include "svg/SVGRenderContext.h" #include "tgfx/core/Color.h" #include "tgfx/core/Matrix.h" @@ -89,8 +90,12 @@ bool SVGNode::onPrepareToRender(SVGRenderContext* context) const { (!display.isValue() || *display != SVGDisplay::None); } -void SVGNode::setAttribute(SVGAttribute attr, const SVGValue& v) { - this->onSetAttribute(attr, v); +void SVGNode::setAttribute(SVGAttribute attribute, const SVGValue& value) { + this->onSetAttribute(attribute, value); +} + +bool SVGNode::setAttribute(const std::string& attributeName, const std::string& attributeValue) { + return SVGNodeConstructor::SetAttribute(*this, attributeName, attributeValue); } template From de6c97f911d4f38a305cf3676003fe761f28bc2e Mon Sep 17 00:00:00 2001 From: YGauroa Date: Thu, 26 Dec 2024 18:23:23 +0800 Subject: [PATCH 17/31] ... --- include/tgfx/svg/SVGDOM.h | 29 +- include/tgfx/svg/SVGFontManager.h | 30 +- src/svg/SVGAttributeParser.cpp | 4 +- src/svg/SVGFontManager.cpp | 24 +- src/svg/SVGParse.cpp | 469 ------------------------------ src/svg/SVGParse.h | 55 ---- src/svg/SVGParseColor.cpp | 2 +- src/svg/SVGUtils.cpp | 441 ++++++++++++++++++++++++++++ src/svg/SVGUtils.h | 24 ++ src/svg/node/SVGImage.cpp | 48 +-- src/svg/node/SVGPath.cpp | 4 +- 11 files changed, 545 insertions(+), 585 deletions(-) delete mode 100644 src/svg/SVGParse.cpp delete mode 100644 src/svg/SVGParse.h diff --git a/include/tgfx/svg/SVGDOM.h b/include/tgfx/svg/SVGDOM.h index 9a15886a..fe9d3ab3 100644 --- a/include/tgfx/svg/SVGDOM.h +++ b/include/tgfx/svg/SVGDOM.h @@ -32,10 +32,30 @@ class SVGNode; using SVGIDMapper = std::unordered_map>; /** + * The SVGDOM class represents an SVG Document Object Model (DOM). It provides functionality to + * traverse the SVG DOM tree and render the SVG. * + * Usage: + * + * 1. Traversing the SVG DOM tree: + * - Use getRoot() to obtain the root node. From the root node, you can access its attributes + * and child nodes, and then visit the child nodes. + * + * 2. Rendering the SVG: + * - The simplest way to render is by calling render(canvas,nullptr). If you need to render text + * with specific fonts or set the size of the SVG, you can use the following methods: + * - If text rendering is required, use collectRenderFonts() to gather the necessary typefaces. + * Traverse the typefaces collected by the fontManager and set the typeface objects. + * - Render the SVG using the render() method. If text rendering is needed, pass in the + * fontManager object. Otherwise, you can pass in nullptr. + * - To set the size of the SVG, use setContainerSize(). If not set, the size of the root SVG + * node will be used. */ class SVGDOM { public: + /** + * Creates an SVGDOM object from the provided data. + */ static std::shared_ptr Make(const std::shared_ptr&); /** @@ -43,13 +63,17 @@ class SVGDOM { */ const std::shared_ptr& getRoot() const; + /** + * Collects the font families and styles used for rendering and saves them in the font manager. + */ void collectRenderFonts(const std::shared_ptr&); /** * Renders the SVG to the provided canvas. * @param canvas The canvas to render to. * @param fontManager The font manager for rendering SVG text. If no text rendering is needed, - * this can be nullptr, and text will not be rendered. + * this can be nullptr, and text will not be rendered. If no specific font is set, the default + * font will be used for rendering. */ void render(Canvas* canvas, const std::shared_ptr& fontManager = nullptr); @@ -58,6 +82,9 @@ class SVGDOM { */ void setContainerSize(const Size&); + /** + * Returns the size of the container that the SVG will be rendered into. + */ const Size& getContainerSize() const; private: diff --git a/include/tgfx/svg/SVGFontManager.h b/include/tgfx/svg/SVGFontManager.h index 390055c5..50e1da33 100644 --- a/include/tgfx/svg/SVGFontManager.h +++ b/include/tgfx/svg/SVGFontManager.h @@ -27,6 +27,9 @@ #include "tgfx/svg/node/SVGNode.h" namespace tgfx { +/** + * Information about SVG fonts, including weight and style (Oblique, Italic, Normal). + */ class SVGFontInfo { public: struct Hash { @@ -56,24 +59,45 @@ class SVGFontInfo { SVGFontStyle::Type _style; }; +/** + * Manages fonts for SVG rendering, using fontFamily and SVGFontInfo as keys to store Typeface + * objects. + */ class SVGFontManager { public: + /** + * Creates an SVGFontManager object with the default typeface.If the default typeface is nullptr, + * retrun nullptr. + */ static std::shared_ptr Make(const std::shared_ptr& defaultTypeface); + /** + * Destructor. + */ ~SVGFontManager() = default; - void setTypeface(const std::string& fontFamily, SVGFontInfo style, + /** + * Adds a font style to the font manager.If the font family and style already exist, this method + * does nothing and returns false. + */ + bool setTypeface(const std::string& fontFamily, SVGFontInfo info, const std::shared_ptr& typeface); + /** + * Get the font families stored in the font manager. + */ std::vector getFontFamilies() const; + /** + * Get the font styles stored in the font manager for the specified font family. + */ std::vector getFontInfos(const std::string& fontFamily) const; protected: - void addFontStyle(const std::string& fontFamily, SVGFontInfo style); + void addFontStyle(const std::string& fontFamily, SVGFontInfo info); std::shared_ptr getTypefaceForRendering(const std::string& fontFamily, - SVGFontInfo style) const; + SVGFontInfo info) const; private: explicit SVGFontManager(const std::shared_ptr& typeface); diff --git a/src/svg/SVGAttributeParser.cpp b/src/svg/SVGAttributeParser.cpp index 0a15a2e1..8c8b01c2 100644 --- a/src/svg/SVGAttributeParser.cpp +++ b/src/svg/SVGAttributeParser.cpp @@ -25,7 +25,7 @@ #include #include "core/utils/Log.h" #include "core/utils/MathExtra.h" -#include "svg/SVGParse.h" +#include "svg/SVGUtils.h" #include "tgfx/core/Color.h" #include "tgfx/core/Typeface.h" #include "tgfx/core/UTF.h" @@ -290,7 +290,7 @@ bool SVGAttributeParser::parseHexColorToken(Color* c) { return false; } - *c = SVGParse::Uint32ToColor(v | 0xff000000); + *c = Uint32ToColor(v | 0xff000000); currentPos = hexEnd; restoreCurPos.clear(); diff --git a/src/svg/SVGFontManager.cpp b/src/svg/SVGFontManager.cpp index eba2330b..48b5f327 100644 --- a/src/svg/SVGFontManager.cpp +++ b/src/svg/SVGFontManager.cpp @@ -25,22 +25,32 @@ SVGFontManager::SVGFontManager(const std::shared_ptr& defaultTypeface) } std::shared_ptr SVGFontManager::Make(const std::shared_ptr& typeface) { + if (!typeface) { + return nullptr; + } return std::shared_ptr(new SVGFontManager(typeface)); } -void SVGFontManager::addFontStyle(const std::string& fontFamily, SVGFontInfo style) { +void SVGFontManager::addFontStyle(const std::string& fontFamily, SVGFontInfo info) { if (typefaceMap.find(fontFamily) != typefaceMap.end()) { return; } - if (typefaceMap[fontFamily].find(style) != typefaceMap[fontFamily].end()) { + if (typefaceMap[fontFamily].find(info) != typefaceMap[fontFamily].end()) { return; } - typefaceMap[fontFamily][style] = nullptr; + typefaceMap[fontFamily][info] = nullptr; } -void SVGFontManager::setTypeface(const std::string& fontFamily, SVGFontInfo style, +bool SVGFontManager::setTypeface(const std::string& fontFamily, SVGFontInfo info, const std::shared_ptr& typeface) { - typefaceMap[fontFamily][style] = typeface; + if (typefaceMap.find(fontFamily) != typefaceMap.end()) { + return false; + } + if (typefaceMap[fontFamily].find(info) != typefaceMap[fontFamily].end()) { + return false; + } + typefaceMap[fontFamily][info] = typeface; + return true; } std::vector SVGFontManager::getFontFamilies() const { @@ -66,12 +76,12 @@ std::vector SVGFontManager::getFontInfos(const std::string& fontFam } std::shared_ptr SVGFontManager::getTypefaceForRendering(const std::string& fontFamily, - SVGFontInfo style) const { + SVGFontInfo info) const { auto familyIter = typefaceMap.find(fontFamily); if (familyIter == typefaceMap.end()) { return defaultTypeface; } - auto styleIter = familyIter->second.find(style); + auto styleIter = familyIter->second.find(info); if (styleIter == familyIter->second.end()) { return defaultTypeface; } else { diff --git a/src/svg/SVGParse.cpp b/src/svg/SVGParse.cpp deleted file mode 100644 index aff483bd..00000000 --- a/src/svg/SVGParse.cpp +++ /dev/null @@ -1,469 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making tgfx available. -// -// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// unless required by applicable law or agreed to in writing, software distributed under the -// license is distributed on an "as is" basis, without warranties or conditions of any kind, -// either express or implied. see the license for the specific language governing permissions -// and limitations under the license. -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -#include "SVGParse.h" -#include -#include -#include -#include -#include "core/utils/Log.h" -#include "tgfx/core/Path.h" -#include "tgfx/core/Point.h" -#include "tgfx/core/Typeface.h" - -namespace tgfx { - -namespace { -inline bool is_between(int c, int min, int max) { - return static_cast(c - min) <= static_cast(max - min); -} - -inline bool is_ws(int c) { - return is_between(c, 1, 32); -} - -inline bool is_digit(int c) { - return is_between(c, '0', '9'); -} - -int to_hex(int c) { - if (is_digit(c)) { - return c - '0'; - } - - c |= 0x20; // make us lower-case - return is_between(c, 'a', 'f') ? c + 10 - 'a' : -1; -} - -inline bool is_hex(int c) { - return to_hex(c) >= 0; -} - -const char* skip_ws(const char str[]) { - ASSERT(str); - while (is_ws(*str)) { - str++; - } - return str; -} - -template -tgfx::Unichar next_fail(const T** ptr, const T* end) { - *ptr = end; - return -1; -} - -inline bool is_sep(int c) { - return is_ws(c) || c == ',' || c == ';'; -} - -const char* skip_sep(const char str[]) { - ASSERT(str); - while (is_sep(*str)) { - str++; - } - return str; -} - -bool lookup_str(const char str[], const char** table, int count) { - while (--count >= 0) { - if (!strcmp(str, table[count])) { - return true; - } - } - return false; -} - -inline bool is_lower(int c) { - return is_between(c, 'a', 'z'); -} - -inline int to_upper(int c) { - return c - 'a' + 'A'; -} - -// If unable to read count points from str into value, this will return nullptr -// to signal the failure. Otherwise, it will return the next offset to read from. -const char* find_points(const char str[], Point value[], int count, bool isRelative, - Point* relative) { - str = SVGParse::FindScalars(str, &value[0].x, count * 2); - if (isRelative) { - for (int index = 0; index < count; index++) { - value[index].x += relative->x; - value[index].y += relative->y; - } - } - return str; -} - -// If unable to read a scalar from str into value, this will return nullptr -// to signal the failure. Otherwise, it will return the next offset to read from. -const char* find_scalar(const char str[], float* value, bool isRelative, float relative) { - str = SVGParse::FindScalar(str, value); - if (!str) { - return nullptr; - } - if (isRelative) { - *value += relative; - } - str = skip_sep(str); - return str; -} - -// https://www.w3.org/TR/SVG11/paths.html#PathDataBNF -// flag: -// "0" | "1" -const char* find_flag(const char str[], bool* value) { - if (!str) { - return nullptr; - } - if (str[0] != '1' && str[0] != '0') { - return nullptr; - } - *value = str[0] != '0'; - str = skip_sep(str + 1); - return str; -} - -} // namespace - -const char* SVGParse::FindHex(const char str[], uint32_t* value) { - ASSERT(str); - str = skip_ws(str); - - if (!is_hex(*str)) { - return nullptr; - } - - uint32_t n = 0; - int max_digits = 8; - int digit; - while ((digit = to_hex(*str)) >= 0) { - if (--max_digits < 0) { - return nullptr; - } - n = (n << 4) | static_cast(digit); - str += 1; - } - - if (*str == 0 || is_ws(*str)) { - if (value) { - *value = n; - } - return str; - } - return nullptr; -} - -const char* SVGParse::FindS32(const char str[], int32_t* value) { - ASSERT(str); - str = skip_ws(str); - - int sign = 1; - int64_t maxAbsValue = std::numeric_limits::max(); - if (*str == '-') { - sign = -1; - maxAbsValue = -static_cast(std::numeric_limits::min()); - str += 1; - } - - if (!is_digit(*str)) { - return nullptr; - } - - int64_t n = 0; - while (is_digit(*str)) { - n = 10 * n + *str - '0'; - if (n > maxAbsValue) { - return nullptr; - } - - str += 1; - } - - if (value) { - *value = static_cast(sign * n); - } - return str; -} - -const char* SVGParse::FindScalar(const char str[], float* value) { - ASSERT(str); - str = skip_ws(str); - - char* stop; - auto v = static_cast(strtod(str, &stop)); - if (str == stop) { - return nullptr; - } - if (value) { - *value = v; - } - return stop; -} - -const char* SVGParse::FindScalars(const char str[], float value[], int count) { - ASSERT(count >= 0); - - if (count > 0) { - for (;;) { - str = SVGParse::FindScalar(str, value); - if (--count == 0 || str == nullptr) { - break; - } - - // keep going - str = skip_sep(str); - if (value) { - value += 1; - } - } - } - return str; -} - -bool SVGParse::FindBool(const char str[], bool* value) { - static const char* yesSet[] = {"yes", "1", "true"}; - static const char* noSet[] = {"no", "0", "false"}; - - if (lookup_str(str, yesSet, std::size(yesSet))) { - if (value) { - *value = true; - } - return true; - } else if (lookup_str(str, noSet, std::size(noSet))) { - if (value) { - *value = false; - } - return true; - } - return false; -} - -int SVGParse::FindList(const char target[], const char list[]) { - auto len = strlen(target); - int index = 0; - - for (;;) { - const char* end = strchr(list, ','); - size_t entryLen = end == nullptr ? strlen(list) : static_cast(end - list); - - if (entryLen == len && memcmp(target, list, len) == 0) { - return index; - } - if (end == nullptr) { - break; - } - - list = end + 1; // skip the ',' - index += 1; - } - return -1; -} - -std::tuple> PathParse::FromSVGString(const char data[]) { - // We will write all data to this local path and only write it - // to result if the whole parsing succeeds. - auto path = std::make_shared(); - auto first = Point::Zero(); - auto opOrigin = Point::Zero(); - auto lastOpOrigin = Point::Zero(); - - // We will use find_points and find_scalar to read into these. - // There might not be enough data to fill them, so to avoid - // MSAN warnings about using uninitialized bytes, we initialize - // them there. - Point points[3] = {}; - float scratch = 0; - char op = '\0'; - char previousOp = '\0'; - bool relative = false; - for (;;) { - if (!data) { - // Truncated data - return {false, nullptr}; - } - data = skip_ws(data); - if (data[0] == '\0') { - break; - } - char ch = data[0]; - if (is_digit(ch) || ch == '-' || ch == '+' || ch == '.') { - if (op == '\0' || op == 'Z') { - return {false, nullptr}; - } - } else if (is_sep(ch)) { - data = skip_sep(data); - } else { - op = ch; - relative = false; - if (is_lower(op)) { - op = static_cast(to_upper(op)); - relative = true; - } - data++; - data = skip_sep(data); - } - switch (op) { - case 'M': // Move - data = find_points(data, points, 1, relative, &opOrigin); - // find_points might have failed, so this might be the - // previous point. However, data will be set to nullptr - // if it failed, so we will check this at the top of the loop. - path->moveTo(points[0]); - previousOp = '\0'; - op = 'L'; - opOrigin = points[0]; - break; - case 'L': // Line - data = find_points(data, points, 1, relative, &opOrigin); - path->lineTo(points[0]); - opOrigin = points[0]; - break; - case 'H': // Horizontal Line - data = find_scalar(data, &scratch, relative, opOrigin.x); - // Similarly, if there wasn't a scalar to read, data will - // be set to nullptr and this lineTo is bogus but will - // be ultimately ignored when the next time through the loop - // detects that and bails out. - path->lineTo(scratch, opOrigin.y); - opOrigin.x = scratch; - break; - case 'V': // Vertical Line - data = find_scalar(data, &scratch, relative, opOrigin.y); - path->lineTo(opOrigin.x, scratch); - opOrigin.y = scratch; - break; - case 'C': // Cubic Bezier Curve - data = find_points(data, points, 3, relative, &opOrigin); - goto cubicCommon; - case 'S': // Continued "Smooth" Cubic Bezier Curve - data = find_points(data, &points[1], 2, relative, &opOrigin); - points[0] = opOrigin; - if (previousOp == 'C' || previousOp == 'S') { - points[0].x -= lastOpOrigin.x - opOrigin.x; - points[0].y -= lastOpOrigin.y - opOrigin.y; - } - cubicCommon: - path->cubicTo(points[0], points[1], points[2]); - lastOpOrigin = points[1]; - opOrigin = points[2]; - break; - case 'Q': // Quadratic Bezier Curve - data = find_points(data, points, 2, relative, &opOrigin); - goto quadraticCommon; - case 'T': // Continued Quadratic Bezier Curve - data = find_points(data, &points[1], 1, relative, &opOrigin); - points[0] = opOrigin; - if (previousOp == 'Q' || previousOp == 'T') { - points[0].x -= lastOpOrigin.x - opOrigin.x; - points[0].y -= lastOpOrigin.y - opOrigin.y; - } - quadraticCommon: - path->quadTo(points[0], points[1]); - lastOpOrigin = points[0]; - opOrigin = points[1]; - break; - case 'A': { // Arc (Elliptical) - auto xyRadii = Point::Zero(); - float angle = 0.0f; - bool largeArc = true; - bool sweep = true; - - if (data = find_points(data, &xyRadii, 1, false, nullptr); !data) break; - if (data = skip_sep(data); !data) break; - if (data = find_scalar(data, &angle, false, 0); !data) break; - if (data = skip_sep(data); !data) break; - if (data = find_flag(data, &largeArc); !data) break; - if (data = skip_sep(data); !data) break; - if (data = find_flag(data, &sweep); !data) break; - if (data = skip_sep(data); !data) break; - if (data = find_points(data, &points[0], 1, relative, &opOrigin); !data) break; - - auto arcSize = largeArc ? PathArcSize::Large : PathArcSize::Small; - path->arcTo(xyRadii.x, xyRadii.y, angle, arcSize, !sweep, points[0]); - path->getLastPoint(&opOrigin); - } break; - case 'Z': // Close Path - path->close(); - opOrigin = first; - break; - default: - return {false, nullptr}; - } - if (previousOp == 0) { - first = opOrigin; - } - previousOp = op; - } - return {true, path}; -} - -/////////////////////////////////////////////////////////////////////////////// - -std::string PathParse::ToSVGString(const std::shared_ptr& path, - PathParse::PathEncoding encoding) { - Point currentPoint = Point::Zero(); - const int relSelector = encoding == PathParse::PathEncoding::Relative ? 1 : 0; - - const auto appendCommand = [&](std::string& inputString, char commandChar, const Point points[], - size_t count) { - // Use lower case cmds for relative encoding. - commandChar = static_cast(commandChar + 32 * relSelector); - inputString.push_back(commandChar); - for (size_t i = 0; i < count; ++i) { - const auto pt = points[i] - currentPoint; - if (i > 0) { - inputString.push_back(' '); - } - inputString.append(std::to_string(pt.x) + " " + std::to_string(pt.y)); - } - - ASSERT(count > 0); - // For relative encoding, track the current point (otherwise == origin). - currentPoint = {points[count - 1].x * static_cast(relSelector), - points[count - 1].y * static_cast(relSelector)}; - }; - - auto pathIter = [&](PathVerb verb, const Point points[4], void* info) -> void { - auto* castedString = reinterpret_cast(info); - switch (verb) { - case PathVerb::Move: - appendCommand(*castedString, 'M', points, 1); - break; - case PathVerb::Line: - appendCommand(*castedString, 'L', points, 1); - break; - case PathVerb::Quad: - appendCommand(*castedString, 'Q', points, 2); - break; - case PathVerb::Cubic: - appendCommand(*castedString, 'C', points, 3); - break; - case PathVerb::Close: - castedString->push_back('Z'); - break; - } - }; - - std::string svgString; - path->decompose(pathIter, &svgString); - return svgString; -} - -} // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGParse.h b/src/svg/SVGParse.h deleted file mode 100644 index 303c3111..00000000 --- a/src/svg/SVGParse.h +++ /dev/null @@ -1,55 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making tgfx available. -// -// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// unless required by applicable law or agreed to in writing, software distributed under the -// license is distributed on an "as is" basis, without warranties or conditions of any kind, -// either express or implied. see the license for the specific language governing permissions -// and limitations under the license. -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include -#include -#include -#include "tgfx/core/Color.h" -#include "tgfx/core/Path.h" - -namespace tgfx { - -class SVGParse { - public: - static const char* FindHex(const char str[], uint32_t* value); - static const char* FindNamedColor(const char str[], Color* color); - static const char* FindS32(const char str[], int32_t* value); - static const char* FindScalar(const char str[], float* value); - static const char* FindScalars(const char str[], float value[], int count); - static bool FindBool(const char str[], bool* value); - // return the index of str in list[], or -1 if not found - static int FindList(const char target[], const char list[]); - - static inline Color Uint32ToColor(uint32_t value) { - return Color::FromRGBA((value >> 16) & 0xff, (value >> 8) & 0xff, (value >> 0) & 0xff, - (value >> 24) & 0xff); - } -}; - -class PathParse { - public: - enum class PathEncoding { Absolute, Relative }; - - static std::tuple> FromSVGString(const char data[]); - - static std::string ToSVGString(const std::shared_ptr& path, - PathEncoding = PathEncoding::Absolute); -}; -} // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGParseColor.cpp b/src/svg/SVGParseColor.cpp index 1f9d6caf..e0678db2 100644 --- a/src/svg/SVGParseColor.cpp +++ b/src/svg/SVGParseColor.cpp @@ -18,7 +18,7 @@ #include #include -#include "svg/SVGParse.h" +#include "svg/SVGUtils.h" #include "tgfx/core/Color.h" namespace { diff --git a/src/svg/SVGUtils.cpp b/src/svg/SVGUtils.cpp index 2bf1802e..0728cc9f 100644 --- a/src/svg/SVGUtils.cpp +++ b/src/svg/SVGUtils.cpp @@ -17,6 +17,7 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "SVGUtils.h" +#include #include #include #include @@ -197,6 +198,61 @@ std::string FloatToString(float value) { return result; } +std::shared_ptr Base64Decode(const std::string& encodedString) { + static const std::array decodingTable = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, + 64, 64, 64, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, 64, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 64, 64, 64, 64, 64, 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64}; + + size_t inLength = encodedString.size(); + if (inLength % 4 != 0) { + return nullptr; + } + + size_t outLength = inLength / 4 * 3; + if (encodedString[inLength - 1] == '=') { + outLength--; + } + if (encodedString[inLength - 2] == '=') { + outLength--; + } + + auto* out = static_cast(malloc(outLength)); + auto outData = Data::MakeAdopted(out, outLength, Data::FreeProc); + + for (size_t i = 0, j = 0; i < inLength;) { + uint32_t a = encodedString[i] == '=' + ? 0 & i++ + : decodingTable[static_cast(encodedString[i++])]; + uint32_t b = encodedString[i] == '=' + ? 0 & i++ + : decodingTable[static_cast(encodedString[i++])]; + uint32_t c = encodedString[i] == '=' + ? 0 & i++ + : decodingTable[static_cast(encodedString[i++])]; + uint32_t d = encodedString[i] == '=' + ? 0 & i++ + : decodingTable[static_cast(encodedString[i++])]; + + uint32_t triple = (a << 18) + (b << 12) + (c << 6) + d; + + if (j < outLength) { + out[j++] = (triple >> 16) & 0xFF; + } + if (j < outLength) { + out[j++] = (triple >> 8) & 0xFF; + } + if (j < outLength) { + out[j++] = triple & 0xFF; + } + } + + return outData; +} + void Base64Encode(unsigned char const* bytesToEncode, size_t length, char* ret) { static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -271,4 +327,389 @@ std::shared_ptr AsDataUri(const Pixmap& pixmap) { return dataUri; } +namespace { +inline bool is_between(int c, int min, int max) { + return static_cast(c - min) <= static_cast(max - min); +} + +inline bool is_ws(int c) { + return is_between(c, 1, 32); +} + +inline bool is_digit(int c) { + return is_between(c, '0', '9'); +} + +int to_hex(int c) { + if (is_digit(c)) { + return c - '0'; + } + + c |= 0x20; // make us lower-case + return is_between(c, 'a', 'f') ? c + 10 - 'a' : -1; +} + +inline bool is_hex(int c) { + return to_hex(c) >= 0; +} + +const char* skip_ws(const char str[]) { + ASSERT(str); + while (is_ws(*str)) { + str++; + } + return str; +} + +template +tgfx::Unichar next_fail(const T** ptr, const T* end) { + *ptr = end; + return -1; +} + +inline bool is_sep(int c) { + return is_ws(c) || c == ',' || c == ';'; +} + +const char* skip_sep(const char str[]) { + ASSERT(str); + while (is_sep(*str)) { + str++; + } + return str; +} + +bool lookup_str(const char str[], const char** table, int count) { + while (--count >= 0) { + if (!strcmp(str, table[count])) { + return true; + } + } + return false; +} + +inline bool is_lower(int c) { + return is_between(c, 'a', 'z'); +} + +inline int to_upper(int c) { + return c - 'a' + 'A'; +} + +// If unable to read count points from str into value, this will return nullptr +// to signal the failure. Otherwise, it will return the next offset to read from. +const char* find_points(const char str[], Point value[], int count, bool isRelative, + Point* relative) { + str = SVGParse::FindScalars(str, &value[0].x, count * 2); + if (isRelative) { + for (int index = 0; index < count; index++) { + value[index].x += relative->x; + value[index].y += relative->y; + } + } + return str; +} + +// If unable to read a scalar from str into value, this will return nullptr +// to signal the failure. Otherwise, it will return the next offset to read from. +const char* find_scalar(const char str[], float* value, bool isRelative, float relative) { + str = SVGParse::FindScalar(str, value); + if (!str) { + return nullptr; + } + if (isRelative) { + *value += relative; + } + str = skip_sep(str); + return str; +} + +// https://www.w3.org/TR/SVG11/paths.html#PathDataBNF +// flag: +// "0" | "1" +const char* find_flag(const char str[], bool* value) { + if (!str) { + return nullptr; + } + if (str[0] != '1' && str[0] != '0') { + return nullptr; + } + *value = str[0] != '0'; + str = skip_sep(str + 1); + return str; +} +} // namespace + +std::tuple> PathMakeFromSVGString(const char data[]) { + // We will write all data to this local path and only write it + // to result if the whole parsing succeeds. + auto path = std::make_shared(); + auto first = Point::Zero(); + auto opOrigin = Point::Zero(); + auto lastOpOrigin = Point::Zero(); + + // We will use find_points and find_scalar to read into these. + // There might not be enough data to fill them, so to avoid + // MSAN warnings about using uninitialized bytes, we initialize + // them there. + Point points[3] = {}; + float scratch = 0; + char op = '\0'; + char previousOp = '\0'; + bool relative = false; + for (;;) { + if (!data) { + // Truncated data + return {false, nullptr}; + } + data = skip_ws(data); + if (data[0] == '\0') { + break; + } + char ch = data[0]; + if (is_digit(ch) || ch == '-' || ch == '+' || ch == '.') { + if (op == '\0' || op == 'Z') { + return {false, nullptr}; + } + } else if (is_sep(ch)) { + data = skip_sep(data); + } else { + op = ch; + relative = false; + if (is_lower(op)) { + op = static_cast(to_upper(op)); + relative = true; + } + data++; + data = skip_sep(data); + } + switch (op) { + case 'M': // Move + data = find_points(data, points, 1, relative, &opOrigin); + // find_points might have failed, so this might be the + // previous point. However, data will be set to nullptr + // if it failed, so we will check this at the top of the loop. + path->moveTo(points[0]); + previousOp = '\0'; + op = 'L'; + opOrigin = points[0]; + break; + case 'L': // Line + data = find_points(data, points, 1, relative, &opOrigin); + path->lineTo(points[0]); + opOrigin = points[0]; + break; + case 'H': // Horizontal Line + data = find_scalar(data, &scratch, relative, opOrigin.x); + // Similarly, if there wasn't a scalar to read, data will + // be set to nullptr and this lineTo is bogus but will + // be ultimately ignored when the next time through the loop + // detects that and bails out. + path->lineTo(scratch, opOrigin.y); + opOrigin.x = scratch; + break; + case 'V': // Vertical Line + data = find_scalar(data, &scratch, relative, opOrigin.y); + path->lineTo(opOrigin.x, scratch); + opOrigin.y = scratch; + break; + case 'C': // Cubic Bezier Curve + data = find_points(data, points, 3, relative, &opOrigin); + goto cubicCommon; + case 'S': // Continued "Smooth" Cubic Bezier Curve + data = find_points(data, &points[1], 2, relative, &opOrigin); + points[0] = opOrigin; + if (previousOp == 'C' || previousOp == 'S') { + points[0].x -= lastOpOrigin.x - opOrigin.x; + points[0].y -= lastOpOrigin.y - opOrigin.y; + } + cubicCommon: + path->cubicTo(points[0], points[1], points[2]); + lastOpOrigin = points[1]; + opOrigin = points[2]; + break; + case 'Q': // Quadratic Bezier Curve + data = find_points(data, points, 2, relative, &opOrigin); + goto quadraticCommon; + case 'T': // Continued Quadratic Bezier Curve + data = find_points(data, &points[1], 1, relative, &opOrigin); + points[0] = opOrigin; + if (previousOp == 'Q' || previousOp == 'T') { + points[0].x -= lastOpOrigin.x - opOrigin.x; + points[0].y -= lastOpOrigin.y - opOrigin.y; + } + quadraticCommon: + path->quadTo(points[0], points[1]); + lastOpOrigin = points[0]; + opOrigin = points[1]; + break; + case 'A': { // Arc (Elliptical) + auto xyRadii = Point::Zero(); + float angle = 0.0f; + bool largeArc = true; + bool sweep = true; + + if (data = find_points(data, &xyRadii, 1, false, nullptr); !data) break; + if (data = skip_sep(data); !data) break; + if (data = find_scalar(data, &angle, false, 0); !data) break; + if (data = skip_sep(data); !data) break; + if (data = find_flag(data, &largeArc); !data) break; + if (data = skip_sep(data); !data) break; + if (data = find_flag(data, &sweep); !data) break; + if (data = skip_sep(data); !data) break; + if (data = find_points(data, &points[0], 1, relative, &opOrigin); !data) break; + + auto arcSize = largeArc ? PathArcSize::Large : PathArcSize::Small; + path->arcTo(xyRadii.x, xyRadii.y, angle, arcSize, !sweep, points[0]); + path->getLastPoint(&opOrigin); + } break; + case 'Z': // Close Path + path->close(); + opOrigin = first; + break; + default: + return {false, nullptr}; + } + if (previousOp == 0) { + first = opOrigin; + } + previousOp = op; + } + return {true, path}; +} + +const char* SVGParse::FindHex(const char str[], uint32_t* value) { + ASSERT(str); + str = skip_ws(str); + + if (!is_hex(*str)) { + return nullptr; + } + + uint32_t n = 0; + int max_digits = 8; + int digit; + while ((digit = to_hex(*str)) >= 0) { + if (--max_digits < 0) { + return nullptr; + } + n = (n << 4) | static_cast(digit); + str += 1; + } + + if (*str == 0 || is_ws(*str)) { + if (value) { + *value = n; + } + return str; + } + return nullptr; +} + +const char* SVGParse::FindS32(const char str[], int32_t* value) { + ASSERT(str); + str = skip_ws(str); + + int sign = 1; + int64_t maxAbsValue = std::numeric_limits::max(); + if (*str == '-') { + sign = -1; + maxAbsValue = -static_cast(std::numeric_limits::min()); + str += 1; + } + + if (!is_digit(*str)) { + return nullptr; + } + + int64_t n = 0; + while (is_digit(*str)) { + n = 10 * n + *str - '0'; + if (n > maxAbsValue) { + return nullptr; + } + + str += 1; + } + + if (value) { + *value = static_cast(sign * n); + } + return str; +} + +const char* SVGParse::FindScalar(const char str[], float* value) { + ASSERT(str); + str = skip_ws(str); + + char* stop; + auto v = static_cast(strtod(str, &stop)); + if (str == stop) { + return nullptr; + } + if (value) { + *value = v; + } + return stop; +} + +const char* SVGParse::FindScalars(const char str[], float value[], int count) { + ASSERT(count >= 0); + + if (count > 0) { + for (;;) { + str = SVGParse::FindScalar(str, value); + if (--count == 0 || str == nullptr) { + break; + } + + // keep going + str = skip_sep(str); + if (value) { + value += 1; + } + } + } + return str; +} + +bool SVGParse::FindBool(const char str[], bool* value) { + static const char* yesSet[] = {"yes", "1", "true"}; + static const char* noSet[] = {"no", "0", "false"}; + + if (lookup_str(str, yesSet, std::size(yesSet))) { + if (value) { + *value = true; + } + return true; + } else if (lookup_str(str, noSet, std::size(noSet))) { + if (value) { + *value = false; + } + return true; + } + return false; +} + +int SVGParse::FindList(const char target[], const char list[]) { + auto len = strlen(target); + int index = 0; + + for (;;) { + const char* end = strchr(list, ','); + size_t entryLen = end == nullptr ? strlen(list) : static_cast(end - list); + + if (entryLen == len && memcmp(target, list, len) == 0) { + return index; + } + if (end == nullptr) { + break; + } + + list = end + 1; // skip the ',' + index += 1; + } + return -1; +} + } // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGUtils.h b/src/svg/SVGUtils.h index b3af4692..16a8ff85 100644 --- a/src/svg/SVGUtils.h +++ b/src/svg/SVGUtils.h @@ -18,6 +18,7 @@ #pragma once +#include #include #include "tgfx/core/Bitmap.h" #include "tgfx/core/Color.h" @@ -49,6 +50,8 @@ enum class PathEncoding { std::string ToSVGPath(const Path& path, PathEncoding = PathEncoding::Absolute); +std::tuple> PathMakeFromSVGString(const char data[]); + std::string ToSVGTransform(const Matrix& matrix); /** @@ -69,10 +72,31 @@ std::string FloatToString(float value); void Base64Encode(unsigned char const* bytesToEncode, size_t length, char* ret); +std::shared_ptr Base64Decode(const std::string& encodedString); + /** * Returns data uri from bytes. * it will use any cached data if available, otherwise will encode as png. */ std::shared_ptr AsDataUri(const Pixmap& pixmap); +inline Color Uint32ToColor(uint32_t value) { + return Color::FromRGBA((value >> 16) & 0xff, (value >> 8) & 0xff, (value >> 0) & 0xff, + (value >> 24) & 0xff); +} + +// Common functions for SVG conversion, used to find specific data types in a string. +// Returns the char pointer after the found data, or nullptr if not found. +class SVGParse { + public: + static const char* FindHex(const char str[], uint32_t* value); + static const char* FindNamedColor(const char str[], Color* color); + static const char* FindS32(const char str[], int32_t* value); + static const char* FindScalar(const char str[], float* value); + static const char* FindScalars(const char str[], float value[], int count); + static bool FindBool(const char str[], bool* value); + // return the index of str in list[], or -1 if not found + static int FindList(const char target[], const char list[]); +}; + } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGImage.cpp b/src/svg/node/SVGImage.cpp index b33acea4..c986af1d 100644 --- a/src/svg/node/SVGImage.cpp +++ b/src/svg/node/SVGImage.cpp @@ -21,6 +21,7 @@ #include "core/utils/Log.h" #include "svg/SVGAttributeParser.h" #include "svg/SVGRenderContext.h" +#include "svg/SVGUtils.h" #include "tgfx/core/Data.h" #include "tgfx/core/Image.h" #include "tgfx/core/Matrix.h" @@ -46,47 +47,6 @@ bool SVGImage::onPrepareToRender(SVGRenderContext* context) const { INHERITED::onPrepareToRender(context); } -std::vector base64_decode(const std::string& encoded_string) { - static constexpr unsigned char kDecodingTable[] = { - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, - 64, 64, 64, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, 64, 0, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, - 23, 24, 25, 64, 64, 64, 64, 64, 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, - 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64}; - - size_t in_len = encoded_string.size(); - if (in_len % 4 != 0) throw std::runtime_error("Invalid base64 length"); - - size_t out_len = in_len / 4 * 3; - if (encoded_string[in_len - 1] == '=') out_len--; - if (encoded_string[in_len - 2] == '=') out_len--; - - std::vector out(out_len); - for (size_t i = 0, j = 0; i < in_len;) { - uint32_t a = encoded_string[i] == '=' - ? 0 & i++ - : kDecodingTable[static_cast(encoded_string[i++])]; - uint32_t b = encoded_string[i] == '=' - ? 0 & i++ - : kDecodingTable[static_cast(encoded_string[i++])]; - uint32_t c = encoded_string[i] == '=' - ? 0 & i++ - : kDecodingTable[static_cast(encoded_string[i++])]; - uint32_t d = encoded_string[i] == '=' - ? 0 & i++ - : kDecodingTable[static_cast(encoded_string[i++])]; - - uint32_t triple = (a << 18) + (b << 12) + (c << 6) + d; - - if (j < out_len) out[j++] = (triple >> 16) & 0xFF; - if (j < out_len) out[j++] = (triple >> 8) & 0xFF; - if (j < out_len) out[j++] = triple & 0xFF; - } - - return out; -} - std::shared_ptr LoadImage(const SVGIRI& href) { const auto& base64URL = href.iri(); auto pos = base64URL.find("base64,"); @@ -94,10 +54,8 @@ std::shared_ptr LoadImage(const SVGIRI& href) { return nullptr; } std::string base64Data = base64URL.substr(pos + 7); - std::vector imageData = base64_decode(base64Data); - auto data = Data::MakeWithCopy(imageData.data(), imageData.size()); - - return Image::MakeFromEncoded(data); + auto data = Base64Decode(base64Data); + return data ? Image::MakeFromEncoded(data) : nullptr; } SVGImage::ImageInfo SVGImage::LoadImage(const SVGIRI& iri, const Rect& viewPort, diff --git a/src/svg/node/SVGPath.cpp b/src/svg/node/SVGPath.cpp index 611a332e..e875fbe2 100644 --- a/src/svg/node/SVGPath.cpp +++ b/src/svg/node/SVGPath.cpp @@ -19,8 +19,8 @@ #include "tgfx/svg/node/SVGPath.h" #include #include "svg/SVGAttributeParser.h" -#include "svg/SVGParse.h" #include "svg/SVGRenderContext.h" +#include "svg/SVGUtils.h" #include "tgfx/core/Canvas.h" #include "tgfx/core/Path.h" #include "tgfx/core/Rect.h" @@ -37,7 +37,7 @@ bool SVGPath::parseAndSetAttribute(const std::string& n, const std::string& v) { template <> bool SVGAttributeParser::parse(Path* path) { - auto [success, parsePath] = PathParse::FromSVGString(currentPos); + auto [success, parsePath] = PathMakeFromSVGString(currentPos); if (success) { *path = *parsePath; } From e5160a5a02b1453973349d41ee459b900d413f6e Mon Sep 17 00:00:00 2001 From: YGauroa Date: Mon, 30 Dec 2024 16:55:11 +0800 Subject: [PATCH 18/31] ... --- include/tgfx/svg/SVGDOM.h | 14 +- include/tgfx/svg/SVGTypes.h | 245 ++++++++++++++----------------- include/tgfx/svg/SVGValue.h | 4 - src/svg/SVGAttributeParser.cpp | 40 ++--- src/svg/node/SVGFeTurbulence.cpp | 4 +- src/svg/node/SVGNode.cpp | 13 +- 6 files changed, 150 insertions(+), 170 deletions(-) diff --git a/include/tgfx/svg/SVGDOM.h b/include/tgfx/svg/SVGDOM.h index fe9d3ab3..05e8c74e 100644 --- a/include/tgfx/svg/SVGDOM.h +++ b/include/tgfx/svg/SVGDOM.h @@ -56,7 +56,7 @@ class SVGDOM { /** * Creates an SVGDOM object from the provided data. */ - static std::shared_ptr Make(const std::shared_ptr&); + static std::shared_ptr Make(const std::shared_ptr& data); /** * Returns the root SVG node. @@ -66,7 +66,7 @@ class SVGDOM { /** * Collects the font families and styles used for rendering and saves them in the font manager. */ - void collectRenderFonts(const std::shared_ptr&); + void collectRenderFonts(const std::shared_ptr& fontManager); /** * Renders the SVG to the provided canvas. @@ -80,7 +80,7 @@ class SVGDOM { /** * Sets the size of the container that the SVG will be rendered into. */ - void setContainerSize(const Size&); + void setContainerSize(const Size& size); /** * Returns the size of the container that the SVG will be rendered into. @@ -88,9 +88,13 @@ class SVGDOM { const Size& getContainerSize() const; private: - SVGDOM(std::shared_ptr, SVGIDMapper&&); + /** + * Construct a new SVGDOM object + */ + SVGDOM(std::shared_ptr root, SVGIDMapper&& mapper); + const std::shared_ptr root = nullptr; const SVGIDMapper nodeIDMapper = {}; - Size containerSize = Size::MakeEmpty(); + Size containerSize = {}; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGTypes.h b/include/tgfx/svg/SVGTypes.h index 964982b1..9fc94116 100644 --- a/include/tgfx/svg/SVGTypes.h +++ b/include/tgfx/svg/SVGTypes.h @@ -46,13 +46,12 @@ enum class SVGPropertyState { }; // https://www.w3.org/TR/SVG11/intro.html#TermProperty -template +template class SVGProperty { public: using ValueT = T; - SVGProperty() : _state(SVGPropertyState::Unspecified) { - } + SVGProperty() = default; explicit SVGProperty(SVGPropertyState state) : _state(state) { } @@ -72,7 +71,7 @@ class SVGProperty { } constexpr bool isInheritable() const { - return kInheritable; + return Inheritable; } bool isValue() const { @@ -117,7 +116,7 @@ class SVGProperty { } private: - SVGPropertyState _state; + SVGPropertyState _state = SVGPropertyState::Unspecified; std::optional _value; }; @@ -137,16 +136,11 @@ class SVGLength { PC, }; - constexpr SVGLength() : _value(0), _unit(Unit::Unknown) { - } + SVGLength() = default; - explicit constexpr SVGLength(float v, Unit u = Unit::Number) : _value(v), _unit(u) { + explicit SVGLength(float v, Unit u = Unit::Number) : _value(v), _unit(u) { } - SVGLength(const SVGLength&) = default; - - SVGLength& operator=(const SVGLength&) = default; - bool operator==(const SVGLength& other) const { return _unit == other._unit && _value == other._value; } @@ -164,8 +158,8 @@ class SVGLength { } private: - float _value; - Unit _unit; + float _value = 0.0f; + Unit _unit = Unit::Unknown; }; // https://www.w3.org/TR/SVG11/linking.html#IRIReference @@ -177,8 +171,7 @@ class SVGIRI { DataURI, }; - SVGIRI() : _type(Type::Local) { - } + SVGIRI() = default; SVGIRI(Type t, SVGStringType iri) : _type(t), _iri(std::move(iri)) { } @@ -200,8 +193,8 @@ class SVGIRI { } private: - Type _type; - SVGStringType _iri; + Type _type = Type::Local; + SVGStringType _iri = {}; }; // https://www.w3.org/TR/SVG11/types.html#InterfaceSVGColor @@ -215,27 +208,19 @@ class SVGColor { using Vars = std::vector; - SVGColor() : SVGColor(Color::Black()) { - } + SVGColor() = default; - explicit SVGColor(const SVGColorType& c) : _type(Type::Color), _color(c), _vars(nullptr) { + explicit SVGColor(const SVGColorType& c) : _color(c) { } SVGColor(Type t, Vars&& vars) - : _type(t), _color(Color::Black()), - _vars(vars.empty() ? nullptr : std::make_shared(std::move(vars))) { + : _type(t), _vars(vars.empty() ? nullptr : std::make_shared(std::move(vars))) { } SVGColor(const SVGColorType& c, Vars&& vars) - : _type(Type::Color), _color(c), - _vars(vars.empty() ? nullptr : std::make_shared(std::move(vars))) { + : _color(c), _vars(vars.empty() ? nullptr : std::make_shared(std::move(vars))) { } - SVGColor(const SVGColor&) = default; - SVGColor& operator=(const SVGColor&) = default; - SVGColor(SVGColor&&) = default; - SVGColor& operator=(SVGColor&&) = default; - bool operator==(const SVGColor& other) const { return _type == other._type && _color == other._color && _vars == other._vars; } @@ -256,14 +241,10 @@ class SVGColor { return _vars ? _vars : nullptr; } - std::shared_ptr vars() { - return _vars ? _vars : nullptr; - } - private: - Type _type; - SVGColorType _color; - std::shared_ptr _vars; + Type _type = Type::Color; + SVGColorType _color = Color::Black(); + std::shared_ptr _vars = nullptr; }; class SVGPaint { @@ -274,24 +255,22 @@ class SVGPaint { IRI, }; - SVGPaint() : _type(Type::None), _color(Color::Black()) { - } + SVGPaint() = default; + explicit SVGPaint(Type t) : _type(t), _color(Color::Black()) { } - explicit SVGPaint(SVGColor c) : _type(Type::Color), _color(std::move(c)) { - } - SVGPaint(SVGIRI iri, SVGColor fallback_color) - : _type(Type::IRI), _color(std::move(fallback_color)), _iri(std::move(iri)) { + + explicit SVGPaint(const SVGColor& color) : _type(Type::Color), _color(std::move(color)) { } - SVGPaint(const SVGPaint&) = default; - SVGPaint& operator=(const SVGPaint&) = default; - SVGPaint(SVGPaint&&) = default; - SVGPaint& operator=(SVGPaint&&) = default; + SVGPaint(SVGIRI iri, SVGColor color) + : _type(Type::IRI), _color(std::move(color)), _iri(std::move(iri)) { + } bool operator==(const SVGPaint& other) const { return _type == other._type && _color == other._color && _iri == other._iri; } + bool operator!=(const SVGPaint& other) const { return !(*this == other); } @@ -299,17 +278,19 @@ class SVGPaint { Type type() const { return _type; } + const SVGColor& color() const { return _color; } + const SVGIRI& iri() const { return _iri; } private: - Type _type; - SVGColor _color; - SVGIRI _iri; + Type _type = Type::None; + SVGColor _color = SVGColor(Color::Black()); + SVGIRI _iri = {}; }; // | none (used for clip/mask/filter properties) @@ -320,8 +301,7 @@ class SVGFuncIRI { IRI, }; - SVGFuncIRI() : _type(Type::None) { - } + SVGFuncIRI() = default; explicit SVGFuncIRI(Type t) : _type(t) { } @@ -346,8 +326,8 @@ class SVGFuncIRI { } private: - Type _type; - SVGIRI _iri; + Type _type = Type::None; + SVGIRI _iri = {}; }; enum class SVGLineCap { @@ -365,17 +345,15 @@ class SVGLineJoin { Inherit, }; - constexpr SVGLineJoin() : _type(Type::Inherit) { - } - constexpr explicit SVGLineJoin(Type t) : _type(t) { - } + SVGLineJoin() = default; - SVGLineJoin(const SVGLineJoin&) = default; - SVGLineJoin& operator=(const SVGLineJoin&) = default; + explicit SVGLineJoin(Type t) : _type(t) { + } bool operator==(const SVGLineJoin& other) const { return _type == other._type; } + bool operator!=(const SVGLineJoin& other) const { return !(*this == other); } @@ -385,7 +363,7 @@ class SVGLineJoin { } private: - Type _type; + Type _type = Type::Inherit; }; class SVGSpreadMethod { @@ -396,17 +374,15 @@ class SVGSpreadMethod { Reflect, }; - constexpr SVGSpreadMethod() : _type(Type::Pad) { - } - constexpr explicit SVGSpreadMethod(Type t) : _type(t) { - } + SVGSpreadMethod() = default; - SVGSpreadMethod(const SVGSpreadMethod&) = default; - SVGSpreadMethod& operator=(const SVGSpreadMethod&) = default; + explicit SVGSpreadMethod(Type t) : _type(t) { + } bool operator==(const SVGSpreadMethod& other) const { return _type == other._type; } + bool operator!=(const SVGSpreadMethod& other) const { return !(*this == other); } @@ -416,7 +392,7 @@ class SVGSpreadMethod { } private: - Type _type; + Type _type = Type::Pad; }; class SVGFillRule { @@ -427,17 +403,15 @@ class SVGFillRule { Inherit, }; - constexpr SVGFillRule() : _type(Type::Inherit) { - } - constexpr explicit SVGFillRule(Type t) : _type(t) { - } + SVGFillRule() = default; - SVGFillRule(const SVGFillRule&) = default; - SVGFillRule& operator=(const SVGFillRule&) = default; + explicit SVGFillRule(Type t) : _type(t) { + } bool operator==(const SVGFillRule& other) const { return _type == other._type; } + bool operator!=(const SVGFillRule& other) const { return !(*this == other); } @@ -451,7 +425,7 @@ class SVGFillRule { } private: - Type _type; + Type _type = Type::Inherit; }; class SVGVisibility { @@ -463,17 +437,15 @@ class SVGVisibility { Inherit, }; - constexpr SVGVisibility() : _type(Type::Visible) { - } - constexpr explicit SVGVisibility(Type t) : _type(t) { - } + SVGVisibility() = default; - SVGVisibility(const SVGVisibility&) = default; - SVGVisibility& operator=(const SVGVisibility&) = default; + explicit SVGVisibility(Type t) : _type(t) { + } bool operator==(const SVGVisibility& other) const { return _type == other._type; } + bool operator!=(const SVGVisibility& other) const { return !(*this == other); } @@ -483,7 +455,7 @@ class SVGVisibility { } private: - Type _type; + Type _type = Type::Visible; }; class SVGDashArray { @@ -494,20 +466,19 @@ class SVGDashArray { Inherit, }; - SVGDashArray() : _type(Type::None) { - } + SVGDashArray() = default; + explicit SVGDashArray(Type t) : _type(t) { } + explicit SVGDashArray(std::vector&& dashArray) : _type(Type::DashArray), _dashArray(std::move(dashArray)) { } - SVGDashArray(const SVGDashArray&) = default; - SVGDashArray& operator=(const SVGDashArray&) = default; - bool operator==(const SVGDashArray& other) const { return _type == other._type && _dashArray == other._dashArray; } + bool operator!=(const SVGDashArray& other) const { return !(*this == other); } @@ -521,28 +492,26 @@ class SVGDashArray { } private: - Type _type; + Type _type = Type::None; std::vector _dashArray; }; class SVGStopColor { public: enum class Type { - kColor, - kCurrentColor, - kICCColor, - kInherit, + Color, + CurrentColor, + ICCColor, + Inherit, }; - SVGStopColor() : _type(Type::kColor), _color(Color::Black()) { - } + SVGStopColor() = default; + explicit SVGStopColor(Type t) : _type(t), _color(Color::Black()) { } - explicit SVGStopColor(const SVGColorType& c) : _type(Type::kColor), _color(c) { - } - SVGStopColor(const SVGStopColor&) = default; - SVGStopColor& operator=(const SVGStopColor&) = default; + explicit SVGStopColor(const SVGColorType& c) : _type(Type::Color), _color(c) { + } bool operator==(const SVGStopColor& other) const { return _type == other._type && _color == other._color; @@ -559,8 +528,8 @@ class SVGStopColor { } private: - Type _type; - SVGColorType _color; + Type _type = Type::Color; + SVGColorType _color = Color::Black(); }; class SVGObjectBoundingBoxUnits { @@ -570,8 +539,8 @@ class SVGObjectBoundingBoxUnits { ObjectBoundingBox, }; - SVGObjectBoundingBoxUnits() : _type(Type::UserSpaceOnUse) { - } + SVGObjectBoundingBoxUnits() = default; + explicit SVGObjectBoundingBoxUnits(Type t) : _type(t) { } @@ -587,7 +556,7 @@ class SVGObjectBoundingBoxUnits { } private: - Type _type; + Type _type = Type::UserSpaceOnUse; }; class SVGFontFamily { @@ -597,14 +566,15 @@ class SVGFontFamily { Inherit, }; - SVGFontFamily() : _type(Type::Inherit) { - } + SVGFontFamily() = default; + explicit SVGFontFamily(std::string family) : _type(Type::Family), _family(std::move(family)) { } bool operator==(const SVGFontFamily& other) const { return _type == other._type && _family == other._family; } + bool operator!=(const SVGFontFamily& other) const { return !(*this == other); } @@ -618,7 +588,7 @@ class SVGFontFamily { } private: - Type _type; + Type _type = Type::Inherit; std::string _family; }; @@ -631,14 +601,15 @@ class SVGFontStyle { Inherit, }; - SVGFontStyle() : _type(Type::Inherit) { - } + SVGFontStyle() = default; + explicit SVGFontStyle(Type t) : _type(t) { } bool operator==(const SVGFontStyle& other) const { return _type == other._type; } + bool operator!=(const SVGFontStyle& other) const { return !(*this == other); } @@ -648,7 +619,7 @@ class SVGFontStyle { } private: - Type _type; + Type _type = Type::Inherit; }; class SVGFontSize { @@ -658,14 +629,15 @@ class SVGFontSize { Inherit, }; - SVGFontSize() : _type(Type::Inherit), _size(0) { - } + SVGFontSize() = default; + explicit SVGFontSize(const SVGLength& s) : _type(Type::Length), _size(s) { } bool operator==(const SVGFontSize& other) const { return _type == other._type && _size == other._size; } + bool operator!=(const SVGFontSize& other) const { return !(*this == other); } @@ -679,8 +651,8 @@ class SVGFontSize { } private: - Type _type; - SVGLength _size; + Type _type = Type::Inherit; + SVGLength _size = SVGLength(0.0f); }; class SVGFontWeight { @@ -702,14 +674,15 @@ class SVGFontWeight { Inherit, }; - SVGFontWeight() : _type(Type::Inherit) { - } + SVGFontWeight() = default; + explicit SVGFontWeight(Type t) : _type(t) { } bool operator==(const SVGFontWeight& other) const { return _type == other._type; } + bool operator!=(const SVGFontWeight& other) const { return !(*this == other); } @@ -719,11 +692,11 @@ class SVGFontWeight { } private: - Type _type; + Type _type = Type::Inherit; }; struct SVGPreserveAspectRatio { - enum Align : uint8_t { + enum class Align { // These values are chosen such that bits [0,1] encode X alignment, and // bits [2,3] encode Y alignment. XMinYMin = 0x00, @@ -739,13 +712,13 @@ struct SVGPreserveAspectRatio { None = 0x10, }; - enum Scale { + enum class Scale { Meet, Slice, }; - Align align = XMidYMid; - Scale scale = Meet; + Align align = Align::XMidYMid; + Scale scale = Scale::Meet; }; class SVGTextAnchor { @@ -757,14 +730,15 @@ class SVGTextAnchor { Inherit, }; - SVGTextAnchor() : _type(Type::Inherit) { - } + SVGTextAnchor() = default; + explicit SVGTextAnchor(Type t) : _type(t) { } bool operator==(const SVGTextAnchor& other) const { return _type == other._type; } + bool operator!=(const SVGTextAnchor& other) const { return !(*this == other); } @@ -774,7 +748,7 @@ class SVGTextAnchor { } private: - Type _type; + Type _type = Type::Inherit; }; // https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveInAttribute @@ -791,10 +765,11 @@ class SVGFeInputType { Unspecified, }; - SVGFeInputType() : _type(Type::Unspecified) { - } + SVGFeInputType() = default; + explicit SVGFeInputType(Type t) : _type(t) { } + explicit SVGFeInputType(SVGStringType id) : _type(Type::FilterPrimitiveReference), _id(std::move(id)) { } @@ -802,6 +777,7 @@ class SVGFeInputType { bool operator==(const SVGFeInputType& other) const { return _type == other._type && _id == other._id; } + bool operator!=(const SVGFeInputType& other) const { return !(*this == other); } @@ -815,7 +791,7 @@ class SVGFeInputType { } private: - Type _type; + Type _type = Type::Unspecified; std::string _id; }; @@ -839,8 +815,8 @@ enum class SVGFeCompositeOperator { class SVGFeTurbulenceBaseFrequency { public: - SVGFeTurbulenceBaseFrequency() : _freqX(0), _freqY(0) { - } + SVGFeTurbulenceBaseFrequency() = default; + SVGFeTurbulenceBaseFrequency(SVGNumberType freqX, SVGNumberType freqY) : _freqX(freqX), _freqY(freqY) { } @@ -848,25 +824,26 @@ class SVGFeTurbulenceBaseFrequency { SVGNumberType freqX() const { return _freqX; } + SVGNumberType freqY() const { return _freqY; } private: - SVGNumberType _freqX; - SVGNumberType _freqY; + SVGNumberType _freqX = 0.0; + SVGNumberType _freqY = 0.0; }; struct SVGFeTurbulenceType { - enum Type { + enum class Type { FractalNoise, Turbulence, }; - Type type; + Type type = Type::Turbulence; + + SVGFeTurbulenceType() = default; - SVGFeTurbulenceType() : type(Turbulence) { - } explicit SVGFeTurbulenceType(Type inputType) : type(inputType) { } }; diff --git a/include/tgfx/svg/SVGValue.h b/include/tgfx/svg/SVGValue.h index fc2376e0..f8463bdd 100644 --- a/include/tgfx/svg/SVGValue.h +++ b/include/tgfx/svg/SVGValue.h @@ -57,8 +57,6 @@ class SVGValue { template class SVGWrapperValue final : public SVGValue { public: - static constexpr Type _type = ValueType; - explicit SVGWrapperValue(const T& v) : INHERITED(ValueType), wrappedValue(v) { } @@ -83,13 +81,11 @@ class SVGWrapperValue final : public SVGValue { using INHERITED = SVGValue; }; -using SVGColorValue = SVGWrapperValue; using SVGLengthValue = SVGWrapperValue; using SVGTransformValue = SVGWrapperValue; using SVGViewBoxValue = SVGWrapperValue; using SVGNumberValue = SVGWrapperValue; using SVGStringValue = SVGWrapperValue; -using SVGStopColorValue = SVGWrapperValue; using SVGPreserveAspectRatioValue = SVGWrapperValue; using SVGObjectBoundingBoxUnitsValue = diff --git a/src/svg/SVGAttributeParser.cpp b/src/svg/SVGAttributeParser.cpp index 8c8b01c2..10a9bd4a 100644 --- a/src/svg/SVGAttributeParser.cpp +++ b/src/svg/SVGAttributeParser.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "core/utils/Log.h" #include "core/utils/MathExtra.h" #include "svg/SVGUtils.h" @@ -823,10 +824,11 @@ bool SVGAttributeParser::parse(SVGLineCap* cap) { // https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty template <> bool SVGAttributeParser::parse(SVGLineJoin* join) { - static const struct { - SVGLineJoin::Type fType; - const char* fName; - } gJoinInfo[] = { + struct JoinInfo { + SVGLineJoin::Type type; + const char* name; + }; + static const std::vector joinInfoMap = { {SVGLineJoin::Type::Miter, "miter"}, {SVGLineJoin::Type::Round, "round"}, {SVGLineJoin::Type::Bevel, "bevel"}, @@ -834,9 +836,9 @@ bool SVGAttributeParser::parse(SVGLineJoin* join) { }; bool parsedValue = false; - for (size_t i = 0; i < std::size(gJoinInfo); ++i) { - if (this->parseExpectedStringToken(gJoinInfo[i].fName)) { - *join = SVGLineJoin(gJoinInfo[i].fType); + for (auto i : joinInfoMap) { + if (this->parseExpectedStringToken(i.name)) { + *join = SVGLineJoin(i.type); parsedValue = true; break; } @@ -1099,21 +1101,21 @@ bool SVGAttributeParser::parse(SVGTextAnchor* anchor) { // https://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute bool SVGAttributeParser::parsePreserveAspectRatio(SVGPreserveAspectRatio* par) { static constexpr std::tuple alignMap[] = { - {"none", SVGPreserveAspectRatio::None}, - {"xMinYMin", SVGPreserveAspectRatio::XMinYMin}, - {"xMidYMin", SVGPreserveAspectRatio::XMidYMin}, - {"xMaxYMin", SVGPreserveAspectRatio::XMaxYMin}, - {"xMinYMid", SVGPreserveAspectRatio::XMinYMid}, - {"xMidYMid", SVGPreserveAspectRatio::XMidYMid}, - {"xMaxYMid", SVGPreserveAspectRatio::XMaxYMid}, - {"xMinYMax", SVGPreserveAspectRatio::XMinYMax}, - {"xMidYMax", SVGPreserveAspectRatio::XMidYMax}, - {"xMaxYMax", SVGPreserveAspectRatio::XMaxYMax}, + {"none", SVGPreserveAspectRatio::Align::None}, + {"xMinYMin", SVGPreserveAspectRatio::Align::XMinYMin}, + {"xMidYMin", SVGPreserveAspectRatio::Align::XMidYMin}, + {"xMaxYMin", SVGPreserveAspectRatio::Align::XMaxYMin}, + {"xMinYMid", SVGPreserveAspectRatio::Align::XMinYMid}, + {"xMidYMid", SVGPreserveAspectRatio::Align::XMidYMid}, + {"xMaxYMid", SVGPreserveAspectRatio::Align::XMaxYMid}, + {"xMinYMax", SVGPreserveAspectRatio::Align::XMinYMax}, + {"xMidYMax", SVGPreserveAspectRatio::Align::XMidYMax}, + {"xMaxYMax", SVGPreserveAspectRatio::Align::XMaxYMax}, }; static constexpr std::tuple scaleMap[] = { - {"meet", SVGPreserveAspectRatio::Meet}, - {"slice", SVGPreserveAspectRatio::Slice}, + {"meet", SVGPreserveAspectRatio::Scale::Meet}, + {"slice", SVGPreserveAspectRatio::Scale::Slice}, }; bool parsedValue = false; diff --git a/src/svg/node/SVGFeTurbulence.cpp b/src/svg/node/SVGFeTurbulence.cpp index dfef1502..157aa895 100644 --- a/src/svg/node/SVGFeTurbulence.cpp +++ b/src/svg/node/SVGFeTurbulence.cpp @@ -45,10 +45,10 @@ bool SVGAttributeParser::parse(SVGFeTurbulenceType* type) { bool parsedValue = false; if (this->parseExpectedStringToken("fractalNoise")) { - *type = SVGFeTurbulenceType(SVGFeTurbulenceType::FractalNoise); + *type = SVGFeTurbulenceType(SVGFeTurbulenceType::Type::FractalNoise); parsedValue = true; } else if (this->parseExpectedStringToken("turbulence")) { - *type = SVGFeTurbulenceType(SVGFeTurbulenceType::Turbulence); + *type = SVGFeTurbulenceType(SVGFeTurbulenceType::Type::Turbulence); parsedValue = true; } diff --git a/src/svg/node/SVGNode.cpp b/src/svg/node/SVGNode.cpp index 50735d30..6f317947 100644 --- a/src/svg/node/SVGNode.cpp +++ b/src/svg/node/SVGNode.cpp @@ -150,32 +150,33 @@ Matrix SVGNode::ComputeViewboxMatrix(const Rect& viewBox, const Rect& viewPort, const auto sx = viewPort.width() / viewBox.width(); const auto sy = viewPort.height() / viewBox.height(); - if (par.align == SVGPreserveAspectRatio::None) { + if (par.align == SVGPreserveAspectRatio::Align::None) { // none -> anisotropic scaling, regardless of fScale return {sx, sy}; } // isotropic scaling - const auto s = par.scale == SVGPreserveAspectRatio::Meet ? std::min(sx, sy) : std::max(sx, sy); + const auto s = + par.scale == SVGPreserveAspectRatio::Scale::Meet ? std::min(sx, sy) : std::max(sx, sy); return {s, s}; }; auto compute_trans = [&](const Point& scale) -> Point { - static constexpr float gAlignCoeffs[] = { + static constexpr float alignCoeffs[] = { 0.0f, // Min 0.5f, // Mid 1.0f // Max }; - const size_t x_coeff = par.align >> 0 & 0x03; - const size_t y_coeff = par.align >> 2 & 0x03; + const size_t x_coeff = static_cast(par.align) >> 0 & 0x03; + const size_t y_coeff = static_cast(par.align) >> 2 & 0x03; const auto tx = -viewBox.x() * scale.x; const auto ty = -viewBox.y() * scale.y; const auto dx = viewPort.width() - viewBox.width() * scale.x; const auto dy = viewPort.height() - viewBox.height() * scale.y; - return {tx + dx * gAlignCoeffs[x_coeff], ty + dy * gAlignCoeffs[y_coeff]}; + return {tx + dx * alignCoeffs[x_coeff], ty + dy * alignCoeffs[y_coeff]}; }; const auto s = compute_scale(); From ac521d18ba091837e1f409c8a6e45f14b1467b99 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Mon, 30 Dec 2024 17:17:54 +0800 Subject: [PATCH 19/31] ... --- src/svg/SVGDOM.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/svg/SVGDOM.cpp b/src/svg/SVGDOM.cpp index fe3fe2b3..7604feac 100644 --- a/src/svg/SVGDOM.cpp +++ b/src/svg/SVGDOM.cpp @@ -48,11 +48,14 @@ std::shared_ptr SVGDOM::Make(const std::shared_ptr& data) { if (!data) { return nullptr; } + // Parse the data into an XML DOM structure auto xmlDom = DOM::MakeFromData(*data); if (!xmlDom) { return nullptr; } + // Convert the XML structure to an SVG structure, translating XML elements and attributes into + // SVG elements and attributes SVGIDMapper mapper; ConstructionContext constructionContext(&mapper); auto root = @@ -61,6 +64,7 @@ std::shared_ptr SVGDOM::Make(const std::shared_ptr& data) { return nullptr; } + // Create SVGDOM with the root node and ID mapper return std::shared_ptr( new SVGDOM(std::static_pointer_cast(root), std::move(mapper))); } From 13c0a7b3b8fbd4df6aec00303ca95781f04ecc3f Mon Sep 17 00:00:00 2001 From: YGauroa Date: Mon, 30 Dec 2024 17:59:34 +0800 Subject: [PATCH 20/31] interface for user and base attribute class define --- include/tgfx/svg/SVGAttribute.h | 118 ++++ include/tgfx/svg/SVGDOM.h | 100 ++++ include/tgfx/svg/SVGFontManager.h | 114 ++++ include/tgfx/svg/SVGTypes.h | 871 ++++++++++++++++++++++++++++++ include/tgfx/svg/SVGValue.h | 93 ++++ include/tgfx/svg/node/SVGNode.h | 295 ++++++++++ src/svg/SVGAttribute.cpp | 63 +++ src/svg/SVGDOM.cpp | 141 +++++ src/svg/SVGFontManager.cpp | 92 ++++ src/svg/node/SVGNode.cpp | 190 +++++++ 10 files changed, 2077 insertions(+) create mode 100644 include/tgfx/svg/SVGAttribute.h create mode 100644 include/tgfx/svg/SVGDOM.h create mode 100644 include/tgfx/svg/SVGFontManager.h create mode 100644 include/tgfx/svg/SVGTypes.h create mode 100644 include/tgfx/svg/SVGValue.h create mode 100644 include/tgfx/svg/node/SVGNode.h create mode 100644 src/svg/SVGAttribute.cpp create mode 100644 src/svg/SVGDOM.cpp create mode 100644 src/svg/SVGFontManager.cpp create mode 100644 src/svg/node/SVGNode.cpp diff --git a/include/tgfx/svg/SVGAttribute.h b/include/tgfx/svg/SVGAttribute.h new file mode 100644 index 00000000..d2da36f6 --- /dev/null +++ b/include/tgfx/svg/SVGAttribute.h @@ -0,0 +1,118 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "tgfx/svg/SVGTypes.h" + +namespace tgfx { +enum class SVGAttribute { + ClipRule, + Color, + ColorInterpolation, + ColorInterpolationFilters, + Cx, // , , : center x position + Cy, // , , : center y position + Fill, + FillOpacity, + FillRule, + Filter, + FilterUnits, + FontFamily, + FontSize, + FontStyle, + FontWeight, + Fx, // : focal point x position + Fy, // : focal point y position + GradientUnits, + GradientTransform, + Height, + Href, + Opacity, + Points, + PreserveAspectRatio, + R, // , : radius + Rx, // ,: horizontal (corner) radius + Ry, // ,: vertical (corner) radius + SpreadMethod, + Stroke, + StrokeDashArray, + StrokeDashOffset, + StrokeOpacity, + StrokeLineCap, + StrokeLineJoin, + StrokeMiterLimit, + StrokeWidth, + Transform, + Text, + TextAnchor, + ViewBox, + Visibility, + Width, + X, + X1, // : first endpoint x + X2, // : second endpoint x + Y, + Y1, // : first endpoint y + Y2, // : second endpoint y + + Unknown, +}; + +struct SVGPresentationAttributes { + static SVGPresentationAttributes MakeInitial(); + + SVGProperty Fill; + SVGProperty FillOpacity; + SVGProperty FillRule; + SVGProperty ClipRule; + + SVGProperty Stroke; + SVGProperty StrokeDashArray; + SVGProperty StrokeDashOffset; + SVGProperty StrokeLineCap; + SVGProperty StrokeLineJoin; + SVGProperty StrokeMiterLimit; + SVGProperty StrokeOpacity; + SVGProperty StrokeWidth; + + SVGProperty Visibility; + + SVGProperty Color; + SVGProperty ColorInterpolation; + SVGProperty ColorInterpolationFilters; + + SVGProperty FontFamily; + SVGProperty FontStyle; + SVGProperty FontSize; + SVGProperty FontWeight; + SVGProperty TextAnchor; + + // uninherited + SVGProperty Opacity; + SVGProperty ClipPath; + SVGProperty Display; + SVGProperty Mask; + SVGProperty Filter; + SVGProperty StopColor; + SVGProperty StopOpacity; + SVGProperty FloodColor; + SVGProperty FloodOpacity; + SVGProperty LightingColor; +}; +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGDOM.h b/include/tgfx/svg/SVGDOM.h new file mode 100644 index 00000000..05e8c74e --- /dev/null +++ b/include/tgfx/svg/SVGDOM.h @@ -0,0 +1,100 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/core/Canvas.h" +#include "tgfx/core/Data.h" +#include "tgfx/core/Picture.h" +#include "tgfx/core/Size.h" +#include "tgfx/svg/SVGFontManager.h" +#include "tgfx/svg/node/SVGSVG.h" + +namespace tgfx { + +class SVGNode; +using SVGIDMapper = std::unordered_map>; + +/** + * The SVGDOM class represents an SVG Document Object Model (DOM). It provides functionality to + * traverse the SVG DOM tree and render the SVG. + * + * Usage: + * + * 1. Traversing the SVG DOM tree: + * - Use getRoot() to obtain the root node. From the root node, you can access its attributes + * and child nodes, and then visit the child nodes. + * + * 2. Rendering the SVG: + * - The simplest way to render is by calling render(canvas,nullptr). If you need to render text + * with specific fonts or set the size of the SVG, you can use the following methods: + * - If text rendering is required, use collectRenderFonts() to gather the necessary typefaces. + * Traverse the typefaces collected by the fontManager and set the typeface objects. + * - Render the SVG using the render() method. If text rendering is needed, pass in the + * fontManager object. Otherwise, you can pass in nullptr. + * - To set the size of the SVG, use setContainerSize(). If not set, the size of the root SVG + * node will be used. + */ +class SVGDOM { + public: + /** + * Creates an SVGDOM object from the provided data. + */ + static std::shared_ptr Make(const std::shared_ptr& data); + + /** + * Returns the root SVG node. + */ + const std::shared_ptr& getRoot() const; + + /** + * Collects the font families and styles used for rendering and saves them in the font manager. + */ + void collectRenderFonts(const std::shared_ptr& fontManager); + + /** + * Renders the SVG to the provided canvas. + * @param canvas The canvas to render to. + * @param fontManager The font manager for rendering SVG text. If no text rendering is needed, + * this can be nullptr, and text will not be rendered. If no specific font is set, the default + * font will be used for rendering. + */ + void render(Canvas* canvas, const std::shared_ptr& fontManager = nullptr); + + /** + * Sets the size of the container that the SVG will be rendered into. + */ + void setContainerSize(const Size& size); + + /** + * Returns the size of the container that the SVG will be rendered into. + */ + const Size& getContainerSize() const; + + private: + /** + * Construct a new SVGDOM object + */ + SVGDOM(std::shared_ptr root, SVGIDMapper&& mapper); + + const std::shared_ptr root = nullptr; + const SVGIDMapper nodeIDMapper = {}; + Size containerSize = {}; +}; +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGFontManager.h b/include/tgfx/svg/SVGFontManager.h new file mode 100644 index 00000000..50e1da33 --- /dev/null +++ b/include/tgfx/svg/SVGFontManager.h @@ -0,0 +1,114 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include +#include "tgfx/core/Typeface.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/node/SVGNode.h" +namespace tgfx { + +/** + * Information about SVG fonts, including weight and style (Oblique, Italic, Normal). + */ +class SVGFontInfo { + public: + struct Hash { + std::size_t operator()(const SVGFontInfo& info) const { + return std::hash()(static_cast(info._weight)) ^ + std::hash()(static_cast(info._style)); + } + }; + + SVGFontInfo(SVGFontWeight::Type weight, SVGFontStyle::Type style) + : _weight(weight), _style(style) { + } + + bool operator==(const SVGFontInfo& other) const { + return _weight == other._weight && _style == other._style; + } + + SVGFontWeight::Type weight() const { + return _weight; + } + SVGFontStyle::Type style() const { + return _style; + } + + private: + SVGFontWeight::Type _weight; + SVGFontStyle::Type _style; +}; + +/** + * Manages fonts for SVG rendering, using fontFamily and SVGFontInfo as keys to store Typeface + * objects. + */ +class SVGFontManager { + public: + /** + * Creates an SVGFontManager object with the default typeface.If the default typeface is nullptr, + * retrun nullptr. + */ + static std::shared_ptr Make(const std::shared_ptr& defaultTypeface); + + /** + * Destructor. + */ + ~SVGFontManager() = default; + + /** + * Adds a font style to the font manager.If the font family and style already exist, this method + * does nothing and returns false. + */ + bool setTypeface(const std::string& fontFamily, SVGFontInfo info, + const std::shared_ptr& typeface); + + /** + * Get the font families stored in the font manager. + */ + std::vector getFontFamilies() const; + + /** + * Get the font styles stored in the font manager for the specified font family. + */ + std::vector getFontInfos(const std::string& fontFamily) const; + + protected: + void addFontStyle(const std::string& fontFamily, SVGFontInfo info); + + std::shared_ptr getTypefaceForRendering(const std::string& fontFamily, + SVGFontInfo info) const; + + private: + explicit SVGFontManager(const std::shared_ptr& typeface); + + std::unordered_map, SVGFontInfo::Hash>> + typefaceMap; + std::shared_ptr defaultTypeface; + + friend class SVGRenderContext; + friend class SVGDOM; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGTypes.h b/include/tgfx/svg/SVGTypes.h new file mode 100644 index 00000000..9fc94116 --- /dev/null +++ b/include/tgfx/svg/SVGTypes.h @@ -0,0 +1,871 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include +#include +#include "tgfx/core/Color.h" +#include "tgfx/core/Matrix.h" +#include "tgfx/core/PathTypes.h" +#include "tgfx/core/Point.h" +#include "tgfx/core/Rect.h" + +namespace tgfx { + +using SVGColorType = Color; +using SVGIntegerType = int; +using SVGNumberType = float; +using SVGStringType = std::string; +using SVGViewBoxType = Rect; +using SVGTransformType = Matrix; +using SVGPointsType = std::vector; + +enum class SVGPropertyState { + Unspecified, + Inherit, + Value, +}; + +// https://www.w3.org/TR/SVG11/intro.html#TermProperty +template +class SVGProperty { + public: + using ValueT = T; + + SVGProperty() = default; + + explicit SVGProperty(SVGPropertyState state) : _state(state) { + } + + explicit SVGProperty(const T& value) : _state(SVGPropertyState::Value) { + _value = value; + } + + explicit SVGProperty(T&& value) : _state(SVGPropertyState::Value) { + _value = std::move(value); + } + + template + void init(Args&&... args) { + _state = SVGPropertyState::Value; + _value.emplace(std::forward(args)...); + } + + constexpr bool isInheritable() const { + return Inheritable; + } + + bool isValue() const { + return _state == SVGPropertyState::Value; + } + + T* getMaybeNull() const { + return _value.value_or(nullptr); + } + + void set(SVGPropertyState state) { + _state = state; + if (_state != SVGPropertyState::Value) { + _value.reset(); + } + } + + void set(const T& value) { + _state = SVGPropertyState::Value; + _value = value; + } + + void set(T&& value) { + _state = SVGPropertyState::Value; + _value = std::move(value); + } + + T* operator->() { + return &_value.value(); + } + + const T* operator->() const { + return &_value.value(); + } + + T& operator*() { + return *_value; + } + + const T& operator*() const { + return *_value; + } + + private: + SVGPropertyState _state = SVGPropertyState::Unspecified; + std::optional _value; +}; + +class SVGLength { + public: + enum class Unit { + Unknown, + Number, + Percentage, + EMS, + EXS, + PX, + CM, + MM, + IN, + PT, + PC, + }; + + SVGLength() = default; + + explicit SVGLength(float v, Unit u = Unit::Number) : _value(v), _unit(u) { + } + + bool operator==(const SVGLength& other) const { + return _unit == other._unit && _value == other._value; + } + + bool operator!=(const SVGLength& other) const { + return !(*this == other); + } + + const float& value() const { + return _value; + } + + const Unit& unit() const { + return _unit; + } + + private: + float _value = 0.0f; + Unit _unit = Unit::Unknown; +}; + +// https://www.w3.org/TR/SVG11/linking.html#IRIReference +class SVGIRI { + public: + enum class Type { + Local, + Nonlocal, + DataURI, + }; + + SVGIRI() = default; + + SVGIRI(Type t, SVGStringType iri) : _type(t), _iri(std::move(iri)) { + } + + Type type() const { + return _type; + } + + const SVGStringType& iri() const { + return _iri; + } + + bool operator==(const SVGIRI& other) const { + return _type == other._type && _iri == other._iri; + } + + bool operator!=(const SVGIRI& other) const { + return !(*this == other); + } + + private: + Type _type = Type::Local; + SVGStringType _iri = {}; +}; + +// https://www.w3.org/TR/SVG11/types.html#InterfaceSVGColor +class SVGColor { + public: + enum class Type { + CurrentColor, + Color, + ICCColor, + }; + + using Vars = std::vector; + + SVGColor() = default; + + explicit SVGColor(const SVGColorType& c) : _color(c) { + } + + SVGColor(Type t, Vars&& vars) + : _type(t), _vars(vars.empty() ? nullptr : std::make_shared(std::move(vars))) { + } + + SVGColor(const SVGColorType& c, Vars&& vars) + : _color(c), _vars(vars.empty() ? nullptr : std::make_shared(std::move(vars))) { + } + + bool operator==(const SVGColor& other) const { + return _type == other._type && _color == other._color && _vars == other._vars; + } + + bool operator!=(const SVGColor& other) const { + return !(*this == other); + } + + Type type() const { + return _type; + } + + const SVGColorType& color() const { + return _color; + } + + std::shared_ptr vars() const { + return _vars ? _vars : nullptr; + } + + private: + Type _type = Type::Color; + SVGColorType _color = Color::Black(); + std::shared_ptr _vars = nullptr; +}; + +class SVGPaint { + public: + enum class Type { + None, + Color, + IRI, + }; + + SVGPaint() = default; + + explicit SVGPaint(Type t) : _type(t), _color(Color::Black()) { + } + + explicit SVGPaint(const SVGColor& color) : _type(Type::Color), _color(std::move(color)) { + } + + SVGPaint(SVGIRI iri, SVGColor color) + : _type(Type::IRI), _color(std::move(color)), _iri(std::move(iri)) { + } + + bool operator==(const SVGPaint& other) const { + return _type == other._type && _color == other._color && _iri == other._iri; + } + + bool operator!=(const SVGPaint& other) const { + return !(*this == other); + } + + Type type() const { + return _type; + } + + const SVGColor& color() const { + return _color; + } + + const SVGIRI& iri() const { + return _iri; + } + + private: + Type _type = Type::None; + SVGColor _color = SVGColor(Color::Black()); + SVGIRI _iri = {}; +}; + +// | none (used for clip/mask/filter properties) +class SVGFuncIRI { + public: + enum class Type { + None, + IRI, + }; + + SVGFuncIRI() = default; + + explicit SVGFuncIRI(Type t) : _type(t) { + } + + explicit SVGFuncIRI(SVGIRI&& iri) : _type(Type::IRI), _iri(std::move(iri)) { + } + + bool operator==(const SVGFuncIRI& other) const { + return _type == other._type && _iri == other._iri; + } + + bool operator!=(const SVGFuncIRI& other) const { + return !(*this == other); + } + + Type type() const { + return _type; + } + + const SVGIRI& iri() const { + return _iri; + } + + private: + Type _type = Type::None; + SVGIRI _iri = {}; +}; + +enum class SVGLineCap { + Butt, + Round, + Square, +}; + +class SVGLineJoin { + public: + enum class Type { + Miter, + Round, + Bevel, + Inherit, + }; + + SVGLineJoin() = default; + + explicit SVGLineJoin(Type t) : _type(t) { + } + + bool operator==(const SVGLineJoin& other) const { + return _type == other._type; + } + + bool operator!=(const SVGLineJoin& other) const { + return !(*this == other); + } + + Type type() const { + return _type; + } + + private: + Type _type = Type::Inherit; +}; + +class SVGSpreadMethod { + public: + enum class Type { + Pad, + Repeat, + Reflect, + }; + + SVGSpreadMethod() = default; + + explicit SVGSpreadMethod(Type t) : _type(t) { + } + + bool operator==(const SVGSpreadMethod& other) const { + return _type == other._type; + } + + bool operator!=(const SVGSpreadMethod& other) const { + return !(*this == other); + } + + Type type() const { + return _type; + } + + private: + Type _type = Type::Pad; +}; + +class SVGFillRule { + public: + enum class Type { + NonZero, + EvenOdd, + Inherit, + }; + + SVGFillRule() = default; + + explicit SVGFillRule(Type t) : _type(t) { + } + + bool operator==(const SVGFillRule& other) const { + return _type == other._type; + } + + bool operator!=(const SVGFillRule& other) const { + return !(*this == other); + } + + Type type() const { + return _type; + } + + PathFillType asFillType() const { + return _type == Type::EvenOdd ? PathFillType::EvenOdd : PathFillType::Winding; + } + + private: + Type _type = Type::Inherit; +}; + +class SVGVisibility { + public: + enum class Type { + Visible, + Hidden, + Collapse, + Inherit, + }; + + SVGVisibility() = default; + + explicit SVGVisibility(Type t) : _type(t) { + } + + bool operator==(const SVGVisibility& other) const { + return _type == other._type; + } + + bool operator!=(const SVGVisibility& other) const { + return !(*this == other); + } + + Type type() const { + return _type; + } + + private: + Type _type = Type::Visible; +}; + +class SVGDashArray { + public: + enum class Type { + None, + DashArray, + Inherit, + }; + + SVGDashArray() = default; + + explicit SVGDashArray(Type t) : _type(t) { + } + + explicit SVGDashArray(std::vector&& dashArray) + : _type(Type::DashArray), _dashArray(std::move(dashArray)) { + } + + bool operator==(const SVGDashArray& other) const { + return _type == other._type && _dashArray == other._dashArray; + } + + bool operator!=(const SVGDashArray& other) const { + return !(*this == other); + } + + Type type() const { + return _type; + } + + const std::vector& dashArray() const { + return _dashArray; + } + + private: + Type _type = Type::None; + std::vector _dashArray; +}; + +class SVGStopColor { + public: + enum class Type { + Color, + CurrentColor, + ICCColor, + Inherit, + }; + + SVGStopColor() = default; + + explicit SVGStopColor(Type t) : _type(t), _color(Color::Black()) { + } + + explicit SVGStopColor(const SVGColorType& c) : _type(Type::Color), _color(c) { + } + + bool operator==(const SVGStopColor& other) const { + return _type == other._type && _color == other._color; + } + bool operator!=(const SVGStopColor& other) const { + return !(*this == other); + } + + Type type() const { + return _type; + } + const SVGColorType& color() const { + return _color; + } + + private: + Type _type = Type::Color; + SVGColorType _color = Color::Black(); +}; + +class SVGObjectBoundingBoxUnits { + public: + enum class Type { + UserSpaceOnUse, + ObjectBoundingBox, + }; + + SVGObjectBoundingBoxUnits() = default; + + explicit SVGObjectBoundingBoxUnits(Type t) : _type(t) { + } + + bool operator==(const SVGObjectBoundingBoxUnits& other) const { + return _type == other._type; + } + bool operator!=(const SVGObjectBoundingBoxUnits& other) const { + return !(*this == other); + } + + Type type() const { + return _type; + } + + private: + Type _type = Type::UserSpaceOnUse; +}; + +class SVGFontFamily { + public: + enum class Type { + Family, + Inherit, + }; + + SVGFontFamily() = default; + + explicit SVGFontFamily(std::string family) : _type(Type::Family), _family(std::move(family)) { + } + + bool operator==(const SVGFontFamily& other) const { + return _type == other._type && _family == other._family; + } + + bool operator!=(const SVGFontFamily& other) const { + return !(*this == other); + } + + Type type() const { + return _type; + } + + const std::string& family() const { + return _family; + } + + private: + Type _type = Type::Inherit; + std::string _family; +}; + +class SVGFontStyle { + public: + enum class Type { + Normal, + Italic, + Oblique, + Inherit, + }; + + SVGFontStyle() = default; + + explicit SVGFontStyle(Type t) : _type(t) { + } + + bool operator==(const SVGFontStyle& other) const { + return _type == other._type; + } + + bool operator!=(const SVGFontStyle& other) const { + return !(*this == other); + } + + Type type() const { + return _type; + } + + private: + Type _type = Type::Inherit; +}; + +class SVGFontSize { + public: + enum class Type { + Length, + Inherit, + }; + + SVGFontSize() = default; + + explicit SVGFontSize(const SVGLength& s) : _type(Type::Length), _size(s) { + } + + bool operator==(const SVGFontSize& other) const { + return _type == other._type && _size == other._size; + } + + bool operator!=(const SVGFontSize& other) const { + return !(*this == other); + } + + Type type() const { + return _type; + } + + const SVGLength& size() const { + return _size; + } + + private: + Type _type = Type::Inherit; + SVGLength _size = SVGLength(0.0f); +}; + +class SVGFontWeight { + public: + enum class Type { + W100, + W200, + W300, + W400, + W500, + W600, + W700, + W800, + W900, + Normal, + Bold, + Bolder, + Lighter, + Inherit, + }; + + SVGFontWeight() = default; + + explicit SVGFontWeight(Type t) : _type(t) { + } + + bool operator==(const SVGFontWeight& other) const { + return _type == other._type; + } + + bool operator!=(const SVGFontWeight& other) const { + return !(*this == other); + } + + Type type() const { + return _type; + } + + private: + Type _type = Type::Inherit; +}; + +struct SVGPreserveAspectRatio { + enum class Align { + // These values are chosen such that bits [0,1] encode X alignment, and + // bits [2,3] encode Y alignment. + XMinYMin = 0x00, + XMidYMin = 0x01, + XMaxYMin = 0x02, + XMinYMid = 0x04, + XMidYMid = 0x05, + XMaxYMid = 0x06, + XMinYMax = 0x08, + XMidYMax = 0x09, + XMaxYMax = 0x0a, + + None = 0x10, + }; + + enum class Scale { + Meet, + Slice, + }; + + Align align = Align::XMidYMid; + Scale scale = Scale::Meet; +}; + +class SVGTextAnchor { + public: + enum class Type { + Start, + Middle, + End, + Inherit, + }; + + SVGTextAnchor() = default; + + explicit SVGTextAnchor(Type t) : _type(t) { + } + + bool operator==(const SVGTextAnchor& other) const { + return _type == other._type; + } + + bool operator!=(const SVGTextAnchor& other) const { + return !(*this == other); + } + + Type type() const { + return _type; + } + + private: + Type _type = Type::Inherit; +}; + +// https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveInAttribute +class SVGFeInputType { + public: + enum class Type { + SourceGraphic, + SourceAlpha, + BackgroundImage, + BackgroundAlpha, + FillPaint, + StrokePaint, + FilterPrimitiveReference, + Unspecified, + }; + + SVGFeInputType() = default; + + explicit SVGFeInputType(Type t) : _type(t) { + } + + explicit SVGFeInputType(SVGStringType id) + : _type(Type::FilterPrimitiveReference), _id(std::move(id)) { + } + + bool operator==(const SVGFeInputType& other) const { + return _type == other._type && _id == other._id; + } + + bool operator!=(const SVGFeInputType& other) const { + return !(*this == other); + } + + const std::string& id() const { + return _id; + } + + Type type() const { + return _type; + } + + private: + Type _type = Type::Unspecified; + std::string _id; +}; + +enum class SVGFeColorMatrixType { + Matrix, + Saturate, + HueRotate, + LuminanceToAlpha, +}; + +using SVGFeColorMatrixValues = std::vector; + +enum class SVGFeCompositeOperator { + Over, + In, + Out, + Atop, + Xor, + Arithmetic, +}; + +class SVGFeTurbulenceBaseFrequency { + public: + SVGFeTurbulenceBaseFrequency() = default; + + SVGFeTurbulenceBaseFrequency(SVGNumberType freqX, SVGNumberType freqY) + : _freqX(freqX), _freqY(freqY) { + } + + SVGNumberType freqX() const { + return _freqX; + } + + SVGNumberType freqY() const { + return _freqY; + } + + private: + SVGNumberType _freqX = 0.0; + SVGNumberType _freqY = 0.0; +}; + +struct SVGFeTurbulenceType { + enum class Type { + FractalNoise, + Turbulence, + }; + + Type type = Type::Turbulence; + + SVGFeTurbulenceType() = default; + + explicit SVGFeTurbulenceType(Type inputType) : type(inputType) { + } +}; + +enum class SVGColorspace { + Auto, + SRGB, + LinearRGB, +}; + +// https://www.w3.org/TR/SVG11/painting.html#DisplayProperty +enum class SVGDisplay { + Inline, + None, +}; + +// https://www.w3.org/TR/SVG11/filters.html#TransferFunctionElementAttributes +enum class SVGFeFuncType { + Identity, + Table, + Discrete, + Linear, + Gamma, +}; +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGValue.h b/include/tgfx/svg/SVGValue.h new file mode 100644 index 00000000..f8463bdd --- /dev/null +++ b/include/tgfx/svg/SVGValue.h @@ -0,0 +1,93 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "tgfx/svg/SVGTypes.h" + +namespace tgfx { + +class SVGValue { + public: + enum class Type { + Color, + Filter, + Length, + Number, + ObjectBoundingBoxUnits, + PreserveAspectRatio, + StopColor, + String, + Transform, + ViewBox, + }; + + Type type() const { + return _type; + } + + template + const T* as() const { + return _type == T::_type ? static_cast(this) : nullptr; + } + + protected: + explicit SVGValue(Type t) : _type(t) { + } + + private: + Type _type; +}; + +template +class SVGWrapperValue final : public SVGValue { + public: + explicit SVGWrapperValue(const T& v) : INHERITED(ValueType), wrappedValue(v) { + } + + // NOLINTBEGIN + // Allow implicit conversion to the wrapped type operator. + operator const T&() const { + return wrappedValue; + } + // NOLINTEND + + const T* operator->() const { + return &wrappedValue; + } + + // Stack-only + void* operator new(size_t) = delete; + void* operator new(size_t, void*) = delete; + + private: + const T& wrappedValue; + + using INHERITED = SVGValue; +}; + +using SVGLengthValue = SVGWrapperValue; +using SVGTransformValue = SVGWrapperValue; +using SVGViewBoxValue = SVGWrapperValue; +using SVGNumberValue = SVGWrapperValue; +using SVGStringValue = SVGWrapperValue; +using SVGPreserveAspectRatioValue = + SVGWrapperValue; +using SVGObjectBoundingBoxUnitsValue = + SVGWrapperValue; +} // namespace tgfx diff --git a/include/tgfx/svg/node/SVGNode.h b/include/tgfx/svg/node/SVGNode.h new file mode 100644 index 00000000..18ebc685 --- /dev/null +++ b/include/tgfx/svg/node/SVGNode.h @@ -0,0 +1,295 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include "tgfx/core/Matrix.h" +#include "tgfx/core/Paint.h" +#include "tgfx/core/Path.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGAttribute.h" +#include "tgfx/svg/SVGTypes.h" + +namespace tgfx { + +class SVGValue; +class SVGRenderContext; + +/** + * Enumeration of SVG element tags, where each SVG element corresponds to a specific tag. + */ +enum class SVGTag { + Circle, + ClipPath, + Defs, + Ellipse, + FeBlend, + FeColorMatrix, + FeComponentTransfer, + FeComposite, + FeDiffuseLighting, + FeDisplacementMap, + FeDistantLight, + FeFlood, + FeFuncA, + FeFuncR, + FeFuncG, + FeFuncB, + FeGaussianBlur, + FeImage, + FeMerge, + FeMergeNode, + FeMorphology, + FeOffset, + FePointLight, + FeSpecularLighting, + FeSpotLight, + FeTurbulence, + Filter, + G, + Image, + Line, + LinearGradient, + Mask, + Path, + Pattern, + Polygon, + Polyline, + RadialGradient, + Rect, + Stop, + Svg, + Text, + TextLiteral, + TextPath, + TSpan, + Use +}; + +#define SVG_PRES_ATTR(attr_name, attr_type, attr_inherited) \ + private: \ + bool set##attr_name(std::optional>&& pr) { \ + if (pr.has_value()) { \ + this->set##attr_name(std::move(*pr)); \ + } \ + return pr.has_value(); \ + } \ + \ + public: \ + const SVGProperty& get##attr_name() const { \ + return presentationAttributes.attr_name; \ + } \ + void set##attr_name(const SVGProperty& v) { \ + auto* dest = &presentationAttributes.attr_name; \ + if (!dest->isInheritable() || v.isValue()) { \ + /* TODO: If dest is not inheritable, handle v == "inherit" */ \ + *dest = v; \ + } else { \ + dest->set(SVGPropertyState::Inherit); \ + } \ + } \ + void set##attr_name(SVGProperty&& v) { \ + auto* dest = &presentationAttributes.attr_name; \ + if (!dest->isInheritable() || v.isValue()) { \ + /* TODO: If dest is not inheritable, handle v == "inherit" */ \ + *dest = std::move(v); \ + } else { \ + dest->set(SVGPropertyState::Inherit); \ + } \ + } + +/** + * Abstract base class for SVG nodes, representing an element in SVG with common attributes and + * methods. + */ +class SVGNode { + public: + virtual ~SVGNode(); + + SVGTag tag() const { + return _tag; + } + + // Inheritable presentation attributes + SVG_PRES_ATTR(ClipRule, SVGFillRule, true) + SVG_PRES_ATTR(Color, SVGColorType, true) + SVG_PRES_ATTR(ColorInterpolation, SVGColorspace, true) + SVG_PRES_ATTR(ColorInterpolationFilters, SVGColorspace, true) + SVG_PRES_ATTR(FillRule, SVGFillRule, true) + SVG_PRES_ATTR(Fill, SVGPaint, true) + SVG_PRES_ATTR(FillOpacity, SVGNumberType, true) + SVG_PRES_ATTR(FontFamily, SVGFontFamily, true) + SVG_PRES_ATTR(FontSize, SVGFontSize, true) + SVG_PRES_ATTR(FontStyle, SVGFontStyle, true) + SVG_PRES_ATTR(FontWeight, SVGFontWeight, true) + SVG_PRES_ATTR(Stroke, SVGPaint, true) + SVG_PRES_ATTR(StrokeDashArray, SVGDashArray, true) + SVG_PRES_ATTR(StrokeDashOffset, SVGLength, true) + SVG_PRES_ATTR(StrokeLineCap, SVGLineCap, true) + SVG_PRES_ATTR(StrokeLineJoin, SVGLineJoin, true) + SVG_PRES_ATTR(StrokeMiterLimit, SVGNumberType, true) + SVG_PRES_ATTR(StrokeOpacity, SVGNumberType, true) + SVG_PRES_ATTR(StrokeWidth, SVGLength, true) + SVG_PRES_ATTR(TextAnchor, SVGTextAnchor, true) + SVG_PRES_ATTR(Visibility, SVGVisibility, true) + + // Non-inheritable presentation attributes + SVG_PRES_ATTR(ClipPath, SVGFuncIRI, false) + SVG_PRES_ATTR(Display, SVGDisplay, false) + SVG_PRES_ATTR(Mask, SVGFuncIRI, false) + SVG_PRES_ATTR(Filter, SVGFuncIRI, false) + SVG_PRES_ATTR(Opacity, SVGNumberType, false) + SVG_PRES_ATTR(StopColor, SVGColor, false) + SVG_PRES_ATTR(StopOpacity, SVGNumberType, false) + SVG_PRES_ATTR(FloodColor, SVGColor, false) + SVG_PRES_ATTR(FloodOpacity, SVGNumberType, false) + SVG_PRES_ATTR(LightingColor, SVGColor, false) + + /** + * Renders the SVG node to the provided context. + */ + void render(const SVGRenderContext& context) const; + + /** + * Converts the SVG node to a paint object. + */ + bool asPaint(const SVGRenderContext& context, Paint* paint) const; + + /** + * Converts the SVG node to a path object. + */ + Path asPath(const SVGRenderContext& context) const; + + /** + * Returns the object bounding box of the SVG node. + */ + Rect objectBoundingBox(const SVGRenderContext& context) const; + + /** + * Returns the parent node of the SVG node. + */ + virtual bool hasChildren() const { + return false; + } + + /** + * Appends a child node to the SVG node. + */ + virtual void appendChild(std::shared_ptr node) = 0; + + /** + * Sets the attribute of the SVG node. + */ + void setAttribute(SVGAttribute attribute, const SVGValue& value); + + /** + * Sets the attribute of the SVG node. + */ + bool setAttribute(const std::string& attributeName, const std::string& attributeValue); + + protected: + explicit SVGNode(SVGTag tag); + + virtual bool parseAndSetAttribute(const std::string& name, const std::string& value); + + static Matrix ComputeViewboxMatrix(const Rect&, const Rect&, SVGPreserveAspectRatio); + + virtual void onSetAttribute(SVGAttribute, const SVGValue&){}; + + // Called before onRender(), to apply local attributes to the context. Unlike onRender(), + // onPrepareToRender() bubbles up the inheritance chain: override should always call + // INHERITED::onPrepareToRender(), unless they intend to short-circuit rendering + // (return false). + // Implementations are expected to return true if rendering is to continue, or false if + // the node/subtree rendering is disabled. + virtual bool onPrepareToRender(SVGRenderContext*) const; + + virtual void onRender(const SVGRenderContext&) const = 0; + + virtual bool onAsPaint(const SVGRenderContext&, Paint*) const { + return false; + } + + virtual Path onAsPath(const SVGRenderContext&) const = 0; + + virtual Rect onObjectBoundingBox(const SVGRenderContext&) const { + return Rect::MakeEmpty(); + } + + private: + SVGTag _tag; + SVGPresentationAttributes presentationAttributes; + + friend class SVGNodeConstructor; + friend class SVGRenderContext; +}; + +//NOLINTBEGIN +#undef SVG_PRES_ATTR // presentation attributes are only defined for the base class + +#define SVG_ATTR_SETTERS(attr_name, attr_type, attr_default, set_cp, set_mv) \ + private: \ + bool set##attr_name(const std::optional& pr) { \ + if (pr.has_value()) { \ + this->set##attr_name(*pr); \ + } \ + return pr.has_value(); \ + } \ + bool set##attr_name(std::optional&& pr) { \ + if (pr.has_value()) { \ + this->set##attr_name(std::move(*pr)); \ + } \ + return pr.has_value(); \ + } \ + \ + public: \ + void set##attr_name(const attr_type& a) { \ + set_cp(a); \ + } \ + void set##attr_name(attr_type&& a) { \ + set_mv(std::move(a)); \ + } + +#define SVG_ATTR(attr_name, attr_type, attr_default) \ + private: \ + attr_type attr_name = attr_default; \ + \ + public: \ + const attr_type& get##attr_name() const { \ + return attr_name; \ + } \ + SVG_ATTR_SETTERS( \ + attr_name, attr_type, attr_default, [this](const attr_type& a) { this->attr_name = a; }, \ + [this](attr_type&& a) { this->attr_name = std::move(a); }) + +#define SVG_OPTIONAL_ATTR(attr_name, attr_type) \ + private: \ + std::optional attr_name; \ + \ + public: \ + const std::optional& get##attr_name() const { \ + return attr_name; \ + } \ + SVG_ATTR_SETTERS( \ + attr_name, attr_type, attr_default, [this](const attr_type& a) { this->attr_name = a; }, \ + [this](attr_type&& a) { this->attr_name = a; }) +//NOLINTEND + +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGAttribute.cpp b/src/svg/SVGAttribute.cpp new file mode 100644 index 00000000..74d9b8a4 --- /dev/null +++ b/src/svg/SVGAttribute.cpp @@ -0,0 +1,63 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/SVGAttribute.h" +#include "tgfx/core/Color.h" + +namespace tgfx { +SVGPresentationAttributes SVGPresentationAttributes::MakeInitial() { + SVGPresentationAttributes result; + + result.Fill.set(SVGPaint(SVGColor(Color::Black()))); + result.FillOpacity.set(static_cast(1)); + result.FillRule.set(SVGFillRule(SVGFillRule::Type::NonZero)); + result.ClipRule.set(SVGFillRule(SVGFillRule::Type::NonZero)); + + result.Stroke.set(SVGPaint(SVGPaint::Type::None)); + result.StrokeDashArray.set(SVGDashArray(SVGDashArray::Type::None)); + result.StrokeDashOffset.set(SVGLength(0)); + result.StrokeLineCap.set(SVGLineCap::Butt); + result.StrokeLineJoin.set(SVGLineJoin(SVGLineJoin::Type::Miter)); + result.StrokeMiterLimit.set(static_cast(4)); + result.StrokeOpacity.set(static_cast(1)); + result.StrokeWidth.set(SVGLength(1)); + + result.Visibility.set(SVGVisibility(SVGVisibility::Type::Visible)); + + result.Color.set(SVGColorType(Color::Black())); + result.ColorInterpolation.set(SVGColorspace::SRGB); + result.ColorInterpolationFilters.set(SVGColorspace::LinearRGB); + + result.FontFamily.init("default"); + result.FontStyle.init(SVGFontStyle::Type::Normal); + result.FontSize.init(SVGLength(24)); + result.FontWeight.init(SVGFontWeight::Type::Normal); + result.TextAnchor.init(SVGTextAnchor::Type::Start); + + result.Display.init(SVGDisplay::Inline); + + result.StopColor.set(SVGColor(Color::Black())); + result.StopOpacity.set(static_cast(1)); + result.FloodColor.set(SVGColor(Color::Black())); + result.FloodOpacity.set(static_cast(1)); + result.LightingColor.set(SVGColor(Color::White())); + + return result; +} + +} // namespace tgfx diff --git a/src/svg/SVGDOM.cpp b/src/svg/SVGDOM.cpp new file mode 100644 index 00000000..7604feac --- /dev/null +++ b/src/svg/SVGDOM.cpp @@ -0,0 +1,141 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/SVGDOM.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "core/utils/Log.h" +#include "svg/SVGAttributeParser.h" +#include "svg/SVGNodeConstructor.h" +#include "svg/SVGRenderContext.h" +#include "tgfx/core/Canvas.h" +#include "tgfx/core/Data.h" +#include "tgfx/core/Recorder.h" +#include "tgfx/core/Size.h" +#include "tgfx/core/Surface.h" +#include "tgfx/svg/SVGAttribute.h" +#include "tgfx/svg/SVGFontManager.h" +#include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/SVGValue.h" +#include "tgfx/svg/node/SVGContainer.h" +#include "tgfx/svg/node/SVGNode.h" +#include "tgfx/svg/xml/XMLDOM.h" + +namespace tgfx { + +std::shared_ptr SVGDOM::Make(const std::shared_ptr& data) { + if (!data) { + return nullptr; + } + // Parse the data into an XML DOM structure + auto xmlDom = DOM::MakeFromData(*data); + if (!xmlDom) { + return nullptr; + } + + // Convert the XML structure to an SVG structure, translating XML elements and attributes into + // SVG elements and attributes + SVGIDMapper mapper; + ConstructionContext constructionContext(&mapper); + auto root = + SVGNodeConstructor::ConstructSVGNode(constructionContext, xmlDom->getRootNode().get()); + if (!root || root->tag() != SVGTag::Svg) { + return nullptr; + } + + // Create SVGDOM with the root node and ID mapper + return std::shared_ptr( + new SVGDOM(std::static_pointer_cast(root), std::move(mapper))); +} + +SVGDOM::SVGDOM(std::shared_ptr root, SVGIDMapper&& mapper) + : root(std::move(root)), nodeIDMapper(std::move(mapper)) { +} + +const std::shared_ptr& SVGDOM::getRoot() const { + return root; +} + +void SVGDOM::collectRenderFonts(const std::shared_ptr& fontManager) { + if (!root || !fontManager) { + return; + } + + auto fontCollector = [](auto collector, const std::shared_ptr& node, + const std::shared_ptr& fontManager) -> void { + if (!node) { + return; + } + if (node->tag() >= SVGTag::Text && node->tag() <= SVGTag::TSpan) { + if (node->getFontFamily().isValue()) { + auto family = node->getFontFamily()->family(); + SVGFontWeight::Type weight = node->getFontWeight().isValue() ? node->getFontWeight()->type() + : SVGFontWeight::Type::Normal; + SVGFontStyle::Type style = node->getFontStyle().isValue() ? node->getFontStyle()->type() + : SVGFontStyle::Type::Normal; + SVGFontInfo fontStyle(weight, style); + fontManager->addFontStyle(family, fontStyle); + } + } else if (node->hasChildren()) { + if (auto container = std::static_pointer_cast(node)) { + for (const auto& child : container->getChildren()) { + collector(collector, child, fontManager); + } + } + } + }; + fontCollector(fontCollector, std::static_pointer_cast(root), fontManager); +} + +void SVGDOM::render(Canvas* canvas, const std::shared_ptr& fontManager) { + // If the container size is not set, use the size of the root SVG element. + auto drawSize = containerSize; + if (drawSize.isEmpty()) { + if (root->getViewBox().has_value()) { + drawSize = root->getViewBox()->size(); + } else { + drawSize = Size::Make(root->getWidth().value(), root->getHeight().value()); + } + } + if (!canvas || !root || drawSize.isEmpty()) { + return; + } + + SVGLengthContext lengthContext(containerSize); + SVGPresentationContext presentationContext; + SVGRenderContext renderContext(canvas, fontManager, nodeIDMapper, lengthContext, + presentationContext, {nullptr, nullptr}, canvas->getMatrix()); + + root->render(renderContext); +} + +const Size& SVGDOM::getContainerSize() const { + return containerSize; +} + +void SVGDOM::setContainerSize(const Size& size) { + containerSize = size; +} + +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGFontManager.cpp b/src/svg/SVGFontManager.cpp new file mode 100644 index 00000000..48b5f327 --- /dev/null +++ b/src/svg/SVGFontManager.cpp @@ -0,0 +1,92 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/SVGFontManager.h" + +namespace tgfx { + +SVGFontManager::SVGFontManager(const std::shared_ptr& defaultTypeface) + : defaultTypeface(defaultTypeface) { +} + +std::shared_ptr SVGFontManager::Make(const std::shared_ptr& typeface) { + if (!typeface) { + return nullptr; + } + return std::shared_ptr(new SVGFontManager(typeface)); +} + +void SVGFontManager::addFontStyle(const std::string& fontFamily, SVGFontInfo info) { + if (typefaceMap.find(fontFamily) != typefaceMap.end()) { + return; + } + if (typefaceMap[fontFamily].find(info) != typefaceMap[fontFamily].end()) { + return; + } + typefaceMap[fontFamily][info] = nullptr; +} + +bool SVGFontManager::setTypeface(const std::string& fontFamily, SVGFontInfo info, + const std::shared_ptr& typeface) { + if (typefaceMap.find(fontFamily) != typefaceMap.end()) { + return false; + } + if (typefaceMap[fontFamily].find(info) != typefaceMap[fontFamily].end()) { + return false; + } + typefaceMap[fontFamily][info] = typeface; + return true; +} + +std::vector SVGFontManager::getFontFamilies() const { + std::vector families; + families.reserve(typefaceMap.size()); + for (const auto& [family, _] : typefaceMap) { + families.push_back(family); + } + return families; +} + +std::vector SVGFontManager::getFontInfos(const std::string& fontFamily) const { + if (auto iter = typefaceMap.find(fontFamily); iter != typefaceMap.end()) { + std::vector styles; + styles.reserve(iter->second.size()); + for (const auto& [style, _] : iter->second) { + styles.push_back(style); + } + return styles; + } else { + return {}; + } +} + +std::shared_ptr SVGFontManager::getTypefaceForRendering(const std::string& fontFamily, + SVGFontInfo info) const { + auto familyIter = typefaceMap.find(fontFamily); + if (familyIter == typefaceMap.end()) { + return defaultTypeface; + } + auto styleIter = familyIter->second.find(info); + if (styleIter == familyIter->second.end()) { + return defaultTypeface; + } else { + return styleIter->second; + } +} + +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGNode.cpp b/src/svg/node/SVGNode.cpp new file mode 100644 index 00000000..05dcdff8 --- /dev/null +++ b/src/svg/node/SVGNode.cpp @@ -0,0 +1,190 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/node/SVGNode.h" +#include +#include +#include +#include "svg/SVGAttributeParser.h" +#include "svg/SVGNodeConstructor.h" +#include "svg/SVGRenderContext.h" +#include "tgfx/core/Color.h" +#include "tgfx/core/Matrix.h" +#include "tgfx/core/Paint.h" +#include "tgfx/core/Path.h" +#include "tgfx/core/PathTypes.h" +#include "tgfx/core/Point.h" +#include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGTypes.h" + +namespace tgfx { + +SVGNode::SVGNode(SVGTag t) : _tag(t) { + // Uninherited presentation attributes need a non-null default value. + presentationAttributes.StopColor.set(SVGColor(Color::Black())); + presentationAttributes.StopOpacity.set(static_cast(1.0f)); + presentationAttributes.FloodColor.set(SVGColor(Color::Black())); + presentationAttributes.FloodOpacity.set(static_cast(1.0f)); + presentationAttributes.LightingColor.set(SVGColor(Color::White())); +} + +SVGNode::~SVGNode() = default; + +void SVGNode::render(const SVGRenderContext& context) const { + SVGRenderContext localContext(context, this); + + if (this->onPrepareToRender(&localContext)) { + this->onRender(localContext); + } +} + +bool SVGNode::asPaint(const SVGRenderContext& context, Paint* paint) const { + SVGRenderContext localContext(context); + + return this->onPrepareToRender(&localContext) && this->onAsPaint(localContext, paint); +} + +Path SVGNode::asPath(const SVGRenderContext& context) const { + SVGRenderContext localContext(context); + if (!this->onPrepareToRender(&localContext)) { + return Path(); + } + + Path path = this->onAsPath(localContext); + if (auto clipPath = localContext.clipPath(); !clipPath.isEmpty()) { + // There is a clip-path present on the current node. + path.addPath(clipPath, PathOp::Intersect); + } + + return path; +} + +Rect SVGNode::objectBoundingBox(const SVGRenderContext& context) const { + return this->onObjectBoundingBox(context); +} + +bool SVGNode::onPrepareToRender(SVGRenderContext* context) const { + // Apply the inheritance of presentation attributes + context->applyPresentationAttributes(presentationAttributes, + this->hasChildren() ? 0 : SVGRenderContext::kLeaf); + + // visibility:hidden and display:none disable rendering. + const auto visibility = context->presentationContext()._inherited.Visibility->type(); + // display is uninherited + const auto display = presentationAttributes.Display; + return visibility != SVGVisibility::Type::Hidden && + (!display.isValue() || *display != SVGDisplay::None); +} + +void SVGNode::setAttribute(SVGAttribute attribute, const SVGValue& value) { + this->onSetAttribute(attribute, value); +} + +bool SVGNode::setAttribute(const std::string& attributeName, const std::string& attributeValue) { + return SVGNodeConstructor::SetAttribute(*this, attributeName, attributeValue); +} + +template +void SetInheritedByDefault(std::optional& presentation_attribute, const T& value) { + if (value.type() != T::Type::kInherit) { + presentation_attribute.set(value); + } else { + // kInherited values are semantically equivalent to + // the absence of a local presentation attribute. + presentation_attribute.reset(); + } +} + +bool SVGNode::parseAndSetAttribute(const std::string& name, const std::string& value) { +#define PARSE_AND_SET(svgName, attrName) \ + this->set##attrName( \ + SVGAttributeParser::parseProperty(svgName, name, \ + value)) + + return PARSE_AND_SET("clip-path", ClipPath) || PARSE_AND_SET("clip-rule", ClipRule) || + PARSE_AND_SET("color", Color) || + PARSE_AND_SET("color-interpolation", ColorInterpolation) || + PARSE_AND_SET("color-interpolation-filters", ColorInterpolationFilters) || + PARSE_AND_SET("display", Display) || PARSE_AND_SET("fill", Fill) || + PARSE_AND_SET("fill-opacity", FillOpacity) || PARSE_AND_SET("fill-rule", FillRule) || + PARSE_AND_SET("filter", Filter) || PARSE_AND_SET("flood-color", FloodColor) || + PARSE_AND_SET("flood-opacity", FloodOpacity) || PARSE_AND_SET("font-family", FontFamily) || + PARSE_AND_SET("font-size", FontSize) || PARSE_AND_SET("font-style", FontStyle) || + PARSE_AND_SET("font-weight", FontWeight) || + PARSE_AND_SET("lighting-color", LightingColor) || PARSE_AND_SET("mask", Mask) || + PARSE_AND_SET("opacity", Opacity) || PARSE_AND_SET("stop-color", StopColor) || + PARSE_AND_SET("stop-opacity", StopOpacity) || PARSE_AND_SET("stroke", Stroke) || + PARSE_AND_SET("stroke-dasharray", StrokeDashArray) || + PARSE_AND_SET("stroke-dashoffset", StrokeDashOffset) || + PARSE_AND_SET("stroke-linecap", StrokeLineCap) || + PARSE_AND_SET("stroke-linejoin", StrokeLineJoin) || + PARSE_AND_SET("stroke-miterlimit", StrokeMiterLimit) || + PARSE_AND_SET("stroke-opacity", StrokeOpacity) || + PARSE_AND_SET("stroke-width", StrokeWidth) || PARSE_AND_SET("text-anchor", TextAnchor) || + PARSE_AND_SET("visibility", Visibility); + +#undef PARSE_AND_SET +} + +// https://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute +Matrix SVGNode::ComputeViewboxMatrix(const Rect& viewBox, const Rect& viewPort, + SVGPreserveAspectRatio PreAspectRatio) { + if (viewBox.isEmpty() || viewPort.isEmpty()) { + return Matrix::MakeScale(0, 0); + } + + auto computeScale = [&]() -> Point { + const auto scaleX = viewPort.width() / viewBox.width(); + const auto scaleY = viewPort.height() / viewBox.height(); + + if (PreAspectRatio.align == SVGPreserveAspectRatio::Align::None) { + // none -> anisotropic scaling, regardless of fScale + return {scaleX, scaleY}; + } + + // isotropic scaling + const auto s = PreAspectRatio.scale == SVGPreserveAspectRatio::Scale::Meet + ? std::min(scaleX, scaleY) + : std::max(scaleX, scaleY); + return {s, s}; + }; + + auto computeTrans = [&](const Point& scale) -> Point { + static constexpr float alignCoeffs[] = { + 0.0f, // Min + 0.5f, // Mid + 1.0f // Max + }; + + const size_t x_coeff = static_cast(PreAspectRatio.align) >> 0 & 0x03; + const size_t y_coeff = static_cast(PreAspectRatio.align) >> 2 & 0x03; + + const auto tx = -viewBox.x() * scale.x; + const auto ty = -viewBox.y() * scale.y; + const auto dx = viewPort.width() - viewBox.width() * scale.x; + const auto dy = viewPort.height() - viewBox.height() * scale.y; + + return {tx + dx * alignCoeffs[x_coeff], ty + dy * alignCoeffs[y_coeff]}; + }; + + auto scale = computeScale(); + auto transform = computeTrans(scale); + + return Matrix::MakeTrans(transform.x, transform.y) * Matrix::MakeScale(scale.x, scale.y); +} +} // namespace tgfx \ No newline at end of file From 966e60b64a390015d0f31b83032bd7b9992b8bae Mon Sep 17 00:00:00 2001 From: YGauroa Date: Mon, 30 Dec 2024 20:09:20 +0800 Subject: [PATCH 21/31] ... --- include/tgfx/svg/SVGTypes.h | 11 +++++++---- include/tgfx/svg/node/SVGNode.h | 27 +++++++++++---------------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/include/tgfx/svg/SVGTypes.h b/include/tgfx/svg/SVGTypes.h index 9fc94116..abb9f8d2 100644 --- a/include/tgfx/svg/SVGTypes.h +++ b/include/tgfx/svg/SVGTypes.h @@ -507,15 +507,16 @@ class SVGStopColor { SVGStopColor() = default; - explicit SVGStopColor(Type t) : _type(t), _color(Color::Black()) { + explicit SVGStopColor(Type t) : _type(t) { } - explicit SVGStopColor(const SVGColorType& c) : _type(Type::Color), _color(c) { + explicit SVGStopColor(const SVGColorType& c) : _color(c) { } bool operator==(const SVGStopColor& other) const { return _type == other._type && _color == other._color; } + bool operator!=(const SVGStopColor& other) const { return !(*this == other); } @@ -523,6 +524,7 @@ class SVGStopColor { Type type() const { return _type; } + const SVGColorType& color() const { return _color; } @@ -547,6 +549,7 @@ class SVGObjectBoundingBoxUnits { bool operator==(const SVGObjectBoundingBoxUnits& other) const { return _type == other._type; } + bool operator!=(const SVGObjectBoundingBoxUnits& other) const { return !(*this == other); } @@ -830,8 +833,8 @@ class SVGFeTurbulenceBaseFrequency { } private: - SVGNumberType _freqX = 0.0; - SVGNumberType _freqY = 0.0; + SVGNumberType _freqX = 0.0f; + SVGNumberType _freqY = 0.0f; }; struct SVGFeTurbulenceType { diff --git a/include/tgfx/svg/node/SVGNode.h b/include/tgfx/svg/node/SVGNode.h index 18ebc685..2cf8a5cf 100644 --- a/include/tgfx/svg/node/SVGNode.h +++ b/include/tgfx/svg/node/SVGNode.h @@ -194,24 +194,19 @@ class SVGNode { */ virtual void appendChild(std::shared_ptr node) = 0; - /** - * Sets the attribute of the SVG node. - */ + protected: + explicit SVGNode(SVGTag tag); + void setAttribute(SVGAttribute attribute, const SVGValue& value); - /** - * Sets the attribute of the SVG node. - */ bool setAttribute(const std::string& attributeName, const std::string& attributeValue); - protected: - explicit SVGNode(SVGTag tag); - virtual bool parseAndSetAttribute(const std::string& name, const std::string& value); - static Matrix ComputeViewboxMatrix(const Rect&, const Rect&, SVGPreserveAspectRatio); + static Matrix ComputeViewboxMatrix(const Rect& viewBox, const Rect& viewPort, + SVGPreserveAspectRatio PreAspectRatio); - virtual void onSetAttribute(SVGAttribute, const SVGValue&){}; + virtual void onSetAttribute(SVGAttribute /*attribute*/, const SVGValue& /*value*/){}; // Called before onRender(), to apply local attributes to the context. Unlike onRender(), // onPrepareToRender() bubbles up the inheritance chain: override should always call @@ -219,17 +214,17 @@ class SVGNode { // (return false). // Implementations are expected to return true if rendering is to continue, or false if // the node/subtree rendering is disabled. - virtual bool onPrepareToRender(SVGRenderContext*) const; + virtual bool onPrepareToRender(SVGRenderContext* context) const; - virtual void onRender(const SVGRenderContext&) const = 0; + virtual void onRender(const SVGRenderContext& context) const = 0; - virtual bool onAsPaint(const SVGRenderContext&, Paint*) const { + virtual bool onAsPaint(const SVGRenderContext& /*context*/, Paint* /*paint*/) const { return false; } - virtual Path onAsPath(const SVGRenderContext&) const = 0; + virtual Path onAsPath(const SVGRenderContext& context) const = 0; - virtual Rect onObjectBoundingBox(const SVGRenderContext&) const { + virtual Rect onObjectBoundingBox(const SVGRenderContext& /*context*/) const { return Rect::MakeEmpty(); } From 0a88c2ba1b32b0e413e44c4c3ab46a6fce33ddc2 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Thu, 2 Jan 2025 14:29:34 +0800 Subject: [PATCH 22/31] =?UTF-8?q?=E3=80=82=E3=80=82=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/tgfx/svg/SVGDOM.h | 2 +- include/tgfx/svg/SVGFontManager.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/tgfx/svg/SVGDOM.h b/include/tgfx/svg/SVGDOM.h index 05e8c74e..07994f04 100644 --- a/include/tgfx/svg/SVGDOM.h +++ b/include/tgfx/svg/SVGDOM.h @@ -43,7 +43,7 @@ using SVGIDMapper = std::unordered_map>; * * 2. Rendering the SVG: * - The simplest way to render is by calling render(canvas,nullptr). If you need to render text - * with specific fonts or set the size of the SVG, you can use the following methods: + * with specific fonts or set the size of the SVG, you can use the following methods. * - If text rendering is required, use collectRenderFonts() to gather the necessary typefaces. * Traverse the typefaces collected by the fontManager and set the typeface objects. * - Render the SVG using the render() method. If text rendering is needed, pass in the diff --git a/include/tgfx/svg/SVGFontManager.h b/include/tgfx/svg/SVGFontManager.h index 50e1da33..1092bc5f 100644 --- a/include/tgfx/svg/SVGFontManager.h +++ b/include/tgfx/svg/SVGFontManager.h @@ -66,7 +66,7 @@ class SVGFontInfo { class SVGFontManager { public: /** - * Creates an SVGFontManager object with the default typeface.If the default typeface is nullptr, + * Creates an SVGFontManager object with the default typeface. If the default typeface is nullptr, * retrun nullptr. */ static std::shared_ptr Make(const std::shared_ptr& defaultTypeface); @@ -77,7 +77,7 @@ class SVGFontManager { ~SVGFontManager() = default; /** - * Adds a font style to the font manager.If the font family and style already exist, this method + * Adds a font style to the font manager. If the font family and style already exist, this method * does nothing and returns false. */ bool setTypeface(const std::string& fontFamily, SVGFontInfo info, From ad18a8cee3a0a391ae6d118c2322214dba22d30b Mon Sep 17 00:00:00 2001 From: YGauroa Date: Fri, 3 Jan 2025 10:33:45 +0800 Subject: [PATCH 23/31] ... --- src/svg/SVGUtils.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/svg/SVGUtils.h b/src/svg/SVGUtils.h index f8f2cd52..8953287b 100644 --- a/src/svg/SVGUtils.h +++ b/src/svg/SVGUtils.h @@ -82,4 +82,23 @@ std::shared_ptr AsDataUri(const Pixmap& pixmap); std::shared_ptr AsDataUri(const std::shared_ptr& encodedData); +inline Color Uint32ToColor(uint32_t value) { + return Color::FromRGBA((value >> 16) & 0xff, (value >> 8) & 0xff, (value >> 0) & 0xff, + (value >> 24) & 0xff); +} + +// Common functions for SVG conversion, used to find specific data types in a string. +// Returns the char pointer after the found data, or nullptr if not found. +class SVGParse { + public: + static const char* FindHex(const char str[], uint32_t* value); + static const char* FindNamedColor(const char str[], Color* color); + static const char* FindS32(const char str[], int32_t* value); + static const char* FindScalar(const char str[], float* value); + static const char* FindScalars(const char str[], float value[], int count); + static bool FindBool(const char str[], bool* value); + // return the index of str in list[], or -1 if not found + static int FindList(const char target[], const char list[]); +}; + } // namespace tgfx \ No newline at end of file From cb1e49291dec720c2e6b017c45fc3e9c4e754ed9 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Fri, 3 Jan 2025 12:31:28 +0800 Subject: [PATCH 24/31] ... --- include/tgfx/core/Color.h | 8 ---- include/tgfx/svg/node/SVGCircle.h | 11 ++--- include/tgfx/svg/node/SVGClipPath.h | 4 +- include/tgfx/svg/node/SVGContainer.h | 8 ++-- include/tgfx/svg/node/SVGEllipse.h | 9 ++-- include/tgfx/svg/node/SVGFe.h | 17 +++++--- include/tgfx/svg/node/SVGFeBlend.h | 18 ++++---- include/tgfx/svg/node/SVGFeColorMatrix.h | 8 ++-- .../tgfx/svg/node/SVGFeComponentTransfer.h | 6 +-- include/tgfx/svg/node/SVGFeComposite.h | 8 ++-- include/tgfx/svg/node/SVGFeDisplacementMap.h | 16 ++++--- include/tgfx/svg/node/SVGFeFlood.h | 5 ++- include/tgfx/svg/node/SVGFeGaussianBlur.h | 10 ++--- include/tgfx/svg/node/SVGFeImage.h | 6 +-- include/tgfx/svg/node/SVGFeLightSource.h | 6 +-- include/tgfx/svg/node/SVGFeLighting.h | 14 +++--- include/tgfx/svg/node/SVGFeMerge.h | 6 +-- include/tgfx/svg/node/SVGFeMorphology.h | 6 +-- include/tgfx/svg/node/SVGFeOffset.h | 6 +-- include/tgfx/svg/node/SVGFeTurbulence.h | 6 +-- include/tgfx/svg/node/SVGFilter.h | 6 +-- include/tgfx/svg/node/SVGGradient.h | 10 +++-- include/tgfx/svg/node/SVGHiddenContainer.h | 2 +- include/tgfx/svg/node/SVGImage.h | 18 ++++---- include/tgfx/svg/node/SVGLine.h | 9 ++-- include/tgfx/svg/node/SVGLinearGradient.h | 7 +-- include/tgfx/svg/node/SVGMask.h | 6 +-- include/tgfx/svg/node/SVGPath.h | 9 ++-- include/tgfx/svg/node/SVGPattern.h | 10 +++-- include/tgfx/svg/node/SVGPoly.h | 11 ++--- include/tgfx/svg/node/SVGRadialGradient.h | 9 ++-- include/tgfx/svg/node/SVGRect.h | 11 ++--- include/tgfx/svg/node/SVGSVG.h | 8 ++-- include/tgfx/svg/node/SVGShape.h | 9 ++-- include/tgfx/svg/node/SVGStop.h | 2 +- include/tgfx/svg/node/SVGText.h | 32 ++++++++------ include/tgfx/svg/node/SVGTransformableNode.h | 10 ++--- include/tgfx/svg/node/SVGUse.h | 10 ++--- src/svg/SVGFilterContext.cpp | 16 +++---- src/svg/SVGFilterContext.h | 26 ++++++----- src/svg/node/SVGCircle.cpp | 12 +++--- src/svg/node/SVGClipPath.cpp | 6 +-- src/svg/node/SVGContainer.cpp | 8 ++-- src/svg/node/SVGEllipse.cpp | 14 +++--- src/svg/node/SVGFe.cpp | 12 +++--- src/svg/node/SVGFeBlend.cpp | 10 ++--- src/svg/node/SVGFeColorMatrix.cpp | 43 +++++++++---------- src/svg/node/SVGFeComposite.cpp | 9 ++-- src/svg/node/SVGFeGaussianBlur.cpp | 8 ++-- src/svg/node/SVGFeLighting.cpp | 4 +- src/svg/node/SVGFeOffset.cpp | 6 +-- src/svg/node/SVGImage.cpp | 14 +++--- src/svg/node/SVGLinearGradient.cpp | 3 +- 53 files changed, 286 insertions(+), 262 deletions(-) diff --git a/include/tgfx/core/Color.h b/include/tgfx/core/Color.h index e60c1c18..d66777eb 100644 --- a/include/tgfx/core/Color.h +++ b/include/tgfx/core/Color.h @@ -24,14 +24,6 @@ namespace tgfx { -enum class ColorChannel { - R, // the red channel - G, // the green channel - B, // the blue channel - A, // the alpha channel - LastEnum = A, -}; - /** * RGBA color value, holding four floating point components. Color components are always in a known * order. diff --git a/include/tgfx/svg/node/SVGCircle.h b/include/tgfx/svg/node/SVGCircle.h index 22563644..687f5230 100644 --- a/include/tgfx/svg/node/SVGCircle.h +++ b/include/tgfx/svg/node/SVGCircle.h @@ -41,17 +41,18 @@ class SVGCircle final : public SVGShape { SVG_ATTR(R, SVGLength, SVGLength(0)) protected: - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - void onDraw(Canvas*, const SVGLengthContext&, const Paint&, PathFillType) const override; + void onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType type) const override; - Path onAsPath(const SVGRenderContext&) const override; + Path onAsPath(const SVGRenderContext& context) const override; - Rect onObjectBoundingBox(const SVGRenderContext&) const override; + Rect onObjectBoundingBox(const SVGRenderContext& context) const override; private: // resolve and return the center and radius values - std::tuple resolve(const SVGLengthContext&) const; + std::tuple resolve(const SVGLengthContext& lengthContext) const; SVGCircle(); diff --git a/include/tgfx/svg/node/SVGClipPath.h b/include/tgfx/svg/node/SVGClipPath.h index 39e0f278..647c7c35 100644 --- a/include/tgfx/svg/node/SVGClipPath.h +++ b/include/tgfx/svg/node/SVGClipPath.h @@ -42,9 +42,9 @@ class SVGClipPath final : public SVGHiddenContainer { SVGClipPath(); - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - Path resolveClip(const SVGRenderContext&) const; + Path resolveClip(const SVGRenderContext& context) const; using INHERITED = SVGHiddenContainer; }; diff --git a/include/tgfx/svg/node/SVGContainer.h b/include/tgfx/svg/node/SVGContainer.h index 54390296..389e337f 100644 --- a/include/tgfx/svg/node/SVGContainer.h +++ b/include/tgfx/svg/node/SVGContainer.h @@ -30,18 +30,18 @@ class SVGRenderContext; class SVGContainer : public SVGTransformableNode { public: - void appendChild(std::shared_ptr) override; + void appendChild(std::shared_ptr node) override; const std::vector>& getChildren() const; bool hasChildren() const final; protected: explicit SVGContainer(SVGTag); - void onRender(const SVGRenderContext&) const override; + void onRender(const SVGRenderContext& context) const override; - Path onAsPath(const SVGRenderContext&) const override; + Path onAsPath(const SVGRenderContext& context) const override; - Rect onObjectBoundingBox(const SVGRenderContext&) const override; + Rect onObjectBoundingBox(const SVGRenderContext& context) const override; template void forEachChild(Func func) const { diff --git a/include/tgfx/svg/node/SVGEllipse.h b/include/tgfx/svg/node/SVGEllipse.h index 06bd8eb2..ae20ae7b 100644 --- a/include/tgfx/svg/node/SVGEllipse.h +++ b/include/tgfx/svg/node/SVGEllipse.h @@ -43,16 +43,17 @@ class SVGEllipse final : public SVGShape { SVG_OPTIONAL_ATTR(Ry, SVGLength) protected: - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - void onDraw(Canvas*, const SVGLengthContext&, const Paint&, PathFillType) const override; + void onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType fillType) const override; - Path onAsPath(const SVGRenderContext&) const override; + Path onAsPath(const SVGRenderContext& context) const override; private: SVGEllipse(); - Rect resolve(const SVGLengthContext&) const; + Rect resolve(const SVGLengthContext& context) const; using INHERITED = SVGShape; }; diff --git a/include/tgfx/svg/node/SVGFe.h b/include/tgfx/svg/node/SVGFe.h index 48c03fc2..f08e64a7 100644 --- a/include/tgfx/svg/node/SVGFe.h +++ b/include/tgfx/svg/node/SVGFe.h @@ -57,17 +57,19 @@ class SVGFe : public SVGHiddenContainer { const SVGFilterContext& filterContext) const; // https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveSubRegion - Rect resolveFilterSubregion(const SVGRenderContext&, const SVGFilterContext&) const; + Rect resolveFilterSubregion(const SVGRenderContext& context, + const SVGFilterContext& filterContext) const; /** * Resolves the colorspace within which this filter effect should be applied. * Spec: https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperties * 'color-interpolation-filters' property. */ - virtual SVGColorspace resolveColorspace(const SVGRenderContext&, const SVGFilterContext&) const; + virtual SVGColorspace resolveColorspace(const SVGRenderContext& context, + const SVGFilterContext& filterContext) const; /** Propagates any inherited presentation attributes in the given context. */ - void applyProperties(SVGRenderContext*) const; + void applyProperties(SVGRenderContext* context) const; SVG_ATTR(In, SVGFeInputType, SVGFeInputType()) @@ -81,12 +83,12 @@ class SVGFe : public SVGHiddenContainer { explicit SVGFe(SVGTag t) : INHERITED(t) { } - virtual std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SVGFilterContext&) const = 0; + virtual std::shared_ptr onMakeImageFilter( + const SVGRenderContext& context, const SVGFilterContext& filterContext) const = 0; virtual std::vector getInputs() const = 0; - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; private: /** @@ -94,7 +96,8 @@ class SVGFe : public SVGHiddenContainer { * filter effect. These attributes are resolved according to the given length context and * the value of 'primitiveUnits' on the parent element. */ - Rect resolveBoundaries(const SVGRenderContext&, const SVGFilterContext&) const; + Rect resolveBoundaries(const SVGRenderContext& context, + const SVGFilterContext& filterContext) const; using INHERITED = SVGHiddenContainer; }; diff --git a/include/tgfx/svg/node/SVGFeBlend.h b/include/tgfx/svg/node/SVGFeBlend.h index 03c3aadf..490ae1bf 100644 --- a/include/tgfx/svg/node/SVGFeBlend.h +++ b/include/tgfx/svg/node/SVGFeBlend.h @@ -31,29 +31,29 @@ namespace tgfx { class SVGFeBlend : public SVGFe { public: enum class Mode { - kNormal, - kMultiply, - kScreen, - kDarken, - kLighten, + Normal, + Multiply, + Screen, + Darken, + Lighten, }; static std::shared_ptr Make() { return std::shared_ptr(new SVGFeBlend()); } - SVG_ATTR(BlendMode, Mode, Mode::kNormal) + SVG_ATTR(BlendMode, Mode, Mode::Normal) SVG_ATTR(In2, SVGFeInputType, SVGFeInputType()) protected: - std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SVGFilterContext&) const override; + std::shared_ptr onMakeImageFilter( + const SVGRenderContext& context, const SVGFilterContext& filterContext) const override; std::vector getInputs() const override { return {this->getIn(), this->getIn2()}; } - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; private: SVGFeBlend() : INHERITED(SVGTag::FeBlend) { diff --git a/include/tgfx/svg/node/SVGFeColorMatrix.h b/include/tgfx/svg/node/SVGFeColorMatrix.h index 9390bf58..554d0a5f 100644 --- a/include/tgfx/svg/node/SVGFeColorMatrix.h +++ b/include/tgfx/svg/node/SVGFeColorMatrix.h @@ -41,14 +41,14 @@ class SVGFeColorMatrix final : public SVGFe { SVG_ATTR(Values, SVGFeColorMatrixValues, SVGFeColorMatrixValues()) protected: - std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SVGFilterContext&) const override; + std::shared_ptr onMakeImageFilter( + const SVGRenderContext& context, const SVGFilterContext& filterContext) const override; std::vector getInputs() const override { return {this->getIn()}; } - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; private: SVGFeColorMatrix() : INHERITED(SVGTag::FeColorMatrix) { @@ -56,7 +56,7 @@ class SVGFeColorMatrix final : public SVGFe { ColorMatrix makeMatrixForType() const; - static ColorMatrix MakeSaturate(SVGNumberType s); + static ColorMatrix MakeSaturate(SVGNumberType sat); static ColorMatrix MakeHueRotate(SVGNumberType degrees); diff --git a/include/tgfx/svg/node/SVGFeComponentTransfer.h b/include/tgfx/svg/node/SVGFeComponentTransfer.h index b03ed2c4..3b312a25 100644 --- a/include/tgfx/svg/node/SVGFeComponentTransfer.h +++ b/include/tgfx/svg/node/SVGFeComponentTransfer.h @@ -18,7 +18,6 @@ #pragma once -#include <__memory/shared_ptr.h> #include #include #include @@ -77,8 +76,9 @@ class SVGFeComponentTransfer final : public SVGFe { } protected: - std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SVGFilterContext&) const override { + std::shared_ptr onMakeImageFilter( + const SVGRenderContext& /*context*/, + const SVGFilterContext& /*filterContext*/) const override { return nullptr; }; diff --git a/include/tgfx/svg/node/SVGFeComposite.h b/include/tgfx/svg/node/SVGFeComposite.h index c5b057d8..46b71e1c 100644 --- a/include/tgfx/svg/node/SVGFeComposite.h +++ b/include/tgfx/svg/node/SVGFeComposite.h @@ -45,16 +45,16 @@ class SVGFeComposite final : public SVGFe { return {this->getIn(), this->getIn2()}; } - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SVGFilterContext&) const override; + std::shared_ptr onMakeImageFilter( + const SVGRenderContext& context, const SVGFilterContext& filterContext) const override; private: SVGFeComposite() : INHERITED(SVGTag::FeComposite) { } - static BlendMode BlendModeForOperator(SVGFeCompositeOperator); + static BlendMode BlendModeForOperator(SVGFeCompositeOperator op); using INHERITED = SVGFe; }; diff --git a/include/tgfx/svg/node/SVGFeDisplacementMap.h b/include/tgfx/svg/node/SVGFeDisplacementMap.h index 3f381512..3bb94a05 100644 --- a/include/tgfx/svg/node/SVGFeDisplacementMap.h +++ b/include/tgfx/svg/node/SVGFeDisplacementMap.h @@ -31,7 +31,12 @@ class SVGRenderContext; class SVGFeDisplacementMap : public SVGFe { public: - using ChannelSelector = ColorChannel; + enum class ChannelSelector { + R, // the red channel + G, // the green channel + B, // the blue channel + A, // the alpha channel + }; static std::shared_ptr Make() { return std::shared_ptr(new SVGFeDisplacementMap()); @@ -42,17 +47,18 @@ class SVGFeDisplacementMap : public SVGFe { SVG_ATTR(YChannelSelector, ChannelSelector, ChannelSelector::A) SVG_ATTR(Scale, SVGNumberType, SVGNumberType(0)) - SVGColorspace resolveColorspace(const SVGRenderContext&, const SVGFilterContext&) const final; + SVGColorspace resolveColorspace(const SVGRenderContext& context, + const SVGFilterContext& filterContext) const final; protected: std::vector getInputs() const override { return {this->getIn(), this->getIn2()}; } - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SVGFilterContext&) const override; + std::shared_ptr onMakeImageFilter( + const SVGRenderContext& context, const SVGFilterContext& filterContext) const override; private: SVGFeDisplacementMap() : INHERITED(SVGTag::FeDisplacementMap) { diff --git a/include/tgfx/svg/node/SVGFeFlood.h b/include/tgfx/svg/node/SVGFeFlood.h index 9a66b056..c7d20325 100644 --- a/include/tgfx/svg/node/SVGFeFlood.h +++ b/include/tgfx/svg/node/SVGFeFlood.h @@ -37,8 +37,9 @@ class SVGFeFlood : public SVGFe { } protected: - std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SVGFilterContext&) const override { + std::shared_ptr onMakeImageFilter( + const SVGRenderContext& /*context*/, + const SVGFilterContext& /*filterContext*/) const override { return nullptr; }; diff --git a/include/tgfx/svg/node/SVGFeGaussianBlur.h b/include/tgfx/svg/node/SVGFeGaussianBlur.h index 64f1fbcb..89010cf1 100644 --- a/include/tgfx/svg/node/SVGFeGaussianBlur.h +++ b/include/tgfx/svg/node/SVGFeGaussianBlur.h @@ -32,8 +32,8 @@ class SVGRenderContext; class SVGFeGaussianBlur : public SVGFe { public: struct StdDeviation { - SVGNumberType fX; - SVGNumberType fY; + SVGNumberType X; + SVGNumberType Y; }; static std::shared_ptr Make() { @@ -43,14 +43,14 @@ class SVGFeGaussianBlur : public SVGFe { SVG_ATTR(stdDeviation, StdDeviation, StdDeviation({0, 0})) protected: - std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SVGFilterContext&) const override; + std::shared_ptr onMakeImageFilter( + const SVGRenderContext& context, const SVGFilterContext& filterContext) const override; std::vector getInputs() const override { return {this->getIn()}; } - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; private: SVGFeGaussianBlur() : INHERITED(SVGTag::FeGaussianBlur) { diff --git a/include/tgfx/svg/node/SVGFeImage.h b/include/tgfx/svg/node/SVGFeImage.h index 14b4c672..d7bc4536 100644 --- a/include/tgfx/svg/node/SVGFeImage.h +++ b/include/tgfx/svg/node/SVGFeImage.h @@ -39,10 +39,10 @@ class SVGFeImage : public SVGFe { SVG_ATTR(PreserveAspectRatio, SVGPreserveAspectRatio, SVGPreserveAspectRatio()) protected: - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SVGFilterContext&) const override; + std::shared_ptr onMakeImageFilter( + const SVGRenderContext& context, const SVGFilterContext& filterContext) const override; std::vector getInputs() const override { return {}; diff --git a/include/tgfx/svg/node/SVGFeLightSource.h b/include/tgfx/svg/node/SVGFeLightSource.h index 8661f8d3..c54c550f 100644 --- a/include/tgfx/svg/node/SVGFeLightSource.h +++ b/include/tgfx/svg/node/SVGFeLightSource.h @@ -53,7 +53,7 @@ class SVGFeDistantLight final : public SkSVGFeLightSource { SVGFeDistantLight() : INHERITED(SVGTag::FeDistantLight) { } - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; using INHERITED = SkSVGFeLightSource; }; @@ -72,7 +72,7 @@ class SVGFePointLight final : public SkSVGFeLightSource { SVGFePointLight() : INHERITED(SVGTag::FePointLight) { } - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; using INHERITED = SkSVGFeLightSource; }; @@ -97,7 +97,7 @@ class SVGFeSpotLight final : public SkSVGFeLightSource { SVGFeSpotLight() : INHERITED(SVGTag::FeSpotLight) { } - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; using INHERITED = SkSVGFeLightSource; }; diff --git a/include/tgfx/svg/node/SVGFeLighting.h b/include/tgfx/svg/node/SVGFeLighting.h index d0e368bf..3d4f06e9 100644 --- a/include/tgfx/svg/node/SVGFeLighting.h +++ b/include/tgfx/svg/node/SVGFeLighting.h @@ -34,8 +34,8 @@ class SVGFeSpotLight; class SVGFeLighting : public SVGFe { public: struct KernelUnitLength { - SVGNumberType fDx; - SVGNumberType fDy; + SVGNumberType Dx; + SVGNumberType Dy; }; SVG_ATTR(SurfaceScale, SVGNumberType, 1) @@ -49,10 +49,10 @@ class SVGFeLighting : public SVGFe { return {this->getIn()}; } - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SVGFilterContext&) const final { + std::shared_ptr onMakeImageFilter( + const SVGRenderContext& /*context*/, const SVGFilterContext& /*filterContext*/) const final { return nullptr; }; @@ -70,7 +70,7 @@ class SVGFeSpecularLighting final : public SVGFeLighting { SVG_ATTR(SpecularExponent, SVGNumberType, 1) protected: - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; private: SVGFeSpecularLighting() : INHERITED(SVGTag::FeSpecularLighting) { @@ -88,7 +88,7 @@ class SVGFeDiffuseLighting final : public SVGFeLighting { SVG_ATTR(DiffuseConstant, SVGNumberType, 1) protected: - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; private: SVGFeDiffuseLighting() : INHERITED(SVGTag::FeDiffuseLighting) { diff --git a/include/tgfx/svg/node/SVGFeMerge.h b/include/tgfx/svg/node/SVGFeMerge.h index 1445a54f..b3656d4e 100644 --- a/include/tgfx/svg/node/SVGFeMerge.h +++ b/include/tgfx/svg/node/SVGFeMerge.h @@ -39,7 +39,7 @@ class SVGFeMergeNode : public SVGHiddenContainer { SVG_ATTR(In, SVGFeInputType, SVGFeInputType()) protected: - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; private: SVGFeMergeNode() : INHERITED(tag) { @@ -56,8 +56,8 @@ class SVGFeMerge : public SVGFe { } protected: - std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SVGFilterContext&) const override; + std::shared_ptr onMakeImageFilter( + const SVGRenderContext& context, const SVGFilterContext& filterContext) const override; std::vector getInputs() const override; diff --git a/include/tgfx/svg/node/SVGFeMorphology.h b/include/tgfx/svg/node/SVGFeMorphology.h index 896630bf..24bf4191 100644 --- a/include/tgfx/svg/node/SVGFeMorphology.h +++ b/include/tgfx/svg/node/SVGFeMorphology.h @@ -46,14 +46,14 @@ class SVGFeMorphology : public SVGFe { SVG_ATTR(MorphRadius, Radius, Radius({0, 0})) protected: - std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SVGFilterContext&) const override; + std::shared_ptr onMakeImageFilter( + const SVGRenderContext& context, const SVGFilterContext& filterContext) const override; std::vector getInputs() const override { return {this->getIn()}; } - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; private: SVGFeMorphology() : INHERITED(SVGTag::FeMorphology) { diff --git a/include/tgfx/svg/node/SVGFeOffset.h b/include/tgfx/svg/node/SVGFeOffset.h index 5182805d..77abd7bf 100644 --- a/include/tgfx/svg/node/SVGFeOffset.h +++ b/include/tgfx/svg/node/SVGFeOffset.h @@ -40,14 +40,14 @@ class SVGFeOffset : public SVGFe { SVG_ATTR(Dy, SVGNumberType, SVGNumberType(0)) protected: - std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SVGFilterContext&) const override; + std::shared_ptr onMakeImageFilter( + const SVGRenderContext& context, const SVGFilterContext& filterContext) const override; std::vector getInputs() const override { return {this->getIn()}; } - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; private: SVGFeOffset() : INHERITED(SVGTag::FeOffset) { diff --git a/include/tgfx/svg/node/SVGFeTurbulence.h b/include/tgfx/svg/node/SVGFeTurbulence.h index 01b2c776..63be89e5 100644 --- a/include/tgfx/svg/node/SVGFeTurbulence.h +++ b/include/tgfx/svg/node/SVGFeTurbulence.h @@ -39,14 +39,14 @@ class SVGFeTurbulence : public SVGFe { SVGFeTurbulenceType(SVGFeTurbulenceType::Type::Turbulence)) protected: - std::shared_ptr onMakeImageFilter(const SVGRenderContext&, - const SVGFilterContext&) const override; + std::shared_ptr onMakeImageFilter( + const SVGRenderContext& context, const SVGFilterContext& filterContext) const override; std::vector getInputs() const override { return {}; } - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; private: SVGFeTurbulence() : INHERITED(SVGTag::FeTurbulence) { diff --git a/include/tgfx/svg/node/SVGFilter.h b/include/tgfx/svg/node/SVGFilter.h index 7211bec2..f8a0f61e 100644 --- a/include/tgfx/svg/node/SVGFilter.h +++ b/include/tgfx/svg/node/SVGFilter.h @@ -33,9 +33,9 @@ class SVGFilter final : public SVGHiddenContainer { } /** Propagates any inherited presentation attributes in the given context. */ - void applyProperties(SVGRenderContext*) const; + void applyProperties(SVGRenderContext* context) const; - std::shared_ptr buildFilterDAG(const SVGRenderContext&) const; + std::shared_ptr buildFilterDAG(const SVGRenderContext& context) const; SVG_ATTR(X, SVGLength, SVGLength(-10, SVGLength::Unit::Percentage)) SVG_ATTR(Y, SVGLength, SVGLength(-10, SVGLength::Unit::Percentage)) @@ -50,7 +50,7 @@ class SVGFilter final : public SVGHiddenContainer { SVGFilter() : INHERITED(SVGTag::Filter) { } - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; using INHERITED = SVGHiddenContainer; }; diff --git a/include/tgfx/svg/node/SVGGradient.h b/include/tgfx/svg/node/SVGGradient.h index 0bde2c70..c6d1a306 100644 --- a/include/tgfx/svg/node/SVGGradient.h +++ b/include/tgfx/svg/node/SVGGradient.h @@ -42,12 +42,14 @@ class SVGGradient : public SVGHiddenContainer { explicit SVGGradient(SVGTag t) : INHERITED(t) { } - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - bool onAsPaint(const SVGRenderContext&, Paint*) const final; + bool onAsPaint(const SVGRenderContext& context, Paint* paint) const final; - virtual std::shared_ptr onMakeShader(const SVGRenderContext&, const std::vector&, - const std::vector&, TileMode, + virtual std::shared_ptr onMakeShader(const SVGRenderContext& context, + const std::vector& colors, + const std::vector& positions, + TileMode tileMode, const Matrix& localMatrix) const = 0; private: diff --git a/include/tgfx/svg/node/SVGHiddenContainer.h b/include/tgfx/svg/node/SVGHiddenContainer.h index 34698dff..79c63df8 100644 --- a/include/tgfx/svg/node/SVGHiddenContainer.h +++ b/include/tgfx/svg/node/SVGHiddenContainer.h @@ -27,7 +27,7 @@ class SVGHiddenContainer : public SVGContainer { explicit SVGHiddenContainer(SVGTag t) : INHERITED(t) { } - void onRender(const SVGRenderContext&) const final { + void onRender(const SVGRenderContext& /*context*/) const final { //abort rendering children nodes } diff --git a/include/tgfx/svg/node/SVGImage.h b/include/tgfx/svg/node/SVGImage.h index cb188d07..6ae3208a 100644 --- a/include/tgfx/svg/node/SVGImage.h +++ b/include/tgfx/svg/node/SVGImage.h @@ -40,19 +40,19 @@ class SVGImage final : public SVGTransformableNode { return std::shared_ptr(new SVGImage()); } - void appendChild(std::shared_ptr) override { + void appendChild(std::shared_ptr /*node*/) override { } struct ImageInfo { - std::shared_ptr fImage; - Rect fDst; + std::shared_ptr image; + Rect destinationRect; }; - bool onPrepareToRender(SVGRenderContext*) const override; - void onRender(const SVGRenderContext&) const override; - Path onAsPath(const SVGRenderContext&) const override; - Rect onObjectBoundingBox(const SVGRenderContext&) const override; - static ImageInfo LoadImage(const SVGIRI&, const Rect&, SVGPreserveAspectRatio); + bool onPrepareToRender(SVGRenderContext* conetxt) const override; + void onRender(const SVGRenderContext& conetxt) const override; + Path onAsPath(const SVGRenderContext& conetxt) const override; + Rect onObjectBoundingBox(const SVGRenderContext& conetxt) const override; + static ImageInfo LoadImage(const SVGIRI& iri, const Rect& viewPort, SVGPreserveAspectRatio ratio); SVG_ATTR(X, SVGLength, SVGLength(0)) SVG_ATTR(Y, SVGLength, SVGLength(0)) @@ -62,7 +62,7 @@ class SVGImage final : public SVGTransformableNode { SVG_ATTR(PreserveAspectRatio, SVGPreserveAspectRatio, SVGPreserveAspectRatio()) protected: - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; private: SVGImage() : INHERITED(SVGTag::Image) { diff --git a/include/tgfx/svg/node/SVGLine.h b/include/tgfx/svg/node/SVGLine.h index 47362112..ad14db9b 100644 --- a/include/tgfx/svg/node/SVGLine.h +++ b/include/tgfx/svg/node/SVGLine.h @@ -41,17 +41,18 @@ class SVGLine final : public SVGShape { SVG_ATTR(Y2, SVGLength, SVGLength(0)) protected: - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - void onDraw(Canvas*, const SVGLengthContext&, const Paint&, PathFillType) const override; + void onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType fillType) const override; - Path onAsPath(const SVGRenderContext&) const override; + Path onAsPath(const SVGRenderContext& context) const override; private: SVGLine(); // resolve and return the two endpoints - std::tuple resolve(const SVGLengthContext&) const; + std::tuple resolve(const SVGLengthContext& context) const; using INHERITED = SVGShape; }; diff --git a/include/tgfx/svg/node/SVGLinearGradient.h b/include/tgfx/svg/node/SVGLinearGradient.h index 6f55242d..b1864caa 100644 --- a/include/tgfx/svg/node/SVGLinearGradient.h +++ b/include/tgfx/svg/node/SVGLinearGradient.h @@ -38,10 +38,11 @@ class SVGLinearGradient final : public SVGGradient { SVG_ATTR(Y2, SVGLength, SVGLength(0, SVGLength::Unit::Percentage)) protected: - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - std::shared_ptr onMakeShader(const SVGRenderContext&, const std::vector&, - const std::vector&, TileMode, + std::shared_ptr onMakeShader(const SVGRenderContext& context, + const std::vector& colors, + const std::vector& positions, TileMode tileMode, const Matrix& localMatrix) const override; private: diff --git a/include/tgfx/svg/node/SVGMask.h b/include/tgfx/svg/node/SVGMask.h index 7dbf6775..873f8c90 100644 --- a/include/tgfx/svg/node/SVGMask.h +++ b/include/tgfx/svg/node/SVGMask.h @@ -47,11 +47,11 @@ class SVGMask final : public SVGHiddenContainer { SVGMask() : INHERITED(SVGTag::Mask) { } - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - Rect bounds(const SVGRenderContext&) const; + Rect bounds(const SVGRenderContext& context) const; - void renderMask(const SVGRenderContext&) const; + void renderMask(const SVGRenderContext& context) const; using INHERITED = SVGHiddenContainer; }; diff --git a/include/tgfx/svg/node/SVGPath.h b/include/tgfx/svg/node/SVGPath.h index 4fd47adf..dbc358f7 100644 --- a/include/tgfx/svg/node/SVGPath.h +++ b/include/tgfx/svg/node/SVGPath.h @@ -36,13 +36,14 @@ class SVGPath final : public SVGShape { SVG_ATTR(ShapePath, Path, Path()) protected: - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - void onDraw(Canvas*, const SVGLengthContext&, const Paint&, PathFillType) const override; + void onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType fillType) const override; - Path onAsPath(const SVGRenderContext&) const override; + Path onAsPath(const SVGRenderContext& context) const override; - Rect onObjectBoundingBox(const SVGRenderContext&) const override; + Rect onObjectBoundingBox(const SVGRenderContext& context) const override; private: SVGPath(); diff --git a/include/tgfx/svg/node/SVGPattern.h b/include/tgfx/svg/node/SVGPattern.h index 6204017e..ca61cae0 100644 --- a/include/tgfx/svg/node/SVGPattern.h +++ b/include/tgfx/svg/node/SVGPattern.h @@ -47,9 +47,9 @@ class SVGPattern final : public SVGHiddenContainer { protected: SVGPattern(); - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - bool onAsPaint(const SVGRenderContext&, Paint*) const override; + bool onAsPaint(const SVGRenderContext& context, Paint* paint) const override; private: struct PatternAttributes { @@ -57,8 +57,10 @@ class SVGPattern final : public SVGHiddenContainer { std::optional patternTransform; }; - const SVGPattern* resolveHref(const SVGRenderContext&, PatternAttributes*) const; - const SVGPattern* hrefTarget(const SVGRenderContext&) const; + const SVGPattern* resolveHref(const SVGRenderContext& context, + PatternAttributes* attribute) const; + + const SVGPattern* hrefTarget(const SVGRenderContext& context) const; using INHERITED = SVGHiddenContainer; }; diff --git a/include/tgfx/svg/node/SVGPoly.h b/include/tgfx/svg/node/SVGPoly.h index db09ca81..56c450e9 100644 --- a/include/tgfx/svg/node/SVGPoly.h +++ b/include/tgfx/svg/node/SVGPoly.h @@ -42,16 +42,17 @@ class SVGPoly final : public SVGShape { SVG_ATTR(Points, SVGPointsType, SVGPointsType()) protected: - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - void onDraw(Canvas*, const SVGLengthContext&, const Paint&, PathFillType) const override; + void onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType fillType) const override; - Path onAsPath(const SVGRenderContext&) const override; + Path onAsPath(const SVGRenderContext& context) const override; - Rect onObjectBoundingBox(const SVGRenderContext&) const override; + Rect onObjectBoundingBox(const SVGRenderContext& context) const override; private: - SVGPoly(SVGTag); + SVGPoly(SVGTag tag); mutable Path path; // mutated in onDraw(), to apply inherited fill types. diff --git a/include/tgfx/svg/node/SVGRadialGradient.h b/include/tgfx/svg/node/SVGRadialGradient.h index 87bcd06d..99861219 100644 --- a/include/tgfx/svg/node/SVGRadialGradient.h +++ b/include/tgfx/svg/node/SVGRadialGradient.h @@ -40,11 +40,12 @@ class SVGRadialGradient final : public SVGGradient { SVG_OPTIONAL_ATTR(Fy, SVGLength) protected: - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - std::shared_ptr onMakeShader(const SVGRenderContext&, const std::vector&, - const std::vector&, TileMode, - const Matrix&) const override; + std::shared_ptr onMakeShader(const SVGRenderContext& context, + const std::vector& colors, + const std::vector& positions, TileMode tileMode, + const Matrix& localMatrix) const override; private: SVGRadialGradient(); diff --git a/include/tgfx/svg/node/SVGRect.h b/include/tgfx/svg/node/SVGRect.h index 8b827ddf..c4624138 100644 --- a/include/tgfx/svg/node/SVGRect.h +++ b/include/tgfx/svg/node/SVGRect.h @@ -45,18 +45,19 @@ class SVGRect final : public SVGShape { SVG_OPTIONAL_ATTR(Ry, SVGLength) protected: - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - void onDraw(Canvas*, const SVGLengthContext&, const Paint&, PathFillType) const override; + void onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType fillType) const override; - Path onAsPath(const SVGRenderContext&) const override; + Path onAsPath(const SVGRenderContext& context) const override; - Rect onObjectBoundingBox(const SVGRenderContext&) const override; + Rect onObjectBoundingBox(const SVGRenderContext& context) const override; private: SVGRect(); - RRect resolve(const SVGLengthContext&) const; + RRect resolve(const SVGLengthContext& lengthContext) const; using INHERITED = SVGShape; }; diff --git a/include/tgfx/svg/node/SVGSVG.h b/include/tgfx/svg/node/SVGSVG.h index 853d0eb6..6f591c33 100644 --- a/include/tgfx/svg/node/SVGSVG.h +++ b/include/tgfx/svg/node/SVGSVG.h @@ -47,14 +47,14 @@ class SVGSVG : public SVGContainer { SVG_OPTIONAL_ATTR(ViewBox, SVGViewBoxType) - Size intrinsicSize(const SVGLengthContext&) const; + Size intrinsicSize(const SVGLengthContext& lengthContext) const; - void renderNode(const SVGRenderContext&, const SVGIRI& iri) const; + void renderNode(const SVGRenderContext& context, const SVGIRI& iri) const; protected: - bool onPrepareToRender(SVGRenderContext*) const override; + bool onPrepareToRender(SVGRenderContext* context) const override; - void onSetAttribute(SVGAttribute, const SVGValue&) override; + void onSetAttribute(SVGAttribute attribute, const SVGValue& value) override; private: explicit SVGSVG(Type t) : INHERITED(SVGTag::Svg), type(t) { diff --git a/include/tgfx/svg/node/SVGShape.h b/include/tgfx/svg/node/SVGShape.h index f2cfec42..2162f6d9 100644 --- a/include/tgfx/svg/node/SVGShape.h +++ b/include/tgfx/svg/node/SVGShape.h @@ -32,14 +32,15 @@ enum class SVGTag; class SVGShape : public SVGTransformableNode { public: - void appendChild(std::shared_ptr) override; + void appendChild(std::shared_ptr node) override; protected: - explicit SVGShape(SVGTag); + explicit SVGShape(SVGTag tag); - void onRender(const SVGRenderContext&) const override; + void onRender(const SVGRenderContext& context) const override; - virtual void onDraw(Canvas*, const SVGLengthContext&, const Paint&, PathFillType) const = 0; + virtual void onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType fillType) const = 0; private: using INHERITED = SVGTransformableNode; diff --git a/include/tgfx/svg/node/SVGStop.h b/include/tgfx/svg/node/SVGStop.h index d9a07508..e82ce40e 100644 --- a/include/tgfx/svg/node/SVGStop.h +++ b/include/tgfx/svg/node/SVGStop.h @@ -36,7 +36,7 @@ class SVGStop : public SVGHiddenContainer { SVG_ATTR(Offset, SVGLength, SVGLength(0, SVGLength::Unit::Percentage)) protected: - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; private: SVGStop(); diff --git a/include/tgfx/svg/node/SVGText.h b/include/tgfx/svg/node/SVGText.h index 9027c278..f994ab5f 100644 --- a/include/tgfx/svg/node/SVGText.h +++ b/include/tgfx/svg/node/SVGText.h @@ -37,20 +37,21 @@ using ShapedTextCallback = // Base class for text-rendering nodes. class SVGTextFragment : public SVGTransformableNode { public: - void renderText(const SVGRenderContext&, const ShapedTextCallback&) const; + void renderText(const SVGRenderContext& context, const ShapedTextCallback& function) const; protected: explicit SVGTextFragment(SVGTag t) : INHERITED(t) { } - virtual void onShapeText(const SVGRenderContext&, const ShapedTextCallback&) const = 0; + virtual void onShapeText(const SVGRenderContext& context, + const ShapedTextCallback& function) const = 0; // Text nodes other than the root element are not rendered directly. - void onRender(const SVGRenderContext&) const override { + void onRender(const SVGRenderContext& /*context*/) const override { } private: - Path onAsPath(const SVGRenderContext&) const override; + Path onAsPath(const SVGRenderContext& context) const override; using INHERITED = SVGTransformableNode; }; @@ -64,15 +65,16 @@ class SVGTextContainer : public SVGTextFragment { SVG_ATTR(Dy, std::vector, {}) SVG_ATTR(Rotate, std::vector, {}) - void appendChild(std::shared_ptr) final; + void appendChild(std::shared_ptr node) final; protected: explicit SVGTextContainer(SVGTag t) : INHERITED(t) { } - void onShapeText(const SVGRenderContext&, const ShapedTextCallback&) const override; + void onShapeText(const SVGRenderContext& context, + const ShapedTextCallback& function) const override; - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; private: std::vector> children; @@ -90,9 +92,9 @@ class SVGText final : public SVGTextContainer { SVGText() : INHERITED(SVGTag::Text) { } - void onRender(const SVGRenderContext&) const override; - Rect onObjectBoundingBox(const SVGRenderContext&) const override; - Path onAsPath(const SVGRenderContext&) const override; + void onRender(const SVGRenderContext& context) const override; + Rect onObjectBoundingBox(const SVGRenderContext& context) const override; + Path onAsPath(const SVGRenderContext& context) const override; using INHERITED = SVGTextContainer; }; @@ -122,9 +124,10 @@ class SVGTextLiteral final : public SVGTextFragment { SVGTextLiteral() : INHERITED(SVGTag::TextLiteral) { } - void onShapeText(const SVGRenderContext&, const ShapedTextCallback&) const override; + void onShapeText(const SVGRenderContext& context, + const ShapedTextCallback& function) const override; - void appendChild(std::shared_ptr) override { + void appendChild(std::shared_ptr /*node*/) override { } using INHERITED = SVGTextFragment; @@ -143,9 +146,10 @@ class SVGTextPath final : public SVGTextContainer { SVGTextPath() : INHERITED(SVGTag::TextPath) { } - void onShapeText(const SVGRenderContext&, const ShapedTextCallback&) const override; + void onShapeText(const SVGRenderContext& context, + const ShapedTextCallback& function) const override; - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; using INHERITED = SVGTextContainer; }; diff --git a/include/tgfx/svg/node/SVGTransformableNode.h b/include/tgfx/svg/node/SVGTransformableNode.h index 2da2bb95..f95eaac4 100644 --- a/include/tgfx/svg/node/SVGTransformableNode.h +++ b/include/tgfx/svg/node/SVGTransformableNode.h @@ -36,15 +36,15 @@ class SVGTransformableNode : public SVGNode { } protected: - SVGTransformableNode(SVGTag); + SVGTransformableNode(SVGTag tag); - bool onPrepareToRender(SVGRenderContext*) const override; + bool onPrepareToRender(SVGRenderContext* context) const override; - void onSetAttribute(SVGAttribute, const SVGValue&) override; + void onSetAttribute(SVGAttribute attribute, const SVGValue& value) override; - void mapToParent(Path*) const; + void mapToParent(Path* path) const; - void mapToParent(Rect*) const; + void mapToParent(Rect* rect) const; private: // FIXME: should be sparse diff --git a/include/tgfx/svg/node/SVGUse.h b/include/tgfx/svg/node/SVGUse.h index ce04c083..a9766ce0 100644 --- a/include/tgfx/svg/node/SVGUse.h +++ b/include/tgfx/svg/node/SVGUse.h @@ -46,15 +46,15 @@ class SVGUse final : public SVGTransformableNode { SVG_ATTR(Href, SVGIRI, SVGIRI()) protected: - bool onPrepareToRender(SVGRenderContext*) const override; - void onRender(const SVGRenderContext&) const override; - Path onAsPath(const SVGRenderContext&) const override; - Rect onObjectBoundingBox(const SVGRenderContext&) const override; + bool onPrepareToRender(SVGRenderContext* context) const override; + void onRender(const SVGRenderContext& context) const override; + Path onAsPath(const SVGRenderContext& context) const override; + Rect onObjectBoundingBox(const SVGRenderContext& context) const override; private: SVGUse(); - bool parseAndSetAttribute(const std::string&, const std::string&) override; + bool parseAndSetAttribute(const std::string& name, const std::string& value) override; using INHERITED = SVGTransformableNode; }; diff --git a/src/svg/SVGFilterContext.cpp b/src/svg/SVGFilterContext.cpp index f53cba62..5c70fff0 100644 --- a/src/svg/SVGFilterContext.cpp +++ b/src/svg/SVGFilterContext.cpp @@ -27,8 +27,8 @@ namespace tgfx { -const SVGFilterContext::Result* SVGFilterContext::findResultById(const SVGStringType& id) const { - auto iter = results.find(id); +const SVGFilterContext::Result* SVGFilterContext::findResultById(const SVGStringType& ID) const { + auto iter = results.find(ID); return iter != results.end() ? &iter->second : nullptr; } @@ -43,11 +43,11 @@ const Rect& SVGFilterContext::filterPrimitiveSubregion(const SVGFeInputType& inp return res ? res->filterSubregion : _filterEffectsRegion; } -void SVGFilterContext::registerResult(const SVGStringType& id, +void SVGFilterContext::registerResult(const SVGStringType& ID, const std::shared_ptr& result, const Rect& subregion, SVGColorspace resultColorspace) { - ASSERT(!id.empty()); - results[id] = {result, subregion, resultColorspace}; + ASSERT(!ID.empty()); + results[ID] = {result, subregion, resultColorspace}; } void SVGFilterContext::setPreviousResult(const std::shared_ptr& result, @@ -118,9 +118,9 @@ std::shared_ptr SVGFilterContext::resolveInput(const SVGRenderConte return std::get<0>(this->getInput(context, inputType)); } -std::shared_ptr SVGFilterContext::resolveInput(const SVGRenderContext&, - const SVGFeInputType&, - SVGColorspace) const { +std::shared_ptr SVGFilterContext::resolveInput( + const SVGRenderContext& /*context*/, const SVGFeInputType& /*inputType*/, + SVGColorspace /*resultColorspace*/) const { //TODO (YGAurora) - ConvertFilterColorspace return nullptr; } diff --git a/src/svg/SVGFilterContext.h b/src/svg/SVGFilterContext.h index 3ba1d042..7350f4ee 100644 --- a/src/svg/SVGFilterContext.h +++ b/src/svg/SVGFilterContext.h @@ -38,25 +38,29 @@ class SVGFilterContext { return _filterEffectsRegion; } - const Rect& filterPrimitiveSubregion(const SVGFeInputType&) const; + const Rect& filterPrimitiveSubregion(const SVGFeInputType& input) const; const SVGObjectBoundingBoxUnits& primitiveUnits() const { return _primitiveUnits; } - void registerResult(const SVGStringType&, const std::shared_ptr&, const Rect&, - SVGColorspace); + void registerResult(const SVGStringType& ID, const std::shared_ptr& result, + const Rect& subregion, SVGColorspace resultColorspace); - void setPreviousResult(const std::shared_ptr&, const Rect&, SVGColorspace); + void setPreviousResult(const std::shared_ptr& result, const Rect& subregion, + SVGColorspace resultColorspace); bool previousResultIsSourceGraphic() const; - SVGColorspace resolveInputColorspace(const SVGRenderContext&, const SVGFeInputType&) const; + SVGColorspace resolveInputColorspace(const SVGRenderContext& context, + const SVGFeInputType& inputType) const; - std::shared_ptr resolveInput(const SVGRenderContext&, const SVGFeInputType&) const; + std::shared_ptr resolveInput(const SVGRenderContext& context, + const SVGFeInputType& inputType) const; - std::shared_ptr resolveInput(const SVGRenderContext&, const SVGFeInputType&, - SVGColorspace) const; + std::shared_ptr resolveInput(const SVGRenderContext& context, + const SVGFeInputType& inputType, + SVGColorspace resultColorspace) const; private: struct Result { @@ -65,10 +69,10 @@ class SVGFilterContext { SVGColorspace colorspace; }; - const Result* findResultById(const SVGStringType&) const; + const Result* findResultById(const SVGStringType& ID) const; - std::tuple, SVGColorspace> getInput(const SVGRenderContext&, - const SVGFeInputType&) const; + std::tuple, SVGColorspace> getInput( + const SVGRenderContext& context, const SVGFeInputType& inputType) const; Rect _filterEffectsRegion; diff --git a/src/svg/node/SVGCircle.cpp b/src/svg/node/SVGCircle.cpp index a094ab5a..af05f265 100644 --- a/src/svg/node/SVGCircle.cpp +++ b/src/svg/node/SVGCircle.cpp @@ -29,11 +29,11 @@ namespace tgfx { SVGCircle::SVGCircle() : INHERITED(SVGTag::Circle) { } -bool SVGCircle::parseAndSetAttribute(const std::string& n, const std::string& v) { - return INHERITED::parseAndSetAttribute(n, v) || - this->setCx(SVGAttributeParser::parse("cx", n, v)) || - this->setCy(SVGAttributeParser::parse("cy", n, v)) || - this->setR(SVGAttributeParser::parse("r", n, v)); +bool SVGCircle::parseAndSetAttribute(const std::string& name, const std::string& value) { + return INHERITED::parseAndSetAttribute(name, value) || + this->setCx(SVGAttributeParser::parse("cx", name, value)) || + this->setCy(SVGAttributeParser::parse("cy", name, value)) || + this->setR(SVGAttributeParser::parse("r", name, value)); } std::tuple SVGCircle::resolve(const SVGLengthContext& lengthContext) const { @@ -45,7 +45,7 @@ std::tuple SVGCircle::resolve(const SVGLengthContext& lengthContex } void SVGCircle::onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, - PathFillType) const { + PathFillType /*type*/) const { auto [pos, r] = this->resolve(lengthContext); if (r > 0) { diff --git a/src/svg/node/SVGClipPath.cpp b/src/svg/node/SVGClipPath.cpp index 85c8de49..d5d19eb8 100644 --- a/src/svg/node/SVGClipPath.cpp +++ b/src/svg/node/SVGClipPath.cpp @@ -27,10 +27,10 @@ namespace tgfx { SVGClipPath::SVGClipPath() : INHERITED(SVGTag::ClipPath) { } -bool SVGClipPath::parseAndSetAttribute(const std::string& n, const std::string& v) { - return INHERITED::parseAndSetAttribute(n, v) || +bool SVGClipPath::parseAndSetAttribute(const std::string& name, const std::string& value) { + return INHERITED::parseAndSetAttribute(name, value) || this->setClipPathUnits( - SVGAttributeParser::parse("clipPathUnits", n, v)); + SVGAttributeParser::parse("clipPathUnits", name, value)); } Path SVGClipPath::resolveClip(const SVGRenderContext& context) const { diff --git a/src/svg/node/SVGContainer.cpp b/src/svg/node/SVGContainer.cpp index 6c973c9d..8ed9bfc7 100644 --- a/src/svg/node/SVGContainer.cpp +++ b/src/svg/node/SVGContainer.cpp @@ -52,8 +52,8 @@ void SVGContainer::onRender(const SVGRenderContext& context) const { Path SVGContainer::onAsPath(const SVGRenderContext& context) const { Path path; - for (const auto& i : children) { - const Path childPath = i->asPath(context); + for (const auto& child : children) { + const Path childPath = child->asPath(context); path.addPath(childPath, PathOp::Union); } @@ -64,8 +64,8 @@ Path SVGContainer::onAsPath(const SVGRenderContext& context) const { Rect SVGContainer::onObjectBoundingBox(const SVGRenderContext& context) const { Rect bounds = Rect::MakeEmpty(); - for (const auto& i : children) { - const Rect childBounds = i->objectBoundingBox(context); + for (const auto& child : children) { + const Rect childBounds = child->objectBoundingBox(context); bounds.join(childBounds); } diff --git a/src/svg/node/SVGEllipse.cpp b/src/svg/node/SVGEllipse.cpp index a092bd83..58a6bc34 100644 --- a/src/svg/node/SVGEllipse.cpp +++ b/src/svg/node/SVGEllipse.cpp @@ -27,12 +27,12 @@ namespace tgfx { SVGEllipse::SVGEllipse() : INHERITED(SVGTag::Ellipse) { } -bool SVGEllipse::parseAndSetAttribute(const std::string& n, const std::string& v) { - return INHERITED::parseAndSetAttribute(n, v) || - this->setCx(SVGAttributeParser::parse("cx", n, v)) || - this->setCy(SVGAttributeParser::parse("cy", n, v)) || - this->setRx(SVGAttributeParser::parse("rx", n, v)) || - this->setRy(SVGAttributeParser::parse("ry", n, v)); +bool SVGEllipse::parseAndSetAttribute(const std::string& name, const std::string& value) { + return INHERITED::parseAndSetAttribute(name, value) || + this->setCx(SVGAttributeParser::parse("cx", name, value)) || + this->setCy(SVGAttributeParser::parse("cy", name, value)) || + this->setRx(SVGAttributeParser::parse("rx", name, value)) || + this->setRy(SVGAttributeParser::parse("ry", name, value)); } Rect SVGEllipse::resolve(const SVGLengthContext& lengthContext) const { @@ -51,7 +51,7 @@ Rect SVGEllipse::resolve(const SVGLengthContext& lengthContext) const { } void SVGEllipse::onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, - PathFillType) const { + PathFillType /*fillType*/) const { canvas->drawOval(this->resolve(lengthContext), paint); } diff --git a/src/svg/node/SVGFe.cpp b/src/svg/node/SVGFe.cpp index aff64990..84d4869b 100644 --- a/src/svg/node/SVGFe.cpp +++ b/src/svg/node/SVGFe.cpp @@ -120,7 +120,7 @@ bool SVGFe::parseAndSetAttribute(const std::string& name, const std::string& val template <> bool SVGAttributeParser::parse(SVGFeInputType* type) { - static constexpr std::tuple gTypeMap[] = { + static constexpr std::tuple typeMap[] = { {"SourceGraphic", SVGFeInputType::Type::SourceGraphic}, {"SourceAlpha", SVGFeInputType::Type::SourceAlpha}, {"BackgroundImage", SVGFeInputType::Type::BackgroundImage}, @@ -132,12 +132,12 @@ bool SVGAttributeParser::parse(SVGFeInputType* type) { SVGStringType resultId; SVGFeInputType::Type tempType; bool parsedValue = false; - if (this->parseEnumMap(gTypeMap, &tempType)) { - *type = SVGFeInputType(tempType); - parsedValue = true; + if (this->parseEnumMap(typeMap, &tempType)) { + *type = SVGFeInputType(tempType); + parsedValue = true; } else if (parse(&resultId)) { - *type = SVGFeInputType(resultId); - parsedValue = true; + *type = SVGFeInputType(resultId); + parsedValue = true; } return parsedValue && this->parseEOSToken(); diff --git a/src/svg/node/SVGFeBlend.cpp b/src/svg/node/SVGFeBlend.cpp index b7ea1852..3f8b494a 100644 --- a/src/svg/node/SVGFeBlend.cpp +++ b/src/svg/node/SVGFeBlend.cpp @@ -44,12 +44,12 @@ std::shared_ptr SVGFeBlend::onMakeImageFilter( template <> bool SVGAttributeParser::parse(SVGFeBlend::Mode* mode) { - static constexpr std::tuple gMap[] = { - {"normal", SVGFeBlend::Mode::kNormal}, {"multiply", SVGFeBlend::Mode::kMultiply}, - {"screen", SVGFeBlend::Mode::kScreen}, {"darken", SVGFeBlend::Mode::kDarken}, - {"lighten", SVGFeBlend::Mode::kLighten}, + static constexpr std::tuple blendMap[] = { + {"normal", SVGFeBlend::Mode::Normal}, {"multiply", SVGFeBlend::Mode::Multiply}, + {"screen", SVGFeBlend::Mode::Screen}, {"darken", SVGFeBlend::Mode::Darken}, + {"lighten", SVGFeBlend::Mode::Lighten}, }; - return this->parseEnumMap(gMap, mode) && this->parseEOSToken(); + return this->parseEnumMap(blendMap, mode) && this->parseEOSToken(); } } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeColorMatrix.cpp b/src/svg/node/SVGFeColorMatrix.cpp index f7f950dc..029c22e3 100644 --- a/src/svg/node/SVGFeColorMatrix.cpp +++ b/src/svg/node/SVGFeColorMatrix.cpp @@ -35,14 +35,14 @@ bool SVGFeColorMatrix::parseAndSetAttribute(const std::string& name, const std:: template <> bool SVGAttributeParser::parse(SVGFeColorMatrixType* type) { - static constexpr std::tuple gTypeMap[] = { + static constexpr std::tuple typeMap[] = { {"matrix", SVGFeColorMatrixType::Matrix}, {"saturate", SVGFeColorMatrixType::Saturate}, {"hueRotate", SVGFeColorMatrixType::HueRotate}, {"luminanceToAlpha", SVGFeColorMatrixType::LuminanceToAlpha}, }; - return this->parseEnumMap(gTypeMap, type) && this->parseEOSToken(); + return this->parseEnumMap(typeMap, type) && this->parseEOSToken(); } ColorMatrix SVGFeColorMatrix::makeMatrixForType() const { @@ -70,20 +70,20 @@ ColorMatrix SVGFeColorMatrix::makeMatrixForType() const { ColorMatrix SVGFeColorMatrix::MakeSaturate(SVGNumberType sat) { enum { - kR_Scale = 0, - kG_Scale = 6, - kB_Scale = 12, - kA_Scale = 18, - - kR_Trans = 4, - kG_Trans = 9, - kB_Trans = 14, - kA_Trans = 19, + R_Scale = 0, + G_Scale = 6, + B_Scale = 12, + A_Scale = 18, + + R_Trans = 4, + G_Trans = 9, + B_Trans = 14, + A_Trans = 19, }; - static const float kHueR = 0.213f; - static const float kHueG = 0.715f; - static const float kHueB = 0.072f; + constexpr float HueR = 0.213f; + constexpr float HueG = 0.715f; + constexpr float HueB = 0.072f; auto setrow = [](float row[], float r, float g, float b) { row[0] = r; @@ -92,23 +92,22 @@ ColorMatrix SVGFeColorMatrix::MakeSaturate(SVGNumberType sat) { }; ColorMatrix matrix; - // m.setSaturation(s); matrix.fill(0.0f); - const float R = kHueR * (1 - sat); - const float G = kHueG * (1 - sat); - const float B = kHueB * (1 - sat); + float R = HueR * (1 - sat); + float G = HueG * (1 - sat); + float B = HueB * (1 - sat); setrow(matrix.data() + 0, R + sat, G, B); setrow(matrix.data() + 5, R, G + sat, B); setrow(matrix.data() + 10, R, G, B + sat); - matrix[kA_Scale] = 1; + matrix[A_Scale] = 1; return matrix; } ColorMatrix SVGFeColorMatrix::MakeHueRotate(SVGNumberType degrees) { - const float theta = DegreesToRadians(degrees); - const SVGNumberType c = std::cos(theta); - const SVGNumberType s = std::sin(theta); + float theta = DegreesToRadians(degrees); + SVGNumberType c = std::cos(theta); + SVGNumberType s = std::sin(theta); return ColorMatrix{0.213f + c * 0.787f + s * -0.213f, 0.715f + c * -0.715f + s * -0.715f, 0.072f + c * -0.072f + s * 0.928f, diff --git a/src/svg/node/SVGFeComposite.cpp b/src/svg/node/SVGFeComposite.cpp index fc70e387..5e3eaf28 100644 --- a/src/svg/node/SVGFeComposite.cpp +++ b/src/svg/node/SVGFeComposite.cpp @@ -20,6 +20,9 @@ #include #include "core/utils/Log.h" #include "svg/SVGAttributeParser.h" +#include "svg/SVGFilterContext.h" +#include "tgfx/core/ImageFilter.h" +#include "tgfx/core/Rect.h" namespace tgfx { @@ -55,9 +58,9 @@ BlendMode SVGFeComposite::BlendModeForOperator(SVGFeCompositeOperator op) { } } -std::shared_ptr SVGFeComposite::onMakeImageFilter(const SVGRenderContext&, - const SVGFilterContext&) const { - //TODO (YGAurora) Implement this by filter blending +std::shared_ptr SVGFeComposite::onMakeImageFilter( + const SVGRenderContext& /*context*/, const SVGFilterContext& /*filterContext*/) const { + //TODO (YGAurora) waiting for blend and arithmetic image filter implementation return nullptr; } diff --git a/src/svg/node/SVGFeGaussianBlur.cpp b/src/svg/node/SVGFeGaussianBlur.cpp index a95dd98c..9ce7702b 100644 --- a/src/svg/node/SVGFeGaussianBlur.cpp +++ b/src/svg/node/SVGFeGaussianBlur.cpp @@ -34,8 +34,8 @@ bool SVGFeGaussianBlur::parseAndSetAttribute(const std::string& name, const std: std::shared_ptr SVGFeGaussianBlur::onMakeImageFilter( const SVGRenderContext& context, const SVGFilterContext& filterContext) const { auto scale = context.transformForCurrentBoundBox(filterContext.primitiveUnits()).scale; - const auto sigmaX = stdDeviation.fX * scale.x * 4; - const auto sigmaY = stdDeviation.fY * scale.y * 4; + const auto sigmaX = stdDeviation.X * scale.x * 4; + const auto sigmaY = stdDeviation.Y * scale.y * 4; return ImageFilter::Blur(sigmaX, sigmaY); } @@ -47,8 +47,8 @@ bool SVGAttributeParser::parse( return false; } - stdDeviation->fX = values[0]; - stdDeviation->fY = values.size() > 1 ? values[1] : values[0]; + stdDeviation->X = values[0]; + stdDeviation->Y = values.size() > 1 ? values[1] : values[0]; return true; } } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeLighting.cpp b/src/svg/node/SVGFeLighting.cpp index d0966c58..54c41846 100644 --- a/src/svg/node/SVGFeLighting.cpp +++ b/src/svg/node/SVGFeLighting.cpp @@ -37,8 +37,8 @@ bool SVGAttributeParser::parse( return false; } - kernelUnitLength->fDx = values[0]; - kernelUnitLength->fDy = values.size() > 1 ? values[1] : values[0]; + kernelUnitLength->Dx = values[0]; + kernelUnitLength->Dy = values.size() > 1 ? values[1] : values[0]; return true; } diff --git a/src/svg/node/SVGFeOffset.cpp b/src/svg/node/SVGFeOffset.cpp index 06d2c6bd..026427ff 100644 --- a/src/svg/node/SVGFeOffset.cpp +++ b/src/svg/node/SVGFeOffset.cpp @@ -28,9 +28,9 @@ bool SVGFeOffset::parseAndSetAttribute(const std::string& name, const std::strin this->setDy(SVGAttributeParser::parse("dy", name, value)); } -std::shared_ptr SVGFeOffset::onMakeImageFilter(const SVGRenderContext&, - const SVGFilterContext&) const { - //TODO (YGAurora) +std::shared_ptr SVGFeOffset::onMakeImageFilter( + const SVGRenderContext& /*context*/, const SVGFilterContext& /*filterContext*/) const { + //TODO (YGaurora) offset filter not implemented return nullptr; } diff --git a/src/svg/node/SVGImage.cpp b/src/svg/node/SVGImage.cpp index c986af1d..f4fd7e2f 100644 --- a/src/svg/node/SVGImage.cpp +++ b/src/svg/node/SVGImage.cpp @@ -59,7 +59,7 @@ std::shared_ptr LoadImage(const SVGIRI& href) { } SVGImage::ImageInfo SVGImage::LoadImage(const SVGIRI& iri, const Rect& viewPort, - SVGPreserveAspectRatio /*par*/) { + SVGPreserveAspectRatio /*ratio*/) { std::shared_ptr image = ::tgfx::LoadImage(iri); if (!image) { return {}; @@ -69,8 +69,6 @@ SVGImage::ImageInfo SVGImage::LoadImage(const SVGIRI& iri, const Rect& viewPort, const Rect viewBox = Rect::MakeWH(image->width(), image->height()); // Map and place at x, y specified by viewport - // const Matrix m = ComputeViewboxMatrix(viewBox, viewPort, par); - // const Rect dst = m.mapRect(viewBox).makeOffset(viewPort.left, viewPort.top); Rect dst = viewBox.makeOffset(viewPort.left, viewPort.top); return {std::move(image), dst}; @@ -83,16 +81,16 @@ void SVGImage::onRender(const SVGRenderContext& context) const { ImageInfo image; const auto imgInfo = LoadImage(Href, viewPort, PreserveAspectRatio); - if (!imgInfo.fImage) { + if (!imgInfo.image) { LOGE("can't render image: load image failed\n"); return; } - auto matrix = Matrix::MakeScale(viewPort.width() / imgInfo.fDst.width(), - viewPort.height() / imgInfo.fDst.height()); - matrix.preTranslate(imgInfo.fDst.x(), imgInfo.fDst.y()); + auto matrix = Matrix::MakeScale(viewPort.width() / imgInfo.destinationRect.width(), + viewPort.height() / imgInfo.destinationRect.height()); + matrix.preTranslate(imgInfo.destinationRect.x(), imgInfo.destinationRect.y()); - context.canvas()->drawImage(imgInfo.fImage, matrix); + context.canvas()->drawImage(imgInfo.image, matrix); } Path SVGImage::onAsPath(const SVGRenderContext&) const { diff --git a/src/svg/node/SVGLinearGradient.cpp b/src/svg/node/SVGLinearGradient.cpp index a8aa2d54..f69eb1f0 100644 --- a/src/svg/node/SVGLinearGradient.cpp +++ b/src/svg/node/SVGLinearGradient.cpp @@ -40,7 +40,8 @@ bool SVGLinearGradient::parseAndSetAttribute(const std::string& name, const std: std::shared_ptr SVGLinearGradient::onMakeShader(const SVGRenderContext& context, const std::vector& colors, const std::vector& positions, - TileMode, const Matrix&) const { + TileMode /*tileMode*/, + const Matrix& /*localMatrix*/) const { SVGLengthContext lengthContext = context.lengthContext(); lengthContext.setPatternUnits(getGradientUnits()); From bccd2c7aad15152732667c7c5951966e63d2b9c5 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Fri, 3 Jan 2025 14:26:57 +0800 Subject: [PATCH 25/31] typo --- include/tgfx/svg/node/SVGTransformableNode.h | 5 ++--- qt/src/main.cpp | 1 + src/svg/SVGFilterContext.cpp | 5 +++-- src/svg/SVGRenderContext.cpp | 1 - src/svg/node/SVGFeDisplacementMap.cpp | 2 +- src/svg/node/SVGFeMerge.cpp | 14 ++++++++++---- src/svg/node/SVGTransformableNode.cpp | 18 +++++++++--------- 7 files changed, 26 insertions(+), 20 deletions(-) diff --git a/include/tgfx/svg/node/SVGTransformableNode.h b/include/tgfx/svg/node/SVGTransformableNode.h index f95eaac4..5555cf97 100644 --- a/include/tgfx/svg/node/SVGTransformableNode.h +++ b/include/tgfx/svg/node/SVGTransformableNode.h @@ -32,7 +32,7 @@ enum class SVGAttribute; class SVGTransformableNode : public SVGNode { public: void setTransform(const SVGTransformType& t) { - fTransform = t; + transform = t; } protected: @@ -47,8 +47,7 @@ class SVGTransformableNode : public SVGNode { void mapToParent(Rect* rect) const; private: - // FIXME: should be sparse - SVGTransformType fTransform; + SVGTransformType transform; using INHERITED = SVGNode; }; diff --git a/qt/src/main.cpp b/qt/src/main.cpp index 2be61b83..2d2dc7de 100644 --- a/qt/src/main.cpp +++ b/qt/src/main.cpp @@ -27,6 +27,7 @@ #include "qobject.h" int main(int argc, char* argv[]) { + QApplication::setApplicationName("Hello2D"); QApplication::setOrganizationName("org.tgfx"); QSurfaceFormat defaultFormat = QSurfaceFormat(); diff --git a/src/svg/SVGFilterContext.cpp b/src/svg/SVGFilterContext.cpp index 5c70fff0..c93a2e65 100644 --- a/src/svg/SVGFilterContext.cpp +++ b/src/svg/SVGFilterContext.cpp @@ -119,9 +119,10 @@ std::shared_ptr SVGFilterContext::resolveInput(const SVGRenderConte } std::shared_ptr SVGFilterContext::resolveInput( - const SVGRenderContext& /*context*/, const SVGFeInputType& /*inputType*/, + const SVGRenderContext& context, const SVGFeInputType& inputType, SVGColorspace /*resultColorspace*/) const { - //TODO (YGAurora) - ConvertFilterColorspace + //TODO (YGAurora) - waiting for color space implementation + resolveInput(context, inputType); return nullptr; } diff --git a/src/svg/SVGRenderContext.cpp b/src/svg/SVGRenderContext.cpp index 3ce8176f..739e2fc6 100644 --- a/src/svg/SVGRenderContext.cpp +++ b/src/svg/SVGRenderContext.cpp @@ -386,7 +386,6 @@ std::optional SVGRenderContext::strokePaint() const { stroke.miterLimit = *props.StrokeMiterLimit; paint->setStroke(stroke); - //TODO (YGAurora): Implement stroke dash array use PathEffect dash_effect(props, *_lengthContext); } diff --git a/src/svg/node/SVGFeDisplacementMap.cpp b/src/svg/node/SVGFeDisplacementMap.cpp index 8039c665..b9975c22 100644 --- a/src/svg/node/SVGFeDisplacementMap.cpp +++ b/src/svg/node/SVGFeDisplacementMap.cpp @@ -37,7 +37,7 @@ bool SVGFeDisplacementMap::parseAndSetAttribute(const std::string& name, const s std::shared_ptr SVGFeDisplacementMap::onMakeImageFilter( const SVGRenderContext&, const SVGFilterContext&) const { - //TODO (YGAurora): Implement DisplacementMap image filter. + //TODO (YGAurora): waiting for displacementMap image filter. return nullptr; } diff --git a/src/svg/node/SVGFeMerge.cpp b/src/svg/node/SVGFeMerge.cpp index 8752a7d5..6d07e07f 100644 --- a/src/svg/node/SVGFeMerge.cpp +++ b/src/svg/node/SVGFeMerge.cpp @@ -17,7 +17,9 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGFeMerge.h" +#include #include "svg/SVGAttributeParser.h" +#include "svg/SVGFilterContext.h" #include "tgfx/core/ImageFilter.h" namespace tgfx { @@ -29,10 +31,14 @@ bool SVGFeMergeNode::parseAndSetAttribute(const std::string& name, const std::st this->setIn(SVGAttributeParser::parse("in", name, value)); } -std::shared_ptr SVGFeMerge::onMakeImageFilter(const SVGRenderContext&, - const SVGFilterContext&) const { - //TODO (YGAurora): Implement relying on ImageFilters::Merge to implement this - return nullptr; +std::shared_ptr SVGFeMerge::onMakeImageFilter( + const SVGRenderContext& context, const SVGFilterContext& filterContext) const { + std::vector> mergeNodeFilters(children.size()); + + this->forEachChild([&](const SVGFeMergeNode* child) { + mergeNodeFilters.push_back(filterContext.resolveInput(context, child->getIn())); + }); + return ImageFilter::Compose(mergeNodeFilters); } std::vector SVGFeMerge::getInputs() const { diff --git a/src/svg/node/SVGTransformableNode.cpp b/src/svg/node/SVGTransformableNode.cpp index b6dac013..7dee50a0 100644 --- a/src/svg/node/SVGTransformableNode.cpp +++ b/src/svg/node/SVGTransformableNode.cpp @@ -27,21 +27,21 @@ namespace tgfx { -SVGTransformableNode::SVGTransformableNode(SVGTag tag) : INHERITED(tag), fTransform(Matrix::I()) { +SVGTransformableNode::SVGTransformableNode(SVGTag tag) : INHERITED(tag), transform(Matrix::I()) { } bool SVGTransformableNode::onPrepareToRender(SVGRenderContext* context) const { - if (!fTransform.isIdentity()) { - auto transform = fTransform; + if (!transform.isIdentity()) { + auto tempTransform = transform; if (auto unit = context->lengthContext().getPatternUnits(); unit.has_value() && unit.value().type() == SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox) { - transform.postScale(context->lengthContext().viewPort().width, - context->lengthContext().viewPort().height); + tempTransform.postScale(context->lengthContext().viewPort().width, + context->lengthContext().viewPort().height); } context->saveOnce(); - context->canvas()->concat(transform); - context->concat(transform); + context->canvas()->concat(tempTransform); + context->concat(tempTransform); } return this->INHERITED::onPrepareToRender(context); @@ -62,11 +62,11 @@ void SVGTransformableNode::onSetAttribute(SVGAttribute attr, const SVGValue& v) void SVGTransformableNode::mapToParent(Path* path) const { // transforms the path to parent node coordinates. - path->transform(fTransform); + path->transform(transform); } void SVGTransformableNode::mapToParent(Rect* rect) const { - *rect = fTransform.mapRect(*rect); + *rect = transform.mapRect(*rect); } } // namespace tgfx \ No newline at end of file From 9f60098d5d8fe6ccd48b43b5d8bb69ef819cd26c Mon Sep 17 00:00:00 2001 From: YGauroa Date: Fri, 3 Jan 2025 14:34:03 +0800 Subject: [PATCH 26/31] typo --- src/svg/SVGAttributeParser.cpp | 3 ++- src/svg/node/SVGFe.cpp | 8 ++++---- src/svg/node/SVGFeMorphology.cpp | 2 +- src/svg/node/SVGFeTurbulence.cpp | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/svg/SVGAttributeParser.cpp b/src/svg/SVGAttributeParser.cpp index 10a9bd4a..1eedc867 100644 --- a/src/svg/SVGAttributeParser.cpp +++ b/src/svg/SVGAttributeParser.cpp @@ -228,7 +228,8 @@ bool SVGAttributeParser::parseIdentToken(std::string* ident) { bool SVGAttributeParser::parseLengthUnitToken(SVGLength::Unit& unit) { struct Unit { - Unit(std::string name, SVGLength::Unit u) : unitName(std::move(name)), unit(u) {}; + Unit(std::string name, SVGLength::Unit u) : unitName(std::move(name)), unit(u) { + } std::string unitName; SVGLength::Unit unit; }; diff --git a/src/svg/node/SVGFe.cpp b/src/svg/node/SVGFe.cpp index 84d4869b..d1eeef15 100644 --- a/src/svg/node/SVGFe.cpp +++ b/src/svg/node/SVGFe.cpp @@ -98,10 +98,10 @@ Rect SVGFe::resolveFilterSubregion(const SVGRenderContext& context, SVGColorspace SVGFe::resolveColorspace(const SVGRenderContext& context, const SVGFilterContext&) const { - constexpr SVGColorspace DEFAULT_COLOR_SPACE = SVGColorspace::SRGB; - const SVGColorspace colorspace = - *context.presentationContext()._inherited.ColorInterpolationFilters; - return colorspace == SVGColorspace::Auto ? DEFAULT_COLOR_SPACE : colorspace; + constexpr SVGColorspace DEFAULT_COLOR_SPACE = SVGColorspace::SRGB; + const SVGColorspace colorspace = + *context.presentationContext()._inherited.ColorInterpolationFilters; + return colorspace == SVGColorspace::Auto ? DEFAULT_COLOR_SPACE : colorspace; } void SVGFe::applyProperties(SVGRenderContext* context) const { diff --git a/src/svg/node/SVGFeMorphology.cpp b/src/svg/node/SVGFeMorphology.cpp index de3a7940..b24f5cd0 100644 --- a/src/svg/node/SVGFeMorphology.cpp +++ b/src/svg/node/SVGFeMorphology.cpp @@ -32,7 +32,7 @@ bool SVGFeMorphology::parseAndSetAttribute(const std::string& name, const std::s std::shared_ptr SVGFeMorphology::onMakeImageFilter(const SVGRenderContext&, const SVGFilterContext&) const { - //TODO (YGAurora) + //TODO (YGAurora) waiting for morphology image filter. return nullptr; } diff --git a/src/svg/node/SVGFeTurbulence.cpp b/src/svg/node/SVGFeTurbulence.cpp index 157aa895..d4cd2274 100644 --- a/src/svg/node/SVGFeTurbulence.cpp +++ b/src/svg/node/SVGFeTurbulence.cpp @@ -57,7 +57,7 @@ bool SVGAttributeParser::parse(SVGFeTurbulenceType* type) { std::shared_ptr SVGFeTurbulence::onMakeImageFilter(const SVGRenderContext&, const SVGFilterContext&) const { - //TODO (YGAurora) + //TODO (YGAurora) waiting for turbulence image filter. return nullptr; } From 2beb3f7c90dd5a93b107913a01409dce285270b6 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Wed, 22 Jan 2025 15:35:58 +0800 Subject: [PATCH 27/31] SVG render --- include/tgfx/core/Font.h | 1 + include/tgfx/core/FontManager.h | 121 ++ include/tgfx/core/FontManagerCustom.h | 102 ++ include/tgfx/core/FontStyle.h | 111 ++ include/tgfx/core/LoadResourceProvider.h | 66 + include/tgfx/core/Stream.h | 13 + include/tgfx/core/Typeface.h | 20 + include/tgfx/svg/SVGDOM.h | 48 +- include/tgfx/svg/SVGFontManager.h | 114 -- include/tgfx/svg/SVGTypes.h | 47 + include/tgfx/svg/node/SVGCircle.h | 7 +- include/tgfx/svg/node/SVGEllipse.h | 7 +- include/tgfx/svg/node/SVGFilter.h | 7 + include/tgfx/svg/node/SVGImage.h | 4 +- include/tgfx/svg/node/SVGLine.h | 7 +- include/tgfx/svg/node/SVGMask.h | 1 + include/tgfx/svg/node/SVGPath.h | 7 +- include/tgfx/svg/node/SVGPoly.h | 7 +- include/tgfx/svg/node/SVGRect.h | 7 +- include/tgfx/svg/node/SVGShape.h | 9 +- include/tgfx/svg/node/SVGText.h | 2 +- include/tgfx/svg/shaper/Shaper.h | 164 +++ include/tgfx/svg/shaper/ShaperFactory.h | 44 + include/tgfx/svg/xml/XMLDOM.h | 7 +- resources/apitest/SVG/complex1.svg | 8 + resources/apitest/SVG/complex2.svg | 1 + resources/apitest/SVG/complex3.svg | 67 + resources/apitest/SVG/complex4.svg | 1 + resources/apitest/SVG/complex5.svg | 1502 ++++++++++++++++++++++ resources/apitest/SVG/complex6.svg | 163 +++ resources/apitest/SVG/complex7.svg | 264 ++++ src/core/FontManager.cpp | 213 +++ src/core/FontManagerCustom.cpp | 136 ++ src/core/LoadResourceProvider.cpp | 57 + src/core/utils/Stream.cpp | 58 + src/svg/ElementWriter.cpp | 1 - src/svg/SVGAttributeParser.cpp | 19 + src/svg/SVGDOM.cpp | 69 +- src/svg/SVGExportContext.cpp | 9 +- src/svg/SVGExportContext.h | 1 - src/svg/SVGFilterContext.cpp | 11 +- src/svg/SVGFontManager.cpp | 92 -- src/svg/SVGLengthContext.cpp | 24 +- src/svg/SVGNodeConstructor.cpp | 4 +- src/svg/SVGRenderContext.cpp | 218 ++-- src/svg/SVGRenderContext.h | 85 +- src/svg/SVGTextBuilder.h | 1 - src/svg/SVGTextContext.cpp | 328 +++++ src/svg/SVGTextContext.h | 217 ++++ src/svg/SVGUtils.cpp | 4 +- src/svg/SVGUtils.h | 2 +- src/svg/node/SVGCircle.cpp | 29 +- src/svg/node/SVGEllipse.cpp | 18 +- src/svg/node/SVGFeColorMatrix.cpp | 13 +- src/svg/node/SVGFeGaussianBlur.cpp | 4 +- src/svg/node/SVGFilter.cpp | 128 +- src/svg/node/SVGImage.cpp | 41 +- src/svg/node/SVGLine.cpp | 21 +- src/svg/node/SVGMask.cpp | 27 +- src/svg/node/SVGNode.cpp | 5 +- src/svg/node/SVGPath.cpp | 21 +- src/svg/node/SVGPoly.cpp | 27 +- src/svg/node/SVGRect.cpp | 33 +- src/svg/node/SVGShape.cpp | 20 +- src/svg/node/SVGText.cpp | 63 +- src/svg/shaper/Shaper.cpp | 130 ++ src/svg/shaper/ShaperFactory.cpp | 49 + src/svg/shaper/ShaperPrimitive.cpp | 199 +++ src/svg/shaper/ShaperPrimitive.h | 109 ++ src/svg/xml/XMLDOM.cpp | 4 +- src/svg/xml/XMLParser.cpp | 34 +- src/svg/xml/XMLParser.h | 7 +- test/baseline/version.json | 9 +- test/src/SVGRenderTest.cpp | 236 +++- 74 files changed, 5125 insertions(+), 580 deletions(-) create mode 100644 include/tgfx/core/FontManager.h create mode 100644 include/tgfx/core/FontManagerCustom.h create mode 100644 include/tgfx/core/FontStyle.h create mode 100644 include/tgfx/core/LoadResourceProvider.h delete mode 100644 include/tgfx/svg/SVGFontManager.h create mode 100644 include/tgfx/svg/shaper/Shaper.h create mode 100644 include/tgfx/svg/shaper/ShaperFactory.h create mode 100644 resources/apitest/SVG/complex1.svg create mode 100644 resources/apitest/SVG/complex2.svg create mode 100644 resources/apitest/SVG/complex3.svg create mode 100644 resources/apitest/SVG/complex4.svg create mode 100644 resources/apitest/SVG/complex5.svg create mode 100644 resources/apitest/SVG/complex6.svg create mode 100644 resources/apitest/SVG/complex7.svg create mode 100644 src/core/FontManager.cpp create mode 100644 src/core/FontManagerCustom.cpp create mode 100644 src/core/LoadResourceProvider.cpp delete mode 100644 src/svg/SVGFontManager.cpp create mode 100644 src/svg/SVGTextContext.cpp create mode 100644 src/svg/SVGTextContext.h create mode 100644 src/svg/shaper/Shaper.cpp create mode 100644 src/svg/shaper/ShaperFactory.cpp create mode 100644 src/svg/shaper/ShaperPrimitive.cpp create mode 100644 src/svg/shaper/ShaperPrimitive.h diff --git a/include/tgfx/core/Font.h b/include/tgfx/core/Font.h index 8b977880..47e26e9e 100644 --- a/include/tgfx/core/Font.h +++ b/include/tgfx/core/Font.h @@ -19,6 +19,7 @@ #pragma once #include "tgfx/core/FontMetrics.h" +#include "tgfx/core/FontStyle.h" #include "tgfx/core/Image.h" #include "tgfx/core/Path.h" #include "tgfx/core/Typeface.h" diff --git a/include/tgfx/core/FontManager.h b/include/tgfx/core/FontManager.h new file mode 100644 index 00000000..47d350a4 --- /dev/null +++ b/include/tgfx/core/FontManager.h @@ -0,0 +1,121 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include "tgfx/core/FontStyle.h" +#include "tgfx/core/Typeface.h" + +namespace tgfx { +/** + * A collection of font styles. There may be multiple typefaces corresponding to different font + * styles under the same family name. + * This is an abstract class that users need to implement. + */ +class FontStyleSet { + public: + virtual ~FontStyleSet() = default; + + /** + * Get the number of font styles + */ + virtual size_t count() = 0; + + /** + * Get the font style at the specified index + */ + virtual void getStyle(size_t index, FontStyle* style, std::string* name) = 0; + + /** + * Create a typeface for the specified font style index + */ + virtual std::shared_ptr createTypeface(size_t index) = 0; + + /** + * Match a typeface based on the font style + */ + virtual std::shared_ptr matchStyle(const FontStyle& style) = 0; + + /** + * Create an empty font style set + */ + static std::shared_ptr CreateEmpty(); + + protected: + std::shared_ptr matchStyleCSS3(const FontStyle& pattern); +}; + +/** + * FontManager provides functionality to enumerate Typefaces and match them based on FontStyle. + */ +class FontManager { + public: + /** + * Destructor for FontManager + */ + virtual ~FontManager() = default; + + /** + * Get the number of font families + */ + size_t countFamilies() const; + + /** + * Get the name of the font family at the given index + */ + std::string getFamilyName(size_t index) const; + + /** + * Create a set of font styles for the given family index + */ + std::shared_ptr createStyleSet(size_t index) const; + + /** + * Match a font family name and return a set of font styles + */ + std::shared_ptr matchFamily(const std::string& familyName) const; + + /** + * Match a font family name and style, and return the corresponding Typeface + */ + std::shared_ptr matchFamilyStyle(const std::string& familyName, FontStyle style) const; + + /** + * Match a font family name, style, character, and language, and return the corresponding Typeface + */ + std::shared_ptr matchFamilyStyleCharacter(const std::string& familyName, + FontStyle style, + const std::vector& bcp47s, + Unichar character) const; + + private: + virtual size_t onCountFamilies() const = 0; + virtual std::string onGetFamilyName(size_t index) const = 0; + virtual std::shared_ptr onCreateStyleSet(size_t index) const = 0; + virtual std::shared_ptr onMatchFamily(const std::string& familyName) const = 0; + virtual std::shared_ptr onMatchFamilyStyle(const std::string& familyName, + FontStyle style) const = 0; + virtual std::shared_ptr onMatchFamilyStyleCharacter( + const std::string& familyName, FontStyle style, const std::vector& bcp47s, + Unichar character) const = 0; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/core/FontManagerCustom.h b/include/tgfx/core/FontManagerCustom.h new file mode 100644 index 00000000..357f543a --- /dev/null +++ b/include/tgfx/core/FontManagerCustom.h @@ -0,0 +1,102 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include "tgfx/core/FontManager.h" +#include "tgfx/core/FontStyle.h" +#include "tgfx/core/Typeface.h" + +namespace tgfx { +/** + * A custom FontManager implementation for font style matching. Users only need to override the + * FontManagerCustom::FontLoader::loadFonts method. + * + * Example usage: + * class FontLoaderTest : public FontManagerCustom::FontLoader { + * public: + * void loadFonts(std::vector>& families) const override { + * auto family = std::make_shared("Noto Sans SC"); + * auto typeface = MakeTypeface("resources/font/NotoSansSC-Regular.otf"); + * typeface->setStyle(FontStyle::Normal()); + * family->appendTypeface(typeface); + * families.push_back(family); + * } + * }; + * + * int Sample() { + * FontLoaderTest loader; + * std::shared_ptr fontManager = std::make_shared(loader); + * SVGDOMOptions options; + * options.fontManager = fontManager; + * auto SVGDom = SVGDOM::Make(*stream, options); + * } + */ + +class FontStyleSetCustom : public FontStyleSet { + public: + explicit FontStyleSetCustom(std::string familyName); + + void appendTypeface(std::shared_ptr typeface); + + size_t count() override; + + void getStyle(size_t index, FontStyle* style, std::string* name) override; + + std::shared_ptr createTypeface(size_t index) override; + + std::shared_ptr matchStyle(const FontStyle& style) override; + + std::string getFamilyName(); + + private: + std::vector> styles; + std::string familyName; +}; + +class FontManagerCustom : public FontManager { + public: + class FontLoader { + public: + virtual ~FontLoader() = default; + virtual void loadFonts(std::vector>& families) const = 0; + }; + + explicit FontManagerCustom(const FontLoader& loader); + + protected: + size_t onCountFamilies() const override; + std::string onGetFamilyName(size_t index) const override; + std::shared_ptr onCreateStyleSet(size_t index) const override; + std::shared_ptr onMatchFamily(const std::string& familyName) const override; + std::shared_ptr onMatchFamilyStyle(const std::string& familyName, + FontStyle style) const override; + std::shared_ptr onMatchFamilyStyleCharacter(const std::string& familyName, + FontStyle style, + const std::vector& bcp47s, + Unichar character) const override; + + private: + std::vector> families; + std::shared_ptr defaultFamily; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/core/FontStyle.h b/include/tgfx/core/FontStyle.h new file mode 100644 index 00000000..60c63970 --- /dev/null +++ b/include/tgfx/core/FontStyle.h @@ -0,0 +1,111 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include + +namespace tgfx { + +/** + * Font style object + */ +class FontStyle { + public: + enum class Weight { + Invisible = 0, + Thin = 100, + ExtraLight = 200, + Light = 300, + Normal = 400, + Medium = 500, + SemiBold = 600, + Bold = 700, + ExtraBold = 800, + Black = 900, + ExtraBlack = 1000, + }; + + enum class Width { + UltraCondensed = 1, + ExtraCondensed = 2, + Condensed = 3, + SemiCondensed = 4, + Normal = 5, + SemiExpanded = 6, + Expanded = 7, + ExtraExpanded = 8, + UltraExpanded = 9, + }; + + enum class Slant { + Upright, + Italic, + Oblique, + }; + + constexpr FontStyle(Weight weight, Width width, Slant slant) + : value(static_cast(std::clamp(weight, Weight::Invisible, Weight::ExtraBlack)) + + (static_cast(std::clamp(width, Width::UltraCondensed, Width::UltraExpanded)) + << 16) + + (static_cast(std::clamp(slant, Slant::Upright, Slant::Oblique)) << 24)) { + } + + constexpr FontStyle() : FontStyle{Weight::Normal, Width::Normal, Slant::Upright} { + } + + bool operator==(const FontStyle& rhs) const { + return value == rhs.value; + } + + Weight weight() const { + return static_cast(value & 0xFFFF); + } + + Width width() const { + return static_cast((value >> 16) & 0xFF); + } + + Slant slant() const { + return static_cast((value >> 24) & 0xFF); + } + + static constexpr FontStyle Normal() { + return FontStyle(Weight::Normal, Width::Normal, Slant::Upright); + } + + static constexpr FontStyle Bold() { + return FontStyle(Weight::Bold, Width::Normal, Slant::Upright); + } + + static constexpr FontStyle Italic() { + return FontStyle(Weight::Normal, Width::Normal, Slant::Italic); + } + + static constexpr FontStyle BoldItalic() { + return FontStyle(Weight::Bold, Width::Normal, Slant::Italic); + } + + private: + uint32_t value; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/core/LoadResourceProvider.h b/include/tgfx/core/LoadResourceProvider.h new file mode 100644 index 00000000..ef3c6899 --- /dev/null +++ b/include/tgfx/core/LoadResourceProvider.h @@ -0,0 +1,66 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include "tgfx/core/Data.h" +#include "tgfx/core/Image.h" + +namespace tgfx { + +/** + * LoadResourceProvider is an interface for loading resources (e.g. images, font) from + * external sources. + */ +class LoadResourceProvider { + public: + /** + * Data is a wrapper for raw data. + */ + virtual ~LoadResourceProvider() = default; + + /** + * Creates an empty resource provider. + */ + static std::shared_ptr MakeEmpty(); + + /** + * Creates a file resource provider with the given base path. + */ +static std::shared_ptr MakeFileProvider(const std::string& basePath); + +/** + * Load a generic resource specified by |path| + |name|, and return as an Data object. + */ +virtual std::shared_ptr load(const std::string& /*resourcePath*/, + const std::string& /*resourceName*/) const { + return nullptr; + } + + /** + * Load an image asset specified by |path| + |name|, and returns the Image object. + */ + virtual std::shared_ptr loadImage(const std::string& /*resourcePath*/, + const std::string& /*resourceName*/) const { + return nullptr; + } +}; + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/core/Stream.h b/include/tgfx/core/Stream.h index b5f85935..12a50cca 100644 --- a/include/tgfx/core/Stream.h +++ b/include/tgfx/core/Stream.h @@ -20,6 +20,7 @@ #include #include +#include "tgfx/core/Data.h" namespace tgfx { /** @@ -35,6 +36,11 @@ class Stream { */ static std::unique_ptr MakeFromFile(const std::string& filePath); + /** + * Creates a stream from the specified data. Returns nullptr on failure. + */ + static std::unique_ptr MakeFromData(std::shared_ptr data); + /** * Returns the total length of the stream. If this cannot be done, returns 0. */ @@ -64,6 +70,13 @@ class Stream { * beginning after this call returns. */ virtual bool rewind() = 0; + + /** + * Returns the starting address for the data. If this cannot be done, returns nullptr. + */ + virtual const void* getMemoryBase() { + return nullptr; + } }; /** diff --git a/include/tgfx/core/Typeface.h b/include/tgfx/core/Typeface.h index 6c4bd6a5..ab7d46c6 100644 --- a/include/tgfx/core/Typeface.h +++ b/include/tgfx/core/Typeface.h @@ -22,6 +22,7 @@ #include #include #include "tgfx/core/Data.h" +#include "tgfx/core/FontStyle.h" namespace tgfx { /** @@ -139,12 +140,31 @@ class Typeface { */ virtual std::vector getGlyphToUnicodeMap() const; + /** + * Returns the font style object of this typeface. + * This method is used for font matching and text layout in SVG rendering. + */ + FontStyle getStyle() const { + return style; + } + + /** + * Sets the font style for this typeface. + * This method is used for font matching and text layout in SVG rendering. + */ + void setStyle(const FontStyle& value) { + style = value; + } + mutable std::mutex locker = {}; private: std::unordered_map> scalerContexts = {}; + FontStyle style = FontStyle::Normal(); friend class ScalerContext; friend class GlyphConverter; + friend class FontManager; + friend class Shaper; }; } // namespace tgfx diff --git a/include/tgfx/svg/SVGDOM.h b/include/tgfx/svg/SVGDOM.h index 07994f04..0e26d15d 100644 --- a/include/tgfx/svg/SVGDOM.h +++ b/include/tgfx/svg/SVGDOM.h @@ -21,16 +21,39 @@ #include #include "tgfx/core/Canvas.h" #include "tgfx/core/Data.h" +#include "tgfx/core/FontManager.h" +#include "tgfx/core/LoadResourceProvider.h" #include "tgfx/core/Picture.h" #include "tgfx/core/Size.h" -#include "tgfx/svg/SVGFontManager.h" +#include "tgfx/core/Stream.h" #include "tgfx/svg/node/SVGSVG.h" +#include "tgfx/svg/shaper/ShaperFactory.h" namespace tgfx { class SVGNode; using SVGIDMapper = std::unordered_map>; +/** + * Options for rendering an SVGDOM object. Users can customize fonts, image resources, and shape + * text. Implementations should be based on the abstract class interfaces. + */ +struct SVGDOMOptions { + /** + * If fontManager is null, text will not be rendered. + */ + std::shared_ptr fontManager = nullptr; + /** + * If resourceProvider is null, base64 image resources will still be parsed, but non-base64 images + * will be loaded from absolute paths. + */ + std::shared_ptr resourceProvider = nullptr; + /** + * If shaperFactory is null, right-to-left and language-specific text shaping will not be applied. + */ + std::shared_ptr shaperFactory = nullptr; +}; + /** * The SVGDOM class represents an SVG Document Object Model (DOM). It provides functionality to * traverse the SVG DOM tree and render the SVG. @@ -42,32 +65,22 @@ using SVGIDMapper = std::unordered_map>; * and child nodes, and then visit the child nodes. * * 2. Rendering the SVG: - * - The simplest way to render is by calling render(canvas,nullptr). If you need to render text - * with specific fonts or set the size of the SVG, you can use the following methods. - * - If text rendering is required, use collectRenderFonts() to gather the necessary typefaces. - * Traverse the typefaces collected by the fontManager and set the typeface objects. - * - Render the SVG using the render() method. If text rendering is needed, pass in the - * fontManager object. Otherwise, you can pass in nullptr. - * - To set the size of the SVG, use setContainerSize(). If not set, the size of the root SVG + * - Use setContainerSize() to set the size of the canvas. If not set, the dimensions of the root * node will be used. + * - Use render() to draw the SVG onto a canvas. */ class SVGDOM { public: /** - * Creates an SVGDOM object from the provided data. + * Creates an SVGDOM object from the provided stream. */ - static std::shared_ptr Make(const std::shared_ptr& data); + static std::shared_ptr Make(Stream& stream, SVGDOMOptions options = {}); /** * Returns the root SVG node. */ const std::shared_ptr& getRoot() const; - /** - * Collects the font families and styles used for rendering and saves them in the font manager. - */ - void collectRenderFonts(const std::shared_ptr& fontManager); - /** * Renders the SVG to the provided canvas. * @param canvas The canvas to render to. @@ -75,7 +88,7 @@ class SVGDOM { * this can be nullptr, and text will not be rendered. If no specific font is set, the default * font will be used for rendering. */ - void render(Canvas* canvas, const std::shared_ptr& fontManager = nullptr); + void render(Canvas* canvas); /** * Sets the size of the container that the SVG will be rendered into. @@ -91,10 +104,11 @@ class SVGDOM { /** * Construct a new SVGDOM object */ - SVGDOM(std::shared_ptr root, SVGIDMapper&& mapper); + SVGDOM(std::shared_ptr root, SVGDOMOptions options, SVGIDMapper&& mapper); const std::shared_ptr root = nullptr; const SVGIDMapper nodeIDMapper = {}; + const SVGDOMOptions options = {}; Size containerSize = {}; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGFontManager.h b/include/tgfx/svg/SVGFontManager.h deleted file mode 100644 index 1092bc5f..00000000 --- a/include/tgfx/svg/SVGFontManager.h +++ /dev/null @@ -1,114 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making tgfx available. -// -// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// unless required by applicable law or agreed to in writing, software distributed under the -// license is distributed on an "as is" basis, without warranties or conditions of any kind, -// either express or implied. see the license for the specific language governing permissions -// and limitations under the license. -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include -#include -#include -#include -#include "tgfx/core/Typeface.h" -#include "tgfx/svg/SVGTypes.h" -#include "tgfx/svg/node/SVGNode.h" -namespace tgfx { - -/** - * Information about SVG fonts, including weight and style (Oblique, Italic, Normal). - */ -class SVGFontInfo { - public: - struct Hash { - std::size_t operator()(const SVGFontInfo& info) const { - return std::hash()(static_cast(info._weight)) ^ - std::hash()(static_cast(info._style)); - } - }; - - SVGFontInfo(SVGFontWeight::Type weight, SVGFontStyle::Type style) - : _weight(weight), _style(style) { - } - - bool operator==(const SVGFontInfo& other) const { - return _weight == other._weight && _style == other._style; - } - - SVGFontWeight::Type weight() const { - return _weight; - } - SVGFontStyle::Type style() const { - return _style; - } - - private: - SVGFontWeight::Type _weight; - SVGFontStyle::Type _style; -}; - -/** - * Manages fonts for SVG rendering, using fontFamily and SVGFontInfo as keys to store Typeface - * objects. - */ -class SVGFontManager { - public: - /** - * Creates an SVGFontManager object with the default typeface. If the default typeface is nullptr, - * retrun nullptr. - */ - static std::shared_ptr Make(const std::shared_ptr& defaultTypeface); - - /** - * Destructor. - */ - ~SVGFontManager() = default; - - /** - * Adds a font style to the font manager. If the font family and style already exist, this method - * does nothing and returns false. - */ - bool setTypeface(const std::string& fontFamily, SVGFontInfo info, - const std::shared_ptr& typeface); - - /** - * Get the font families stored in the font manager. - */ - std::vector getFontFamilies() const; - - /** - * Get the font styles stored in the font manager for the specified font family. - */ - std::vector getFontInfos(const std::string& fontFamily) const; - - protected: - void addFontStyle(const std::string& fontFamily, SVGFontInfo info); - - std::shared_ptr getTypefaceForRendering(const std::string& fontFamily, - SVGFontInfo info) const; - - private: - explicit SVGFontManager(const std::shared_ptr& typeface); - - std::unordered_map, SVGFontInfo::Hash>> - typefaceMap; - std::shared_ptr defaultTypeface; - - friend class SVGRenderContext; - friend class SVGDOM; -}; - -} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGTypes.h b/include/tgfx/svg/SVGTypes.h index abb9f8d2..ec68e6e4 100644 --- a/include/tgfx/svg/SVGTypes.h +++ b/include/tgfx/svg/SVGTypes.h @@ -750,6 +750,19 @@ class SVGTextAnchor { return _type; } + float getAlignmentFactor() const { + switch (_type) { + case SVGTextAnchor::Type::Start: + return 0.0f; + case SVGTextAnchor::Type::Middle: + return -0.5f; + case SVGTextAnchor::Type::End: + return -1.0f; + case SVGTextAnchor::Type::Inherit: + return 0.0f; + } + } + private: Type _type = Type::Inherit; }; @@ -851,6 +864,11 @@ struct SVGFeTurbulenceType { } }; +enum class SVGXmlSpace { + Default, + Preserve, +}; + enum class SVGColorspace { Auto, SRGB, @@ -871,4 +889,33 @@ enum class SVGFeFuncType { Linear, Gamma, }; + +class SVGMaskType { + public: + enum class Type { + Luminance, + Alpha, + }; + + SVGMaskType() = default; + + explicit SVGMaskType(Type t) : _type(t) { + } + + bool operator==(const SVGMaskType& other) const { + return _type == other._type; + } + + bool operator!=(const SVGMaskType& other) const { + return !(*this == other); + } + + Type type() const { + return _type; + } + + private: + Type _type = Type::Luminance; +}; + } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/node/SVGCircle.h b/include/tgfx/svg/node/SVGCircle.h index 687f5230..d290a369 100644 --- a/include/tgfx/svg/node/SVGCircle.h +++ b/include/tgfx/svg/node/SVGCircle.h @@ -43,8 +43,11 @@ class SVGCircle final : public SVGShape { protected: bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - void onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, - PathFillType type) const override; + void onDrawFill(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType type) const override; + + void onDrawStroke(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType fillType, std::shared_ptr pathEffect) const override; Path onAsPath(const SVGRenderContext& context) const override; diff --git a/include/tgfx/svg/node/SVGEllipse.h b/include/tgfx/svg/node/SVGEllipse.h index ae20ae7b..e7f9536c 100644 --- a/include/tgfx/svg/node/SVGEllipse.h +++ b/include/tgfx/svg/node/SVGEllipse.h @@ -45,8 +45,11 @@ class SVGEllipse final : public SVGShape { protected: bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - void onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, - PathFillType fillType) const override; + void onDrawFill(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType fillType) const override; + + void onDrawStroke(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType fillType, std::shared_ptr pathEffect) const override; Path onAsPath(const SVGRenderContext& context) const override; diff --git a/include/tgfx/svg/node/SVGFilter.h b/include/tgfx/svg/node/SVGFilter.h index f8a0f61e..8b14184e 100644 --- a/include/tgfx/svg/node/SVGFilter.h +++ b/include/tgfx/svg/node/SVGFilter.h @@ -19,6 +19,7 @@ #pragma once #include +#include "svg/SVGFilterContext.h" #include "tgfx/core/ImageFilter.h" #include "tgfx/svg/SVGTypes.h" #include "tgfx/svg/node/SVGHiddenContainer.h" @@ -52,6 +53,12 @@ class SVGFilter final : public SVGHiddenContainer { bool parseAndSetAttribute(const std::string& name, const std::string& value) override; + std::shared_ptr buildDropShadowFilter(const SVGRenderContext& context, + const SVGFilterContext& filterContext) const; + + std::shared_ptr buildInnerShadowFilter(const SVGRenderContext& context, + const SVGFilterContext& filterContext) const; + using INHERITED = SVGHiddenContainer; }; } // namespace tgfx diff --git a/include/tgfx/svg/node/SVGImage.h b/include/tgfx/svg/node/SVGImage.h index 6ae3208a..a29d512f 100644 --- a/include/tgfx/svg/node/SVGImage.h +++ b/include/tgfx/svg/node/SVGImage.h @@ -20,6 +20,7 @@ #include #include "tgfx/core/Image.h" +#include "tgfx/core/LoadResourceProvider.h" #include "tgfx/core/Path.h" #include "tgfx/core/Rect.h" #include "tgfx/svg/SVGTypes.h" @@ -52,7 +53,8 @@ class SVGImage final : public SVGTransformableNode { void onRender(const SVGRenderContext& conetxt) const override; Path onAsPath(const SVGRenderContext& conetxt) const override; Rect onObjectBoundingBox(const SVGRenderContext& conetxt) const override; - static ImageInfo LoadImage(const SVGIRI& iri, const Rect& viewPort, SVGPreserveAspectRatio ratio); + static ImageInfo LoadImage(const std::shared_ptr& resourceProvider, + const SVGIRI& iri, const Rect& viewPort, SVGPreserveAspectRatio ratio); SVG_ATTR(X, SVGLength, SVGLength(0)) SVG_ATTR(Y, SVGLength, SVGLength(0)) diff --git a/include/tgfx/svg/node/SVGLine.h b/include/tgfx/svg/node/SVGLine.h index ad14db9b..8a671b5d 100644 --- a/include/tgfx/svg/node/SVGLine.h +++ b/include/tgfx/svg/node/SVGLine.h @@ -43,8 +43,11 @@ class SVGLine final : public SVGShape { protected: bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - void onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, - PathFillType fillType) const override; + void onDrawFill(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType fillType) const override; + + void onDrawStroke(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType fillType, std::shared_ptr pathEffect) const override; Path onAsPath(const SVGRenderContext& context) const override; diff --git a/include/tgfx/svg/node/SVGMask.h b/include/tgfx/svg/node/SVGMask.h index 873f8c90..67d6e657 100644 --- a/include/tgfx/svg/node/SVGMask.h +++ b/include/tgfx/svg/node/SVGMask.h @@ -40,6 +40,7 @@ class SVGMask final : public SVGHiddenContainer { SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox)) SVG_ATTR(MaskContentUnits, SVGObjectBoundingBoxUnits, SVGObjectBoundingBoxUnits(SVGObjectBoundingBoxUnits::Type::UserSpaceOnUse)) + SVG_ATTR(MaskType, SVGMaskType, SVGMaskType(SVGMaskType::Type::Luminance)) private: friend class SVGRenderContext; diff --git a/include/tgfx/svg/node/SVGPath.h b/include/tgfx/svg/node/SVGPath.h index dbc358f7..0d8092e5 100644 --- a/include/tgfx/svg/node/SVGPath.h +++ b/include/tgfx/svg/node/SVGPath.h @@ -38,8 +38,11 @@ class SVGPath final : public SVGShape { protected: bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - void onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, - PathFillType fillType) const override; + void onDrawFill(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType fillType) const override; + + void onDrawStroke(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType fillType, std::shared_ptr pathEffect) const override; Path onAsPath(const SVGRenderContext& context) const override; diff --git a/include/tgfx/svg/node/SVGPoly.h b/include/tgfx/svg/node/SVGPoly.h index 56c450e9..75e6a273 100644 --- a/include/tgfx/svg/node/SVGPoly.h +++ b/include/tgfx/svg/node/SVGPoly.h @@ -44,8 +44,11 @@ class SVGPoly final : public SVGShape { protected: bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - void onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, - PathFillType fillType) const override; + void onDrawFill(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType fillType) const override; + + void onDrawStroke(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType fillType, std::shared_ptr pathEffect) const override; Path onAsPath(const SVGRenderContext& context) const override; diff --git a/include/tgfx/svg/node/SVGRect.h b/include/tgfx/svg/node/SVGRect.h index c4624138..8be66f7f 100644 --- a/include/tgfx/svg/node/SVGRect.h +++ b/include/tgfx/svg/node/SVGRect.h @@ -47,8 +47,11 @@ class SVGRect final : public SVGShape { protected: bool parseAndSetAttribute(const std::string& name, const std::string& value) override; - void onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, - PathFillType fillType) const override; + void onDrawFill(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType fillType) const override; + + void onDrawStroke(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType fillType, std::shared_ptr pathEffect) const override; Path onAsPath(const SVGRenderContext& context) const override; diff --git a/include/tgfx/svg/node/SVGShape.h b/include/tgfx/svg/node/SVGShape.h index 2162f6d9..29005cfd 100644 --- a/include/tgfx/svg/node/SVGShape.h +++ b/include/tgfx/svg/node/SVGShape.h @@ -21,6 +21,7 @@ #include #include "tgfx/core/Canvas.h" #include "tgfx/core/Paint.h" +#include "tgfx/core/PathEffect.h" #include "tgfx/svg/node/SVGTransformableNode.h" namespace tgfx { @@ -39,8 +40,12 @@ class SVGShape : public SVGTransformableNode { void onRender(const SVGRenderContext& context) const override; - virtual void onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, - PathFillType fillType) const = 0; + virtual void onDrawFill(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType fillType) const = 0; + + virtual void onDrawStroke(Canvas* canvas, const SVGLengthContext& lengthContext, + const Paint& paint, PathFillType fillType, + std::shared_ptr pathEffect) const = 0; private: using INHERITED = SVGTransformableNode; diff --git a/include/tgfx/svg/node/SVGText.h b/include/tgfx/svg/node/SVGText.h index f994ab5f..7bf54fc2 100644 --- a/include/tgfx/svg/node/SVGText.h +++ b/include/tgfx/svg/node/SVGText.h @@ -65,7 +65,7 @@ class SVGTextContainer : public SVGTextFragment { SVG_ATTR(Dy, std::vector, {}) SVG_ATTR(Rotate, std::vector, {}) - void appendChild(std::shared_ptr node) final; + void appendChild(std::shared_ptr child) final; protected: explicit SVGTextContainer(SVGTag t) : INHERITED(t) { diff --git a/include/tgfx/svg/shaper/Shaper.h b/include/tgfx/svg/shaper/Shaper.h new file mode 100644 index 00000000..478452ee --- /dev/null +++ b/include/tgfx/svg/shaper/Shaper.h @@ -0,0 +1,164 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include "tgfx/core/Font.h" +#include "tgfx/core/FontManager.h" +#include "tgfx/core/Point.h" +#include "tgfx/core/Shape.h" +#include "tgfx/core/Typeface.h" +#include "tgfx/svg/SVGTypes.h" + +namespace tgfx { + +/** + * ISO 15924, Codes for the representation of names of scripts. + * https://en.wikipedia.org/wiki/ISO_15924 + */ +using FourByteTag = uint32_t; +static inline constexpr FourByteTag SetFourByteTag(char a, char b, char c, char d) { + return ((static_cast(a) << 24) | (static_cast(b) << 16) | + (static_cast(c) << 8) | static_cast(d)); +} + +class RunIterator { + public: + virtual ~RunIterator() = default; + /** Set state to that of current run and move iterator to end of that run. */ + virtual void consume() = 0; + /** Offset to one past the last (utf8) element in the current run. */ + virtual size_t endOfCurrentRun() const = 0; + /** Return true if consume should no longer be called. */ + virtual bool atEnd() const = 0; +}; + +class FontRunIterator : public RunIterator { + public: + virtual const Font& currentFont() const = 0; +}; + +class BiDiRunIterator : public RunIterator { + public: + /** The unicode bidi embedding level (even ltr, odd rtl) */ + virtual uint8_t currentLevel() const = 0; +}; + +class ScriptRunIterator : public RunIterator { + public: + // Should be iso15924 codes. + virtual FourByteTag currentScript() const = 0; +}; + +class LanguageRunIterator : public RunIterator { + public: + /** Should be BCP-47, c locale names may also work. */ + virtual std::string currentLanguage() const = 0; +}; + +struct RunFeature { + FourByteTag tag; + uint32_t value; + size_t start; // Offset to the start (utf8) element of the run. + size_t end; // Offset to one past the last (utf8) element of the run. +}; + +class RunHandler { + public: + virtual ~RunHandler() = default; + + struct Range { + constexpr Range() : _begin(0), _size(0) { + } + constexpr Range(size_t begin, size_t size) : _begin(begin), _size(size) { + } + + constexpr size_t begin() const { + return _begin; + } + constexpr size_t end() const { + return begin() + size(); + } + constexpr size_t size() const { + return _size; + } + + private: + size_t _begin; + size_t _size; + }; + + struct RunInfo { + const Font& font; + uint8_t bidiLevel; + Point advance; + size_t glyphCount; + Range utf8Range; + }; + + struct Buffer { + GlyphID* glyphs; // required + Point* positions; // required, if (!offsets) put glyphs[i] at positions[i] + // if ( offsets) positions[i+1]-positions[i] are advances + Point* offsets; // optional, if ( offsets) put glyphs[i] at positions[i]+offsets[i] + uint32_t* clusters; // optional, utf8+clusters[i] starts run which produced glyphs[i] + Point point; // offset to add to all positions + }; + + /** Called when beginning a line. */ + virtual void beginLine() = 0; + + /** Called once for each run in a line. Can compute baselines and offsets. */ + virtual void runInfo(const RunInfo&) = 0; + + /** Called after all runInfo calls for a line. */ + virtual void commitRunInfo() = 0; + + /** Called for each run in a line after commitRunInfo. The buffer will be filled out. */ + virtual Buffer runBuffer(const RunInfo&) = 0; + + /** Called after each runBuffer is filled out. */ + virtual void commitRunBuffer(const RunInfo&) = 0; + + /** Called when ending a line. */ + virtual void commitLine() = 0; +}; + +class Shaper { + public: + Shaper() = default; + virtual ~Shaper() = default; + Shaper(const Shaper&) = delete; + Shaper& operator=(const Shaper&) = delete; + + virtual void shape(const char* utf8, size_t utf8Bytes, FontRunIterator& fontIter, + BiDiRunIterator& bidiIter, ScriptRunIterator& scriptIter, + LanguageRunIterator& langIter, const RunFeature* features, size_t featuresSize, + float width, RunHandler* handler) const = 0; + + static std::unique_ptr MakeFontMgrRunIterator( + const char* utf8, size_t utf8Bytes, const Font& font, std::shared_ptr fallback); + + static std::unique_ptr MakeStdLanguageRunIterator(const char* utf8, + size_t utf8Bytes); +}; + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/shaper/ShaperFactory.h b/include/tgfx/svg/shaper/ShaperFactory.h new file mode 100644 index 00000000..b5192e64 --- /dev/null +++ b/include/tgfx/svg/shaper/ShaperFactory.h @@ -0,0 +1,44 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "tgfx/svg/shaper/Shaper.h" + +namespace tgfx { +namespace shapers { + +class Factory { + public: + virtual ~Factory() = default; + + virtual std::unique_ptr makeShaper() = 0; + + virtual std::unique_ptr makeBidiRunIterator(const char* utf8, size_t utf8Bytes, + uint8_t bidiLevel) = 0; + + virtual std::unique_ptr makeScriptRunIterator(const char* utf8, + size_t utf8Bytes, + FourByteTag script) = 0; +}; + +std::shared_ptr PrimitiveFactory(); + +} // namespace shapers +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/xml/XMLDOM.h b/include/tgfx/svg/xml/XMLDOM.h index 70999a5a..b875bd5c 100644 --- a/include/tgfx/svg/xml/XMLDOM.h +++ b/include/tgfx/svg/xml/XMLDOM.h @@ -22,6 +22,7 @@ #include #include #include "tgfx/core/Data.h" +#include "tgfx/core/Stream.h" namespace tgfx { @@ -80,11 +81,11 @@ class DOM { ~DOM(); /** - * Constructs a DOM tree from XML text data. - * @param data XML text data. + * Constructs a DOM tree from XML text stream. + * @param stream XML text stream. * @return The DOM tree. Returns nullptr if construction fails. */ - static std::shared_ptr MakeFromData(const Data& data); + static std::shared_ptr Make(Stream& stream); /** * Creates a deep copy of a DOM tree. diff --git a/resources/apitest/SVG/complex1.svg b/resources/apitest/SVG/complex1.svg new file mode 100644 index 00000000..224471c8 --- /dev/null +++ b/resources/apitest/SVG/complex1.svg @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/resources/apitest/SVG/complex2.svg b/resources/apitest/SVG/complex2.svg new file mode 100644 index 00000000..3b5ba170 --- /dev/null +++ b/resources/apitest/SVG/complex2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/apitest/SVG/complex3.svg b/resources/apitest/SVG/complex3.svg new file mode 100644 index 00000000..4d34e2ab --- /dev/null +++ b/resources/apitest/SVG/complex3.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/apitest/SVG/complex4.svg b/resources/apitest/SVG/complex4.svg new file mode 100644 index 00000000..91b3264e --- /dev/null +++ b/resources/apitest/SVG/complex4.svg @@ -0,0 +1 @@ +5日6日7日8日9日10日11日00.20.40.6商品用券情况用券未使用券 \ No newline at end of file diff --git a/resources/apitest/SVG/complex5.svg b/resources/apitest/SVG/complex5.svg new file mode 100644 index 00000000..55d46b91 --- /dev/null +++ b/resources/apitest/SVG/complex5.svg @@ -0,0 +1,1502 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/apitest/SVG/complex6.svg b/resources/apitest/SVG/complex6.svg new file mode 100644 index 00000000..e9a04ef3 --- /dev/null +++ b/resources/apitest/SVG/complex6.svg @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/apitest/SVG/complex7.svg b/resources/apitest/SVG/complex7.svg new file mode 100644 index 00000000..9a0d53d3 --- /dev/null +++ b/resources/apitest/SVG/complex7.svg @@ -0,0 +1,264 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core/FontManager.cpp b/src/core/FontManager.cpp new file mode 100644 index 00000000..ca71d7d5 --- /dev/null +++ b/src/core/FontManager.cpp @@ -0,0 +1,213 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/core/FontManager.h" +#include +#include +#include "core/utils/Log.h" + +namespace tgfx { + +/** +* Width has the greatest priority. +* If the value of pattern.width is 5 (normal) or less, +* narrower width values are checked first, then wider values. +* If the value of pattern.width is greater than 5 (normal), +* wider values are checked first, followed by narrower values. +* +* Italic/Oblique has the next highest priority. +* If italic requested and there is some italic font, use it. +* If oblique requested and there is some oblique font, use it. +* If italic requested and there is some oblique font, use it. +* If oblique requested and there is some italic font, use it. +* +* Exact match. +* If pattern.weight < 400, weights below pattern.weight are checked +* in descending order followed by weights above pattern.weight +* in ascending order until a match is found. +* If pattern.weight > 500, weights above pattern.weight are checked +* in ascending order followed by weights below pattern.weight +* in descending order until a match is found. +* If pattern.weight is 400, 500 is checked first +* and then the rule for pattern.weight < 400 is used. +* If pattern.weight is 500, 400 is checked first +* and then the rule for pattern.weight < 400 is used. +*/ +std::shared_ptr FontStyleSet::matchStyleCSS3(const FontStyle& pattern) { + auto count = this->count(); + if (0 == count) { + return nullptr; + } + + struct Score { + int score; + int index; + Score& operator+=(int rhs) { + this->score += rhs; + return *this; + } + Score& operator<<=(int rhs) { + this->score <<= rhs; + return *this; + } + bool operator<(const Score& that) const { + return this->score < that.score; + } + }; + + int patternWeight = static_cast(pattern.weight()); + int patternWidth = static_cast(pattern.width()); + int patternSlant = static_cast(pattern.slant()); + + Score maxScore = {0, 0}; + for (size_t i = 0; i < count; ++i) { + FontStyle current; + this->getStyle(i, ¤t, nullptr); + int currentWeight = static_cast(current.weight()); + int currentWidth = static_cast(current.width()); + int currentSlant = static_cast(current.slant()); + + Score currentScore = {0, static_cast(i)}; + + // CSS stretch / FontStyle::Width + // Takes priority over everything else. + if (pattern.width() <= FontStyle::Width::Normal) { + if (current.width() <= pattern.width()) { + currentScore += 10 - patternWidth + static_cast(current.width()); + } else { + currentScore += 10 - static_cast(current.width()); + } + } else { + if (current.width() > pattern.width()) { + currentScore += 10 + patternWidth - currentWidth; + } else { + currentScore += currentWidth; + } + } + currentScore <<= 8; + + // CSS style (normal, italic, oblique) / FontStyle::Slant (upright, italic, oblique) + // Takes priority over all valid weights. + DEBUG_ASSERT(0 <= patternSlant && patternSlant <= 2 && 0 <= currentSlant && currentSlant <= 2); + static const int score[3][3] = { + /* Upright Italic Oblique [current]*/ + /* Upright */ {3, 1, 2}, + /* Italic */ {1, 3, 2}, + /* Oblique */ {1, 2, 3}, + /* [pattern] */ + }; + currentScore += score[patternSlant][currentSlant]; + currentScore <<= 8; + + // CSS weight / FontStyle::Weight + // The 'closer' to the target weight, the higher the score. + // 1000 is the 'heaviest' recognized weight + if (patternWeight == currentWeight) { + currentScore += 1000; + // less than 400 prefer lighter weights + } else if (patternWeight < 400) { + if (currentWeight <= patternWeight) { + currentScore += 1000 - patternWeight + currentWeight; + } else { + currentScore += 1000 - currentWeight; + } + // between 400 and 500 prefer heavier up to 500, then lighter weights + } else if (patternWeight <= 500) { + if (currentWeight >= patternWeight && currentWeight <= 500) { + currentScore += 1000 + patternWeight - currentWeight; + } else if (current.weight() <= pattern.weight()) { + currentScore += 500 + currentWeight; + } else { + currentScore += 1000 - currentWeight; + } + // greater than 500 prefer heavier weights + } else if (patternWeight > 500) { + if (currentWeight > patternWeight) { + currentScore += 1000 + patternWeight - currentWeight; + } else { + currentScore += currentWeight; + } + } + + if (maxScore < currentScore) { + maxScore = currentScore; + } + } + + return this->createTypeface(static_cast(maxScore.index)); +} + +class EmptyFontStyleSet : public FontStyleSet { + public: + size_t count() override { + return 0; + } + + void getStyle(size_t, FontStyle*, std::string*) override { + } + + std::shared_ptr createTypeface(size_t) override { + return nullptr; + } + + std::shared_ptr matchStyle(const FontStyle&) override { + return nullptr; + } +}; + +std::shared_ptr FontStyleSet::CreateEmpty() { + return std::shared_ptr(new EmptyFontStyleSet); +} + +namespace { +std::shared_ptr emptyOnNull(std::shared_ptr&& styleSet) { + if (!styleSet) { + styleSet = FontStyleSet::CreateEmpty(); + } + return std::move(styleSet); +} +} // anonymous namespace + +size_t FontManager::countFamilies() const { + return onCountFamilies(); +} + +std::string FontManager::getFamilyName(size_t index) const { + return onGetFamilyName(index); +} + +std::shared_ptr FontManager::createStyleSet(size_t index) const { + return emptyOnNull(onCreateStyleSet(index)); +} + +std::shared_ptr FontManager::matchFamily(const std::string& familyName) const { + return emptyOnNull(onMatchFamily(familyName)); +} + +std::shared_ptr FontManager::matchFamilyStyle(const std::string& familyName, + FontStyle style) const { + return onMatchFamilyStyle(familyName, style); +} + +std::shared_ptr FontManager::matchFamilyStyleCharacter( + const std::string& familyName, FontStyle style, const std::vector& bcp47s, + Unichar character) const { + return onMatchFamilyStyleCharacter(familyName, style, bcp47s, character); +} + +} // namespace tgfx \ No newline at end of file diff --git a/src/core/FontManagerCustom.cpp b/src/core/FontManagerCustom.cpp new file mode 100644 index 00000000..6827c898 --- /dev/null +++ b/src/core/FontManagerCustom.cpp @@ -0,0 +1,136 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/core/FontManagerCustom.h" +#include +#include +#include +#include +#include "core/utils/Log.h" +#include "tgfx/core/FontStyle.h" + +namespace tgfx { + +FontStyleSetCustom::FontStyleSetCustom(std::string familyName) : familyName(std::move(familyName)) { +} + +void FontStyleSetCustom::appendTypeface(std::shared_ptr typeface) { + styles.emplace_back(std::move(typeface)); +} + +size_t FontStyleSetCustom::count() { + return styles.size(); +} + +void FontStyleSetCustom::getStyle(size_t /*index*/, FontStyle* style, std::string* name) { + if (style) { + *style = FontStyle::Normal(); + } + if (name) { + *name = ""; + } +} + +std::shared_ptr FontStyleSetCustom::createTypeface(size_t index) { + if (styles.size() <= index) { + return nullptr; + } + return styles[index]; +} + +std::shared_ptr FontStyleSetCustom::matchStyle(const FontStyle& style) { + std::shared_ptr typeface = nullptr; + if (!styles.empty()) { + typeface = this->matchStyleCSS3(style); + if (!typeface) { + typeface = styles[0]; + } + } + return typeface; +} + +std::string FontStyleSetCustom::getFamilyName() { + return familyName; +} + +FontManagerCustom::FontManagerCustom(const FontLoader& loader) : defaultFamily(nullptr) { + + loader.loadFonts(families); + + // Try to pick a default font. + static const std::array defaultNames = { + "Arial", "Verdana", "Times New Roman", "Droid Sans", "DejaVu Serif", ""}; + for (const auto& defaultName : defaultNames) { + auto set = this->onMatchFamily(defaultName); + if (!set) { + continue; + } + + auto style = + FontStyle(FontStyle::Weight::Normal, FontStyle::Width::Normal, FontStyle::Slant::Upright); + auto typeface = set->matchStyle(style); + if (!typeface) { + continue; + } + + defaultFamily = set; + break; + } + if (nullptr == defaultFamily) { + defaultFamily = families[0]; + } +} + +size_t FontManagerCustom::onCountFamilies() const { + return families.size(); +} + +std::string FontManagerCustom::onGetFamilyName(size_t index) const { + DEBUG_ASSERT(index < families.size()); + return families[index]->getFamilyName(); +} + +std::shared_ptr FontManagerCustom::onCreateStyleSet(size_t index) const { + DEBUG_ASSERT(index < families.size()); + return families[index]; +} + +std::shared_ptr FontManagerCustom::onMatchFamily( + const std::string& familyName) const { + for (const auto& family : families) { + if (family->getFamilyName() == familyName) { + return family; + } + } + return nullptr; +} + +std::shared_ptr FontManagerCustom::onMatchFamilyStyle(const std::string& familyName, + FontStyle style) const { + if (auto set = this->matchFamily(familyName)) { + return set->matchStyle(style); + } + return defaultFamily->matchStyle(style); +} + +std::shared_ptr FontManagerCustom::onMatchFamilyStyleCharacter( + const std::string&, FontStyle, const std::vector&, Unichar) const { + return nullptr; +} + +} // namespace tgfx \ No newline at end of file diff --git a/src/core/LoadResourceProvider.cpp b/src/core/LoadResourceProvider.cpp new file mode 100644 index 00000000..54f4fce8 --- /dev/null +++ b/src/core/LoadResourceProvider.cpp @@ -0,0 +1,57 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/core/LoadResourceProvider.h" +#include +#include +#include + +namespace tgfx { + +class FileResourceProvider final : public LoadResourceProvider { + public: + explicit FileResourceProvider(std::string basePath) : basePath(std::move(basePath)) { + } + + std::shared_ptr load(const std::string& resourcePath, + const std::string& resourceName) const override { + return Data::MakeFromFile(basePath + "/" + resourcePath + "/" + resourceName); + }; + + std::shared_ptr loadImage(const std::string& resourcePath, + const std::string& resourceName) const override { + return Image::MakeFromFile(basePath + "/" + resourcePath + "/" + resourceName); + } + + private: + const std::string basePath; +}; + +std::shared_ptr LoadResourceProvider::MakeEmpty() { + return std::make_shared(); +} + +std::shared_ptr LoadResourceProvider::MakeFileProvider( + const std::string& basePath) { + if (std::filesystem::exists(basePath)) { + return std::make_shared(basePath); + } + return nullptr; +} + +} // namespace tgfx \ No newline at end of file diff --git a/src/core/utils/Stream.cpp b/src/core/utils/Stream.cpp index bdeb70b1..63e41448 100644 --- a/src/core/utils/Stream.cpp +++ b/src/core/utils/Stream.cpp @@ -17,10 +17,14 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/core/Stream.h" +#include +#include #include #include #include +#include #include "core/utils/Log.h" +#include "tgfx/core/Data.h" namespace tgfx { static std::mutex& locker = *new std::mutex; @@ -61,6 +65,53 @@ class FileStream : public Stream { size_t length = 0; }; +class MemoryStream : public Stream { + public: + explicit MemoryStream(std::shared_ptr data) : data(std::move(data)) { + } + + ~MemoryStream() override = default; + + size_t size() const override { + return data->size(); + } + + bool seek(size_t position) override { + offset = position > data->size() ? data->size() : position; + return true; + } + + bool move(int moveOffset) override { + return this->seek(offset + static_cast(moveOffset)); + } + + size_t read(void* buffer, size_t size) override { + size_t dataSize = data->size(); + + if (size > dataSize - offset) { + size = dataSize - offset; + } + if (buffer) { + memcpy(buffer, data->bytes() + offset, size); + } + offset += size; + return size; + } + + bool rewind() override { + offset = 0; + return true; + } + + const void* getMemoryBase() override { + return data->data(); + } + + private: + std::shared_ptr data = nullptr; + size_t offset = 0; +}; + std::string GetProtocolFromPath(const std::string& path) { size_t pos = path.find("://"); if (pos != std::string::npos) { @@ -99,6 +150,13 @@ std::unique_ptr Stream::MakeFromFile(const std::string& filePath) { return std::make_unique(file, length); } +std::unique_ptr Stream::MakeFromData(std::shared_ptr data) { + if (data == nullptr) { + return nullptr; + } + return std::make_unique(data); +} + void StreamFactory::RegisterCustomProtocol(const std::string& customProtocol, std::shared_ptr factory) { if (customProtocol.empty() || factory == nullptr) { diff --git a/src/svg/ElementWriter.cpp b/src/svg/ElementWriter.cpp index 20e0fe14..d7a0cd8a 100644 --- a/src/svg/ElementWriter.cpp +++ b/src/svg/ElementWriter.cpp @@ -17,7 +17,6 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "ElementWriter.h" -#include <_types/_uint32_t.h> #include #include #include "SVGExportContext.h" diff --git a/src/svg/SVGAttributeParser.cpp b/src/svg/SVGAttributeParser.cpp index 1eedc867..fb1736c8 100644 --- a/src/svg/SVGAttributeParser.cpp +++ b/src/svg/SVGAttributeParser.cpp @@ -1203,4 +1203,23 @@ bool SVGAttributeParser::parse(SVGDisplay* display) { return parsedValue && this->parseEOSToken(); } + +template <> +bool SVGAttributeParser::parse(SVGMaskType* maskType) { + static constexpr std::tuple typeMap[] = { + {"luminance", SVGMaskType::Type::Luminance}, + {"alpha", SVGMaskType::Type::Alpha}, + }; + + bool parsedValue = false; + SVGMaskType::Type type; + + if (this->parseEnumMap(typeMap, &type)) { + *maskType = SVGMaskType(type); + parsedValue = true; + } + + return parsedValue && this->parseEOSToken(); +} + } // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGDOM.cpp b/src/svg/SVGDOM.cpp index 7604feac..b53ae75b 100644 --- a/src/svg/SVGDOM.cpp +++ b/src/svg/SVGDOM.cpp @@ -27,6 +27,7 @@ #include #include "core/utils/Log.h" #include "svg/SVGAttributeParser.h" +#include "svg/SVGLengthContext.h" #include "svg/SVGNodeConstructor.h" #include "svg/SVGRenderContext.h" #include "tgfx/core/Canvas.h" @@ -35,7 +36,6 @@ #include "tgfx/core/Size.h" #include "tgfx/core/Surface.h" #include "tgfx/svg/SVGAttribute.h" -#include "tgfx/svg/SVGFontManager.h" #include "tgfx/svg/SVGTypes.h" #include "tgfx/svg/SVGValue.h" #include "tgfx/svg/node/SVGContainer.h" @@ -44,12 +44,9 @@ namespace tgfx { -std::shared_ptr SVGDOM::Make(const std::shared_ptr& data) { - if (!data) { - return nullptr; - } +std::shared_ptr SVGDOM::Make(Stream& stream, SVGDOMOptions options) { // Parse the data into an XML DOM structure - auto xmlDom = DOM::MakeFromData(*data); + auto xmlDom = DOM::Make(stream); if (!xmlDom) { return nullptr; } @@ -66,66 +63,46 @@ std::shared_ptr SVGDOM::Make(const std::shared_ptr& data) { // Create SVGDOM with the root node and ID mapper return std::shared_ptr( - new SVGDOM(std::static_pointer_cast(root), std::move(mapper))); + new SVGDOM(std::static_pointer_cast(root), std::move(options), std::move(mapper))); } -SVGDOM::SVGDOM(std::shared_ptr root, SVGIDMapper&& mapper) - : root(std::move(root)), nodeIDMapper(std::move(mapper)) { +SVGDOM::SVGDOM(std::shared_ptr root, SVGDOMOptions options, SVGIDMapper&& mapper) + : root(std::move(root)), nodeIDMapper(std::move(mapper)), options(std::move(options)) { } const std::shared_ptr& SVGDOM::getRoot() const { return root; } -void SVGDOM::collectRenderFonts(const std::shared_ptr& fontManager) { - if (!root || !fontManager) { - return; - } - - auto fontCollector = [](auto collector, const std::shared_ptr& node, - const std::shared_ptr& fontManager) -> void { - if (!node) { - return; - } - if (node->tag() >= SVGTag::Text && node->tag() <= SVGTag::TSpan) { - if (node->getFontFamily().isValue()) { - auto family = node->getFontFamily()->family(); - SVGFontWeight::Type weight = node->getFontWeight().isValue() ? node->getFontWeight()->type() - : SVGFontWeight::Type::Normal; - SVGFontStyle::Type style = node->getFontStyle().isValue() ? node->getFontStyle()->type() - : SVGFontStyle::Type::Normal; - SVGFontInfo fontStyle(weight, style); - fontManager->addFontStyle(family, fontStyle); - } - } else if (node->hasChildren()) { - if (auto container = std::static_pointer_cast(node)) { - for (const auto& child : container->getChildren()) { - collector(collector, child, fontManager); - } - } - } - }; - fontCollector(fontCollector, std::static_pointer_cast(root), fontManager); -} - -void SVGDOM::render(Canvas* canvas, const std::shared_ptr& fontManager) { +void SVGDOM::render(Canvas* canvas) { // If the container size is not set, use the size of the root SVG element. auto drawSize = containerSize; if (drawSize.isEmpty()) { + auto rootWidth = root->getWidth(); + auto rootHeight = root->getHeight(); if (root->getViewBox().has_value()) { - drawSize = root->getViewBox()->size(); + SVGLengthContext viewportLengthContext(root->getViewBox()->size()); + drawSize = Size::Make( + viewportLengthContext.resolve(rootWidth, SVGLengthContext::LengthType::Horizontal), + viewportLengthContext.resolve(rootHeight, SVGLengthContext::LengthType::Vertical)); } else { - drawSize = Size::Make(root->getWidth().value(), root->getHeight().value()); + drawSize = Size::Make(rootWidth.value(), rootHeight.value()); } } if (!canvas || !root || drawSize.isEmpty()) { return; } - SVGLengthContext lengthContext(containerSize); + SVGLengthContext lengthContext(drawSize); SVGPresentationContext presentationContext; - SVGRenderContext renderContext(canvas, fontManager, nodeIDMapper, lengthContext, - presentationContext, {nullptr, nullptr}, canvas->getMatrix()); + + auto resourceProvider = + options.resourceProvider ? options.resourceProvider : LoadResourceProvider::MakeEmpty(); + auto shaperFactory = options.shaperFactory ? options.shaperFactory : shapers::PrimitiveFactory(); + + SVGRenderContext renderContext(canvas, options.fontManager, resourceProvider, shaperFactory, + nodeIDMapper, lengthContext, presentationContext, + {nullptr, nullptr}, canvas->getMatrix()); root->render(renderContext); } diff --git a/src/svg/SVGExportContext.cpp b/src/svg/SVGExportContext.cpp index 51ce7f68..198a32eb 100644 --- a/src/svg/SVGExportContext.cpp +++ b/src/svg/SVGExportContext.cpp @@ -17,7 +17,6 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "SVGExportContext.h" -#include <_types/_uint32_t.h> #include #include #include @@ -380,6 +379,14 @@ Bitmap SVGExportContext::ImageExportToBitmap(Context* context, auto surface = Surface::Make(context, image->width(), image->height()); auto* canvas = surface->getCanvas(); canvas->drawImage(image); + + Bitmap bitmap(surface->width(), surface->height()); + auto* pixels = bitmap.lockPixels(); + if (surface->readPixels(bitmap.info(), pixels)) { + bitmap.unlockPixels(); + return bitmap; + } + bitmap.unlockPixels(); return Bitmap(); } diff --git a/src/svg/SVGExportContext.h b/src/svg/SVGExportContext.h index d8f1fdb7..d35ee7b3 100644 --- a/src/svg/SVGExportContext.h +++ b/src/svg/SVGExportContext.h @@ -18,7 +18,6 @@ #pragma once -#include <_types/_uint32_t.h> #include #include #include diff --git a/src/svg/SVGFilterContext.cpp b/src/svg/SVGFilterContext.cpp index c93a2e65..afb3d0fc 100644 --- a/src/svg/SVGFilterContext.cpp +++ b/src/svg/SVGFilterContext.cpp @@ -20,8 +20,10 @@ #include #include "core/utils/Log.h" #include "svg/SVGRenderContext.h" +#include "tgfx/core/BlendMode.h" #include "tgfx/core/ColorFilter.h" #include "tgfx/core/ImageFilter.h" +#include "tgfx/core/Paint.h" #include "tgfx/core/Rect.h" #include "tgfx/svg/SVGTypes.h" @@ -77,14 +79,16 @@ std::tuple, SVGColorspace> SVGFilterContext::getInp case SVGFeInputType::Type::FillPaint: { const auto& fillPaint = context.fillPaint(); if (fillPaint.has_value()) { - //TODO (YGAurora) - Implement shader image filter by paint + auto colorFilter = ColorFilter::Blend(fillPaint->getColor(), BlendMode::DstIn); + result = ImageFilter::ColorFilter(colorFilter); } break; } case SVGFeInputType::Type::StrokePaint: { const auto& strokePaint = context.strokePaint(); if (strokePaint.has_value()) { - //TODO (YGAurora) - Implement shader image filter by paint + auto colorFilter = ColorFilter::Blend(strokePaint->getColor(), BlendMode::DstIn); + result = ImageFilter::ColorFilter(colorFilter); } break; } @@ -122,8 +126,7 @@ std::shared_ptr SVGFilterContext::resolveInput( const SVGRenderContext& context, const SVGFeInputType& inputType, SVGColorspace /*resultColorspace*/) const { //TODO (YGAurora) - waiting for color space implementation - resolveInput(context, inputType); - return nullptr; + return resolveInput(context, inputType); } } // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGFontManager.cpp b/src/svg/SVGFontManager.cpp deleted file mode 100644 index 48b5f327..00000000 --- a/src/svg/SVGFontManager.cpp +++ /dev/null @@ -1,92 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making tgfx available. -// -// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// unless required by applicable law or agreed to in writing, software distributed under the -// license is distributed on an "as is" basis, without warranties or conditions of any kind, -// either express or implied. see the license for the specific language governing permissions -// and limitations under the license. -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -#include "tgfx/svg/SVGFontManager.h" - -namespace tgfx { - -SVGFontManager::SVGFontManager(const std::shared_ptr& defaultTypeface) - : defaultTypeface(defaultTypeface) { -} - -std::shared_ptr SVGFontManager::Make(const std::shared_ptr& typeface) { - if (!typeface) { - return nullptr; - } - return std::shared_ptr(new SVGFontManager(typeface)); -} - -void SVGFontManager::addFontStyle(const std::string& fontFamily, SVGFontInfo info) { - if (typefaceMap.find(fontFamily) != typefaceMap.end()) { - return; - } - if (typefaceMap[fontFamily].find(info) != typefaceMap[fontFamily].end()) { - return; - } - typefaceMap[fontFamily][info] = nullptr; -} - -bool SVGFontManager::setTypeface(const std::string& fontFamily, SVGFontInfo info, - const std::shared_ptr& typeface) { - if (typefaceMap.find(fontFamily) != typefaceMap.end()) { - return false; - } - if (typefaceMap[fontFamily].find(info) != typefaceMap[fontFamily].end()) { - return false; - } - typefaceMap[fontFamily][info] = typeface; - return true; -} - -std::vector SVGFontManager::getFontFamilies() const { - std::vector families; - families.reserve(typefaceMap.size()); - for (const auto& [family, _] : typefaceMap) { - families.push_back(family); - } - return families; -} - -std::vector SVGFontManager::getFontInfos(const std::string& fontFamily) const { - if (auto iter = typefaceMap.find(fontFamily); iter != typefaceMap.end()) { - std::vector styles; - styles.reserve(iter->second.size()); - for (const auto& [style, _] : iter->second) { - styles.push_back(style); - } - return styles; - } else { - return {}; - } -} - -std::shared_ptr SVGFontManager::getTypefaceForRendering(const std::string& fontFamily, - SVGFontInfo info) const { - auto familyIter = typefaceMap.find(fontFamily); - if (familyIter == typefaceMap.end()) { - return defaultTypeface; - } - auto styleIter = familyIter->second.find(info); - if (styleIter == familyIter->second.end()) { - return defaultTypeface; - } else { - return styleIter->second; - } -} - -} // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGLengthContext.cpp b/src/svg/SVGLengthContext.cpp index 01136d24..c7d0d42a 100644 --- a/src/svg/SVGLengthContext.cpp +++ b/src/svg/SVGLengthContext.cpp @@ -50,33 +50,33 @@ constexpr float CMMultiplier = MMMultiplier * 10; } // namespace -float SVGLengthContext::resolve(const SVGLength& l, LengthType t) const { - switch (l.unit()) { +float SVGLengthContext::resolve(const SVGLength& length, LengthType type) const { + switch (length.unit()) { case SVGLength::Unit::Number: { if (patternUnit.has_value()) { if (patternUnit.value().type() == SVGObjectBoundingBoxUnits::Type::ObjectBoundingBox) { - return l.value() * length_size_for_type(_viewPort, t); + return length.value() * length_size_for_type(_viewPort, type); } else { - return l.value(); + return length.value(); } } else { - return l.value(); + return length.value(); } } case SVGLength::Unit::PX: - return l.value(); + return length.value(); case SVGLength::Unit::Percentage: - return l.value() * length_size_for_type(_viewPort, t) / 100; + return length.value() * length_size_for_type(_viewPort, type) / 100; case SVGLength::Unit::CM: - return l.value() * dpi * CMMultiplier; + return length.value() * dpi * CMMultiplier; case SVGLength::Unit::MM: - return l.value() * dpi * MMMultiplier; + return length.value() * dpi * MMMultiplier; case SVGLength::Unit::IN: - return l.value() * dpi * INMultiplier; + return length.value() * dpi * INMultiplier; case SVGLength::Unit::PT: - return l.value() * dpi * PTMultiplier; + return length.value() * dpi * PTMultiplier; case SVGLength::Unit::PC: - return l.value() * dpi * PCMultiplier; + return length.value() * dpi * PCMultiplier; default: //unsupported unit type ASSERT(false); diff --git a/src/svg/SVGNodeConstructor.cpp b/src/svg/SVGNodeConstructor.cpp index c06800a0..f2385a7d 100644 --- a/src/svg/SVGNodeConstructor.cpp +++ b/src/svg/SVGNodeConstructor.cpp @@ -19,6 +19,7 @@ #include "SVGNodeConstructor.h" #include "core/utils/Log.h" #include "svg/SVGAttributeParser.h" +#include "svg/SVGUtils.h" #include "tgfx/svg/node/SVGCircle.h" #include "tgfx/svg/node/SVGClipPath.h" #include "tgfx/svg/node/SVGDefs.h" @@ -159,9 +160,6 @@ class StyleIterator { if (pos) { const char* sep = this->nextSeparator(); - ASSERT(*sep == ';'); - ASSERT(*sep == '\0') - const char* valueSep = strchr(pos, ':'); if (valueSep && valueSep < sep) { name = TrimmedString(pos, valueSep - 1); diff --git a/src/svg/SVGRenderContext.cpp b/src/svg/SVGRenderContext.cpp index 739e2fc6..a3b28b11 100644 --- a/src/svg/SVGRenderContext.cpp +++ b/src/svg/SVGRenderContext.cpp @@ -102,61 +102,56 @@ SVGPresentationContext::SVGPresentationContext() : _inherited(SVGPresentationAttributes::MakeInitial()) { } -SVGRenderContext::SVGRenderContext(Canvas* canvas, - const std::shared_ptr& fontManager, +SVGRenderContext::SVGRenderContext(Canvas* canvas, const std::shared_ptr& fontManager, + const std::shared_ptr& resourceProvider, + const std::shared_ptr& shaperFactory, const SVGIDMapper& mapper, const SVGLengthContext& lengthContext, const SVGPresentationContext& presentContext, - const OBBScope& obbs, const Matrix& matrix) - : fontManager(fontManager), nodeIDMapper(mapper), _lengthContext(lengthContext), - _presentationContext(presentContext), renderCanvas(canvas), recorder(), - _canvas(recorder.beginRecording()), scope(obbs), matrix(matrix) { + const OBBScope& scope, const Matrix& matrix) + : _fontManager(fontManager), _resourceProvider(resourceProvider), shaperFactory(shaperFactory), + nodeIDMapper(mapper), _lengthContext(lengthContext), _presentationContext(presentContext), + _canvas(canvas), canvasSaveCount(canvas->getSaveCount()), scope(scope), _matrix(matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other) - : SVGRenderContext(other._canvas, other.fontManager, other.nodeIDMapper, *other._lengthContext, - *other._presentationContext, other.scope, other.matrix) { + : SVGRenderContext(other._canvas, other._fontManager, other._resourceProvider, + other.shaperFactory, other.nodeIDMapper, *other._lengthContext, + *other._presentationContext, other.scope, other._matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, Canvas* canvas) - : SVGRenderContext(canvas, other.fontManager, other.nodeIDMapper, *other._lengthContext, - *other._presentationContext, other.scope, other.matrix) { + : SVGRenderContext(canvas, other._fontManager, other._resourceProvider, other.shaperFactory, + other.nodeIDMapper, *other._lengthContext, *other._presentationContext, + other.scope, other._matrix) { } -SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, const SVGLengthContext& lengthCtx) - : SVGRenderContext(other._canvas, other.fontManager, other.nodeIDMapper, lengthCtx, - *other._presentationContext, other.scope, other.matrix) { +SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, + const SVGLengthContext& lengthContext) + : SVGRenderContext(other._canvas, other._fontManager, other._resourceProvider, + other.shaperFactory, other.nodeIDMapper, lengthContext, + *other._presentationContext, other.scope, other._matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, Canvas* canvas, - const SVGLengthContext& lengthCtx) - : SVGRenderContext(canvas, other.fontManager, other.nodeIDMapper, lengthCtx, - *other._presentationContext, other.scope, other.matrix) { + const SVGLengthContext& lengthContext) + : SVGRenderContext(canvas, other._fontManager, other._resourceProvider, other.shaperFactory, + other.nodeIDMapper, lengthContext, *other._presentationContext, other.scope, + other._matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, const SVGNode* node) - : SVGRenderContext(other._canvas, other.fontManager, other.nodeIDMapper, *other._lengthContext, - *other._presentationContext, OBBScope{node, this}, other.matrix) { + : SVGRenderContext(other._canvas, other._fontManager, other._resourceProvider, + other.shaperFactory, other.nodeIDMapper, *other._lengthContext, + *other._presentationContext, OBBScope{node, this}, other._matrix) { } SVGRenderContext::~SVGRenderContext() { - _canvas->restoreToCount(0); - auto picture = recorder.finishRecordingAsPicture(); - if (!picture) { - return; - } - - renderCanvas->save(); - if (_clipPath.has_value()) { - renderCanvas->clipPath(_clipPath.value()); - } - auto matrix = Matrix::I(); - renderCanvas->drawPicture(picture, &matrix, &picturePaint); - renderCanvas->restore(); + _canvas->restoreToCount(canvasSaveCount); } SVGRenderContext SVGRenderContext::CopyForPaint(const SVGRenderContext& context, Canvas* canvas, - const SVGLengthContext& lengthCtx) { - SVGRenderContext copyContext(context, canvas, lengthCtx); + const SVGLengthContext& lengthContext) { + SVGRenderContext copyContext(context, canvas, lengthContext); copyContext.deferredPaintOpacity = context.deferredPaintOpacity; return copyContext; } @@ -165,8 +160,11 @@ std::shared_ptr SVGRenderContext::findNodeById(const SVGIRI& iri) const if (iri.type() != SVGIRI::Type::Local) { return nullptr; } - auto p = nodeIDMapper.find(iri.iri())->second; - return p; + auto iter = nodeIDMapper.find(iri.iri()); + if (iter != nodeIDMapper.end()) { + return iter->second; + } + return nullptr; } void SVGRenderContext::applyPresentationAttributes(const SVGPresentationAttributes& attrs, @@ -207,23 +205,28 @@ void SVGRenderContext::applyPresentationAttributes(const SVGPresentationAttribut #undef ApplyLazyInheritedAttribute if (attrs.ClipPath.isValue()) { - _clipPath = this->applyClip(*attrs.ClipPath); + saveOnce(); + _canvas->clipPath(applyClip(*attrs.ClipPath)); } const bool hasFilter = attrs.Filter.isValue(); if (attrs.Opacity.isValue()) { - picturePaint.setAlpha(this->applyOpacity(*attrs.Opacity, flags, hasFilter)); + _canvas->saveLayerAlpha(this->applyOpacity(*attrs.Opacity, flags, hasFilter)); } if (attrs.Mask.isValue()) { if (auto maskFilter = this->applyMask(*attrs.Mask)) { - picturePaint.setMaskFilter(maskFilter); + Paint maskPaint; + maskPaint.setMaskFilter(maskFilter); + _canvas->saveLayer(&maskPaint); } } if (hasFilter) { if (auto imageFilter = this->applyFilter(*attrs.Filter)) { - picturePaint.setImageFilter(imageFilter); + Paint filterPaint; + filterPaint.setImageFilter(imageFilter); + _canvas->saveLayer(&filterPaint); } } } @@ -240,12 +243,11 @@ float SVGRenderContext::applyOpacity(float opacity, uint32_t flags, bool hasFilt // - it only has a stroke or a fill (but not both); // - it does not have a filter. // Going forward, we may needto refine this heuristic (e.g. to accommodate markers). - if ((flags & kLeaf) && (hasFill ^ hasStroke) && !hasFilter) { + if ((flags & static_cast(ApplyFlags::Leaf)) && (hasFill ^ hasStroke) && !hasFilter) { deferredPaintOpacity *= opacity; return 1.0f; - } else { - return opacity; } + return opacity; } std::shared_ptr SVGRenderContext::applyFilter(const SVGFuncIRI& filter) const { @@ -263,7 +265,9 @@ std::shared_ptr SVGRenderContext::applyFilter(const SVGFuncIRI& fil } void SVGRenderContext::saveOnce() { - _canvas->save(); + if (_canvas->getSaveCount() == canvasSaveCount) { + _canvas->save(); + } } Path SVGRenderContext::applyClip(const SVGFuncIRI& clip) const { @@ -295,26 +299,22 @@ std::shared_ptr SVGRenderContext::applyMask(const SVGFuncIRI& mask) Recorder maskRecorder; auto* maskCanvas = maskRecorder.beginRecording(); { - SVGRenderContext maskCtx(*this, maskCanvas); - maskNode->renderMask(maskCtx); + SVGRenderContext maskContext(*this, maskCanvas); + maskNode->renderMask(maskContext); } auto picture = maskRecorder.finishRecordingAsPicture(); if (!picture) { return nullptr; } - auto bound = picture->getBounds(); - maskBound.join(bound); - - auto transMatrix = matrix * Matrix::MakeTrans(-maskBound.left, -maskBound.top); + auto transMatrix = _matrix * Matrix::MakeTrans(-maskBound.left, -maskBound.top); auto shaderImage = - Image::MakeFrom(picture, static_cast(bound.width() * matrix.getScaleX()), - static_cast(bound.height() * matrix.getScaleY()), &transMatrix); + Image::MakeFrom(picture, static_cast(maskBound.width() * _matrix.getScaleX()), + static_cast(maskBound.height() * _matrix.getScaleY()), &transMatrix); auto shader = Shader::MakeImageShader(shaderImage, TileMode::Decal, TileMode::Decal); if (!shader) { return nullptr; } - // return nullptr; return MaskFilter::MakeShader(shader); } @@ -339,8 +339,9 @@ std::optional SVGRenderContext::commonPaint(const SVGPaint& svgPaint, flo // and node being rendered. SVGPresentationContext presentContext; presentContext._namedColors = _presentationContext->_namedColors; - SVGRenderContext localContext(_canvas, fontManager, nodeIDMapper, *_lengthContext, - presentContext, scope, Matrix::I()); + SVGRenderContext localContext(_canvas, _fontManager, _resourceProvider, shaperFactory, + nodeIDMapper, *_lengthContext, presentContext, scope, + Matrix::I()); const auto node = this->findNodeById(svgPaint.iri()); if (!node || !node->asPaint(localContext, &(paint.value()))) { @@ -378,20 +379,21 @@ std::optional SVGRenderContext::strokePaint() const { if (paint.has_value()) { paint->setStyle(PaintStyle::Stroke); - paint->setStrokeWidth( - _lengthContext->resolve(*props.StrokeWidth, SVGLengthContext::LengthType::Other)); Stroke stroke; + stroke.width = _lengthContext->resolve(*props.StrokeWidth, SVGLengthContext::LengthType::Other); stroke.cap = toCap(*props.StrokeLineCap); stroke.join = toJoin(*props.StrokeLineJoin); stroke.miterLimit = *props.StrokeMiterLimit; paint->setStroke(stroke); - - dash_effect(props, *_lengthContext); } - return paint; } +std::shared_ptr SVGRenderContext::strokePathEffect() const { + const auto& props = _presentationContext->_inherited; + return dash_effect(props, *_lengthContext); +} + SVGColorType SVGRenderContext::resolveSVGColor(const SVGColor& color) const { if (_presentationContext->_namedColors) { for (auto&& ident : *color.vars()) { @@ -411,25 +413,72 @@ SVGColorType SVGRenderContext::resolveSVGColor(const SVGColor& color) const { } } -std::tuple SVGRenderContext::resolveFont() const { - // Since there may be SVGs without any text, we accept the absence of a font manager. - // However, this will result in the inability to render text. - if (!fontManager) { - return {false, Font()}; - } - const auto& family = _presentationContext->_inherited.FontFamily->family(); - auto fontWeight = _presentationContext->_inherited.FontWeight->type(); - auto fontStyle = _presentationContext->_inherited.FontStyle->type(); +Font SVGRenderContext::resolveFont() const { + if (!_fontManager) { + return Font(); + } + auto weight = [](const SVGFontWeight& w) { + switch (w.type()) { + case SVGFontWeight::Type::W100: + return FontStyle::Weight::Thin; + case SVGFontWeight::Type::W200: + return FontStyle::Weight::ExtraLight; + case SVGFontWeight::Type::W300: + return FontStyle::Weight::Light; + case SVGFontWeight::Type::W400: + return FontStyle::Weight::Normal; + case SVGFontWeight::Type::W500: + return FontStyle::Weight::Medium; + case SVGFontWeight::Type::W600: + return FontStyle::Weight::SemiBold; + case SVGFontWeight::Type::W700: + return FontStyle::Weight::Bold; + case SVGFontWeight::Type::W800: + return FontStyle::Weight::ExtraBold; + case SVGFontWeight::Type::W900: + return FontStyle::Weight::Black; + case SVGFontWeight::Type::Normal: + return FontStyle::Weight::Normal; + case SVGFontWeight::Type::Bold: + return FontStyle::Weight::Bold; + case SVGFontWeight::Type::Bolder: + return FontStyle::Weight::ExtraBold; + case SVGFontWeight::Type::Lighter: + return FontStyle::Weight::Light; + case SVGFontWeight::Type::Inherit: { + return FontStyle::Weight::Normal; + } + } + }; + + auto slant = [](const SVGFontStyle& s) { + switch (s.type()) { + case SVGFontStyle::Type::Normal: + return FontStyle::Slant::Upright; + case SVGFontStyle::Type::Italic: + return FontStyle::Slant::Italic; + case SVGFontStyle::Type::Oblique: + return FontStyle::Slant::Oblique; + case SVGFontStyle::Type::Inherit: { + return FontStyle::Slant::Upright; + } + } + }; - SVGFontInfo info(fontWeight, fontStyle); - auto typeface = fontManager->getTypefaceForRendering(family, info); + const auto& family = presentationContext()._inherited.FontFamily->family(); + const FontStyle style(weight(*presentationContext()._inherited.FontWeight), + FontStyle::Width::Normal, + slant(*presentationContext()._inherited.FontStyle)); + + const auto size = lengthContext().resolve(presentationContext()._inherited.FontSize->size(), + SVGLengthContext::LengthType::Vertical); + + auto typeface = _fontManager->matchFamilyStyle(family, style); if (!typeface) { - return {false, Font()}; + typeface = _fontManager->matchFamilyStyle("", style); } - - float size = _lengthContext->resolve(_presentationContext->_inherited.FontSize->size(), - SVGLengthContext::LengthType::Vertical); - return {true, Font(typeface, size)}; + Font font(std::move(typeface), size); + return font; } SVGRenderContext::OBBTransform SVGRenderContext::transformForCurrentBoundBox( @@ -458,4 +507,23 @@ Rect SVGRenderContext::resolveOBBRect(const SVGLength& x, const SVGLength& y, co transform.scale.y * r.y() + transform.offset.y, transform.scale.x * r.width(), transform.scale.y * r.height()); } +std::unique_ptr SVGRenderContext::makeShaper() const { + DEBUG_ASSERT(shaperFactory); + return shaperFactory->makeShaper(); +} + +std::unique_ptr SVGRenderContext::makeBidiRunIterator(const char* utf8, + size_t utf8Bytes, + uint8_t bidiLevel) const { + DEBUG_ASSERT(shaperFactory); + return shaperFactory->makeBidiRunIterator(utf8, utf8Bytes, bidiLevel); +} + +std::unique_ptr SVGRenderContext::makeScriptRunIterator(const char* utf8, + size_t utf8Bytes) const { + DEBUG_ASSERT(shaperFactory); + constexpr FourByteTag unknownScript = SetFourByteTag('Z', 'z', 'z', 'z'); + return shaperFactory->makeScriptRunIterator(utf8, utf8Bytes, unknownScript); +} + } // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGRenderContext.h b/src/svg/SVGRenderContext.h index 9f96fdd5..3a09aa6b 100644 --- a/src/svg/SVGRenderContext.h +++ b/src/svg/SVGRenderContext.h @@ -23,12 +23,16 @@ #include #include #include +#include "core/utils/Log.h" #include "svg/SVGLengthContext.h" #include "tgfx/core/Canvas.h" +#include "tgfx/core/FontManager.h" +#include "tgfx/core/LoadResourceProvider.h" #include "tgfx/core/MaskFilter.h" #include "tgfx/core/Matrix.h" #include "tgfx/core/Paint.h" #include "tgfx/core/Path.h" +#include "tgfx/core/PathEffect.h" #include "tgfx/core/Point.h" #include "tgfx/core/Recorder.h" #include "tgfx/core/Rect.h" @@ -36,8 +40,8 @@ #include "tgfx/gpu/Context.h" #include "tgfx/svg/SVGAttribute.h" #include "tgfx/svg/SVGDOM.h" -#include "tgfx/svg/SVGFontManager.h" #include "tgfx/svg/SVGTypes.h" +#include "tgfx/svg/shaper/ShaperFactory.h" namespace tgfx { @@ -110,8 +114,8 @@ class CopyOnWrite { struct SVGPresentationContext { SVGPresentationContext(); - SVGPresentationContext(const SVGPresentationContext&) = default; - SVGPresentationContext& operator=(const SVGPresentationContext&) = default; + SVGPresentationContext(const SVGPresentationContext& other) = default; + SVGPresentationContext& operator=(const SVGPresentationContext& other) = default; const std::unordered_map* _namedColors = nullptr; // Inherited presentation attributes, computed for the current node. @@ -126,18 +130,23 @@ class SVGRenderContext { const SVGRenderContext* context; }; - SVGRenderContext(Canvas*, const std::shared_ptr&, const SVGIDMapper&, - const SVGLengthContext&, const SVGPresentationContext&, const OBBScope&, + SVGRenderContext(Canvas* canvas, const std::shared_ptr& fontManager, + const std::shared_ptr& resourceProvider, + const std::shared_ptr& shaperFactory, + const SVGIDMapper& mapper, const SVGLengthContext& lengthContext, + const SVGPresentationContext& presentContext, const OBBScope& scope, const Matrix& matrix); - SVGRenderContext(const SVGRenderContext&); - SVGRenderContext(const SVGRenderContext&, Canvas*); - SVGRenderContext(const SVGRenderContext&, const SVGLengthContext&); - SVGRenderContext(const SVGRenderContext&, Canvas*, const SVGLengthContext&); + SVGRenderContext(const SVGRenderContext& other); + SVGRenderContext(const SVGRenderContext& other, Canvas* canvas); + SVGRenderContext(const SVGRenderContext& other, const SVGLengthContext& lengthContext); + SVGRenderContext(const SVGRenderContext& other, Canvas* canvas, + const SVGLengthContext& lengthContext); // Establish a new OBB scope. Normally used when entering a node's render scope. - SVGRenderContext(const SVGRenderContext&, const SVGNode*); + SVGRenderContext(const SVGRenderContext& other, const SVGNode* node); ~SVGRenderContext(); - static SVGRenderContext CopyForPaint(const SVGRenderContext&, Canvas*, const SVGLengthContext&); + static SVGRenderContext CopyForPaint(const SVGRenderContext& context, Canvas* canvas, + const SVGLengthContext& lengthContext); const SVGLengthContext& lengthContext() const { return *_lengthContext; @@ -158,11 +167,15 @@ class SVGRenderContext { void saveOnce(); void concat(const Matrix& inputMatrix) { - matrix.preConcat(inputMatrix); + _matrix.preConcat(inputMatrix); } - enum ApplyFlags { - kLeaf = 1 << 0, // the target node doesn't have descendants + const Matrix& matrix() const { + return _matrix; + } + + enum class ApplyFlags { + Leaf = 1 << 0, // the target node doesn't have descendants }; void applyPresentationAttributes(const SVGPresentationAttributes& attrs, uint32_t flags); @@ -175,19 +188,38 @@ class SVGRenderContext { std::optional strokePaint() const; + std::shared_ptr strokePathEffect() const; + SVGColorType resolveSVGColor(const SVGColor&) const; - std::tuple resolveFont() const; + Font resolveFont() const; // The local computed clip path (not inherited). Path clipPath() const { return _clipPath.value_or(Path()); }; + const std::shared_ptr& resourceProvider() const { + return _resourceProvider; + } + + const std::shared_ptr& fontManager() const { + return _fontManager; + } + + std::unique_ptr makeShaper() const; + + std::unique_ptr makeBidiRunIterator(const char* utf8, size_t utf8Bytes, + uint8_t bidiLevel) const; + + std::unique_ptr makeScriptRunIterator(const char* utf8, + size_t utf8Bytes) const; + // Returns the translate/scale transformation required to map into the current OBB scope, // with the specified units. struct OBBTransform { - Point offset, scale; + Point offset; + Point scale; }; OBBTransform transformForCurrentBoundBox(SVGObjectBoundingBoxUnits) const; @@ -202,20 +234,21 @@ class SVGRenderContext { private: float applyOpacity(float opacity, uint32_t flags, bool hasFilter); - std::shared_ptr applyFilter(const SVGFuncIRI&) const; - Path applyClip(const SVGFuncIRI&) const; - std::shared_ptr applyMask(const SVGFuncIRI&); + std::shared_ptr applyFilter(const SVGFuncIRI& filter) const; + Path applyClip(const SVGFuncIRI& clip) const; + std::shared_ptr applyMask(const SVGFuncIRI& mask); - std::optional commonPaint(const SVGPaint&, float opacity) const; + std::optional commonPaint(const SVGPaint& paint, float opacity) const; + + std::shared_ptr _fontManager; + std::shared_ptr _resourceProvider; + std::shared_ptr shaperFactory; - std::shared_ptr fontManager; const SVGIDMapper& nodeIDMapper; CopyOnWrite _lengthContext; CopyOnWrite _presentationContext; - Canvas* renderCanvas; - - Recorder recorder; Canvas* _canvas; + int canvasSaveCount = 0; // clipPath, if present for the current context (not inherited). std::optional _clipPath; @@ -226,8 +259,6 @@ class SVGRenderContext { // Current object bounding box scope. const OBBScope scope; - Paint picturePaint; - - Matrix matrix = Matrix::I(); + Matrix _matrix = Matrix::I(); }; } // namespace tgfx diff --git a/src/svg/SVGTextBuilder.h b/src/svg/SVGTextBuilder.h index 98b41bb6..5932d69b 100644 --- a/src/svg/SVGTextBuilder.h +++ b/src/svg/SVGTextBuilder.h @@ -18,7 +18,6 @@ #pragma once -#include #include #include "core/utils/GlyphConverter.h" #include "tgfx/core/GlyphRun.h" diff --git a/src/svg/SVGTextContext.cpp b/src/svg/SVGTextContext.cpp new file mode 100644 index 00000000..6567d9f2 --- /dev/null +++ b/src/svg/SVGTextContext.cpp @@ -0,0 +1,328 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SVGTextContext.h" +#include +#include +#include +#include "core/utils/Log.h" +#include "core/utils/MathExtra.h" +#include "tgfx/core/Font.h" +#include "tgfx/core/Matrix.h" +#include "tgfx/core/Paint.h" +#include "tgfx/core/Point.h" +#include "tgfx/core/Typeface.h" +#include "tgfx/core/UTF.h" +#include "tgfx/svg/shaper/ShaperFactory.h" + +namespace tgfx { + +namespace { + +std::vector ResolveLengths(const SVGLengthContext& lctx, + const std::vector& lengths, + SVGLengthContext::LengthType lt) { + std::vector resolved; + resolved.reserve(lengths.size()); + + for (const auto& l : lengths) { + resolved.push_back(lctx.resolve(l, lt)); + } + + return resolved; +} + +} // namespace + +SVGTextContext::ScopedPosResolver::ScopedPosResolver(const SVGTextContainer& txt, + const SVGLengthContext& lctx, + SVGTextContext* tctx, size_t charIndexOffset) + : textContext(tctx), parent(tctx->posResolver), charIndexOffset(charIndexOffset), + x(ResolveLengths(lctx, txt.getX(), SVGLengthContext::LengthType::Horizontal)), + y(ResolveLengths(lctx, txt.getY(), SVGLengthContext::LengthType::Vertical)), + dX(ResolveLengths(lctx, txt.getDx(), SVGLengthContext::LengthType::Horizontal)), + dY(ResolveLengths(lctx, txt.getDy(), SVGLengthContext::LengthType::Vertical)), + rotate(txt.getRotate()) { + textContext->posResolver = this; +} + +SVGTextContext::ScopedPosResolver::ScopedPosResolver(const SVGTextContainer& txt, + const SVGLengthContext& lctx, + SVGTextContext* tctx) + : ScopedPosResolver(txt, lctx, tctx, tctx->currentCharIndex) { +} + +SVGTextContext::ScopedPosResolver::~ScopedPosResolver() { + textContext->posResolver = parent; +} + +/////////////////////////////////////////////////////////////////////////////// + +SVGTextContext::PosAttrs SVGTextContext::ScopedPosResolver::resolve(size_t charIndex) const { + PosAttrs attrs; + + if (charIndex < fLastPosIndex) { + ASSERT(charIndex >= charIndexOffset); + const auto localCharIndex = charIndex - charIndexOffset; + + const auto hasAllLocal = localCharIndex < x.size() && localCharIndex < y.size() && + localCharIndex < dX.size() && localCharIndex < dY.size() && + localCharIndex < rotate.size(); + if (!hasAllLocal && parent) { + attrs = parent->resolve(charIndex); + } + + if (localCharIndex < x.size()) { + attrs[PosAttrs::x] = x[localCharIndex]; + } + if (localCharIndex < y.size()) { + attrs[PosAttrs::y] = y[localCharIndex]; + } + if (localCharIndex < dX.size()) { + attrs[PosAttrs::dx] = dX[localCharIndex]; + } + if (localCharIndex < dY.size()) { + attrs[PosAttrs::dy] = dY[localCharIndex]; + } + + // Rotation semantics are interestingly different [1]: + // + // - values are not cumulative + // - if explicit values are present at any level in the ancestor chain, those take + // precedence (closest ancestor) + // - last specified value applies to all remaining chars (closest ancestor) + // - these rules apply at node scope (not chunk scope) + // + // This means we need to discriminate between explicit rotation (rotate value provided for + // current char) and implicit rotation (ancestor has some values - but not for the requested + // char - we use the last specified value). + // + // [1] https://www.w3.org/TR/SVG11/text.html#TSpanElementRotateAttribute + if (!rotate.empty()) { + if (localCharIndex < rotate.size()) { + // Explicit rotation value overrides anything in the ancestor chain. + attrs[PosAttrs::rotate] = rotate[localCharIndex]; + attrs.setImplicitRotate(false); + } else if (!attrs.has(PosAttrs::rotate) || attrs.isImplicitRotate()) { + // Local implicit rotation (last specified value) overrides ancestor implicit + // rotation. + attrs[PosAttrs::rotate] = rotate.back(); + attrs.setImplicitRotate(true); + } + } + + if (!attrs.hasAny()) { + // Once we stop producing explicit position data, there is no reason to + // continue trying for higher indices. We can suppress future lookups. + fLastPosIndex = charIndex; + } + } + + return attrs; +} + +void SVGTextContext::ShapeBuffer::append(Unichar ch, PositionAdjustment pos) { + // relative pos adjustments are cumulative + if (!utf8PosAdjust.empty()) { + pos.offset += utf8PosAdjust.back().offset; + } + + auto unf8String = UTF::ToUTF8(ch); + utf8.append(unf8String); + utf8PosAdjust.insert(utf8PosAdjust.end(), unf8String.size(), pos); +} + +void SVGTextContext::shapePendingBuffer(const SVGRenderContext& ctx, const Font& font) { + const char* utf8 = shapeBuffer.utf8.data(); + size_t utf8Bytes = shapeBuffer.utf8.size(); + + std::unique_ptr font_runs = + Shaper::MakeFontMgrRunIterator(utf8, utf8Bytes, font, ctx.fontManager()); + if (!font_runs) { + return; + } + + // Try to use the passed in shaping callbacks to shape, for example, using harfbuzz and ICU. + const uint8_t defaultLTR = 0; + std::unique_ptr bidi = ctx.makeBidiRunIterator(utf8, utf8Bytes, defaultLTR); + std::unique_ptr language = + Shaper::MakeStdLanguageRunIterator(utf8, utf8Bytes); + std::unique_ptr script = ctx.makeScriptRunIterator(utf8, utf8Bytes); + + if (bidi && script && language) { + shaper->shape(utf8, utf8Bytes, *font_runs, *bidi, *script, *language, nullptr, 0, FLT_MAX, + this); + shapeBuffer.reset(); + return; + } // If any of the callbacks fail, we'll fallback to the primitive shaping. +} + +SVGTextContext::SVGTextContext(const SVGRenderContext& ctx, const ShapedTextCallback& cb, + const SVGTextPath* tpath) + : originalContext(ctx), callback(cb), shaper(ctx.makeShaper()), + chunkAlignmentFactor(ctx.presentationContext()._inherited.TextAnchor->getAlignmentFactor()) { + + if (tpath) { + chunkPos.x = tpath->getStartOffset().value(); + } +} + +SVGTextContext::~SVGTextContext() { + this->flushChunk(originalContext); +} + +void SVGTextContext::shapeFragment(const std::string& txt, const SVGRenderContext& ctx, + SVGXmlSpace xs) { + // https://www.w3.org/TR/SVG11/text.html#WhiteSpace + // https://www.w3.org/TR/2008/REC-xml-20081126/#NT-S + auto filterWSDefault = [this](Unichar ch) -> Unichar { + // Remove all newline chars. + if (ch == '\n') { + return -1; + } + + // Convert tab chars to space. + if (ch == '\t') { + ch = ' '; + } + + // Consolidate contiguous space chars and strip leading spaces (fPrevCharSpace + // starts off as true). + if (prevCharSpace && ch == ' ') { + return -1; + } + return ch; + }; + auto filterWSPreserve = [](Unichar ch) -> Unichar { + // Convert newline and tab chars to space. + if (ch == '\n' || ch == '\t') { + ch = ' '; + } + return ch; + }; + + // Stash paints for access from SkShaper callbacks. + currentFill = ctx.fillPaint(); + currentStroke = ctx.strokePaint(); + + const auto font = ctx.resolveFont(); + shapeBuffer.reserve(txt.size()); + + const char* chatPointer = txt.c_str(); + const char* charEnd = chatPointer + txt.size(); + + while (chatPointer < charEnd) { + auto ch = UTF::NextUTF8(&chatPointer, charEnd); + ch = (xs == SVGXmlSpace::Default) ? filterWSDefault(ch) : filterWSPreserve(ch); + + if (ch < 0) { + // invalid utf or char filtered out + continue; + } + + ASSERT(posResolver); + const auto pos = posResolver->resolve(currentCharIndex++); + + // Absolute position adjustments define a new chunk. + // (https://www.w3.org/TR/SVG11/text.html#TextLayoutIntroduction) + if (pos.has(PosAttrs::x) || pos.has(PosAttrs::y)) { + this->shapePendingBuffer(ctx, font); + this->flushChunk(ctx); + + // New chunk position. + if (pos.has(PosAttrs::x)) { + chunkPos.x = pos[PosAttrs::x]; + } + if (pos.has(PosAttrs::y)) { + chunkPos.y = pos[PosAttrs::y]; + } + } + + shapeBuffer.append(ch, + { + { + pos.has(PosAttrs::dx) ? pos[PosAttrs::dx] : 0, + pos.has(PosAttrs::dy) ? pos[PosAttrs::dy] : 0, + }, + pos.has(PosAttrs::rotate) ? DegreesToRadians(pos[PosAttrs::rotate]) : 0, + }); + + prevCharSpace = (ch == ' '); + } + + this->shapePendingBuffer(ctx, font); + + // Note: at this point we have shaped and buffered RunRecs for the current fragment. + // The active text chunk continues until an explicit or implicit flush. +} + +Matrix SVGTextContext::computeGlyphXform(GlyphID /*glyph*/, const Font& /*font*/, + const Point& glyph_pos, + const PositionAdjustment& pos_adjust) const { + Point pos = chunkPos + glyph_pos + pos_adjust.offset + chunkAdvance * chunkAlignmentFactor; + return Matrix::MakeRotate(RadiansToDegrees(pos_adjust.rotation), pos.x, pos.y); +} + +RunHandler::Buffer SVGTextContext::runBuffer(const RunInfo& ri) { + ASSERT(ri.glyphCount); + + RunRec runRecord = { + ri.font, + currentFill.has_value() ? std::make_unique(*currentFill) : nullptr, + currentStroke.has_value() ? std::make_unique(*currentStroke) : nullptr, + std::make_unique(ri.glyphCount), + std::make_unique(ri.glyphCount), + std::make_unique(ri.glyphCount), + ri.glyphCount, + ri.advance, + }; + + runs.push_back(std::move(runRecord)); + + // Ensure sufficient space to temporarily fetch cluster information. + shapeClusterBuffer.resize(std::max(shapeClusterBuffer.size(), ri.glyphCount)); + + return { + runs.back().glyphs.get(), + runs.back().glyphPos.get(), + nullptr, + shapeClusterBuffer.data(), + chunkAdvance, + }; +} + +void SVGTextContext::commitRunBuffer(const RunInfo& ri) { + const auto& current_run = runs.back(); + + // stash position adjustments + for (size_t i = 0; i < ri.glyphCount; ++i) { + const auto utf8_index = shapeClusterBuffer[i]; + current_run.glyphPosAdjust[i] = shapeBuffer.utf8PosAdjust[utf8_index]; + } + + chunkAdvance += ri.advance; +} + +void SVGTextContext::commitLine() { + if (!shapeBuffer.utf8PosAdjust.empty()) { + // Offset adjustments are cumulative - only advance the current chunk with the last value. + chunkAdvance += shapeBuffer.utf8PosAdjust.back().offset; + } +} + +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGTextContext.h b/src/svg/SVGTextContext.h new file mode 100644 index 00000000..eabd9411 --- /dev/null +++ b/src/svg/SVGTextContext.h @@ -0,0 +1,217 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include "svg/SVGRenderContext.h" +#include "tgfx/core/Font.h" +#include "tgfx/core/Matrix.h" +#include "tgfx/core/Paint.h" +#include "tgfx/core/Point.h" +#include "tgfx/core/Typeface.h" +#include "tgfx/svg/node/SVGText.h" +#include "tgfx/svg/shaper/Shaper.h" + +namespace tgfx { + +class SVGTextContext final : RunHandler { + public: + using ShapedTextCallback = + std::function&)>; + + // Helper for encoding optional positional attributes. + class PosAttrs { + public: + enum Attr : size_t { + x = 0, + y = 1, + dx = 2, + dy = 3, + rotate = 4, + }; + + float operator[](Attr a) const { + return storage[a]; + } + float& operator[](Attr a) { + return storage[a]; + } + + bool has(Attr a) const { + return storage[a] != _NONE; + } + bool hasAny() const { + return this->has(x) || this->has(y) || this->has(dx) || this->has(dy) || this->has(rotate); + } + + void setImplicitRotate(bool imp) { + implicitRotate = imp; + } + bool isImplicitRotate() const { + return implicitRotate; + } + + private: + inline static constexpr auto _NONE = std::numeric_limits::infinity(); + float storage[5] = {_NONE, _NONE, _NONE, _NONE, _NONE}; + bool implicitRotate = false; + }; + + // Helper for cascading position attribute resolution (x, y, dx, dy, rotate) [1]: + // - each text position element can specify an arbitrary-length attribute array + // - for each character, we look up a given attribute first in its local attribute array, + // then in the ancestor chain (cascading/fallback) - and return the first value encountered. + // - the lookup is based on character index relative to the text content subtree + // (i.e. the index crosses chunk boundaries) + // + // [1] https://www.w3.org/TR/SVG11/text.html#TSpanElementXAttribute + class ScopedPosResolver { + public: + ScopedPosResolver(const SVGTextContainer&, const SVGLengthContext&, SVGTextContext*, size_t); + + ScopedPosResolver(const SVGTextContainer&, const SVGLengthContext&, SVGTextContext*); + + ~ScopedPosResolver(); + + PosAttrs resolve(size_t charIndex) const; + + private: + SVGTextContext* textContext; + const ScopedPosResolver* parent; // parent resolver (fallback) + const size_t charIndexOffset; // start index for the current resolver + const std::vector x, y, dX, dY; + const std::vector& rotate; + + // cache for the last known index with explicit positioning + mutable size_t fLastPosIndex = std::numeric_limits::max(); + }; + + SVGTextContext(const SVGRenderContext&, const ShapedTextCallback&, const SVGTextPath* = nullptr); + ~SVGTextContext() override; + + // Shape and queue codepoints for final alignment. + void shapeFragment(const std::string&, const SVGRenderContext&, SVGXmlSpace); + + // Perform final adjustments and push shaped blobs to the callback. + void flushChunk(const SVGRenderContext& ctx); + + const ShapedTextCallback& getCallback() const { + return callback; + } + + private: + struct PositionAdjustment { + Point offset; + float rotation; + }; + + struct ShapeBuffer { + std::string utf8; + // per-utf8-char cumulative pos adjustments + std::vector utf8PosAdjust; + + void reserve(size_t size) { + utf8.reserve(size); + utf8PosAdjust.reserve(utf8PosAdjust.size()); + } + + void reset() { + utf8.clear(); + utf8PosAdjust.clear(); + } + + void append(Unichar, PositionAdjustment); + }; + + struct RunRec { + Font font; + std::unique_ptr fillPaint, strokePaint; + std::unique_ptr glyphs; // filled by SkShaper + std::unique_ptr glyphPos; // filled by SkShaper + std::unique_ptr glyphPosAdjust; // deferred positioning adjustments + size_t glyphCount; + Point advance; + }; + + // Caches path information to accelerate position lookups. + class PathData { + public: + PathData(const SVGRenderContext&, const SVGTextPath&); + + Matrix getMatrixAt(float offset) const; + + float length() const { + return fLength; + } + + private: + // std::vector> fContours; + float fLength = 0; // total path length + }; + + void shapePendingBuffer(const SVGRenderContext&, const Font&); + + Matrix computeGlyphXform(GlyphID, const Font&, const Point& glyph_pos, + const PositionAdjustment&) const; + + //callbacks + void beginLine() override { + } + + void runInfo(const RunInfo&) override { + } + + void commitRunInfo() override { + } + + Buffer runBuffer(const RunInfo& ri) override; + + void commitRunBuffer(const RunInfo& ri) override; + + void commitLine() override; + + // http://www.w3.org/TR/SVG11/text.html#TextLayout + const SVGRenderContext& originalContext; // original render context + const ShapedTextCallback& callback; + std::unique_ptr shaper = nullptr; + std::vector runs = {}; + const ScopedPosResolver* posResolver = nullptr; + + // shaper state + ShapeBuffer shapeBuffer = {}; + std::vector shapeClusterBuffer = {}; + + // chunk state + Point chunkPos = {0, 0}; // current text chunk position + Point chunkAdvance = {0, 0}; // cumulative advance + float chunkAlignmentFactor; // current chunk alignment + + // tracks the global text subtree char index (cross chunks). Used for position resolution. + size_t currentCharIndex = 0; + + // cached for access from SkShaper callbacks. + std::optional currentFill; + std::optional currentStroke; + + bool prevCharSpace = true; // WS filter state + bool forcePrimitiveShaping = false; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGUtils.cpp b/src/svg/SVGUtils.cpp index ddfd56bf..e1e7b77d 100644 --- a/src/svg/SVGUtils.cpp +++ b/src/svg/SVGUtils.cpp @@ -17,7 +17,6 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "SVGUtils.h" -#include #include #include #include @@ -456,7 +455,8 @@ const char* find_flag(const char str[], bool* value) { } } // namespace -std::tuple> PathMakeFromSVGString(const char data[]) { +std::tuple> PathMakeFromSVGString(const std::string& pathString) { + const char* data = pathString.c_str(); // We will write all data to this local path and only write it // to result if the whole parsing succeeds. auto path = std::make_shared(); diff --git a/src/svg/SVGUtils.h b/src/svg/SVGUtils.h index 8953287b..5c429d1b 100644 --- a/src/svg/SVGUtils.h +++ b/src/svg/SVGUtils.h @@ -50,7 +50,7 @@ enum class PathEncoding { std::string ToSVGPath(const Path& path, PathEncoding = PathEncoding::Absolute); -std::tuple> PathMakeFromSVGString(const char data[]); +std::tuple> PathMakeFromSVGString(const std::string& pathString); std::string ToSVGTransform(const Matrix& matrix); diff --git a/src/svg/node/SVGCircle.cpp b/src/svg/node/SVGCircle.cpp index af05f265..3bf34b73 100644 --- a/src/svg/node/SVGCircle.cpp +++ b/src/svg/node/SVGCircle.cpp @@ -31,9 +31,9 @@ SVGCircle::SVGCircle() : INHERITED(SVGTag::Circle) { bool SVGCircle::parseAndSetAttribute(const std::string& name, const std::string& value) { return INHERITED::parseAndSetAttribute(name, value) || - this->setCx(SVGAttributeParser::parse("cx", name, value)) || - this->setCy(SVGAttributeParser::parse("cy", name, value)) || - this->setR(SVGAttributeParser::parse("r", name, value)); + setCx(SVGAttributeParser::parse("cx", name, value)) || + setCy(SVGAttributeParser::parse("cy", name, value)) || + setR(SVGAttributeParser::parse("r", name, value)); } std::tuple SVGCircle::resolve(const SVGLengthContext& lengthContext) const { @@ -44,15 +44,32 @@ std::tuple SVGCircle::resolve(const SVGLengthContext& lengthContex return std::make_tuple(Point::Make(cx, cy), r); } -void SVGCircle::onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, - PathFillType /*type*/) const { - auto [pos, r] = this->resolve(lengthContext); +void SVGCircle::onDrawFill(Canvas* canvas, const SVGLengthContext& lengthContext, + const Paint& paint, PathFillType /*type*/) const { + auto [pos, r] = resolve(lengthContext); if (r > 0) { canvas->drawCircle(pos.x, pos.y, r, paint); } } +void SVGCircle::onDrawStroke(Canvas* canvas, const SVGLengthContext& lengthContext, + const Paint& paint, PathFillType /*fillType*/, + std::shared_ptr pathEffect) const { + if (!pathEffect) { + return; + } + + auto [pos, r] = resolve(lengthContext); + if (r > 0) { + Path path; + path.addOval(Rect::MakeXYWH(pos.x - r, pos.y - r, 2 * r, 2 * r)); + if (pathEffect->filterPath(&path)) { + canvas->drawPath(path, paint); + } + } +}; + Path SVGCircle::onAsPath(const SVGRenderContext& context) const { auto [pos, r] = this->resolve(context.lengthContext()); diff --git a/src/svg/node/SVGEllipse.cpp b/src/svg/node/SVGEllipse.cpp index 58a6bc34..c462e23f 100644 --- a/src/svg/node/SVGEllipse.cpp +++ b/src/svg/node/SVGEllipse.cpp @@ -50,11 +50,25 @@ Rect SVGEllipse::resolve(const SVGLengthContext& lengthContext) const { return (rx > 0 && ry > 0) ? Rect::MakeXYWH(cx - rx, cy - ry, rx * 2, ry * 2) : Rect::MakeEmpty(); } -void SVGEllipse::onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, - PathFillType /*fillType*/) const { +void SVGEllipse::onDrawFill(Canvas* canvas, const SVGLengthContext& lengthContext, + const Paint& paint, PathFillType /*fillType*/) const { canvas->drawOval(this->resolve(lengthContext), paint); } +void SVGEllipse::onDrawStroke(Canvas* canvas, const SVGLengthContext& lengthContext, + const Paint& paint, PathFillType /*fillType*/, + std::shared_ptr pathEffect) const { + if (!pathEffect) { + return; + } + + Path path; + path.addOval(resolve(lengthContext)); + if (pathEffect->filterPath(&path)) { + canvas->drawPath(path, paint); + } +}; + Path SVGEllipse::onAsPath(const SVGRenderContext& context) const { Path path; path.addOval(this->resolve(context.lengthContext())); diff --git a/src/svg/node/SVGFeColorMatrix.cpp b/src/svg/node/SVGFeColorMatrix.cpp index 029c22e3..6a27e6c6 100644 --- a/src/svg/node/SVGFeColorMatrix.cpp +++ b/src/svg/node/SVGFeColorMatrix.cpp @@ -20,6 +20,7 @@ #include #include "core/utils/MathExtra.h" #include "svg/SVGAttributeParser.h" +#include "svg/SVGFilterContext.h" #include "tgfx/core/ColorFilter.h" #include "tgfx/core/ImageFilter.h" @@ -143,9 +144,15 @@ ColorMatrix SVGFeColorMatrix::MakeLuminanceToAlpha() { 0, 0, 0, 0, 0, LUM_COEFF_R, LUM_COEFF_G, LUM_COEFF_B, 0, 0}; } -std::shared_ptr SVGFeColorMatrix::onMakeImageFilter(const SVGRenderContext&, - const SVGFilterContext&) const { - return ImageFilter::ColorFilter(ColorFilter::Matrix(makeMatrixForType())); +std::shared_ptr SVGFeColorMatrix::onMakeImageFilter( + const SVGRenderContext& context, const SVGFilterContext& filterContext) const { + auto colorspace = resolveColorspace(context, filterContext); + auto colorFilter = ImageFilter::ColorFilter(ColorFilter::Matrix(makeMatrixForType())); + + if (auto inputFilter = filterContext.resolveInput(context, this->getIn(), colorspace)) { + colorFilter = ImageFilter::Compose(colorFilter, inputFilter); + } + return colorFilter; } } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGFeGaussianBlur.cpp b/src/svg/node/SVGFeGaussianBlur.cpp index 9ce7702b..7cd7c08f 100644 --- a/src/svg/node/SVGFeGaussianBlur.cpp +++ b/src/svg/node/SVGFeGaussianBlur.cpp @@ -34,8 +34,8 @@ bool SVGFeGaussianBlur::parseAndSetAttribute(const std::string& name, const std: std::shared_ptr SVGFeGaussianBlur::onMakeImageFilter( const SVGRenderContext& context, const SVGFilterContext& filterContext) const { auto scale = context.transformForCurrentBoundBox(filterContext.primitiveUnits()).scale; - const auto sigmaX = stdDeviation.X * scale.x * 4; - const auto sigmaY = stdDeviation.Y * scale.y * 4; + const auto sigmaX = stdDeviation.X * scale.x * 4 * context.matrix().getScaleX(); + const auto sigmaY = stdDeviation.Y * scale.y * 4 * context.matrix().getScaleY(); return ImageFilter::Blur(sigmaX, sigmaY); } diff --git a/src/svg/node/SVGFilter.cpp b/src/svg/node/SVGFilter.cpp index f5944f43..e0b22ff2 100644 --- a/src/svg/node/SVGFilter.cpp +++ b/src/svg/node/SVGFilter.cpp @@ -17,12 +17,21 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGFilter.h" +#include +#include #include "svg/SVGAttributeParser.h" #include "svg/SVGFilterContext.h" #include "svg/SVGRenderContext.h" #include "tgfx/core/ImageFilter.h" +#include "tgfx/core/Point.h" #include "tgfx/core/Rect.h" #include "tgfx/svg/node/SVGFe.h" +#include "tgfx/svg/node/SVGFeBlend.h" +#include "tgfx/svg/node/SVGFeColorMatrix.h" +#include "tgfx/svg/node/SVGFeComposite.h" +#include "tgfx/svg/node/SVGFeGaussianBlur.h" +#include "tgfx/svg/node/SVGFeOffset.h" +#include "tgfx/svg/node/SVGNode.h" namespace tgfx { @@ -48,6 +57,13 @@ std::shared_ptr SVGFilter::buildFilterDAG(const SVGRenderContext& c PrimitiveUnits); SVGRenderContext localContext(context); this->applyProperties(&localContext); + if (auto dropShadowFilter = buildDropShadowFilter(localContext, filterContext)) { + return dropShadowFilter; + } + if (auto innerShadowFilter = buildInnerShadowFilter(localContext, filterContext)) { + return innerShadowFilter; + } + SVGColorspace cs = SVGColorspace::SRGB; for (const auto& child : children) { if (!SVGFe::IsFilterEffect(child)) { @@ -67,6 +83,9 @@ std::shared_ptr SVGFilter::buildFilterDAG(const SVGRenderContext& c const Rect filterSubregion = feNode.resolveFilterSubregion(localChildCtx, filterContext); cs = feNode.resolveColorspace(localChildCtx, filterContext); filter = feNode.makeImageFilter(localChildCtx, filterContext); + if (!filter) { + return nullptr; + } if (!feResultType.empty()) { filterContext.registerResult(feResultType, filter, filterSubregion, cs); @@ -76,13 +95,112 @@ std::shared_ptr SVGFilter::buildFilterDAG(const SVGRenderContext& c filterContext.setPreviousResult(filter, filterSubregion, cs); } - // Convert to final destination colorspace - if (cs != SVGColorspace::SRGB) { - //TODO (YGAurora): Implement color space conversion - filter = nullptr; + //TODO (YGAurora): Implement color space conversion + return filter; +} + +std::shared_ptr SVGFilter::buildDropShadowFilter( + const SVGRenderContext& context, const SVGFilterContext& filterContext) const { + if (children.size() < 3) { + return nullptr; } - return filter; + size_t blurIndex = 0; + for (size_t i = 0; i < children.size(); i++) { + if (children[i]->tag() == SVGTag::FeGaussianBlur && i >= 1 && + children[i - 1]->tag() == SVGTag::FeOffset) { + blurIndex = i; + break; + } + } + + if (blurIndex < 1 || blurIndex == children.size() - 1) { + return nullptr; + } + + std::shared_ptr offsetFe = + std::static_pointer_cast(children[blurIndex - 1]); + std::shared_ptr blurFe = + std::static_pointer_cast(children[blurIndex]); + std::shared_ptr colorMatrixFe = nullptr; + + if (blurIndex + 1 < children.size() && children[blurIndex + 1]->tag() == SVGTag::FeColorMatrix) { + colorMatrixFe = std::static_pointer_cast(children[blurIndex + 1]); + } else if (blurIndex + 2 < children.size() && + children[blurIndex + 1]->tag() == SVGTag::FeComposite && + children[blurIndex + 2]->tag() == SVGTag::FeColorMatrix) { + auto compositeFe = std::static_pointer_cast(children[blurIndex + 1]); + if (compositeFe->getOperator() == SVGFeCompositeOperator::Arithmetic) { + return nullptr; + } + colorMatrixFe = std::static_pointer_cast(children[blurIndex + 2]); + } else { + return nullptr; + } + + auto scale = context.transformForCurrentBoundBox(filterContext.primitiveUnits()).scale; + auto dx = offsetFe->getDx() * scale.x * context.matrix().getScaleX(); + auto dy = offsetFe->getDy() * scale.y * context.matrix().getScaleY(); + + auto blurrinessX = blurFe->getstdDeviation().X * scale.x * 4 * context.matrix().getScaleX(); + auto blurrinessY = blurFe->getstdDeviation().Y * scale.y * 4 * context.matrix().getScaleY(); + + auto colorMatrix = colorMatrixFe->getValues(); + Color color{colorMatrix[4], colorMatrix[9], colorMatrix[14], colorMatrix[18]}; + + return ImageFilter::DropShadow(dx, dy, blurrinessX, blurrinessY, color); +} + +std::shared_ptr SVGFilter::buildInnerShadowFilter( + const SVGRenderContext& context, const SVGFilterContext& filterContext) const { + if (children.size() < 4) { + return nullptr; + } + size_t compositeIndex = 0; + for (size_t i = 0; i < children.size(); i++) { + if (children[i]->tag() == SVGTag::FeComposite) { + auto compositeFe = std::static_pointer_cast(children[i]); + if (compositeFe->getOperator() == SVGFeCompositeOperator::Arithmetic) { + compositeIndex = i; + break; + } + } + } + if (compositeIndex < 2 || compositeIndex + 1 >= children.size()) { + return nullptr; + } + if (children[compositeIndex + 1]->tag() == SVGTag::FeColorMatrix) { + std::shared_ptr blurFe; + std::shared_ptr offsetFe; + + if (children[compositeIndex - 2]->tag() == SVGTag::FeGaussianBlur && + children[compositeIndex - 1]->tag() == SVGTag::FeOffset) { + blurFe = std::static_pointer_cast(children[compositeIndex - 2]); + offsetFe = std::static_pointer_cast(children[compositeIndex - 1]); + } else if (children[compositeIndex - 2]->tag() == SVGTag::FeOffset && + children[compositeIndex - 1]->tag() == SVGTag::FeGaussianBlur) { + offsetFe = std::static_pointer_cast(children[compositeIndex - 2]); + blurFe = std::static_pointer_cast(children[compositeIndex - 1]); + } else { + return nullptr; + } + + auto scale = context.transformForCurrentBoundBox(filterContext.primitiveUnits()).scale; + auto dx = offsetFe->getDx() * scale.x * context.matrix().getScaleX(); + auto dy = offsetFe->getDy() * scale.y * context.matrix().getScaleY(); + auto blurrinessX = blurFe->getstdDeviation().X * scale.x * 4 * context.matrix().getScaleX(); + auto blurrinessY = blurFe->getstdDeviation().Y * scale.y * 4 * context.matrix().getScaleY(); + auto colorMatrixFe = std::static_pointer_cast(children[compositeIndex + 1]); + auto colorMatrix = colorMatrixFe->getValues(); + Color color{colorMatrix[4], colorMatrix[9], colorMatrix[14], colorMatrix[18]}; + + if (compositeIndex + 2 < children.size() && + children[compositeIndex + 2]->tag() == SVGTag::FeBlend) { + return ImageFilter::InnerShadow(dx, dy, blurrinessX, blurrinessY, color); + } + return ImageFilter::InnerShadowOnly(dx, dy, blurrinessX, blurrinessY, color); + } + return nullptr; } } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGImage.cpp b/src/svg/node/SVGImage.cpp index f4fd7e2f..323fa658 100644 --- a/src/svg/node/SVGImage.cpp +++ b/src/svg/node/SVGImage.cpp @@ -17,6 +17,7 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/svg/node/SVGImage.h" +#include #include #include "core/utils/Log.h" #include "svg/SVGAttributeParser.h" @@ -26,6 +27,7 @@ #include "tgfx/core/Image.h" #include "tgfx/core/Matrix.h" #include "tgfx/core/Rect.h" +#include "tgfx/svg/SVGTypes.h" namespace tgfx { @@ -47,20 +49,35 @@ bool SVGImage::onPrepareToRender(SVGRenderContext* context) const { INHERITED::onPrepareToRender(context); } -std::shared_ptr LoadImage(const SVGIRI& href) { - const auto& base64URL = href.iri(); - auto pos = base64URL.find("base64,"); - if (pos == std::string::npos) { - return nullptr; +std::shared_ptr LoadImage(const std::shared_ptr& resourceProvider, + const SVGIRI& href) { + + switch (href.type()) { + case SVGIRI::Type::DataURI: { + const auto& base64URL = href.iri(); + auto pos = base64URL.find("base64,"); + if (pos == std::string::npos) { + return nullptr; + } + std::string base64Data = base64URL.substr(pos + 7); + auto data = Base64Decode(base64Data); + return data ? Image::MakeFromEncoded(data) : nullptr; + } + case SVGIRI::Type::Nonlocal: { + std::filesystem::path path(href.iri()); + auto directory = path.parent_path().string(); + auto filename = path.filename().string(); + return resourceProvider->loadImage(directory, filename); + } + default: + return nullptr; } - std::string base64Data = base64URL.substr(pos + 7); - auto data = Base64Decode(base64Data); - return data ? Image::MakeFromEncoded(data) : nullptr; } -SVGImage::ImageInfo SVGImage::LoadImage(const SVGIRI& iri, const Rect& viewPort, - SVGPreserveAspectRatio /*ratio*/) { - std::shared_ptr image = ::tgfx::LoadImage(iri); +SVGImage::ImageInfo SVGImage::LoadImage( + const std::shared_ptr& resourceProvider, const SVGIRI& iri, + const Rect& viewPort, SVGPreserveAspectRatio /*ratio*/) { + std::shared_ptr image = ::tgfx::LoadImage(resourceProvider, iri); if (!image) { return {}; } @@ -80,7 +97,7 @@ void SVGImage::onRender(const SVGRenderContext& context) const { const Rect viewPort = lengthContext.resolveRect(X, Y, Width, Height); ImageInfo image; - const auto imgInfo = LoadImage(Href, viewPort, PreserveAspectRatio); + const auto imgInfo = LoadImage(context.resourceProvider(), Href, viewPort, PreserveAspectRatio); if (!imgInfo.image) { LOGE("can't render image: load image failed\n"); return; diff --git a/src/svg/node/SVGLine.cpp b/src/svg/node/SVGLine.cpp index 163db8df..9b8afa93 100644 --- a/src/svg/node/SVGLine.cpp +++ b/src/svg/node/SVGLine.cpp @@ -44,12 +44,29 @@ std::tuple SVGLine::resolve(const SVGLengthContext& lengthContext) lengthContext.resolve(Y2, SVGLengthContext::LengthType::Vertical))); } -void SVGLine::onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, - PathFillType) const { +void SVGLine::onDrawFill(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType) const { auto [p0, p1] = this->resolve(lengthContext); canvas->drawLine(p0, p1, paint); } +void SVGLine::onDrawStroke(Canvas* canvas, const SVGLengthContext& lengthContext, + const Paint& paint, PathFillType /*fillType*/, + std::shared_ptr pathEffect) const { + if (!pathEffect) { + return; + } + + auto [p0, p1] = this->resolve(lengthContext); + + Path path; + path.moveTo(p0); + path.lineTo(p1); + if (pathEffect->filterPath(&path)) { + canvas->drawPath(path, paint); + } +}; + Path SVGLine::onAsPath(const SVGRenderContext& context) const { auto [p0, p1] = this->resolve(context.lengthContext()); diff --git a/src/svg/node/SVGMask.cpp b/src/svg/node/SVGMask.cpp index 7fda9b71..799313bd 100644 --- a/src/svg/node/SVGMask.cpp +++ b/src/svg/node/SVGMask.cpp @@ -36,7 +36,8 @@ bool SVGMask::parseAndSetAttribute(const std::string& n, const std::string& v) { this->setMaskUnits( SVGAttributeParser::parse("maskUnits", n, v)) || this->setMaskContentUnits( - SVGAttributeParser::parse("maskContentUnits", n, v)); + SVGAttributeParser::parse("maskContentUnits", n, v)) || + this->setMaskType(SVGAttributeParser::parse("mask-type", n, v)); } Rect SVGMask::bounds(const SVGRenderContext& context) const { @@ -56,7 +57,7 @@ constexpr float LUM_COEFF_R = 0.2126f; constexpr float LUM_COEFF_G = 0.7152f; constexpr float LUM_COEFF_B = 0.0722f; -std::array MakeLuminanceToAlpha() { +constexpr std::array MakeLuminanceToAlpha() { return std::array{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, LUM_COEFF_R, LUM_COEFF_G, LUM_COEFF_B, 0, 0}; } @@ -69,24 +70,22 @@ void SVGMask::renderMask(const SVGRenderContext& context) const { // propagation currently occurs. // The local context also restores the filter layer created below on scope exit. - Recorder recorder; - auto* canvas = recorder.beginRecording(); + int saveCount = context.canvas()->getSaveCount(); + if (MaskType.type() != SVGMaskType::Type::Alpha) { + auto luminanceFilter = ColorFilter::Matrix(MakeLuminanceToAlpha()); + Paint luminancePaint; + luminancePaint.setColorFilter(luminanceFilter); + context.canvas()->saveLayer(&luminancePaint); + } + { - // Ensure the render context is destructed, drawing to the canvas upon destruction - SVGRenderContext localContext(context, canvas); + SVGRenderContext localContext(context); this->onPrepareToRender(&localContext); for (const auto& child : children) { child->render(localContext); } } - auto picture = recorder.finishRecordingAsPicture(); - if (!picture) { - return; - } - auto luminanceFilter = ColorFilter::Matrix(MakeLuminanceToAlpha()); - Paint luminancePaint; - luminancePaint.setColorFilter(luminanceFilter); - context.canvas()->drawPicture(picture, nullptr, &luminancePaint); + context.canvas()->restoreToCount(saveCount); } } // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGNode.cpp b/src/svg/node/SVGNode.cpp index 05dcdff8..3e194934 100644 --- a/src/svg/node/SVGNode.cpp +++ b/src/svg/node/SVGNode.cpp @@ -80,8 +80,9 @@ Rect SVGNode::objectBoundingBox(const SVGRenderContext& context) const { bool SVGNode::onPrepareToRender(SVGRenderContext* context) const { // Apply the inheritance of presentation attributes - context->applyPresentationAttributes(presentationAttributes, - this->hasChildren() ? 0 : SVGRenderContext::kLeaf); + context->applyPresentationAttributes( + presentationAttributes, + this->hasChildren() ? 0 : static_cast(SVGRenderContext::ApplyFlags::Leaf)); // visibility:hidden and display:none disable rendering. const auto visibility = context->presentationContext()._inherited.Visibility->type(); diff --git a/src/svg/node/SVGPath.cpp b/src/svg/node/SVGPath.cpp index e875fbe2..140a97e7 100644 --- a/src/svg/node/SVGPath.cpp +++ b/src/svg/node/SVGPath.cpp @@ -44,14 +44,29 @@ bool SVGAttributeParser::parse(Path* path) { return success; } -void SVGPath::onDraw(Canvas* canvas, const SVGLengthContext&, const Paint& paint, - PathFillType fillType) const { +void SVGPath::onDrawFill(Canvas* canvas, const SVGLengthContext&, const Paint& paint, + PathFillType fillType) const { // the passed fillType follows inheritance rules and needs to be applied at draw time. - Path path = ShapePath; // Note: point and verb data are CoW + Path path = ShapePath; path.setFillType(fillType); canvas->drawPath(path, paint); } +void SVGPath::onDrawStroke(Canvas* canvas, const SVGLengthContext& /*lengthContext*/, + const Paint& paint, PathFillType fillType, + std::shared_ptr pathEffect) const { + if (!pathEffect) { + return; + } + + Path path = ShapePath; + path.setFillType(fillType); + canvas->drawPath(path, paint); + if (pathEffect->filterPath(&path)) { + canvas->drawPath(path, paint); + } +}; + Path SVGPath::onAsPath(const SVGRenderContext& context) const { Path path = ShapePath; // clip-rule can be inherited and needs to be applied at clip time. diff --git a/src/svg/node/SVGPoly.cpp b/src/svg/node/SVGPoly.cpp index dc34db9b..0a17a4ca 100644 --- a/src/svg/node/SVGPoly.cpp +++ b/src/svg/node/SVGPoly.cpp @@ -33,18 +33,39 @@ bool SVGPoly::parseAndSetAttribute(const std::string& n, const std::string& v) { } if (this->setPoints(SVGAttributeParser::parse("points", n, v))) { - // TODO (YGAurora): construct the polygon path by points. + if (!Points.empty()) { + path.reset(); + path.moveTo(Points[0]); + for (uint32_t i = 1; i < Points.size(); i++) { + path.lineTo(Points[i]); + } + path.close(); + } } return false; } -void SVGPoly::onDraw(Canvas* canvas, const SVGLengthContext&, const Paint& paint, - PathFillType fillType) const { +void SVGPoly::onDrawFill(Canvas* canvas, const SVGLengthContext&, const Paint& paint, + PathFillType fillType) const { // the passed fillType follows inheritance rules and needs to be applied at draw time. path.setFillType(fillType); canvas->drawPath(path, paint); } +void SVGPoly::onDrawStroke(Canvas* canvas, const SVGLengthContext& /*lengthContext*/, + const Paint& paint, PathFillType fillType, + std::shared_ptr pathEffect) const { + if (!pathEffect) { + return; + } + + path.setFillType(fillType); + canvas->drawPath(path, paint); + if (pathEffect->filterPath(&path)) { + canvas->drawPath(path, paint); + } +}; + Path SVGPoly::onAsPath(const SVGRenderContext& context) const { Path resultPath = path; diff --git a/src/svg/node/SVGRect.cpp b/src/svg/node/SVGRect.cpp index b176c520..e3c2e027 100644 --- a/src/svg/node/SVGRect.cpp +++ b/src/svg/node/SVGRect.cpp @@ -22,6 +22,7 @@ #include "svg/SVGAttributeParser.h" #include "svg/SVGRenderContext.h" #include "tgfx/core/Canvas.h" +#include "tgfx/core/Path.h" #include "tgfx/core/Point.h" #include "tgfx/core/RRect.h" #include "tgfx/core/Rect.h" @@ -59,17 +60,37 @@ RRect SVGRect::resolve(const SVGLengthContext& lengthContext) const { return rrect; } -void SVGRect::onDraw(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, - PathFillType) const { - auto rect = this->resolve(lengthContext); - auto offset = Point::Make(rect.rect.left, rect.rect.top); - rect.rect = rect.rect.makeOffset(-offset.x, -offset.y); +void SVGRect::onDrawFill(Canvas* canvas, const SVGLengthContext& lengthContext, const Paint& paint, + PathFillType) const { + auto rrect = this->resolve(lengthContext); + auto offset = Point::Make(rrect.rect.left, rrect.rect.top); + rrect.rect = rrect.rect.makeOffset(-offset.x, -offset.y); canvas->save(); canvas->translate(offset.x, offset.y); - canvas->drawRRect(rect, paint); + canvas->drawRRect(rrect, paint); canvas->restore(); } +void SVGRect::onDrawStroke(Canvas* canvas, const SVGLengthContext& lengthContext, + const Paint& paint, PathFillType /*fillType*/, + std::shared_ptr pathEffect) const { + if (!pathEffect) { + return; + } + + auto rrect = this->resolve(lengthContext); + auto offset = Point::Make(rrect.rect.left, rrect.rect.top); + rrect.rect = rrect.rect.makeOffset(-offset.x, -offset.y); + Path path; + path.addRRect(rrect); + if (pathEffect->filterPath(&path)) { + canvas->save(); + canvas->translate(offset.x, offset.y); + canvas->drawPath(path, paint); + canvas->restore(); + } +}; + Path SVGRect::onAsPath(const SVGRenderContext& context) const { Path path; path.addRRect(this->resolve(context.lengthContext())); diff --git a/src/svg/node/SVGShape.cpp b/src/svg/node/SVGShape.cpp index 4986eec4..6433add1 100644 --- a/src/svg/node/SVGShape.cpp +++ b/src/svg/node/SVGShape.cpp @@ -31,19 +31,25 @@ void SVGShape::onRender(const SVGRenderContext& context) const { const auto fillType = context.presentationContext()._inherited.FillRule->asFillType(); auto selfRect = onObjectBoundingBox(context); - auto lengthCtx = context.lengthContext(); - lengthCtx.setViewPort(Size::Make(selfRect.width(), selfRect.height())); - auto paintCtx = SVGRenderContext::CopyForPaint(context, context.canvas(), lengthCtx); + auto lengthContext = context.lengthContext(); + lengthContext.setViewPort(Size::Make(selfRect.width(), selfRect.height())); + auto paintContext = SVGRenderContext::CopyForPaint(context, context.canvas(), lengthContext); - const auto fillPaint = paintCtx.fillPaint(); - const auto strokePaint = paintCtx.strokePaint(); + auto fillPaint = paintContext.fillPaint(); + auto strokePaint = paintContext.strokePaint(); if (fillPaint.has_value()) { - this->onDraw(context.canvas(), context.lengthContext(), fillPaint.value(), fillType); + onDrawFill(context.canvas(), context.lengthContext(), fillPaint.value(), fillType); } if (strokePaint.has_value()) { - this->onDraw(context.canvas(), context.lengthContext(), strokePaint.value(), fillType); + auto strokePathEffect = context.strokePathEffect(); + if (strokePathEffect) { + onDrawStroke(context.canvas(), context.lengthContext(), strokePaint.value(), fillType, + strokePathEffect); + } else { + onDrawFill(context.canvas(), context.lengthContext(), strokePaint.value(), fillType); + } } } diff --git a/src/svg/node/SVGText.cpp b/src/svg/node/SVGText.cpp index 0db02779..85acd78c 100644 --- a/src/svg/node/SVGText.cpp +++ b/src/svg/node/SVGText.cpp @@ -27,37 +27,33 @@ #include "tgfx/core/Font.h" #include "tgfx/core/Path.h" #include "tgfx/core/TextBlob.h" -#include "tgfx/svg/SVGFontManager.h" +#include "tgfx/svg/SVGTypes.h" namespace tgfx { namespace { -std::vector ResolveLengths(const SVGLengthContext& lengthCtx, +std::vector ResolveLengths(const SVGRenderContext& context, const std::vector& lengths, SVGLengthContext::LengthType lengthType) { + auto lengthContext = context.lengthContext(); std::vector resolved; resolved.reserve(lengths.size()); for (const auto& length : lengths) { - resolved.push_back(lengthCtx.resolve(length, lengthType)); + if (length.unit() == SVGLength::Unit::EMS || length.unit() == SVGLength::Unit::EXS) { + auto fontSize = context.presentationContext()._inherited.FontSize->size(); + auto resolvedLength = + lengthContext.resolve(fontSize, SVGLengthContext::LengthType::Horizontal) * + length.value(); + resolved.push_back(resolvedLength); + } else { + resolved.push_back(lengthContext.resolve(length, lengthType)); + } } return resolved; } -float ComputeAlignmentFactor(const SVGPresentationContext& context) { - switch (context._inherited.TextAnchor->type()) { - case SVGTextAnchor::Type::Start: - return 0.0f; - case SVGTextAnchor::Type::Middle: - return -0.5f; - case SVGTextAnchor::Type::End: - return -1.0f; - case SVGTextAnchor::Type::Inherit: - ASSERT(false); - return 0.0f; - } -} } // namespace void SVGTextContainer::appendChild(std::shared_ptr child) { @@ -89,17 +85,17 @@ Path SVGTextFragment::onAsPath(const SVGRenderContext&) const { void SVGTextContainer::onShapeText(const SVGRenderContext& context, const ShapedTextCallback& function) const { - auto x = ResolveLengths(context.lengthContext(), X, SVGLengthContext::LengthType::Horizontal); - auto y = ResolveLengths(context.lengthContext(), Y, SVGLengthContext::LengthType::Vertical); - auto dx = ResolveLengths(context.lengthContext(), Dx, SVGLengthContext::LengthType::Horizontal); - auto dy = ResolveLengths(context.lengthContext(), Dy, SVGLengthContext::LengthType::Vertical); + auto x = ResolveLengths(context, X, SVGLengthContext::LengthType::Horizontal); + auto y = ResolveLengths(context, Y, SVGLengthContext::LengthType::Vertical); + auto dx = ResolveLengths(context, Dx, SVGLengthContext::LengthType::Horizontal); + auto dy = ResolveLengths(context, Dy, SVGLengthContext::LengthType::Vertical); // TODO (YGAurora) : Handle rotate for (uint32_t i = 0; i < children.size(); i++) { auto child = children[i]; context.canvas()->save(); - float offsetX = x[i] + (i < dx.size() ? dx[i] : 0); - float offsetY = y[i] + (i < dy.size() ? dy[i] : 0); + float offsetX = (i < x.size() ? x[i] : 0.0f) + (i < dx.size() ? dx[i] : 0.0f); + float offsetY = (i < y.size() ? y[i] : 0.0f) + (i < dy.size() ? dy[i] : 0.0f); context.canvas()->translate(offsetX, offsetY); child->renderText(context, function); context.canvas()->restore(); @@ -119,8 +115,8 @@ bool SVGTextContainer::parseAndSetAttribute(const std::string& name, const std:: void SVGTextLiteral::onShapeText(const SVGRenderContext& context, const ShapedTextCallback& function) const { - auto [success, font] = context.resolveFont(); - if (!success) { + auto font = context.resolveFont(); + if (!font.getTypeface()) { return; } auto textBlob = TextBlob::MakeFrom(Text, font); @@ -129,27 +125,28 @@ void SVGTextLiteral::onShapeText(const SVGRenderContext& context, void SVGText::onRender(const SVGRenderContext& context) const { - auto renderer = [](const SVGRenderContext& renderCtx, + auto renderer = [](const SVGRenderContext& context, const std::shared_ptr& textBlob) -> void { if (!textBlob) { return; } auto bound = textBlob->getBounds(); - float x = ComputeAlignmentFactor(renderCtx.presentationContext()) * bound.width(); + float x = + context.presentationContext()._inherited.TextAnchor->getAlignmentFactor() * bound.width(); float y = 0; - auto lengthCtx = renderCtx.lengthContext(); - lengthCtx.setViewPort(Size::Make(bound.width(), bound.height())); - SVGRenderContext paintCtx(renderCtx, renderCtx.canvas(), lengthCtx); - const auto fillPaint = paintCtx.fillPaint(); - const auto strokePaint = paintCtx.strokePaint(); + auto lengthContext = context.lengthContext(); + lengthContext.setViewPort(Size::Make(bound.width(), bound.height())); + SVGRenderContext paintContext(context, context.canvas(), lengthContext); + auto fillPaint = paintContext.fillPaint(); + auto strokePaint = paintContext.strokePaint(); if (fillPaint.has_value()) { - renderCtx.canvas()->drawTextBlob(textBlob, x, y, fillPaint.value()); + context.canvas()->drawTextBlob(textBlob, x, y, fillPaint.value()); } if (strokePaint.has_value()) { - renderCtx.canvas()->drawTextBlob(textBlob, x, y, strokePaint.value()); + context.canvas()->drawTextBlob(textBlob, x, y, strokePaint.value()); } }; diff --git a/src/svg/shaper/Shaper.cpp b/src/svg/shaper/Shaper.cpp new file mode 100644 index 00000000..5caec346 --- /dev/null +++ b/src/svg/shaper/Shaper.cpp @@ -0,0 +1,130 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/shaper/Shaper.h" +#include +#include +#include +#include "core/utils/Log.h" +#include "svg/shaper/ShaperPrimitive.h" +#include "tgfx/core/Font.h" +#include "tgfx/core/FontManager.h" +#include "tgfx/core/UTF.h" +#include "tgfx/svg/shaper/Shaper.h" + +namespace tgfx { +class FontManagerRunIterator final : public FontRunIterator { + public: + FontManagerRunIterator(const char* utf8, size_t utf8Bytes, const Font& inputfont, + std::shared_ptr fallbackMgr, const char* requestName, + FontStyle requestStyle, const LanguageRunIterator* lang) + : current(utf8), begin(utf8), end(current + utf8Bytes), + fallbackManager(std::move(fallbackMgr)), font(inputfont), fallbackFont(inputfont), + requestName(requestName), requestStyle(requestStyle), languageIter(lang) { + // If fallback is not wanted, clients should use TrivialFontRunIterator. + fallbackFont.setTypeface(nullptr); + } + + void consume() override { + ASSERT(current < end); + Unichar unichar = UTF::NextUTF8(¤t, end); + // If the starting typeface can handle this character, use it. + if (font.getGlyphID(unichar)) { + _currentFont = &font; + // If the current fallback can handle this character, use it. + } else if (fallbackFont.getTypeface() && fallbackFont.getGlyphID(unichar)) { + _currentFont = &fallbackFont; + // If not, try to find a fallback typeface + } else { + std::vector languages; + if (languageIter) { + languages.push_back(languageIter->currentLanguage()); + } + auto candidate = + fallbackManager->matchFamilyStyleCharacter(requestName, requestStyle, languages, unichar); + if (candidate) { + fallbackFont.setTypeface(std::move(candidate)); + _currentFont = &fallbackFont; + } else { + _currentFont = &font; + } + } + + while (current < end) { + const char* prev = current; + unichar = UTF::NextUTF8(¤t, end); + + // End run if not using initial typeface and initial typeface has this character. + if (_currentFont->getTypeface() != font.getTypeface() && font.getGlyphID(unichar)) { + current = prev; + return; + } + + // End run if current typeface does not have this character and some other font does. + if (!_currentFont->getGlyphID(unichar)) { + std::vector languages; + if (languageIter) { + languages.push_back(languageIter->currentLanguage()); + } + auto candidate = fallbackManager->matchFamilyStyleCharacter(requestName, requestStyle, + languages, unichar); + if (candidate) { + current = prev; + return; + } + } + } + } + + size_t endOfCurrentRun() const override { + return static_cast(current - begin); + } + + bool atEnd() const override { + return current == end; + } + + const Font& currentFont() const override { + return *_currentFont; + } + + private: + const char* current = nullptr; + const char* const begin = nullptr; + const char* const end = nullptr; + std::shared_ptr fallbackManager; + Font font; + Font fallbackFont; + Font* _currentFont = nullptr; + const char* const requestName; + const FontStyle requestStyle; + LanguageRunIterator const* const languageIter; +}; + +std::unique_ptr Shaper::MakeFontMgrRunIterator( + const char* utf8, size_t utf8Bytes, const Font& font, std::shared_ptr fallback) { + return std::make_unique(utf8, utf8Bytes, font, std::move(fallback), + nullptr, font.getTypeface()->getStyle(), nullptr); +} + +std::unique_ptr Shaper::MakeStdLanguageRunIterator(const char* /*utf8*/, + size_t utf8Bytes) { + return std::make_unique(std::locale().name().c_str(), utf8Bytes); +} + +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/shaper/ShaperFactory.cpp b/src/svg/shaper/ShaperFactory.cpp new file mode 100644 index 00000000..95c50f78 --- /dev/null +++ b/src/svg/shaper/ShaperFactory.cpp @@ -0,0 +1,49 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/shaper/ShaperFactory.h" +#include +#include "svg/shaper/ShaperPrimitive.h" +#include "tgfx/svg/shaper/Shaper.h" + +namespace tgfx { + +namespace { +class PrimitiveFactoryImpl final : public shapers::Factory { + std::unique_ptr makeShaper() override { + return std::make_unique(); + } + + std::unique_ptr makeBidiRunIterator(const char*, size_t, uint8_t) override { + return std::make_unique(0, 0); + } + + std::unique_ptr makeScriptRunIterator(const char*, size_t, + FourByteTag) override { + return std::make_unique(0, 0); + } +}; +} // namespace + +namespace shapers { +std::shared_ptr PrimitiveFactory() { + return std::make_shared(); +} +} // namespace shapers + +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/shaper/ShaperPrimitive.cpp b/src/svg/shaper/ShaperPrimitive.cpp new file mode 100644 index 00000000..9ae30571 --- /dev/null +++ b/src/svg/shaper/ShaperPrimitive.cpp @@ -0,0 +1,199 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "svg/shaper/ShaperPrimitive.h" +#include "core/utils/Log.h" +#include "tgfx/core/Font.h" +#include "tgfx/core/Point.h" +#include "tgfx/core/Typeface.h" +#include "tgfx/core/UTF.h" + +namespace tgfx { + +namespace { + +inline bool is_breaking_whitespace(Unichar c) { + switch (c) { + case 0x0020: // SPACE + //case 0x00A0: // NO-BREAK SPACE + case 0x1680: // OGHAM SPACE MARK + case 0x180E: // MONGOLIAN VOWEL SEPARATOR + case 0x2000: // EN QUAD + case 0x2001: // EM QUAD + case 0x2002: // EN SPACE (nut) + case 0x2003: // EM SPACE (mutton) + case 0x2004: // THREE-PER-EM SPACE (thick space) + case 0x2005: // FOUR-PER-EM SPACE (mid space) + case 0x2006: // SIX-PER-EM SPACE + case 0x2007: // FIGURE SPACE + case 0x2008: // PUNCTUATION SPACE + case 0x2009: // THIN SPACE + case 0x200A: // HAIR SPACE + case 0x200B: // ZERO WIDTH SPACE + case 0x202F: // NARROW NO-BREAK SPACE + case 0x205F: // MEDIUM MATHEMATICAL SPACE + case 0x3000: // IDEOGRAPHIC SPACE + //case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE + return true; + default: + return false; + } +} + +size_t linebreak(const char text[], const char stop[], float width, const float* advance, + size_t* trailing) { + float accumulatedWidth = 0; + int glyphIndex = 0; + const char* start = text; + const char* wordStart = text; + bool prevWS = true; + *trailing = 0; + + while (text < stop) { + const char* prevText = text; + Unichar uni = UTF::NextUTF8(&text, stop); + accumulatedWidth += advance[glyphIndex++]; + bool currWS = is_breaking_whitespace(uni); + + if (!currWS && prevWS) { + wordStart = prevText; + } + prevWS = currWS; + + if (width < accumulatedWidth) { + bool consumeWhitespace = false; + if (currWS) { + // previous fit, put this and following whitespace in trailing + if (prevText == start) { + // don't put this in trailing if it's the first thing + prevText = text; + } + consumeWhitespace = true; + } else if (wordStart != start) { + // backup to the last whitespace that fit + text = wordStart; + } else if (prevText > start) { + // backup to just before the glyph that didn't fit + text = prevText; + } else { + // let it overflow, put any following whitespace in trailing + prevText = text; + consumeWhitespace = true; + } + if (consumeWhitespace) { + const char* next = text; + while (next < stop && is_breaking_whitespace(UTF::NextUTF8(&next, stop))) { + text = next; + } + if (trailing) { + *trailing = static_cast(text - prevText); + } + } + break; + } + } + return static_cast(text - start); +} + +} // namespace + +void ShaperPrimitive::shape(const char* utf8, size_t utf8Bytes, FontRunIterator& fontIter, + BiDiRunIterator& /*bidiIter*/, ScriptRunIterator& /*scriptIter*/, + LanguageRunIterator& /*langIter*/, const RunFeature* /*features*/, + size_t /*featuresSize*/, float width, RunHandler* handler) const { + Font font; + if (!fontIter.atEnd()) { + fontIter.consume(); + font = fontIter.currentFont(); + } + DEBUG_ASSERT(font.getTypeface()); + + std::vector glyphs; + std::vector advances; + + { + const char* textStart = utf8; + const char* textStop = utf8 + utf8Bytes; + while (textStart < textStop) { + auto unichar = UTF::NextUTF8(&textStart, textStop); + auto glyphID = font.getGlyphID(unichar); + glyphs.push_back(glyphID); + advances.push_back(font.getAdvance(glyphID)); + } + } + + int glyphOffset = 0; + size_t utf8Offset = 0; + do { + size_t bytesCollapsed = 0; + size_t bytesConsumed = + linebreak(utf8, utf8 + utf8Bytes, width, advances.data() + glyphOffset, &bytesCollapsed); + size_t bytesVisible = bytesConsumed - bytesCollapsed; + + auto numGlyphs = static_cast(UTF::CountUTF8(utf8, bytesVisible)); + + const char* textStart = utf8; + const char* textStop = utf8 + utf8Bytes; + float runWidth = 0.0f; + while (textStart < textStop) { + auto unichar = UTF::NextUTF8(&textStart, textStop); + auto glyphID = font.getGlyphID(unichar); + runWidth += font.getAdvance(glyphID) + font.getBounds(glyphID).width(); + } + + const RunHandler::RunInfo info = {font, + 0, + {runWidth, 0}, + numGlyphs, + RunHandler::Range(utf8Offset, bytesVisible)}; + + handler->beginLine(); + if (info.glyphCount) { + handler->runInfo(info); + } + handler->commitRunInfo(); + if (info.glyphCount) { + const auto buffer = handler->runBuffer(info); + + memcpy(buffer.glyphs, glyphs.data() + glyphOffset, info.glyphCount * sizeof(GlyphID)); + auto position = buffer.point; + for (size_t i = 0; i < info.glyphCount; ++i) { + buffer.positions[i] = position; + position.x += advances[i + static_cast(glyphOffset)]; + } + if (buffer.clusters) { + const auto* textPointer = utf8; + for (size_t i = 0; i < info.glyphCount; ++i) { + // Each character maps to exactly one glyph. + buffer.clusters[i] = + static_cast(static_cast(textPointer - utf8) + utf8Offset); + UTF::NextUTF8(&textPointer, utf8 + utf8Bytes); + } + } + handler->commitRunBuffer(info); + } + handler->commitLine(); + + glyphOffset += UTF::CountUTF8(utf8, bytesConsumed); + utf8Offset += bytesConsumed; + utf8 += bytesConsumed; + utf8Bytes -= bytesConsumed; + } while (0 < utf8Bytes); +} + +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/shaper/ShaperPrimitive.h b/src/svg/shaper/ShaperPrimitive.h new file mode 100644 index 00000000..785d0ad4 --- /dev/null +++ b/src/svg/shaper/ShaperPrimitive.h @@ -0,0 +1,109 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "tgfx/svg/shaper/Shaper.h" + +namespace tgfx { + +template +class TrivialRunIterator : public RunIteratorSubclass { + public: + explicit TrivialRunIterator(size_t utf8Bytes) : end(utf8Bytes), _atEnd(end == 0) { + } + void consume() override { + _atEnd = true; + } + size_t endOfCurrentRun() const override { + return _atEnd ? end : 0; + } + bool atEnd() const override { + return _atEnd; + } + + private: + size_t end; + bool _atEnd; +}; + +class TrivialFontRunIterator : public TrivialRunIterator { + public: + TrivialFontRunIterator(Font font, size_t utf8Bytes) + : TrivialRunIterator(utf8Bytes), font(std::move(font)) { + } + const Font& currentFont() const override { + return font; + } + + private: + Font font; +}; + +class TrivialBiDiRunIterator : public TrivialRunIterator { + public: + TrivialBiDiRunIterator(uint8_t bidiLevel, size_t utf8Bytes) + : TrivialRunIterator(utf8Bytes), bidiLevel(bidiLevel) { + } + uint8_t currentLevel() const override { + return bidiLevel; + } + + private: + uint8_t bidiLevel; +}; + +class TrivialScriptRunIterator : public TrivialRunIterator { + public: + TrivialScriptRunIterator(FourByteTag script, size_t utf8Bytes) + : TrivialRunIterator(utf8Bytes), script(script) { + } + FourByteTag currentScript() const override { + return script; + } + + private: + FourByteTag script; +}; + +class TrivialLanguageRunIterator : public TrivialRunIterator { + public: + TrivialLanguageRunIterator(const char* language, size_t utf8Bytes) + : TrivialRunIterator(utf8Bytes), language(language) { + } + + std::string currentLanguage() const override { + return language; + } + + private: + std::string language; +}; + +class ShaperPrimitive : public Shaper { + public: + ShaperPrimitive() = default; + + private: + void shape(const char* utf8, size_t utf8Bytes, FontRunIterator& fontIter, + BiDiRunIterator& bidiIter, ScriptRunIterator& scriptIter, + LanguageRunIterator& langIter, const RunFeature* features, size_t featuresSize, + float width, RunHandler* handler) const override; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/xml/XMLDOM.cpp b/src/svg/xml/XMLDOM.cpp index f53d6e25..2292e2b8 100644 --- a/src/svg/xml/XMLDOM.cpp +++ b/src/svg/xml/XMLDOM.cpp @@ -31,9 +31,9 @@ DOM::DOM(std::shared_ptr root) { DOM::~DOM() = default; -std::shared_ptr DOM::MakeFromData(const Data& data) { +std::shared_ptr DOM::Make(Stream& stream) { DOMParser parser; - if (!parser.parse(data)) { + if (!parser.parse(stream)) { return nullptr; } auto root = parser.getRoot(); diff --git a/src/svg/xml/XMLParser.cpp b/src/svg/xml/XMLParser.cpp index bf882823..a986b0c2 100644 --- a/src/svg/xml/XMLParser.cpp +++ b/src/svg/xml/XMLParser.cpp @@ -17,6 +17,7 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "XMLParser.h" +#include #include #include #include "core/utils/Log.h" @@ -51,7 +52,7 @@ class AutoTCallVProc : public std::unique_ptr> { } }; -constexpr const void* kHashSeed = &kHashSeed; +constexpr const void* HASH_SEED = &HASH_SEED; const XML_Memory_Handling_Suite XML_alloc = {malloc, realloc, free}; @@ -120,7 +121,7 @@ void XMLCALL entity_decl_handler(void* data, const XML_Char* entityName, XMLParser::XMLParser() = default; XMLParser::~XMLParser() = default; -bool XMLParser::parse(const Data& data) { +bool XMLParser::parse(Stream& stream) { ParsingContext parsingContext(this); if (!parsingContext._XMLParser) { LOGE("could not create XML parser\n"); @@ -130,7 +131,7 @@ bool XMLParser::parse(const Data& data) { // Avoid calls to rand_s if this is not set. This seed helps prevent DOS // with a known hash sequence so an address is sufficient. The provided // seed should not be zero as that results in a call to rand_s. - auto seed = static_cast(reinterpret_cast(kHashSeed) & 0xFFFFFFFF); + auto seed = static_cast(reinterpret_cast(HASH_SEED) & 0xFFFFFFFF); XML_SetHashSalt(parsingContext._XMLParser, seed ? seed : 1); XML_SetUserData(parsingContext._XMLParser, &parsingContext); @@ -140,9 +141,30 @@ bool XMLParser::parse(const Data& data) { // Disable entity processing, to inhibit internal entity expansion. See expat CVE-2013-0340. XML_SetEntityDeclHandler(parsingContext._XMLParser, entity_decl_handler); - XML_Status status = - XML_Parse(parsingContext._XMLParser, reinterpret_cast(data.bytes()), - static_cast(data.size()), true); + XML_Status status = XML_STATUS_OK; + if (stream.getMemoryBase() && stream.size() != 0) { + const char* base = reinterpret_cast(stream.getMemoryBase()); + status = XML_Parse(parsingContext._XMLParser, base, static_cast(stream.size()), true); + } else { + static constexpr int BUFFER_SIZE = 4096; + bool done = false; + size_t length = stream.size(); + size_t currentPos = 0; + do { + void* buffer = XML_GetBuffer(parsingContext._XMLParser, BUFFER_SIZE); + if (!buffer) { + return false; + } + + size_t readLength = stream.read(buffer, BUFFER_SIZE); + currentPos += readLength; + done = currentPos >= length; + status = XML_ParseBuffer(parsingContext._XMLParser, static_cast(readLength), done); + if (XML_STATUS_ERROR == status) { + break; + } + } while (!done); + } return XML_STATUS_ERROR != status; } diff --git a/src/svg/xml/XMLParser.h b/src/svg/xml/XMLParser.h index 206c4859..912031f2 100644 --- a/src/svg/xml/XMLParser.h +++ b/src/svg/xml/XMLParser.h @@ -21,6 +21,7 @@ #include #include "tgfx/core/Data.h" +#include "tgfx/core/Stream.h" namespace tgfx { class XMLParser { @@ -29,12 +30,12 @@ class XMLParser { virtual ~XMLParser(); /** - * Parses data in XML format, with the parsing results returned via callback functions. - * @param data The data to be parsed. + * Parses stream in XML format, with the parsing results returned via callback functions. + * @param stream The stream to be parsed. * @return true if parsing is successful. * @return false if parsing fails. */ - bool parse(const Data& data); + bool parse(Stream& stream); protected: /** diff --git a/test/baseline/version.json b/test/baseline/version.json index 63500113..1002b15f 100644 --- a/test/baseline/version.json +++ b/test/baseline/version.json @@ -202,7 +202,14 @@ "png_image": "b1db872", "radialGradient": "b1db872", "text": "b1db872", - "textFont": "348f70e" + "textFont": "348f70e", + "complex1": "ba1c7c2", + "complex2": "ba1c7c2", + "complex3": "ba1c7c2", + "complex4": "ba1c7c2", + "complex5": "ba1c7c2", + "complex6": "ba1c7c2", + "complex7": "ba1c7c2" }, "SurfaceTest": { "ImageSnapshot1": "d010fb8", diff --git a/test/src/SVGRenderTest.cpp b/test/src/SVGRenderTest.cpp index 76933804..4969581f 100644 --- a/test/src/SVGRenderTest.cpp +++ b/test/src/SVGRenderTest.cpp @@ -16,10 +16,13 @@ // ///////////////////////////////////////////////////////////////////////////////////////////////// +#include #include "gtest/gtest.h" #include "tgfx/core/Data.h" +#include "tgfx/core/FontManagerCustom.h" +#include "tgfx/core/FontStyle.h" +#include "tgfx/core/Stream.h" #include "tgfx/svg/SVGDOM.h" -#include "tgfx/svg/SVGFontManager.h" #include "tgfx/svg/xml/XMLDOM.h" #include "utils/TestUtils.h" @@ -36,7 +39,9 @@ TGFX_TEST(SVGRenderTest, XMLParse) { auto data = Data::MakeWithCopy(xml.data(), xml.size()); EXPECT_TRUE(data != nullptr); - auto xmlDOM = DOM::MakeFromData(*data); + auto stream = Stream::MakeFromData(data); + EXPECT_TRUE(stream != nullptr); + auto xmlDOM = DOM::Make(*stream); EXPECT_TRUE(xmlDOM != nullptr); auto rootNode = xmlDOM->getRootNode(); @@ -80,9 +85,9 @@ TGFX_TEST(SVGRenderTest, XMLParse) { } TGFX_TEST(SVGRenderTest, PathSVG) { - auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/path.svg")); - ASSERT_TRUE(data != nullptr); - auto SVGDom = SVGDOM::Make(data); + auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/path.svg")); + ASSERT_TRUE(stream != nullptr); + auto SVGDom = SVGDOM::Make(*stream); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -98,9 +103,9 @@ TGFX_TEST(SVGRenderTest, PathSVG) { } TGFX_TEST(SVGRenderTest, PNGImageSVG) { - auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/png.svg")); - ASSERT_TRUE(data != nullptr); - auto SVGDom = SVGDOM::Make(data); + auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/png.svg")); + ASSERT_TRUE(stream != nullptr); + auto SVGDom = SVGDOM::Make(*stream); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -116,9 +121,9 @@ TGFX_TEST(SVGRenderTest, PNGImageSVG) { } TGFX_TEST(SVGRenderTest, JPGImageSVG) { - auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/jpg.svg")); - ASSERT_TRUE(data != nullptr); - auto SVGDom = SVGDOM::Make(data); + auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/jpg.svg")); + ASSERT_TRUE(stream != nullptr); + auto SVGDom = SVGDOM::Make(*stream); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -134,9 +139,9 @@ TGFX_TEST(SVGRenderTest, JPGImageSVG) { } TGFX_TEST(SVGRenderTest, MaskSVG) { - auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/mask.svg")); - ASSERT_TRUE(data != nullptr); - auto SVGDom = SVGDOM::Make(data); + auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/mask.svg")); + ASSERT_TRUE(stream != nullptr); + auto SVGDom = SVGDOM::Make(*stream); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -152,9 +157,10 @@ TGFX_TEST(SVGRenderTest, MaskSVG) { } TGFX_TEST(SVGRenderTest, GradientSVG) { - auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/radialGradient.svg")); - ASSERT_TRUE(data != nullptr); - auto SVGDom = SVGDOM::Make(data); + auto stream = + Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/radialGradient.svg")); + ASSERT_TRUE(stream != nullptr); + auto SVGDom = SVGDOM::Make(*stream); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -170,9 +176,9 @@ TGFX_TEST(SVGRenderTest, GradientSVG) { } TGFX_TEST(SVGRenderTest, BlurSVG) { - auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/blur.svg")); - ASSERT_TRUE(data != nullptr); - auto SVGDom = SVGDOM::Make(data); + auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/blur.svg")); + ASSERT_TRUE(stream != nullptr); + auto SVGDom = SVGDOM::Make(*stream); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -187,10 +193,34 @@ TGFX_TEST(SVGRenderTest, BlurSVG) { EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/blur")); } +class FontLoaderTest : public FontManagerCustom::FontLoader { + public: + void loadFonts(std::vector>& families) const override { + auto family = std::make_shared("Noto Sans SC"); + auto typeface = MakeTypeface("resources/font/NotoSansSC-Regular.otf"); + typeface->setStyle(FontStyle::Normal()); + family->appendTypeface(typeface); + families.push_back(family); + + family = std::make_shared("Noto Serif SC"); + typeface = MakeTypeface("resources/font/NotoSerifSC-Regular.otf"); + typeface->setStyle(FontStyle::Normal()); + family->appendTypeface(typeface); + families.push_back(family); + } +}; + TGFX_TEST(SVGRenderTest, TextSVG) { - auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/text.svg")); - ASSERT_TRUE(data != nullptr); - auto SVGDom = SVGDOM::Make(data); + auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/text.svg")); + ASSERT_TRUE(stream != nullptr); + + FontLoaderTest loader; + std::shared_ptr fontManager = std::make_shared(loader); + + SVGDOMOptions options; + options.fontManager = fontManager; + + auto SVGDom = SVGDOM::Make(*stream, options); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -201,19 +231,21 @@ TGFX_TEST(SVGRenderTest, TextSVG) { static_cast(rootNode->getHeight().value())); auto* canvas = surface->getCanvas(); - auto typeface = MakeTypeface("resources/font/NotoSansSC-Regular.otf"); - ASSERT_TRUE(typeface != nullptr); - auto fontManager = SVGFontManager::Make(typeface); - ASSERT_TRUE(fontManager != nullptr); - - SVGDom->render(canvas, fontManager); + SVGDom->render(canvas); EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/text")); } TGFX_TEST(SVGRenderTest, TextFontSVG) { - auto data = Data::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/textFont.svg")); - ASSERT_TRUE(data != nullptr); - auto SVGDom = SVGDOM::Make(data); + auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/textFont.svg")); + ASSERT_TRUE(stream != nullptr); + + FontLoaderTest loader; + std::shared_ptr fontManager = std::make_shared(loader); + + SVGDOMOptions options; + options.fontManager = fontManager; + + auto SVGDom = SVGDOM::Make(*stream, options); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -224,27 +256,127 @@ TGFX_TEST(SVGRenderTest, TextFontSVG) { static_cast(rootNode->getHeight().value())); auto* canvas = surface->getCanvas(); - auto defaultTypeface = MakeTypeface("resources/font/NotoSansSC-Regular.otf"); - ASSERT_TRUE(defaultTypeface != nullptr); - auto fontManager = SVGFontManager::Make(defaultTypeface); - ASSERT_TRUE(fontManager != nullptr); - - SVGDom->collectRenderFonts(fontManager); - auto families = fontManager->getFontFamilies(); - ASSERT_TRUE(families.size() == 1); - auto family = fontManager->getFontFamilies()[0]; - ASSERT_TRUE(family == "Noto Serif SC"); - auto infos = fontManager->getFontInfos(family); - ASSERT_TRUE(infos.size() == 1); - auto info = infos[0]; - ASSERT_TRUE(info.weight() == SVGFontWeight::Type::Normal); - ASSERT_TRUE(info.style() == SVGFontStyle::Type::Normal); - - auto typeface = MakeTypeface("resources/font/NotoSerifSC-Regular.otf"); - fontManager->setTypeface(family, info, typeface); - - SVGDom->render(canvas, fontManager); + SVGDom->render(canvas); EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/textFont")); } +TGFX_TEST(SVGRenderTest, ComplexSVG) { + FontLoaderTest loader; + std::shared_ptr fontManager = std::make_shared(loader); + + SVGDOMOptions options; + options.fontManager = fontManager; + + ContextScope scope; + auto* context = scope.getContext(); + ASSERT_TRUE(context != nullptr); + + { + auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/complex1.svg")); + ASSERT_TRUE(stream != nullptr); + + auto SVGDom = SVGDOM::Make(*stream, options); + auto rootNode = SVGDom->getRoot(); + ASSERT_TRUE(rootNode != nullptr); + + auto surface = Surface::Make(context, 100, 100); + auto* canvas = surface->getCanvas(); + + SVGDom->render(canvas); + EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/complex1")); + } + + { + auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/complex2.svg")); + ASSERT_TRUE(stream != nullptr); + + auto SVGDom = SVGDOM::Make(*stream, options); + auto rootNode = SVGDom->getRoot(); + ASSERT_TRUE(rootNode != nullptr); + + auto surface = Surface::Make(context, 160, 160); + auto* canvas = surface->getCanvas(); + + canvas->scale(10, 10); + SVGDom->render(canvas); + EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/complex2")); + } + + { + auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/complex3.svg")); + ASSERT_TRUE(stream != nullptr); + + auto SVGDom = SVGDOM::Make(*stream, options); + auto rootNode = SVGDom->getRoot(); + ASSERT_TRUE(rootNode != nullptr); + + auto surface = Surface::Make(context, 300, 300); + auto* canvas = surface->getCanvas(); + + canvas->scale(2, 2); + SVGDom->render(canvas); + EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/complex3")); + } + + { + auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/complex4.svg")); + ASSERT_TRUE(stream != nullptr); + + auto SVGDom = SVGDOM::Make(*stream, options); + auto rootNode = SVGDom->getRoot(); + ASSERT_TRUE(rootNode != nullptr); + + auto surface = Surface::Make(context, 500, 400); + auto* canvas = surface->getCanvas(); + + SVGDom->render(canvas); + EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/complex4")); + } + + { + auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/complex5.svg")); + ASSERT_TRUE(stream != nullptr); + + auto SVGDom = SVGDOM::Make(*stream, options); + auto rootNode = SVGDom->getRoot(); + ASSERT_TRUE(rootNode != nullptr); + + auto surface = Surface::Make(context, 1300, 1300); + auto* canvas = surface->getCanvas(); + + SVGDom->render(canvas); + EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/complex5")); + } + + { + auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/complex6.svg")); + ASSERT_TRUE(stream != nullptr); + + auto SVGDom = SVGDOM::Make(*stream, options); + auto rootNode = SVGDom->getRoot(); + ASSERT_TRUE(rootNode != nullptr); + + auto surface = Surface::Make(context, 375, 812); + auto* canvas = surface->getCanvas(); + + SVGDom->render(canvas); + EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/complex6")); + } + + { + auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/complex7.svg")); + ASSERT_TRUE(stream != nullptr); + + auto SVGDom = SVGDOM::Make(*stream, options); + auto rootNode = SVGDom->getRoot(); + ASSERT_TRUE(rootNode != nullptr); + + auto surface = Surface::Make(context, 1090, 2026); + auto* canvas = surface->getCanvas(); + + SVGDom->render(canvas); + EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/complex7")); + } +} + } // namespace tgfx \ No newline at end of file From b6f5bfc8cf2ccc0e4f5633848c104b79a870d387 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Wed, 22 Jan 2025 17:50:13 +0800 Subject: [PATCH 28/31] =?UTF-8?q?=E3=80=82=E3=80=82=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/tgfx/core/Font.h | 1 - include/tgfx/core/FontManager.h | 3 - include/tgfx/core/LoadResourceProvider.h | 18 +-- include/tgfx/core/Typeface.h | 28 ++-- .../tgfx/{core => svg}/FontManagerCustom.h | 0 src/core/FontManager.cpp | 129 ------------------ src/core/LoadResourceProvider.cpp | 5 +- src/{core => svg}/FontManagerCustom.cpp | 9 +- src/svg/shaper/Shaper.cpp | 3 +- test/src/SVGRenderTest.cpp | 6 +- 10 files changed, 33 insertions(+), 169 deletions(-) rename include/tgfx/{core => svg}/FontManagerCustom.h (100%) rename src/{core => svg}/FontManagerCustom.cpp (96%) diff --git a/include/tgfx/core/Font.h b/include/tgfx/core/Font.h index 47e26e9e..8b977880 100644 --- a/include/tgfx/core/Font.h +++ b/include/tgfx/core/Font.h @@ -19,7 +19,6 @@ #pragma once #include "tgfx/core/FontMetrics.h" -#include "tgfx/core/FontStyle.h" #include "tgfx/core/Image.h" #include "tgfx/core/Path.h" #include "tgfx/core/Typeface.h" diff --git a/include/tgfx/core/FontManager.h b/include/tgfx/core/FontManager.h index 47d350a4..1010a646 100644 --- a/include/tgfx/core/FontManager.h +++ b/include/tgfx/core/FontManager.h @@ -58,9 +58,6 @@ class FontStyleSet { * Create an empty font style set */ static std::shared_ptr CreateEmpty(); - - protected: - std::shared_ptr matchStyleCSS3(const FontStyle& pattern); }; /** diff --git a/include/tgfx/core/LoadResourceProvider.h b/include/tgfx/core/LoadResourceProvider.h index ef3c6899..2fb86910 100644 --- a/include/tgfx/core/LoadResourceProvider.h +++ b/include/tgfx/core/LoadResourceProvider.h @@ -37,21 +37,21 @@ class LoadResourceProvider { virtual ~LoadResourceProvider() = default; /** - * Creates an empty resource provider. - */ + * Creates an empty resource provider. + */ static std::shared_ptr MakeEmpty(); /** - * Creates a file resource provider with the given base path. - */ -static std::shared_ptr MakeFileProvider(const std::string& basePath); + * Creates a file resource provider with the given base path. + */ + static std::shared_ptr MakeFileProvider(const std::string& basePath); -/** + /** * Load a generic resource specified by |path| + |name|, and return as an Data object. */ -virtual std::shared_ptr load(const std::string& /*resourcePath*/, - const std::string& /*resourceName*/) const { - return nullptr; + virtual std::shared_ptr load(const std::string& /*resourcePath*/, + const std::string& /*resourceName*/) const { + return nullptr; } /** diff --git a/include/tgfx/core/Typeface.h b/include/tgfx/core/Typeface.h index ab7d46c6..8c6a52a6 100644 --- a/include/tgfx/core/Typeface.h +++ b/include/tgfx/core/Typeface.h @@ -132,30 +132,32 @@ class Typeface { */ virtual std::shared_ptr copyTableData(FontTableTag tag) const = 0; - protected: - /** - * Gets the mapping from GlyphID to unicode. The array index is GlyphID, and the array value is - * unicode. The array length is glyphsCount(). - * This method is only implemented when compiling the SVG or PDF export module. - */ - virtual std::vector getGlyphToUnicodeMap() const; - /** * Returns the font style object of this typeface. - * This method is used for font matching and text layout in SVG rendering. + * Note: This value is set by the user and is not read from the font file. This method is used for + * font matching and text layout in SVG rendering. */ - FontStyle getStyle() const { + FontStyle getFontStyle() const { return style; } /** * Sets the font style for this typeface. - * This method is used for font matching and text layout in SVG rendering. + * Note: This value is set by the user and is not read from the font file. This method is used for + * font matching and text layout in SVG rendering. */ - void setStyle(const FontStyle& value) { + void setFontStyle(const FontStyle& value) { style = value; } + protected: + /** + * Gets the mapping from GlyphID to unicode. The array index is GlyphID, and the array value is + * unicode. The array length is glyphsCount(). + * This method is only implemented when compiling the SVG or PDF export module. + */ + virtual std::vector getGlyphToUnicodeMap() const; + mutable std::mutex locker = {}; private: @@ -164,7 +166,5 @@ class Typeface { friend class ScalerContext; friend class GlyphConverter; - friend class FontManager; - friend class Shaper; }; } // namespace tgfx diff --git a/include/tgfx/core/FontManagerCustom.h b/include/tgfx/svg/FontManagerCustom.h similarity index 100% rename from include/tgfx/core/FontManagerCustom.h rename to include/tgfx/svg/FontManagerCustom.h diff --git a/src/core/FontManager.cpp b/src/core/FontManager.cpp index ca71d7d5..b121e7ed 100644 --- a/src/core/FontManager.cpp +++ b/src/core/FontManager.cpp @@ -23,135 +23,6 @@ namespace tgfx { -/** -* Width has the greatest priority. -* If the value of pattern.width is 5 (normal) or less, -* narrower width values are checked first, then wider values. -* If the value of pattern.width is greater than 5 (normal), -* wider values are checked first, followed by narrower values. -* -* Italic/Oblique has the next highest priority. -* If italic requested and there is some italic font, use it. -* If oblique requested and there is some oblique font, use it. -* If italic requested and there is some oblique font, use it. -* If oblique requested and there is some italic font, use it. -* -* Exact match. -* If pattern.weight < 400, weights below pattern.weight are checked -* in descending order followed by weights above pattern.weight -* in ascending order until a match is found. -* If pattern.weight > 500, weights above pattern.weight are checked -* in ascending order followed by weights below pattern.weight -* in descending order until a match is found. -* If pattern.weight is 400, 500 is checked first -* and then the rule for pattern.weight < 400 is used. -* If pattern.weight is 500, 400 is checked first -* and then the rule for pattern.weight < 400 is used. -*/ -std::shared_ptr FontStyleSet::matchStyleCSS3(const FontStyle& pattern) { - auto count = this->count(); - if (0 == count) { - return nullptr; - } - - struct Score { - int score; - int index; - Score& operator+=(int rhs) { - this->score += rhs; - return *this; - } - Score& operator<<=(int rhs) { - this->score <<= rhs; - return *this; - } - bool operator<(const Score& that) const { - return this->score < that.score; - } - }; - - int patternWeight = static_cast(pattern.weight()); - int patternWidth = static_cast(pattern.width()); - int patternSlant = static_cast(pattern.slant()); - - Score maxScore = {0, 0}; - for (size_t i = 0; i < count; ++i) { - FontStyle current; - this->getStyle(i, ¤t, nullptr); - int currentWeight = static_cast(current.weight()); - int currentWidth = static_cast(current.width()); - int currentSlant = static_cast(current.slant()); - - Score currentScore = {0, static_cast(i)}; - - // CSS stretch / FontStyle::Width - // Takes priority over everything else. - if (pattern.width() <= FontStyle::Width::Normal) { - if (current.width() <= pattern.width()) { - currentScore += 10 - patternWidth + static_cast(current.width()); - } else { - currentScore += 10 - static_cast(current.width()); - } - } else { - if (current.width() > pattern.width()) { - currentScore += 10 + patternWidth - currentWidth; - } else { - currentScore += currentWidth; - } - } - currentScore <<= 8; - - // CSS style (normal, italic, oblique) / FontStyle::Slant (upright, italic, oblique) - // Takes priority over all valid weights. - DEBUG_ASSERT(0 <= patternSlant && patternSlant <= 2 && 0 <= currentSlant && currentSlant <= 2); - static const int score[3][3] = { - /* Upright Italic Oblique [current]*/ - /* Upright */ {3, 1, 2}, - /* Italic */ {1, 3, 2}, - /* Oblique */ {1, 2, 3}, - /* [pattern] */ - }; - currentScore += score[patternSlant][currentSlant]; - currentScore <<= 8; - - // CSS weight / FontStyle::Weight - // The 'closer' to the target weight, the higher the score. - // 1000 is the 'heaviest' recognized weight - if (patternWeight == currentWeight) { - currentScore += 1000; - // less than 400 prefer lighter weights - } else if (patternWeight < 400) { - if (currentWeight <= patternWeight) { - currentScore += 1000 - patternWeight + currentWeight; - } else { - currentScore += 1000 - currentWeight; - } - // between 400 and 500 prefer heavier up to 500, then lighter weights - } else if (patternWeight <= 500) { - if (currentWeight >= patternWeight && currentWeight <= 500) { - currentScore += 1000 + patternWeight - currentWeight; - } else if (current.weight() <= pattern.weight()) { - currentScore += 500 + currentWeight; - } else { - currentScore += 1000 - currentWeight; - } - // greater than 500 prefer heavier weights - } else if (patternWeight > 500) { - if (currentWeight > patternWeight) { - currentScore += 1000 + patternWeight - currentWeight; - } else { - currentScore += currentWeight; - } - } - - if (maxScore < currentScore) { - maxScore = currentScore; - } - } - - return this->createTypeface(static_cast(maxScore.index)); -} - class EmptyFontStyleSet : public FontStyleSet { public: size_t count() override { diff --git a/src/core/LoadResourceProvider.cpp b/src/core/LoadResourceProvider.cpp index 54f4fce8..06edd4ae 100644 --- a/src/core/LoadResourceProvider.cpp +++ b/src/core/LoadResourceProvider.cpp @@ -48,10 +48,7 @@ std::shared_ptr LoadResourceProvider::MakeEmpty() { std::shared_ptr LoadResourceProvider::MakeFileProvider( const std::string& basePath) { - if (std::filesystem::exists(basePath)) { - return std::make_shared(basePath); - } - return nullptr; + return std::make_shared(basePath); } } // namespace tgfx \ No newline at end of file diff --git a/src/core/FontManagerCustom.cpp b/src/svg/FontManagerCustom.cpp similarity index 96% rename from src/core/FontManagerCustom.cpp rename to src/svg/FontManagerCustom.cpp index 6827c898..1ad279a5 100644 --- a/src/core/FontManagerCustom.cpp +++ b/src/svg/FontManagerCustom.cpp @@ -16,7 +16,7 @@ // ///////////////////////////////////////////////////////////////////////////////////////////////// -#include "tgfx/core/FontManagerCustom.h" +#include "tgfx/svg/FontManagerCustom.h" #include #include #include @@ -55,10 +55,9 @@ std::shared_ptr FontStyleSetCustom::createTypeface(size_t index) { std::shared_ptr FontStyleSetCustom::matchStyle(const FontStyle& style) { std::shared_ptr typeface = nullptr; - if (!styles.empty()) { - typeface = this->matchStyleCSS3(style); - if (!typeface) { - typeface = styles[0]; + for (const auto& item : styles) { + if (item->getFontStyle() == style) { + return typeface = item; } } return typeface; diff --git a/src/svg/shaper/Shaper.cpp b/src/svg/shaper/Shaper.cpp index 5caec346..8b5dbfa9 100644 --- a/src/svg/shaper/Shaper.cpp +++ b/src/svg/shaper/Shaper.cpp @@ -119,7 +119,8 @@ class FontManagerRunIterator final : public FontRunIterator { std::unique_ptr Shaper::MakeFontMgrRunIterator( const char* utf8, size_t utf8Bytes, const Font& font, std::shared_ptr fallback) { return std::make_unique(utf8, utf8Bytes, font, std::move(fallback), - nullptr, font.getTypeface()->getStyle(), nullptr); + nullptr, font.getTypeface()->getFontStyle(), + nullptr); } std::unique_ptr Shaper::MakeStdLanguageRunIterator(const char* /*utf8*/, diff --git a/test/src/SVGRenderTest.cpp b/test/src/SVGRenderTest.cpp index 4969581f..8fa59a8b 100644 --- a/test/src/SVGRenderTest.cpp +++ b/test/src/SVGRenderTest.cpp @@ -19,9 +19,9 @@ #include #include "gtest/gtest.h" #include "tgfx/core/Data.h" -#include "tgfx/core/FontManagerCustom.h" #include "tgfx/core/FontStyle.h" #include "tgfx/core/Stream.h" +#include "tgfx/svg/FontManagerCustom.h" #include "tgfx/svg/SVGDOM.h" #include "tgfx/svg/xml/XMLDOM.h" #include "utils/TestUtils.h" @@ -198,13 +198,13 @@ class FontLoaderTest : public FontManagerCustom::FontLoader { void loadFonts(std::vector>& families) const override { auto family = std::make_shared("Noto Sans SC"); auto typeface = MakeTypeface("resources/font/NotoSansSC-Regular.otf"); - typeface->setStyle(FontStyle::Normal()); + typeface->setFontStyle(FontStyle::Normal()); family->appendTypeface(typeface); families.push_back(family); family = std::make_shared("Noto Serif SC"); typeface = MakeTypeface("resources/font/NotoSerifSC-Regular.otf"); - typeface->setStyle(FontStyle::Normal()); + typeface->setFontStyle(FontStyle::Normal()); family->appendTypeface(typeface); families.push_back(family); } From df5ce50e45b14ebf91459c2cb797912eaab8c734 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Thu, 23 Jan 2025 10:38:09 +0800 Subject: [PATCH 29/31] typo --- include/tgfx/core/LoadResourceProvider.h | 3 +++ src/core/LoadResourceProvider.cpp | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/tgfx/core/LoadResourceProvider.h b/include/tgfx/core/LoadResourceProvider.h index 2fb86910..04c574ef 100644 --- a/include/tgfx/core/LoadResourceProvider.h +++ b/include/tgfx/core/LoadResourceProvider.h @@ -61,6 +61,9 @@ class LoadResourceProvider { const std::string& /*resourceName*/) const { return nullptr; } + + protected: + LoadResourceProvider() = default; }; } // namespace tgfx \ No newline at end of file diff --git a/src/core/LoadResourceProvider.cpp b/src/core/LoadResourceProvider.cpp index 06edd4ae..1c21382c 100644 --- a/src/core/LoadResourceProvider.cpp +++ b/src/core/LoadResourceProvider.cpp @@ -18,6 +18,7 @@ #include "tgfx/core/LoadResourceProvider.h" #include +#include #include #include @@ -43,7 +44,7 @@ class FileResourceProvider final : public LoadResourceProvider { }; std::shared_ptr LoadResourceProvider::MakeEmpty() { - return std::make_shared(); + return std::shared_ptr(new LoadResourceProvider); } std::shared_ptr LoadResourceProvider::MakeFileProvider( From 04e1c44cd3a59ea32647a3eb349e6bc0cae87da5 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Fri, 24 Jan 2025 19:08:28 +0800 Subject: [PATCH 30/31] optimize --- include/tgfx/core/FontManager.h | 118 ------- include/tgfx/core/FontStyle.h | 40 +-- include/tgfx/core/Typeface.h | 22 +- include/tgfx/svg/FontManager.h | 57 +++ include/tgfx/svg/FontManagerCustom.h | 102 ------ .../ResourceLoader.h} | 29 +- include/tgfx/svg/SVGDOM.h | 19 +- include/tgfx/svg/node/{SVGG.h => SVGGroup.h} | 8 +- include/tgfx/svg/node/SVGImage.h | 4 +- include/tgfx/svg/node/{SVGSVG.h => SVGRoot.h} | 8 +- include/tgfx/svg/shaper/Shaper.h | 164 --------- src/core/FontManager.cpp | 84 ----- src/core/vectors/coregraphics/CGTypeface.cpp | 67 ++++ src/core/vectors/freetype/FTTypeface.cpp | 5 + src/core/vectors/freetype/SystemFont.cpp | 49 ++- src/core/vectors/freetype/SystemFont.h | 3 + src/svg/FontManager.cpp | 37 ++ src/svg/FontManagerCustom.cpp | 135 ------- .../ResourceLoader.cpp} | 15 +- src/svg/SVGDOM.cpp | 19 +- src/svg/SVGNodeConstructor.cpp | 10 +- src/svg/SVGRenderContext.cpp | 59 +--- src/svg/SVGRenderContext.h | 21 +- src/svg/SVGTextContext.cpp | 328 ------------------ src/svg/SVGTextContext.h | 217 ------------ .../svg/SystemFontManager.cpp | 34 +- .../ShaperFactory.cpp => SystemFontManager.h} | 39 +-- src/svg/SystemResourceLoader.h | 62 ++++ src/svg/node/SVGImage.cpp | 8 +- src/svg/node/{SVGSVG.cpp => SVGRoot.cpp} | 10 +- src/svg/shaper/Shaper.cpp | 131 ------- src/svg/shaper/ShaperPrimitive.cpp | 199 ----------- src/svg/shaper/ShaperPrimitive.h | 109 ------ test/src/SVGRenderTest.cpp | 68 ++-- 34 files changed, 429 insertions(+), 1851 deletions(-) delete mode 100644 include/tgfx/core/FontManager.h create mode 100644 include/tgfx/svg/FontManager.h delete mode 100644 include/tgfx/svg/FontManagerCustom.h rename include/tgfx/{core/LoadResourceProvider.h => svg/ResourceLoader.h} (65%) rename include/tgfx/svg/node/{SVGG.h => SVGGroup.h} (86%) rename include/tgfx/svg/node/{SVGSVG.h => SVGRoot.h} (90%) delete mode 100644 include/tgfx/svg/shaper/Shaper.h delete mode 100644 src/core/FontManager.cpp create mode 100644 src/svg/FontManager.cpp delete mode 100644 src/svg/FontManagerCustom.cpp rename src/{core/LoadResourceProvider.cpp => svg/ResourceLoader.cpp} (76%) delete mode 100644 src/svg/SVGTextContext.cpp delete mode 100644 src/svg/SVGTextContext.h rename include/tgfx/svg/shaper/ShaperFactory.h => src/svg/SystemFontManager.cpp (62%) rename src/svg/{shaper/ShaperFactory.cpp => SystemFontManager.h} (54%) create mode 100644 src/svg/SystemResourceLoader.h rename src/svg/node/{SVGSVG.cpp => SVGRoot.cpp} (92%) delete mode 100644 src/svg/shaper/Shaper.cpp delete mode 100644 src/svg/shaper/ShaperPrimitive.cpp delete mode 100644 src/svg/shaper/ShaperPrimitive.h diff --git a/include/tgfx/core/FontManager.h b/include/tgfx/core/FontManager.h deleted file mode 100644 index 1010a646..00000000 --- a/include/tgfx/core/FontManager.h +++ /dev/null @@ -1,118 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making tgfx available. -// -// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// unless required by applicable law or agreed to in writing, software distributed under the -// license is distributed on an "as is" basis, without warranties or conditions of any kind, -// either express or implied. see the license for the specific language governing permissions -// and limitations under the license. -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include -#include -#include -#include "tgfx/core/FontStyle.h" -#include "tgfx/core/Typeface.h" - -namespace tgfx { -/** - * A collection of font styles. There may be multiple typefaces corresponding to different font - * styles under the same family name. - * This is an abstract class that users need to implement. - */ -class FontStyleSet { - public: - virtual ~FontStyleSet() = default; - - /** - * Get the number of font styles - */ - virtual size_t count() = 0; - - /** - * Get the font style at the specified index - */ - virtual void getStyle(size_t index, FontStyle* style, std::string* name) = 0; - - /** - * Create a typeface for the specified font style index - */ - virtual std::shared_ptr createTypeface(size_t index) = 0; - - /** - * Match a typeface based on the font style - */ - virtual std::shared_ptr matchStyle(const FontStyle& style) = 0; - - /** - * Create an empty font style set - */ - static std::shared_ptr CreateEmpty(); -}; - -/** - * FontManager provides functionality to enumerate Typefaces and match them based on FontStyle. - */ -class FontManager { - public: - /** - * Destructor for FontManager - */ - virtual ~FontManager() = default; - - /** - * Get the number of font families - */ - size_t countFamilies() const; - - /** - * Get the name of the font family at the given index - */ - std::string getFamilyName(size_t index) const; - - /** - * Create a set of font styles for the given family index - */ - std::shared_ptr createStyleSet(size_t index) const; - - /** - * Match a font family name and return a set of font styles - */ - std::shared_ptr matchFamily(const std::string& familyName) const; - - /** - * Match a font family name and style, and return the corresponding Typeface - */ - std::shared_ptr matchFamilyStyle(const std::string& familyName, FontStyle style) const; - - /** - * Match a font family name, style, character, and language, and return the corresponding Typeface - */ - std::shared_ptr matchFamilyStyleCharacter(const std::string& familyName, - FontStyle style, - const std::vector& bcp47s, - Unichar character) const; - - private: - virtual size_t onCountFamilies() const = 0; - virtual std::string onGetFamilyName(size_t index) const = 0; - virtual std::shared_ptr onCreateStyleSet(size_t index) const = 0; - virtual std::shared_ptr onMatchFamily(const std::string& familyName) const = 0; - virtual std::shared_ptr onMatchFamilyStyle(const std::string& familyName, - FontStyle style) const = 0; - virtual std::shared_ptr onMatchFamilyStyleCharacter( - const std::string& familyName, FontStyle style, const std::vector& bcp47s, - Unichar character) const = 0; -}; - -} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/core/FontStyle.h b/include/tgfx/core/FontStyle.h index 60c63970..92fda35d 100644 --- a/include/tgfx/core/FontStyle.h +++ b/include/tgfx/core/FontStyle.h @@ -31,29 +31,29 @@ namespace tgfx { class FontStyle { public: enum class Weight { - Invisible = 0, - Thin = 100, - ExtraLight = 200, - Light = 300, - Normal = 400, - Medium = 500, - SemiBold = 600, - Bold = 700, - ExtraBold = 800, - Black = 900, - ExtraBlack = 1000, + Invisible, + Thin, + ExtraLight, + Light, + Normal, + Medium, + SemiBold, + Bold, + ExtraBold, + Black, + ExtraBlack, }; enum class Width { - UltraCondensed = 1, - ExtraCondensed = 2, - Condensed = 3, - SemiCondensed = 4, - Normal = 5, - SemiExpanded = 6, - Expanded = 7, - ExtraExpanded = 8, - UltraExpanded = 9, + UltraCondensed, + ExtraCondensed, + Condensed, + SemiCondensed, + Normal, + SemiExpanded, + Expanded, + ExtraExpanded, + UltraExpanded, }; enum class Slant { diff --git a/include/tgfx/core/Typeface.h b/include/tgfx/core/Typeface.h index 8c6a52a6..2e63f338 100644 --- a/include/tgfx/core/Typeface.h +++ b/include/tgfx/core/Typeface.h @@ -57,6 +57,9 @@ class Typeface { static std::shared_ptr MakeFromName(const std::string& fontFamily, const std::string& fontStyle); + static std::shared_ptr MakeFromStyle(const std::string& fontFamily, + FontStyle fontStyle); + /** * Creates a new typeface for the given file path and ttc index. Returns nullptr if the typeface * can't be created. @@ -132,24 +135,6 @@ class Typeface { */ virtual std::shared_ptr copyTableData(FontTableTag tag) const = 0; - /** - * Returns the font style object of this typeface. - * Note: This value is set by the user and is not read from the font file. This method is used for - * font matching and text layout in SVG rendering. - */ - FontStyle getFontStyle() const { - return style; - } - - /** - * Sets the font style for this typeface. - * Note: This value is set by the user and is not read from the font file. This method is used for - * font matching and text layout in SVG rendering. - */ - void setFontStyle(const FontStyle& value) { - style = value; - } - protected: /** * Gets the mapping from GlyphID to unicode. The array index is GlyphID, and the array value is @@ -162,7 +147,6 @@ class Typeface { private: std::unordered_map> scalerContexts = {}; - FontStyle style = FontStyle::Normal(); friend class ScalerContext; friend class GlyphConverter; diff --git a/include/tgfx/svg/FontManager.h b/include/tgfx/svg/FontManager.h new file mode 100644 index 00000000..cab02139 --- /dev/null +++ b/include/tgfx/svg/FontManager.h @@ -0,0 +1,57 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include "tgfx/core/FontStyle.h" +#include "tgfx/core/Typeface.h" + +namespace tgfx { +/** + * FontManager provides functionality to enumerate Typefaces and match them based on FontStyle. + */ +class FontManager { + public: + /** + * Destructor for FontManager + */ + virtual ~FontManager() = default; + + /** + * Match a font family name and style, and return the corresponding Typeface + */ + std::shared_ptr matchTypeface(const std::string& familyName, FontStyle style) const; + + /** + * Match a font family name, style, character, and language, and return the corresponding Typeface + */ + std::shared_ptr getFallbackTypeface(const std::string& familyName, FontStyle style, + Unichar character) const; + + private: + virtual std::shared_ptr onMatchTypeface(const std::string& familyName, + FontStyle style) const = 0; + virtual std::shared_ptr onGetFallbackTypeface(const std::string& familyName, + FontStyle style, + Unichar character) const = 0; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/FontManagerCustom.h b/include/tgfx/svg/FontManagerCustom.h deleted file mode 100644 index 357f543a..00000000 --- a/include/tgfx/svg/FontManagerCustom.h +++ /dev/null @@ -1,102 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making tgfx available. -// -// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// unless required by applicable law or agreed to in writing, software distributed under the -// license is distributed on an "as is" basis, without warranties or conditions of any kind, -// either express or implied. see the license for the specific language governing permissions -// and limitations under the license. -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include -#include -#include -#include "tgfx/core/FontManager.h" -#include "tgfx/core/FontStyle.h" -#include "tgfx/core/Typeface.h" - -namespace tgfx { -/** - * A custom FontManager implementation for font style matching. Users only need to override the - * FontManagerCustom::FontLoader::loadFonts method. - * - * Example usage: - * class FontLoaderTest : public FontManagerCustom::FontLoader { - * public: - * void loadFonts(std::vector>& families) const override { - * auto family = std::make_shared("Noto Sans SC"); - * auto typeface = MakeTypeface("resources/font/NotoSansSC-Regular.otf"); - * typeface->setStyle(FontStyle::Normal()); - * family->appendTypeface(typeface); - * families.push_back(family); - * } - * }; - * - * int Sample() { - * FontLoaderTest loader; - * std::shared_ptr fontManager = std::make_shared(loader); - * SVGDOMOptions options; - * options.fontManager = fontManager; - * auto SVGDom = SVGDOM::Make(*stream, options); - * } - */ - -class FontStyleSetCustom : public FontStyleSet { - public: - explicit FontStyleSetCustom(std::string familyName); - - void appendTypeface(std::shared_ptr typeface); - - size_t count() override; - - void getStyle(size_t index, FontStyle* style, std::string* name) override; - - std::shared_ptr createTypeface(size_t index) override; - - std::shared_ptr matchStyle(const FontStyle& style) override; - - std::string getFamilyName(); - - private: - std::vector> styles; - std::string familyName; -}; - -class FontManagerCustom : public FontManager { - public: - class FontLoader { - public: - virtual ~FontLoader() = default; - virtual void loadFonts(std::vector>& families) const = 0; - }; - - explicit FontManagerCustom(const FontLoader& loader); - - protected: - size_t onCountFamilies() const override; - std::string onGetFamilyName(size_t index) const override; - std::shared_ptr onCreateStyleSet(size_t index) const override; - std::shared_ptr onMatchFamily(const std::string& familyName) const override; - std::shared_ptr onMatchFamilyStyle(const std::string& familyName, - FontStyle style) const override; - std::shared_ptr onMatchFamilyStyleCharacter(const std::string& familyName, - FontStyle style, - const std::vector& bcp47s, - Unichar character) const override; - - private: - std::vector> families; - std::shared_ptr defaultFamily; -}; - -} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/core/LoadResourceProvider.h b/include/tgfx/svg/ResourceLoader.h similarity index 65% rename from include/tgfx/core/LoadResourceProvider.h rename to include/tgfx/svg/ResourceLoader.h index 04c574ef..146fcd22 100644 --- a/include/tgfx/core/LoadResourceProvider.h +++ b/include/tgfx/svg/ResourceLoader.h @@ -29,41 +29,24 @@ namespace tgfx { * LoadResourceProvider is an interface for loading resources (e.g. images, font) from * external sources. */ -class LoadResourceProvider { +class ResourceLoader { public: /** * Data is a wrapper for raw data. */ - virtual ~LoadResourceProvider() = default; - - /** - * Creates an empty resource provider. - */ - static std::shared_ptr MakeEmpty(); - - /** - * Creates a file resource provider with the given base path. - */ - static std::shared_ptr MakeFileProvider(const std::string& basePath); + virtual ~ResourceLoader() = default; /** * Load a generic resource specified by |path| + |name|, and return as an Data object. */ - virtual std::shared_ptr load(const std::string& /*resourcePath*/, - const std::string& /*resourceName*/) const { - return nullptr; - } + virtual std::shared_ptr load(const std::string& resourcePath, + const std::string& resourceName) const = 0; /** * Load an image asset specified by |path| + |name|, and returns the Image object. */ - virtual std::shared_ptr loadImage(const std::string& /*resourcePath*/, - const std::string& /*resourceName*/) const { - return nullptr; - } - - protected: - LoadResourceProvider() = default; + virtual std::shared_ptr loadImage(const std::string& resourcePath, + const std::string& resourceName) const = 0; }; } // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/SVGDOM.h b/include/tgfx/svg/SVGDOM.h index 0e26d15d..90e1b1cb 100644 --- a/include/tgfx/svg/SVGDOM.h +++ b/include/tgfx/svg/SVGDOM.h @@ -21,13 +21,12 @@ #include #include "tgfx/core/Canvas.h" #include "tgfx/core/Data.h" -#include "tgfx/core/FontManager.h" -#include "tgfx/core/LoadResourceProvider.h" #include "tgfx/core/Picture.h" #include "tgfx/core/Size.h" #include "tgfx/core/Stream.h" -#include "tgfx/svg/node/SVGSVG.h" -#include "tgfx/svg/shaper/ShaperFactory.h" +#include "tgfx/svg/FontManager.h" +#include "tgfx/svg/ResourceLoader.h" +#include "tgfx/svg/node/SVGRoot.h" namespace tgfx { @@ -47,11 +46,7 @@ struct SVGDOMOptions { * If resourceProvider is null, base64 image resources will still be parsed, but non-base64 images * will be loaded from absolute paths. */ - std::shared_ptr resourceProvider = nullptr; - /** - * If shaperFactory is null, right-to-left and language-specific text shaping will not be applied. - */ - std::shared_ptr shaperFactory = nullptr; + std::shared_ptr resourceProvider = nullptr; }; /** @@ -79,7 +74,7 @@ class SVGDOM { /** * Returns the root SVG node. */ - const std::shared_ptr& getRoot() const; + const std::shared_ptr& getRoot() const; /** * Renders the SVG to the provided canvas. @@ -104,9 +99,9 @@ class SVGDOM { /** * Construct a new SVGDOM object */ - SVGDOM(std::shared_ptr root, SVGDOMOptions options, SVGIDMapper&& mapper); + SVGDOM(std::shared_ptr root, SVGDOMOptions options, SVGIDMapper&& mapper); - const std::shared_ptr root = nullptr; + const std::shared_ptr root = nullptr; const SVGIDMapper nodeIDMapper = {}; const SVGDOMOptions options = {}; Size containerSize = {}; diff --git a/include/tgfx/svg/node/SVGG.h b/include/tgfx/svg/node/SVGGroup.h similarity index 86% rename from include/tgfx/svg/node/SVGG.h rename to include/tgfx/svg/node/SVGGroup.h index e4b9b6a3..a713a2e5 100644 --- a/include/tgfx/svg/node/SVGG.h +++ b/include/tgfx/svg/node/SVGGroup.h @@ -22,14 +22,14 @@ #include "tgfx/svg/node/SVGContainer.h" namespace tgfx { -class SVGG : public SVGContainer { +class SVGGroup : public SVGContainer { public: - static std::shared_ptr Make() { - return std::shared_ptr(new SVGG()); + static std::shared_ptr Make() { + return std::shared_ptr(new SVGGroup()); } private: - SVGG() : INHERITED(SVGTag::G) { + SVGGroup() : INHERITED(SVGTag::G) { } using INHERITED = SVGContainer; diff --git a/include/tgfx/svg/node/SVGImage.h b/include/tgfx/svg/node/SVGImage.h index a29d512f..d51f3020 100644 --- a/include/tgfx/svg/node/SVGImage.h +++ b/include/tgfx/svg/node/SVGImage.h @@ -20,9 +20,9 @@ #include #include "tgfx/core/Image.h" -#include "tgfx/core/LoadResourceProvider.h" #include "tgfx/core/Path.h" #include "tgfx/core/Rect.h" +#include "tgfx/svg/ResourceLoader.h" #include "tgfx/svg/SVGTypes.h" #include "tgfx/svg/node/SVGNode.h" #include "tgfx/svg/node/SVGTransformableNode.h" @@ -53,7 +53,7 @@ class SVGImage final : public SVGTransformableNode { void onRender(const SVGRenderContext& conetxt) const override; Path onAsPath(const SVGRenderContext& conetxt) const override; Rect onObjectBoundingBox(const SVGRenderContext& conetxt) const override; - static ImageInfo LoadImage(const std::shared_ptr& resourceProvider, + static ImageInfo LoadImage(const std::shared_ptr& resourceProvider, const SVGIRI& iri, const Rect& viewPort, SVGPreserveAspectRatio ratio); SVG_ATTR(X, SVGLength, SVGLength(0)) diff --git a/include/tgfx/svg/node/SVGSVG.h b/include/tgfx/svg/node/SVGRoot.h similarity index 90% rename from include/tgfx/svg/node/SVGSVG.h rename to include/tgfx/svg/node/SVGRoot.h index 6f591c33..04e6a1f4 100644 --- a/include/tgfx/svg/node/SVGSVG.h +++ b/include/tgfx/svg/node/SVGRoot.h @@ -29,14 +29,14 @@ namespace tgfx { class SVGLengthContext; -class SVGSVG : public SVGContainer { +class SVGRoot : public SVGContainer { public: enum class Type { kRoot, kInner, }; - static std::shared_ptr Make(Type t = Type::kInner) { - return std::shared_ptr(new SVGSVG(t)); + static std::shared_ptr Make(Type t = Type::kInner) { + return std::shared_ptr(new SVGRoot(t)); } SVG_ATTR(X, SVGLength, SVGLength(0)) @@ -57,7 +57,7 @@ class SVGSVG : public SVGContainer { void onSetAttribute(SVGAttribute attribute, const SVGValue& value) override; private: - explicit SVGSVG(Type t) : INHERITED(SVGTag::Svg), type(t) { + explicit SVGRoot(Type t) : INHERITED(SVGTag::Svg), type(t) { } // Some attributes behave differently for the outermost svg element. diff --git a/include/tgfx/svg/shaper/Shaper.h b/include/tgfx/svg/shaper/Shaper.h deleted file mode 100644 index 478452ee..00000000 --- a/include/tgfx/svg/shaper/Shaper.h +++ /dev/null @@ -1,164 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making tgfx available. -// -// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// unless required by applicable law or agreed to in writing, software distributed under the -// license is distributed on an "as is" basis, without warranties or conditions of any kind, -// either express or implied. see the license for the specific language governing permissions -// and limitations under the license. -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include -#include -#include -#include "tgfx/core/Font.h" -#include "tgfx/core/FontManager.h" -#include "tgfx/core/Point.h" -#include "tgfx/core/Shape.h" -#include "tgfx/core/Typeface.h" -#include "tgfx/svg/SVGTypes.h" - -namespace tgfx { - -/** - * ISO 15924, Codes for the representation of names of scripts. - * https://en.wikipedia.org/wiki/ISO_15924 - */ -using FourByteTag = uint32_t; -static inline constexpr FourByteTag SetFourByteTag(char a, char b, char c, char d) { - return ((static_cast(a) << 24) | (static_cast(b) << 16) | - (static_cast(c) << 8) | static_cast(d)); -} - -class RunIterator { - public: - virtual ~RunIterator() = default; - /** Set state to that of current run and move iterator to end of that run. */ - virtual void consume() = 0; - /** Offset to one past the last (utf8) element in the current run. */ - virtual size_t endOfCurrentRun() const = 0; - /** Return true if consume should no longer be called. */ - virtual bool atEnd() const = 0; -}; - -class FontRunIterator : public RunIterator { - public: - virtual const Font& currentFont() const = 0; -}; - -class BiDiRunIterator : public RunIterator { - public: - /** The unicode bidi embedding level (even ltr, odd rtl) */ - virtual uint8_t currentLevel() const = 0; -}; - -class ScriptRunIterator : public RunIterator { - public: - // Should be iso15924 codes. - virtual FourByteTag currentScript() const = 0; -}; - -class LanguageRunIterator : public RunIterator { - public: - /** Should be BCP-47, c locale names may also work. */ - virtual std::string currentLanguage() const = 0; -}; - -struct RunFeature { - FourByteTag tag; - uint32_t value; - size_t start; // Offset to the start (utf8) element of the run. - size_t end; // Offset to one past the last (utf8) element of the run. -}; - -class RunHandler { - public: - virtual ~RunHandler() = default; - - struct Range { - constexpr Range() : _begin(0), _size(0) { - } - constexpr Range(size_t begin, size_t size) : _begin(begin), _size(size) { - } - - constexpr size_t begin() const { - return _begin; - } - constexpr size_t end() const { - return begin() + size(); - } - constexpr size_t size() const { - return _size; - } - - private: - size_t _begin; - size_t _size; - }; - - struct RunInfo { - const Font& font; - uint8_t bidiLevel; - Point advance; - size_t glyphCount; - Range utf8Range; - }; - - struct Buffer { - GlyphID* glyphs; // required - Point* positions; // required, if (!offsets) put glyphs[i] at positions[i] - // if ( offsets) positions[i+1]-positions[i] are advances - Point* offsets; // optional, if ( offsets) put glyphs[i] at positions[i]+offsets[i] - uint32_t* clusters; // optional, utf8+clusters[i] starts run which produced glyphs[i] - Point point; // offset to add to all positions - }; - - /** Called when beginning a line. */ - virtual void beginLine() = 0; - - /** Called once for each run in a line. Can compute baselines and offsets. */ - virtual void runInfo(const RunInfo&) = 0; - - /** Called after all runInfo calls for a line. */ - virtual void commitRunInfo() = 0; - - /** Called for each run in a line after commitRunInfo. The buffer will be filled out. */ - virtual Buffer runBuffer(const RunInfo&) = 0; - - /** Called after each runBuffer is filled out. */ - virtual void commitRunBuffer(const RunInfo&) = 0; - - /** Called when ending a line. */ - virtual void commitLine() = 0; -}; - -class Shaper { - public: - Shaper() = default; - virtual ~Shaper() = default; - Shaper(const Shaper&) = delete; - Shaper& operator=(const Shaper&) = delete; - - virtual void shape(const char* utf8, size_t utf8Bytes, FontRunIterator& fontIter, - BiDiRunIterator& bidiIter, ScriptRunIterator& scriptIter, - LanguageRunIterator& langIter, const RunFeature* features, size_t featuresSize, - float width, RunHandler* handler) const = 0; - - static std::unique_ptr MakeFontMgrRunIterator( - const char* utf8, size_t utf8Bytes, const Font& font, std::shared_ptr fallback); - - static std::unique_ptr MakeStdLanguageRunIterator(const char* utf8, - size_t utf8Bytes); -}; - -} // namespace tgfx \ No newline at end of file diff --git a/src/core/FontManager.cpp b/src/core/FontManager.cpp deleted file mode 100644 index b121e7ed..00000000 --- a/src/core/FontManager.cpp +++ /dev/null @@ -1,84 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making tgfx available. -// -// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// unless required by applicable law or agreed to in writing, software distributed under the -// license is distributed on an "as is" basis, without warranties or conditions of any kind, -// either express or implied. see the license for the specific language governing permissions -// and limitations under the license. -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -#include "tgfx/core/FontManager.h" -#include -#include -#include "core/utils/Log.h" - -namespace tgfx { - -class EmptyFontStyleSet : public FontStyleSet { - public: - size_t count() override { - return 0; - } - - void getStyle(size_t, FontStyle*, std::string*) override { - } - - std::shared_ptr createTypeface(size_t) override { - return nullptr; - } - - std::shared_ptr matchStyle(const FontStyle&) override { - return nullptr; - } -}; - -std::shared_ptr FontStyleSet::CreateEmpty() { - return std::shared_ptr(new EmptyFontStyleSet); -} - -namespace { -std::shared_ptr emptyOnNull(std::shared_ptr&& styleSet) { - if (!styleSet) { - styleSet = FontStyleSet::CreateEmpty(); - } - return std::move(styleSet); -} -} // anonymous namespace - -size_t FontManager::countFamilies() const { - return onCountFamilies(); -} - -std::string FontManager::getFamilyName(size_t index) const { - return onGetFamilyName(index); -} - -std::shared_ptr FontManager::createStyleSet(size_t index) const { - return emptyOnNull(onCreateStyleSet(index)); -} - -std::shared_ptr FontManager::matchFamily(const std::string& familyName) const { - return emptyOnNull(onMatchFamily(familyName)); -} - -std::shared_ptr FontManager::matchFamilyStyle(const std::string& familyName, - FontStyle style) const { - return onMatchFamilyStyle(familyName, style); -} - -std::shared_ptr FontManager::matchFamilyStyleCharacter( - const std::string& familyName, FontStyle style, const std::vector& bcp47s, - Unichar character) const { - return onMatchFamilyStyleCharacter(familyName, style, bcp47s, character); -} - -} // namespace tgfx \ No newline at end of file diff --git a/src/core/vectors/coregraphics/CGTypeface.cpp b/src/core/vectors/coregraphics/CGTypeface.cpp index e3dad9c1..5be35b23 100644 --- a/src/core/vectors/coregraphics/CGTypeface.cpp +++ b/src/core/vectors/coregraphics/CGTypeface.cpp @@ -17,8 +17,10 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "CGTypeface.h" +#include #include "CGScalerContext.h" #include "core/utils/UniqueID.h" +#include "tgfx/core/FontStyle.h" #include "tgfx/core/Typeface.h" #include "tgfx/core/UTF.h" @@ -33,6 +35,7 @@ std::string StringFromCFString(CFStringRef src) { std::shared_ptr Typeface::MakeFromName(const std::string& fontFamily, const std::string& fontStyle) { + CFMutableDictionaryRef cfAttributes = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (!fontFamily.empty()) { @@ -65,6 +68,70 @@ std::shared_ptr Typeface::MakeFromName(const std::string& fontFamily, return typeface; } +static constexpr std::array FontWeightMap = {-1.0f, -0.6f, -0.5f, -0.4f, 0.0f, 0.23f, + 0.3f, 0.4f, 0.56f, 0.62f, 0.7f}; + +static constexpr std::array FontWidthMap = {-0.8f, -0.6f, -0.4f, -0.2f, 0.0f, + 0.2f, 0.4f, 0.6f, 0.8f}; + +static constexpr std::array FontSlantMap = {-1.0f, 0.0f, 1.0f}; + +std::shared_ptr Typeface::MakeFromStyle(const std::string& fontFamily, + FontStyle fontStyle) { + + CFMutableDictionaryRef cfAttributes = CFDictionaryCreateMutable( + kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!fontFamily.empty()) { + const auto* cfFontName = + CFStringCreateWithCString(kCFAllocatorDefault, fontFamily.c_str(), kCFStringEncodingUTF8); + if (cfFontName) { + CFDictionaryAddValue(cfAttributes, kCTFontFamilyNameAttribute, cfFontName); + CFRelease(cfFontName); + } + } + + CFMutableDictionaryRef cfTraits = CFDictionaryCreateMutable( + kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (cfTraits) { + float fontWeight = FontWeightMap[static_cast(fontStyle.weight())]; + CFNumberRef cfWeight = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &fontWeight); + if (cfWeight) { + CFDictionaryAddValue(cfTraits, kCTFontWeightTrait, cfWeight); + CFRelease(cfWeight); + } + + float fontWidth = FontWidthMap[static_cast(fontStyle.width())]; + CFNumberRef cfWidth = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &fontWidth); + if (cfWidth) { + CFDictionaryAddValue(cfTraits, kCTFontWidthTrait, cfWidth); + CFRelease(cfWidth); + } + + float fontSlant = FontSlantMap[static_cast(fontStyle.slant())]; + CFNumberRef cfSlant = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &fontSlant); + if (cfSlant) { + CFDictionaryAddValue(cfTraits, kCTFontSlantTrait, cfSlant); + CFRelease(cfSlant); + } + + CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute, cfTraits); + CFRelease(cfTraits); + } + + std::shared_ptr typeface; + const auto* cfDesc = CTFontDescriptorCreateWithAttributes(cfAttributes); + if (cfDesc) { + const auto* ctFont = CTFontCreateWithFontDescriptor(cfDesc, 0, nullptr); + if (ctFont) { + typeface = CGTypeface::Make(ctFont); + CFRelease(ctFont); + } + CFRelease(cfDesc); + } + CFRelease(cfAttributes); + return typeface; +} + std::shared_ptr Typeface::MakeFromPath(const std::string& fontPath, int) { CGDataProviderRef cgDataProvider = CGDataProviderCreateWithFilename(fontPath.c_str()); if (cgDataProvider == nullptr) { diff --git a/src/core/vectors/freetype/FTTypeface.cpp b/src/core/vectors/freetype/FTTypeface.cpp index 4d48df2c..51dae603 100644 --- a/src/core/vectors/freetype/FTTypeface.cpp +++ b/src/core/vectors/freetype/FTTypeface.cpp @@ -31,6 +31,11 @@ std::shared_ptr Typeface::MakeFromName(const std::string& fontFamily, return SystemFont::MakeFromName(fontFamily, fontStyle); } +std::shared_ptr Typeface::MakeFromStyle(const std::string& fontFamily, + FontStyle fontStyle) { + return SystemFont::MakeFromStyle(fontFamily, fontStyle); +} + std::shared_ptr Typeface::MakeFromPath(const std::string& fontPath, int ttcIndex) { return FTTypeface::Make(FTFontData(fontPath, ttcIndex)); } diff --git a/src/core/vectors/freetype/SystemFont.cpp b/src/core/vectors/freetype/SystemFont.cpp index 1cfa1889..48aec333 100644 --- a/src/core/vectors/freetype/SystemFont.cpp +++ b/src/core/vectors/freetype/SystemFont.cpp @@ -18,6 +18,7 @@ #include "SystemFont.h" #include +#include "tgfx/core/FontStyle.h" #ifdef _WIN32 #include @@ -136,6 +137,33 @@ DWriteFontStyle ToDWriteFontStyle(const std::string& fontStyle) { return dWriteFontStyle; } +static constexpr std::array FontWeightMap = { + DWRITE_FONT_WEIGHT_THIN, DWRITE_FONT_WEIGHT_THIN, DWRITE_FONT_WEIGHT_EXTRA_LIGHT, + DWRITE_FONT_WEIGHT_LIGHT, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_WEIGHT_MEDIUM, + DWRITE_FONT_WEIGHT_SEMI_BOLD, DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_WEIGHT_EXTRA_BOLD, + DWRITE_FONT_WEIGHT_BLACK, DWRITE_FONT_WEIGHT_EXTRA_BLACK}; + +static constexpr std::array FontWidthMap = { + DWRITE_FONT_STRETCH_ULTRA_CONDENSED, DWRITE_FONT_STRETCH_EXTRA_CONDENSED, + DWRITE_FONT_STRETCH_CONDENSED, DWRITE_FONT_STRETCH_SEMI_CONDENSED, + DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STRETCH_SEMI_EXPANDED, + DWRITE_FONT_STRETCH_EXPANDED, DWRITE_FONT_STRETCH_EXTRA_EXPANDED, + DWRITE_FONT_STRETCH_ULTRA_EXPANDED}; + +static constexpr std::array FontSlantMap = { + DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STYLE_ITALIC, DWRITE_FONT_STYLE_OBLIQUE}; + +DWriteFontStyle ToDWriteFontStyle(FontStyle fontStyle) { + std::string key; + key.resize(fontStyle.length()); + std::transform(fontStyle.begin(), fontStyle.end(), key.begin(), ::tolower); + DWriteFontStyle dWriteFontStyle{}; + dWriteFontStyle.weight = FontWeightMap[static_cast(fontStyle.weight())]; + dWriteFontStyle.stretch = FontWidthMap[static_cast(fontStyle.width())]; + dWriteFontStyle.fontStyle = FontSlantMap[static_cast(fontStyle.slant())]; + return dWriteFontStyle; +} + std::wstring ToWstring(const std::string& input) { std::wstring result; int len = MultiByteToWideChar(CP_ACP, 0, input.c_str(), input.size(), nullptr, 0); @@ -174,7 +202,7 @@ void SafeRelease(T** ppT) { } std::shared_ptr MakeFromFontName(const std::string& fontFamily, - const std::string& fontStyle) { + const DWriteFontStyle& fontStyle) { if (fontFamily.empty()) { return nullptr; } @@ -187,10 +215,8 @@ std::shared_ptr MakeFromFontName(const std::string& fontFamily, hResult = writeFactory->GetSystemFontSet(&fontSet); } if (SUCCEEDED(hResult)) { - DWriteFontStyle dWriteFontStyle = ToDWriteFontStyle(fontStyle); - hResult = - fontSet->GetMatchingFonts(ToWstring(fontFamily).c_str(), dWriteFontStyle.weight, - dWriteFontStyle.stretch, dWriteFontStyle.fontStyle, &fontSet); + hResult = fontSet->GetMatchingFonts(ToWstring(fontFamily).c_str(), fontStyle.weight, + fontStyle.stretch, fontStyle.fontStyle, &fontSet); } UINT32 fontCount = 0; if (SUCCEEDED(hResult)) { @@ -237,8 +263,19 @@ std::shared_ptr MakeFromFontName(const std::string& fontFamily, std::shared_ptr SystemFont::MakeFromName(const std::string& fontFamily, const std::string& fontStyle) { #ifdef _WIN32 - return MakeFromFontName(fontFamily, fontStyle); + auto dWriteFontStyle = ToDWriteFontStyle(fontStyle); + return MakeFromFontName(fontFamily, dWriteFontStyle); +#endif + return nullptr; +} + +std::shared_ptr SystemFont::MakeFromStyle(const std::string& fontFamily, + FontStyle fontStyle) { +#ifdef _WIN32 + auto dWriteFontStyle = ToDWriteFontStyle(fontStyle); + return MakeFromFontName(fontFamily, dWriteFontStyle); #endif return nullptr; } + } // namespace tgfx diff --git a/src/core/vectors/freetype/SystemFont.h b/src/core/vectors/freetype/SystemFont.h index b8020117..0a3462ab 100644 --- a/src/core/vectors/freetype/SystemFont.h +++ b/src/core/vectors/freetype/SystemFont.h @@ -25,5 +25,8 @@ class SystemFont { public: static std::shared_ptr MakeFromName(const std::string& fontFamily, const std::string& fontStyle); + + static std::shared_ptr MakeFromStyle(const std::string& fontFamily, + FontStyle fontStyle); }; } // namespace tgfx \ No newline at end of file diff --git a/src/svg/FontManager.cpp b/src/svg/FontManager.cpp new file mode 100644 index 00000000..c968e714 --- /dev/null +++ b/src/svg/FontManager.cpp @@ -0,0 +1,37 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "tgfx/svg/FontManager.h" +#include +#include +#include "core/utils/Log.h" + +namespace tgfx { + +std::shared_ptr FontManager::matchTypeface(const std::string& familyName, + FontStyle style) const { + return onMatchTypeface(familyName, style); +} + +std::shared_ptr FontManager::getFallbackTypeface(const std::string& familyName, + FontStyle style, + Unichar character) const { + return onGetFallbackTypeface(familyName, style, character); +} + +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/FontManagerCustom.cpp b/src/svg/FontManagerCustom.cpp deleted file mode 100644 index 1ad279a5..00000000 --- a/src/svg/FontManagerCustom.cpp +++ /dev/null @@ -1,135 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making tgfx available. -// -// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// unless required by applicable law or agreed to in writing, software distributed under the -// license is distributed on an "as is" basis, without warranties or conditions of any kind, -// either express or implied. see the license for the specific language governing permissions -// and limitations under the license. -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -#include "tgfx/svg/FontManagerCustom.h" -#include -#include -#include -#include -#include "core/utils/Log.h" -#include "tgfx/core/FontStyle.h" - -namespace tgfx { - -FontStyleSetCustom::FontStyleSetCustom(std::string familyName) : familyName(std::move(familyName)) { -} - -void FontStyleSetCustom::appendTypeface(std::shared_ptr typeface) { - styles.emplace_back(std::move(typeface)); -} - -size_t FontStyleSetCustom::count() { - return styles.size(); -} - -void FontStyleSetCustom::getStyle(size_t /*index*/, FontStyle* style, std::string* name) { - if (style) { - *style = FontStyle::Normal(); - } - if (name) { - *name = ""; - } -} - -std::shared_ptr FontStyleSetCustom::createTypeface(size_t index) { - if (styles.size() <= index) { - return nullptr; - } - return styles[index]; -} - -std::shared_ptr FontStyleSetCustom::matchStyle(const FontStyle& style) { - std::shared_ptr typeface = nullptr; - for (const auto& item : styles) { - if (item->getFontStyle() == style) { - return typeface = item; - } - } - return typeface; -} - -std::string FontStyleSetCustom::getFamilyName() { - return familyName; -} - -FontManagerCustom::FontManagerCustom(const FontLoader& loader) : defaultFamily(nullptr) { - - loader.loadFonts(families); - - // Try to pick a default font. - static const std::array defaultNames = { - "Arial", "Verdana", "Times New Roman", "Droid Sans", "DejaVu Serif", ""}; - for (const auto& defaultName : defaultNames) { - auto set = this->onMatchFamily(defaultName); - if (!set) { - continue; - } - - auto style = - FontStyle(FontStyle::Weight::Normal, FontStyle::Width::Normal, FontStyle::Slant::Upright); - auto typeface = set->matchStyle(style); - if (!typeface) { - continue; - } - - defaultFamily = set; - break; - } - if (nullptr == defaultFamily) { - defaultFamily = families[0]; - } -} - -size_t FontManagerCustom::onCountFamilies() const { - return families.size(); -} - -std::string FontManagerCustom::onGetFamilyName(size_t index) const { - DEBUG_ASSERT(index < families.size()); - return families[index]->getFamilyName(); -} - -std::shared_ptr FontManagerCustom::onCreateStyleSet(size_t index) const { - DEBUG_ASSERT(index < families.size()); - return families[index]; -} - -std::shared_ptr FontManagerCustom::onMatchFamily( - const std::string& familyName) const { - for (const auto& family : families) { - if (family->getFamilyName() == familyName) { - return family; - } - } - return nullptr; -} - -std::shared_ptr FontManagerCustom::onMatchFamilyStyle(const std::string& familyName, - FontStyle style) const { - if (auto set = this->matchFamily(familyName)) { - return set->matchStyle(style); - } - return defaultFamily->matchStyle(style); -} - -std::shared_ptr FontManagerCustom::onMatchFamilyStyleCharacter( - const std::string&, FontStyle, const std::vector&, Unichar) const { - return nullptr; -} - -} // namespace tgfx \ No newline at end of file diff --git a/src/core/LoadResourceProvider.cpp b/src/svg/ResourceLoader.cpp similarity index 76% rename from src/core/LoadResourceProvider.cpp rename to src/svg/ResourceLoader.cpp index 1c21382c..e4d31076 100644 --- a/src/core/LoadResourceProvider.cpp +++ b/src/svg/ResourceLoader.cpp @@ -16,7 +16,7 @@ // ///////////////////////////////////////////////////////////////////////////////////////////////// -#include "tgfx/core/LoadResourceProvider.h" +#include "tgfx/svg/ResourceLoader.h" #include #include #include @@ -24,9 +24,9 @@ namespace tgfx { -class FileResourceProvider final : public LoadResourceProvider { +class SystemResourceLoader final : public ResourceLoader { public: - explicit FileResourceProvider(std::string basePath) : basePath(std::move(basePath)) { + explicit SystemResourceLoader(std::string basePath) : basePath(std::move(basePath)) { } std::shared_ptr load(const std::string& resourcePath, @@ -43,13 +43,4 @@ class FileResourceProvider final : public LoadResourceProvider { const std::string basePath; }; -std::shared_ptr LoadResourceProvider::MakeEmpty() { - return std::shared_ptr(new LoadResourceProvider); -} - -std::shared_ptr LoadResourceProvider::MakeFileProvider( - const std::string& basePath) { - return std::make_shared(basePath); -} - } // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGDOM.cpp b/src/svg/SVGDOM.cpp index b53ae75b..51d40423 100644 --- a/src/svg/SVGDOM.cpp +++ b/src/svg/SVGDOM.cpp @@ -30,6 +30,8 @@ #include "svg/SVGLengthContext.h" #include "svg/SVGNodeConstructor.h" #include "svg/SVGRenderContext.h" +#include "svg/SystemFontManager.h" +#include "svg/SystemResourceLoader.h" #include "tgfx/core/Canvas.h" #include "tgfx/core/Data.h" #include "tgfx/core/Recorder.h" @@ -63,14 +65,14 @@ std::shared_ptr SVGDOM::Make(Stream& stream, SVGDOMOptions options) { // Create SVGDOM with the root node and ID mapper return std::shared_ptr( - new SVGDOM(std::static_pointer_cast(root), std::move(options), std::move(mapper))); + new SVGDOM(std::static_pointer_cast(root), std::move(options), std::move(mapper))); } -SVGDOM::SVGDOM(std::shared_ptr root, SVGDOMOptions options, SVGIDMapper&& mapper) +SVGDOM::SVGDOM(std::shared_ptr root, SVGDOMOptions options, SVGIDMapper&& mapper) : root(std::move(root)), nodeIDMapper(std::move(mapper)), options(std::move(options)) { } -const std::shared_ptr& SVGDOM::getRoot() const { +const std::shared_ptr& SVGDOM::getRoot() const { return root; } @@ -96,13 +98,12 @@ void SVGDOM::render(Canvas* canvas) { SVGLengthContext lengthContext(drawSize); SVGPresentationContext presentationContext; - auto resourceProvider = - options.resourceProvider ? options.resourceProvider : LoadResourceProvider::MakeEmpty(); - auto shaperFactory = options.shaperFactory ? options.shaperFactory : shapers::PrimitiveFactory(); + auto resourceLoader = + options.resourceProvider ? options.resourceProvider : SystemResourceLoader::Make(); + auto fontManager = options.fontManager ? options.fontManager : SystemFontManager::Make(); - SVGRenderContext renderContext(canvas, options.fontManager, resourceProvider, shaperFactory, - nodeIDMapper, lengthContext, presentationContext, - {nullptr, nullptr}, canvas->getMatrix()); + SVGRenderContext renderContext(canvas, fontManager, resourceLoader, nodeIDMapper, lengthContext, + presentationContext, {nullptr, nullptr}, canvas->getMatrix()); root->render(renderContext); } diff --git a/src/svg/SVGNodeConstructor.cpp b/src/svg/SVGNodeConstructor.cpp index f2385a7d..bfc5228f 100644 --- a/src/svg/SVGNodeConstructor.cpp +++ b/src/svg/SVGNodeConstructor.cpp @@ -39,7 +39,7 @@ #include "tgfx/svg/node/SVGFeOffset.h" #include "tgfx/svg/node/SVGFeTurbulence.h" #include "tgfx/svg/node/SVGFilter.h" -#include "tgfx/svg/node/SVGG.h" +#include "tgfx/svg/node/SVGGroup.h" #include "tgfx/svg/node/SVGImage.h" #include "tgfx/svg/node/SVGLine.h" #include "tgfx/svg/node/SVGLinearGradient.h" @@ -50,7 +50,7 @@ #include "tgfx/svg/node/SVGPoly.h" #include "tgfx/svg/node/SVGRadialGradient.h" #include "tgfx/svg/node/SVGRect.h" -#include "tgfx/svg/node/SVGSVG.h" +#include "tgfx/svg/node/SVGRoot.h" #include "tgfx/svg/node/SVGStop.h" #include "tgfx/svg/node/SVGText.h" #include "tgfx/svg/node/SVGUse.h" @@ -231,7 +231,7 @@ std::unordered_map SVGNodeConstructor::attributePars using ElementFactory = std::function()>; std::unordered_map ElementFactories = { - {"a", []() -> std::shared_ptr { return SVGG::Make(); }}, + {"a", []() -> std::shared_ptr { return SVGGroup::Make(); }}, {"circle", []() -> std::shared_ptr { return SVGCircle::Make(); }}, {"clipPath", []() -> std::shared_ptr { return SVGClipPath::Make(); }}, {"defs", []() -> std::shared_ptr { return SVGDefs::Make(); }}, @@ -263,7 +263,7 @@ std::unordered_map ElementFactories = { {"feSpotLight", []() -> std::shared_ptr { return SVGFeSpotLight::Make(); }}, {"feTurbulence", []() -> std::shared_ptr { return SVGFeTurbulence::Make(); }}, {"filter", []() -> std::shared_ptr { return SVGFilter::Make(); }}, - {"g", []() -> std::shared_ptr { return SVGG::Make(); }}, + {"g", []() -> std::shared_ptr { return SVGGroup::Make(); }}, {"image", []() -> std::shared_ptr { return SVGImage::Make(); }}, {"line", []() -> std::shared_ptr { return SVGLine::Make(); }}, {"linearGradient", []() -> std::shared_ptr { return SVGLinearGradient::Make(); }}, @@ -331,7 +331,7 @@ std::shared_ptr SVGNodeConstructor::ConstructSVGNode(const Construction const std::string& elementName) -> std::shared_ptr { if (elementName == "svg") { // Outermost SVG element must be tagged as such. - return SVGSVG::Make(context.parentNode ? SVGSVG::Type::kInner : SVGSVG::Type::kRoot); + return SVGRoot::Make(context.parentNode ? SVGRoot::Type::kInner : SVGRoot::Type::kRoot); } if (auto iter = ElementFactories.find(elementName); iter != ElementFactories.end()) { diff --git a/src/svg/SVGRenderContext.cpp b/src/svg/SVGRenderContext.cpp index a3b28b11..d049cdfd 100644 --- a/src/svg/SVGRenderContext.cpp +++ b/src/svg/SVGRenderContext.cpp @@ -103,46 +103,44 @@ SVGPresentationContext::SVGPresentationContext() } SVGRenderContext::SVGRenderContext(Canvas* canvas, const std::shared_ptr& fontManager, - const std::shared_ptr& resourceProvider, - const std::shared_ptr& shaperFactory, + const std::shared_ptr& resourceProvider, const SVGIDMapper& mapper, const SVGLengthContext& lengthContext, const SVGPresentationContext& presentContext, const OBBScope& scope, const Matrix& matrix) - : _fontManager(fontManager), _resourceProvider(resourceProvider), shaperFactory(shaperFactory), - nodeIDMapper(mapper), _lengthContext(lengthContext), _presentationContext(presentContext), - _canvas(canvas), canvasSaveCount(canvas->getSaveCount()), scope(scope), _matrix(matrix) { + : _fontManager(fontManager), _resourceProvider(resourceProvider), nodeIDMapper(mapper), + _lengthContext(lengthContext), _presentationContext(presentContext), _canvas(canvas), + canvasSaveCount(canvas->getSaveCount()), scope(scope), _matrix(matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other) : SVGRenderContext(other._canvas, other._fontManager, other._resourceProvider, - other.shaperFactory, other.nodeIDMapper, *other._lengthContext, - *other._presentationContext, other.scope, other._matrix) { + other.nodeIDMapper, *other._lengthContext, *other._presentationContext, + other.scope, other._matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, Canvas* canvas) - : SVGRenderContext(canvas, other._fontManager, other._resourceProvider, other.shaperFactory, - other.nodeIDMapper, *other._lengthContext, *other._presentationContext, - other.scope, other._matrix) { + : SVGRenderContext(canvas, other._fontManager, other._resourceProvider, other.nodeIDMapper, + *other._lengthContext, *other._presentationContext, other.scope, + other._matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, const SVGLengthContext& lengthContext) : SVGRenderContext(other._canvas, other._fontManager, other._resourceProvider, - other.shaperFactory, other.nodeIDMapper, lengthContext, - *other._presentationContext, other.scope, other._matrix) { + other.nodeIDMapper, lengthContext, *other._presentationContext, other.scope, + other._matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, Canvas* canvas, const SVGLengthContext& lengthContext) - : SVGRenderContext(canvas, other._fontManager, other._resourceProvider, other.shaperFactory, - other.nodeIDMapper, lengthContext, *other._presentationContext, other.scope, - other._matrix) { + : SVGRenderContext(canvas, other._fontManager, other._resourceProvider, other.nodeIDMapper, + lengthContext, *other._presentationContext, other.scope, other._matrix) { } SVGRenderContext::SVGRenderContext(const SVGRenderContext& other, const SVGNode* node) : SVGRenderContext(other._canvas, other._fontManager, other._resourceProvider, - other.shaperFactory, other.nodeIDMapper, *other._lengthContext, - *other._presentationContext, OBBScope{node, this}, other._matrix) { + other.nodeIDMapper, *other._lengthContext, *other._presentationContext, + OBBScope{node, this}, other._matrix) { } SVGRenderContext::~SVGRenderContext() { @@ -339,9 +337,8 @@ std::optional SVGRenderContext::commonPaint(const SVGPaint& svgPaint, flo // and node being rendered. SVGPresentationContext presentContext; presentContext._namedColors = _presentationContext->_namedColors; - SVGRenderContext localContext(_canvas, _fontManager, _resourceProvider, shaperFactory, - nodeIDMapper, *_lengthContext, presentContext, scope, - Matrix::I()); + SVGRenderContext localContext(_canvas, _fontManager, _resourceProvider, nodeIDMapper, + *_lengthContext, presentContext, scope, Matrix::I()); const auto node = this->findNodeById(svgPaint.iri()); if (!node || !node->asPaint(localContext, &(paint.value()))) { @@ -473,9 +470,9 @@ Font SVGRenderContext::resolveFont() const { const auto size = lengthContext().resolve(presentationContext()._inherited.FontSize->size(), SVGLengthContext::LengthType::Vertical); - auto typeface = _fontManager->matchFamilyStyle(family, style); + auto typeface = _fontManager->matchTypeface(family, style); if (!typeface) { - typeface = _fontManager->matchFamilyStyle("", style); + typeface = _fontManager->matchTypeface("", style); } Font font(std::move(typeface), size); return font; @@ -507,23 +504,5 @@ Rect SVGRenderContext::resolveOBBRect(const SVGLength& x, const SVGLength& y, co transform.scale.y * r.y() + transform.offset.y, transform.scale.x * r.width(), transform.scale.y * r.height()); } -std::unique_ptr SVGRenderContext::makeShaper() const { - DEBUG_ASSERT(shaperFactory); - return shaperFactory->makeShaper(); -} - -std::unique_ptr SVGRenderContext::makeBidiRunIterator(const char* utf8, - size_t utf8Bytes, - uint8_t bidiLevel) const { - DEBUG_ASSERT(shaperFactory); - return shaperFactory->makeBidiRunIterator(utf8, utf8Bytes, bidiLevel); -} - -std::unique_ptr SVGRenderContext::makeScriptRunIterator(const char* utf8, - size_t utf8Bytes) const { - DEBUG_ASSERT(shaperFactory); - constexpr FourByteTag unknownScript = SetFourByteTag('Z', 'z', 'z', 'z'); - return shaperFactory->makeScriptRunIterator(utf8, utf8Bytes, unknownScript); -} } // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGRenderContext.h b/src/svg/SVGRenderContext.h index 3a09aa6b..86c8f440 100644 --- a/src/svg/SVGRenderContext.h +++ b/src/svg/SVGRenderContext.h @@ -26,8 +26,6 @@ #include "core/utils/Log.h" #include "svg/SVGLengthContext.h" #include "tgfx/core/Canvas.h" -#include "tgfx/core/FontManager.h" -#include "tgfx/core/LoadResourceProvider.h" #include "tgfx/core/MaskFilter.h" #include "tgfx/core/Matrix.h" #include "tgfx/core/Paint.h" @@ -38,10 +36,11 @@ #include "tgfx/core/Rect.h" #include "tgfx/core/Size.h" #include "tgfx/gpu/Context.h" +#include "tgfx/svg/FontManager.h" +#include "tgfx/svg/ResourceLoader.h" #include "tgfx/svg/SVGAttribute.h" #include "tgfx/svg/SVGDOM.h" #include "tgfx/svg/SVGTypes.h" -#include "tgfx/svg/shaper/ShaperFactory.h" namespace tgfx { @@ -131,8 +130,7 @@ class SVGRenderContext { }; SVGRenderContext(Canvas* canvas, const std::shared_ptr& fontManager, - const std::shared_ptr& resourceProvider, - const std::shared_ptr& shaperFactory, + const std::shared_ptr& resourceProvider, const SVGIDMapper& mapper, const SVGLengthContext& lengthContext, const SVGPresentationContext& presentContext, const OBBScope& scope, const Matrix& matrix); @@ -199,7 +197,7 @@ class SVGRenderContext { return _clipPath.value_or(Path()); }; - const std::shared_ptr& resourceProvider() const { + const std::shared_ptr& resourceProvider() const { return _resourceProvider; } @@ -207,14 +205,6 @@ class SVGRenderContext { return _fontManager; } - std::unique_ptr makeShaper() const; - - std::unique_ptr makeBidiRunIterator(const char* utf8, size_t utf8Bytes, - uint8_t bidiLevel) const; - - std::unique_ptr makeScriptRunIterator(const char* utf8, - size_t utf8Bytes) const; - // Returns the translate/scale transformation required to map into the current OBB scope, // with the specified units. struct OBBTransform { @@ -241,8 +231,7 @@ class SVGRenderContext { std::optional commonPaint(const SVGPaint& paint, float opacity) const; std::shared_ptr _fontManager; - std::shared_ptr _resourceProvider; - std::shared_ptr shaperFactory; + std::shared_ptr _resourceProvider; const SVGIDMapper& nodeIDMapper; CopyOnWrite _lengthContext; diff --git a/src/svg/SVGTextContext.cpp b/src/svg/SVGTextContext.cpp deleted file mode 100644 index 6567d9f2..00000000 --- a/src/svg/SVGTextContext.cpp +++ /dev/null @@ -1,328 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making tgfx available. -// -// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// unless required by applicable law or agreed to in writing, software distributed under the -// license is distributed on an "as is" basis, without warranties or conditions of any kind, -// either express or implied. see the license for the specific language governing permissions -// and limitations under the license. -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -#include "SVGTextContext.h" -#include -#include -#include -#include "core/utils/Log.h" -#include "core/utils/MathExtra.h" -#include "tgfx/core/Font.h" -#include "tgfx/core/Matrix.h" -#include "tgfx/core/Paint.h" -#include "tgfx/core/Point.h" -#include "tgfx/core/Typeface.h" -#include "tgfx/core/UTF.h" -#include "tgfx/svg/shaper/ShaperFactory.h" - -namespace tgfx { - -namespace { - -std::vector ResolveLengths(const SVGLengthContext& lctx, - const std::vector& lengths, - SVGLengthContext::LengthType lt) { - std::vector resolved; - resolved.reserve(lengths.size()); - - for (const auto& l : lengths) { - resolved.push_back(lctx.resolve(l, lt)); - } - - return resolved; -} - -} // namespace - -SVGTextContext::ScopedPosResolver::ScopedPosResolver(const SVGTextContainer& txt, - const SVGLengthContext& lctx, - SVGTextContext* tctx, size_t charIndexOffset) - : textContext(tctx), parent(tctx->posResolver), charIndexOffset(charIndexOffset), - x(ResolveLengths(lctx, txt.getX(), SVGLengthContext::LengthType::Horizontal)), - y(ResolveLengths(lctx, txt.getY(), SVGLengthContext::LengthType::Vertical)), - dX(ResolveLengths(lctx, txt.getDx(), SVGLengthContext::LengthType::Horizontal)), - dY(ResolveLengths(lctx, txt.getDy(), SVGLengthContext::LengthType::Vertical)), - rotate(txt.getRotate()) { - textContext->posResolver = this; -} - -SVGTextContext::ScopedPosResolver::ScopedPosResolver(const SVGTextContainer& txt, - const SVGLengthContext& lctx, - SVGTextContext* tctx) - : ScopedPosResolver(txt, lctx, tctx, tctx->currentCharIndex) { -} - -SVGTextContext::ScopedPosResolver::~ScopedPosResolver() { - textContext->posResolver = parent; -} - -/////////////////////////////////////////////////////////////////////////////// - -SVGTextContext::PosAttrs SVGTextContext::ScopedPosResolver::resolve(size_t charIndex) const { - PosAttrs attrs; - - if (charIndex < fLastPosIndex) { - ASSERT(charIndex >= charIndexOffset); - const auto localCharIndex = charIndex - charIndexOffset; - - const auto hasAllLocal = localCharIndex < x.size() && localCharIndex < y.size() && - localCharIndex < dX.size() && localCharIndex < dY.size() && - localCharIndex < rotate.size(); - if (!hasAllLocal && parent) { - attrs = parent->resolve(charIndex); - } - - if (localCharIndex < x.size()) { - attrs[PosAttrs::x] = x[localCharIndex]; - } - if (localCharIndex < y.size()) { - attrs[PosAttrs::y] = y[localCharIndex]; - } - if (localCharIndex < dX.size()) { - attrs[PosAttrs::dx] = dX[localCharIndex]; - } - if (localCharIndex < dY.size()) { - attrs[PosAttrs::dy] = dY[localCharIndex]; - } - - // Rotation semantics are interestingly different [1]: - // - // - values are not cumulative - // - if explicit values are present at any level in the ancestor chain, those take - // precedence (closest ancestor) - // - last specified value applies to all remaining chars (closest ancestor) - // - these rules apply at node scope (not chunk scope) - // - // This means we need to discriminate between explicit rotation (rotate value provided for - // current char) and implicit rotation (ancestor has some values - but not for the requested - // char - we use the last specified value). - // - // [1] https://www.w3.org/TR/SVG11/text.html#TSpanElementRotateAttribute - if (!rotate.empty()) { - if (localCharIndex < rotate.size()) { - // Explicit rotation value overrides anything in the ancestor chain. - attrs[PosAttrs::rotate] = rotate[localCharIndex]; - attrs.setImplicitRotate(false); - } else if (!attrs.has(PosAttrs::rotate) || attrs.isImplicitRotate()) { - // Local implicit rotation (last specified value) overrides ancestor implicit - // rotation. - attrs[PosAttrs::rotate] = rotate.back(); - attrs.setImplicitRotate(true); - } - } - - if (!attrs.hasAny()) { - // Once we stop producing explicit position data, there is no reason to - // continue trying for higher indices. We can suppress future lookups. - fLastPosIndex = charIndex; - } - } - - return attrs; -} - -void SVGTextContext::ShapeBuffer::append(Unichar ch, PositionAdjustment pos) { - // relative pos adjustments are cumulative - if (!utf8PosAdjust.empty()) { - pos.offset += utf8PosAdjust.back().offset; - } - - auto unf8String = UTF::ToUTF8(ch); - utf8.append(unf8String); - utf8PosAdjust.insert(utf8PosAdjust.end(), unf8String.size(), pos); -} - -void SVGTextContext::shapePendingBuffer(const SVGRenderContext& ctx, const Font& font) { - const char* utf8 = shapeBuffer.utf8.data(); - size_t utf8Bytes = shapeBuffer.utf8.size(); - - std::unique_ptr font_runs = - Shaper::MakeFontMgrRunIterator(utf8, utf8Bytes, font, ctx.fontManager()); - if (!font_runs) { - return; - } - - // Try to use the passed in shaping callbacks to shape, for example, using harfbuzz and ICU. - const uint8_t defaultLTR = 0; - std::unique_ptr bidi = ctx.makeBidiRunIterator(utf8, utf8Bytes, defaultLTR); - std::unique_ptr language = - Shaper::MakeStdLanguageRunIterator(utf8, utf8Bytes); - std::unique_ptr script = ctx.makeScriptRunIterator(utf8, utf8Bytes); - - if (bidi && script && language) { - shaper->shape(utf8, utf8Bytes, *font_runs, *bidi, *script, *language, nullptr, 0, FLT_MAX, - this); - shapeBuffer.reset(); - return; - } // If any of the callbacks fail, we'll fallback to the primitive shaping. -} - -SVGTextContext::SVGTextContext(const SVGRenderContext& ctx, const ShapedTextCallback& cb, - const SVGTextPath* tpath) - : originalContext(ctx), callback(cb), shaper(ctx.makeShaper()), - chunkAlignmentFactor(ctx.presentationContext()._inherited.TextAnchor->getAlignmentFactor()) { - - if (tpath) { - chunkPos.x = tpath->getStartOffset().value(); - } -} - -SVGTextContext::~SVGTextContext() { - this->flushChunk(originalContext); -} - -void SVGTextContext::shapeFragment(const std::string& txt, const SVGRenderContext& ctx, - SVGXmlSpace xs) { - // https://www.w3.org/TR/SVG11/text.html#WhiteSpace - // https://www.w3.org/TR/2008/REC-xml-20081126/#NT-S - auto filterWSDefault = [this](Unichar ch) -> Unichar { - // Remove all newline chars. - if (ch == '\n') { - return -1; - } - - // Convert tab chars to space. - if (ch == '\t') { - ch = ' '; - } - - // Consolidate contiguous space chars and strip leading spaces (fPrevCharSpace - // starts off as true). - if (prevCharSpace && ch == ' ') { - return -1; - } - return ch; - }; - auto filterWSPreserve = [](Unichar ch) -> Unichar { - // Convert newline and tab chars to space. - if (ch == '\n' || ch == '\t') { - ch = ' '; - } - return ch; - }; - - // Stash paints for access from SkShaper callbacks. - currentFill = ctx.fillPaint(); - currentStroke = ctx.strokePaint(); - - const auto font = ctx.resolveFont(); - shapeBuffer.reserve(txt.size()); - - const char* chatPointer = txt.c_str(); - const char* charEnd = chatPointer + txt.size(); - - while (chatPointer < charEnd) { - auto ch = UTF::NextUTF8(&chatPointer, charEnd); - ch = (xs == SVGXmlSpace::Default) ? filterWSDefault(ch) : filterWSPreserve(ch); - - if (ch < 0) { - // invalid utf or char filtered out - continue; - } - - ASSERT(posResolver); - const auto pos = posResolver->resolve(currentCharIndex++); - - // Absolute position adjustments define a new chunk. - // (https://www.w3.org/TR/SVG11/text.html#TextLayoutIntroduction) - if (pos.has(PosAttrs::x) || pos.has(PosAttrs::y)) { - this->shapePendingBuffer(ctx, font); - this->flushChunk(ctx); - - // New chunk position. - if (pos.has(PosAttrs::x)) { - chunkPos.x = pos[PosAttrs::x]; - } - if (pos.has(PosAttrs::y)) { - chunkPos.y = pos[PosAttrs::y]; - } - } - - shapeBuffer.append(ch, - { - { - pos.has(PosAttrs::dx) ? pos[PosAttrs::dx] : 0, - pos.has(PosAttrs::dy) ? pos[PosAttrs::dy] : 0, - }, - pos.has(PosAttrs::rotate) ? DegreesToRadians(pos[PosAttrs::rotate]) : 0, - }); - - prevCharSpace = (ch == ' '); - } - - this->shapePendingBuffer(ctx, font); - - // Note: at this point we have shaped and buffered RunRecs for the current fragment. - // The active text chunk continues until an explicit or implicit flush. -} - -Matrix SVGTextContext::computeGlyphXform(GlyphID /*glyph*/, const Font& /*font*/, - const Point& glyph_pos, - const PositionAdjustment& pos_adjust) const { - Point pos = chunkPos + glyph_pos + pos_adjust.offset + chunkAdvance * chunkAlignmentFactor; - return Matrix::MakeRotate(RadiansToDegrees(pos_adjust.rotation), pos.x, pos.y); -} - -RunHandler::Buffer SVGTextContext::runBuffer(const RunInfo& ri) { - ASSERT(ri.glyphCount); - - RunRec runRecord = { - ri.font, - currentFill.has_value() ? std::make_unique(*currentFill) : nullptr, - currentStroke.has_value() ? std::make_unique(*currentStroke) : nullptr, - std::make_unique(ri.glyphCount), - std::make_unique(ri.glyphCount), - std::make_unique(ri.glyphCount), - ri.glyphCount, - ri.advance, - }; - - runs.push_back(std::move(runRecord)); - - // Ensure sufficient space to temporarily fetch cluster information. - shapeClusterBuffer.resize(std::max(shapeClusterBuffer.size(), ri.glyphCount)); - - return { - runs.back().glyphs.get(), - runs.back().glyphPos.get(), - nullptr, - shapeClusterBuffer.data(), - chunkAdvance, - }; -} - -void SVGTextContext::commitRunBuffer(const RunInfo& ri) { - const auto& current_run = runs.back(); - - // stash position adjustments - for (size_t i = 0; i < ri.glyphCount; ++i) { - const auto utf8_index = shapeClusterBuffer[i]; - current_run.glyphPosAdjust[i] = shapeBuffer.utf8PosAdjust[utf8_index]; - } - - chunkAdvance += ri.advance; -} - -void SVGTextContext::commitLine() { - if (!shapeBuffer.utf8PosAdjust.empty()) { - // Offset adjustments are cumulative - only advance the current chunk with the last value. - chunkAdvance += shapeBuffer.utf8PosAdjust.back().offset; - } -} - -} // namespace tgfx \ No newline at end of file diff --git a/src/svg/SVGTextContext.h b/src/svg/SVGTextContext.h deleted file mode 100644 index eabd9411..00000000 --- a/src/svg/SVGTextContext.h +++ /dev/null @@ -1,217 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making tgfx available. -// -// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// unless required by applicable law or agreed to in writing, software distributed under the -// license is distributed on an "as is" basis, without warranties or conditions of any kind, -// either express or implied. see the license for the specific language governing permissions -// and limitations under the license. -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include -#include -#include "svg/SVGRenderContext.h" -#include "tgfx/core/Font.h" -#include "tgfx/core/Matrix.h" -#include "tgfx/core/Paint.h" -#include "tgfx/core/Point.h" -#include "tgfx/core/Typeface.h" -#include "tgfx/svg/node/SVGText.h" -#include "tgfx/svg/shaper/Shaper.h" - -namespace tgfx { - -class SVGTextContext final : RunHandler { - public: - using ShapedTextCallback = - std::function&)>; - - // Helper for encoding optional positional attributes. - class PosAttrs { - public: - enum Attr : size_t { - x = 0, - y = 1, - dx = 2, - dy = 3, - rotate = 4, - }; - - float operator[](Attr a) const { - return storage[a]; - } - float& operator[](Attr a) { - return storage[a]; - } - - bool has(Attr a) const { - return storage[a] != _NONE; - } - bool hasAny() const { - return this->has(x) || this->has(y) || this->has(dx) || this->has(dy) || this->has(rotate); - } - - void setImplicitRotate(bool imp) { - implicitRotate = imp; - } - bool isImplicitRotate() const { - return implicitRotate; - } - - private: - inline static constexpr auto _NONE = std::numeric_limits::infinity(); - float storage[5] = {_NONE, _NONE, _NONE, _NONE, _NONE}; - bool implicitRotate = false; - }; - - // Helper for cascading position attribute resolution (x, y, dx, dy, rotate) [1]: - // - each text position element can specify an arbitrary-length attribute array - // - for each character, we look up a given attribute first in its local attribute array, - // then in the ancestor chain (cascading/fallback) - and return the first value encountered. - // - the lookup is based on character index relative to the text content subtree - // (i.e. the index crosses chunk boundaries) - // - // [1] https://www.w3.org/TR/SVG11/text.html#TSpanElementXAttribute - class ScopedPosResolver { - public: - ScopedPosResolver(const SVGTextContainer&, const SVGLengthContext&, SVGTextContext*, size_t); - - ScopedPosResolver(const SVGTextContainer&, const SVGLengthContext&, SVGTextContext*); - - ~ScopedPosResolver(); - - PosAttrs resolve(size_t charIndex) const; - - private: - SVGTextContext* textContext; - const ScopedPosResolver* parent; // parent resolver (fallback) - const size_t charIndexOffset; // start index for the current resolver - const std::vector x, y, dX, dY; - const std::vector& rotate; - - // cache for the last known index with explicit positioning - mutable size_t fLastPosIndex = std::numeric_limits::max(); - }; - - SVGTextContext(const SVGRenderContext&, const ShapedTextCallback&, const SVGTextPath* = nullptr); - ~SVGTextContext() override; - - // Shape and queue codepoints for final alignment. - void shapeFragment(const std::string&, const SVGRenderContext&, SVGXmlSpace); - - // Perform final adjustments and push shaped blobs to the callback. - void flushChunk(const SVGRenderContext& ctx); - - const ShapedTextCallback& getCallback() const { - return callback; - } - - private: - struct PositionAdjustment { - Point offset; - float rotation; - }; - - struct ShapeBuffer { - std::string utf8; - // per-utf8-char cumulative pos adjustments - std::vector utf8PosAdjust; - - void reserve(size_t size) { - utf8.reserve(size); - utf8PosAdjust.reserve(utf8PosAdjust.size()); - } - - void reset() { - utf8.clear(); - utf8PosAdjust.clear(); - } - - void append(Unichar, PositionAdjustment); - }; - - struct RunRec { - Font font; - std::unique_ptr fillPaint, strokePaint; - std::unique_ptr glyphs; // filled by SkShaper - std::unique_ptr glyphPos; // filled by SkShaper - std::unique_ptr glyphPosAdjust; // deferred positioning adjustments - size_t glyphCount; - Point advance; - }; - - // Caches path information to accelerate position lookups. - class PathData { - public: - PathData(const SVGRenderContext&, const SVGTextPath&); - - Matrix getMatrixAt(float offset) const; - - float length() const { - return fLength; - } - - private: - // std::vector> fContours; - float fLength = 0; // total path length - }; - - void shapePendingBuffer(const SVGRenderContext&, const Font&); - - Matrix computeGlyphXform(GlyphID, const Font&, const Point& glyph_pos, - const PositionAdjustment&) const; - - //callbacks - void beginLine() override { - } - - void runInfo(const RunInfo&) override { - } - - void commitRunInfo() override { - } - - Buffer runBuffer(const RunInfo& ri) override; - - void commitRunBuffer(const RunInfo& ri) override; - - void commitLine() override; - - // http://www.w3.org/TR/SVG11/text.html#TextLayout - const SVGRenderContext& originalContext; // original render context - const ShapedTextCallback& callback; - std::unique_ptr shaper = nullptr; - std::vector runs = {}; - const ScopedPosResolver* posResolver = nullptr; - - // shaper state - ShapeBuffer shapeBuffer = {}; - std::vector shapeClusterBuffer = {}; - - // chunk state - Point chunkPos = {0, 0}; // current text chunk position - Point chunkAdvance = {0, 0}; // cumulative advance - float chunkAlignmentFactor; // current chunk alignment - - // tracks the global text subtree char index (cross chunks). Used for position resolution. - size_t currentCharIndex = 0; - - // cached for access from SkShaper callbacks. - std::optional currentFill; - std::optional currentStroke; - - bool prevCharSpace = true; // WS filter state - bool forcePrimitiveShaping = false; -}; - -} // namespace tgfx \ No newline at end of file diff --git a/include/tgfx/svg/shaper/ShaperFactory.h b/src/svg/SystemFontManager.cpp similarity index 62% rename from include/tgfx/svg/shaper/ShaperFactory.h rename to src/svg/SystemFontManager.cpp index b5192e64..c99e831c 100644 --- a/include/tgfx/svg/shaper/ShaperFactory.h +++ b/src/svg/SystemFontManager.cpp @@ -16,29 +16,25 @@ // ///////////////////////////////////////////////////////////////////////////////////////////////// -#pragma once - +#include "SystemFontManager.h" +#include +#include #include -#include "tgfx/svg/shaper/Shaper.h" +#include +#include "core/utils/Log.h" +#include "tgfx/core/FontStyle.h" +#include "tgfx/core/Typeface.h" namespace tgfx { -namespace shapers { - -class Factory { - public: - virtual ~Factory() = default; - - virtual std::unique_ptr makeShaper() = 0; - - virtual std::unique_ptr makeBidiRunIterator(const char* utf8, size_t utf8Bytes, - uint8_t bidiLevel) = 0; - virtual std::unique_ptr makeScriptRunIterator(const char* utf8, - size_t utf8Bytes, - FourByteTag script) = 0; -}; +std::shared_ptr SystemFontManager::onMatchTypeface(const std::string& familyName, + FontStyle style) const { + return Typeface::MakeFromStyle(familyName, style); +} -std::shared_ptr PrimitiveFactory(); +std::shared_ptr SystemFontManager::onGetFallbackTypeface(const std::string& familyName, + FontStyle style, Unichar) const { + return onMatchTypeface(familyName, style); +} -} // namespace shapers } // namespace tgfx \ No newline at end of file diff --git a/src/svg/shaper/ShaperFactory.cpp b/src/svg/SystemFontManager.h similarity index 54% rename from src/svg/shaper/ShaperFactory.cpp rename to src/svg/SystemFontManager.h index 95c50f78..bcc76b47 100644 --- a/src/svg/shaper/ShaperFactory.cpp +++ b/src/svg/SystemFontManager.h @@ -16,34 +16,31 @@ // ///////////////////////////////////////////////////////////////////////////////////////////////// -#include "tgfx/svg/shaper/ShaperFactory.h" +#pragma once + +#include #include -#include "svg/shaper/ShaperPrimitive.h" -#include "tgfx/svg/shaper/Shaper.h" +#include +#include "tgfx/core/FontStyle.h" +#include "tgfx/core/Typeface.h" +#include "tgfx/svg/FontManager.h" namespace tgfx { -namespace { -class PrimitiveFactoryImpl final : public shapers::Factory { - std::unique_ptr makeShaper() override { - return std::make_unique(); +class SystemFontManager : public FontManager { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SystemFontManager); } - std::unique_ptr makeBidiRunIterator(const char*, size_t, uint8_t) override { - return std::make_unique(0, 0); - } + protected: + SystemFontManager() = default; - std::unique_ptr makeScriptRunIterator(const char*, size_t, - FourByteTag) override { - return std::make_unique(0, 0); - } -}; -} // namespace + std::shared_ptr onMatchTypeface(const std::string& familyName, + FontStyle style) const override; -namespace shapers { -std::shared_ptr PrimitiveFactory() { - return std::make_shared(); -} -} // namespace shapers + std::shared_ptr onGetFallbackTypeface(const std::string& familyName, FontStyle style, + Unichar character) const override; +}; } // namespace tgfx \ No newline at end of file diff --git a/src/svg/SystemResourceLoader.h b/src/svg/SystemResourceLoader.h new file mode 100644 index 00000000..cf6ea340 --- /dev/null +++ b/src/svg/SystemResourceLoader.h @@ -0,0 +1,62 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "tgfx/svg/ResourceLoader.h" + +namespace tgfx { +class SystemResourceLoader final : public ResourceLoader { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new SystemResourceLoader); + } + + std::shared_ptr load(const std::string& resourcePath, + const std::string& resourceName) const override { + if (resourceName.empty() && resourcePath.empty()) { + return nullptr; + } + if (resourcePath.empty()) { + return Data::MakeFromFile(resourceName); + } + if (resourceName.empty()) { + return Data::MakeFromFile(resourcePath); + } + return Data::MakeFromFile(resourcePath + "/" + resourceName); + }; + + std::shared_ptr loadImage(const std::string& resourcePath, + const std::string& resourceName) const override { + if (resourceName.empty() && resourcePath.empty()) { + return nullptr; + } + if (resourcePath.empty()) { + return Image::MakeFromFile(resourceName); + } + if (resourceName.empty()) { + return Image::MakeFromFile(resourcePath); + } + return Image::MakeFromFile(resourcePath + "/" + resourceName); + } + + protected: + SystemResourceLoader() = default; +}; + +} // namespace tgfx \ No newline at end of file diff --git a/src/svg/node/SVGImage.cpp b/src/svg/node/SVGImage.cpp index 323fa658..3e1cadc2 100644 --- a/src/svg/node/SVGImage.cpp +++ b/src/svg/node/SVGImage.cpp @@ -49,7 +49,7 @@ bool SVGImage::onPrepareToRender(SVGRenderContext* context) const { INHERITED::onPrepareToRender(context); } -std::shared_ptr LoadImage(const std::shared_ptr& resourceProvider, +std::shared_ptr LoadImage(const std::shared_ptr& resourceProvider, const SVGIRI& href) { switch (href.type()) { @@ -74,9 +74,9 @@ std::shared_ptr LoadImage(const std::shared_ptr& re } } -SVGImage::ImageInfo SVGImage::LoadImage( - const std::shared_ptr& resourceProvider, const SVGIRI& iri, - const Rect& viewPort, SVGPreserveAspectRatio /*ratio*/) { +SVGImage::ImageInfo SVGImage::LoadImage(const std::shared_ptr& resourceProvider, + const SVGIRI& iri, const Rect& viewPort, + SVGPreserveAspectRatio /*ratio*/) { std::shared_ptr image = ::tgfx::LoadImage(resourceProvider, iri); if (!image) { return {}; diff --git a/src/svg/node/SVGSVG.cpp b/src/svg/node/SVGRoot.cpp similarity index 92% rename from src/svg/node/SVGSVG.cpp rename to src/svg/node/SVGRoot.cpp index 4ad7298e..0955d936 100644 --- a/src/svg/node/SVGSVG.cpp +++ b/src/svg/node/SVGRoot.cpp @@ -16,7 +16,7 @@ // ///////////////////////////////////////////////////////////////////////////////////////////////// -#include "tgfx/svg/node/SVGSVG.h" +#include "tgfx/svg/node/SVGRoot.h" #include "svg/SVGRenderContext.h" #include "tgfx/core/Size.h" #include "tgfx/svg/SVGAttribute.h" @@ -25,7 +25,7 @@ namespace tgfx { -void SVGSVG::renderNode(const SVGRenderContext& context, const SVGIRI& iri) const { +void SVGRoot::renderNode(const SVGRenderContext& context, const SVGIRI& iri) const { SVGRenderContext localContext(context, this); auto node = localContext.findNodeById(iri); if (!node) { @@ -41,7 +41,7 @@ void SVGSVG::renderNode(const SVGRenderContext& context, const SVGIRI& iri) cons } } -bool SVGSVG::onPrepareToRender(SVGRenderContext* context) const { +bool SVGRoot::onPrepareToRender(SVGRenderContext* context) const { // x/y are ignored for outermost svg elements const auto x = type == Type::kInner ? X : SVGLength(0); const auto y = type == Type::kInner ? Y : SVGLength(0); @@ -77,7 +77,7 @@ bool SVGSVG::onPrepareToRender(SVGRenderContext* context) const { } // https://www.w3.org/TR/SVG11/coords.html#IntrinsicSizing -Size SVGSVG::intrinsicSize(const SVGLengthContext& lengthContext) const { +Size SVGRoot::intrinsicSize(const SVGLengthContext& lengthContext) const { // Percentage values do not provide an intrinsic size. if (Width.unit() == SVGLength::Unit::Percentage || Height.unit() == SVGLength::Unit::Percentage) { return Size::Make(0, 0); @@ -87,7 +87,7 @@ Size SVGSVG::intrinsicSize(const SVGLengthContext& lengthContext) const { lengthContext.resolve(Height, SVGLengthContext::LengthType::Vertical)); } -void SVGSVG::onSetAttribute(SVGAttribute attr, const SVGValue& v) { +void SVGRoot::onSetAttribute(SVGAttribute attr, const SVGValue& v) { if (type != Type::kInner && type != Type::kRoot) return; switch (attr) { case SVGAttribute::X: diff --git a/src/svg/shaper/Shaper.cpp b/src/svg/shaper/Shaper.cpp deleted file mode 100644 index 8b5dbfa9..00000000 --- a/src/svg/shaper/Shaper.cpp +++ /dev/null @@ -1,131 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making tgfx available. -// -// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// unless required by applicable law or agreed to in writing, software distributed under the -// license is distributed on an "as is" basis, without warranties or conditions of any kind, -// either express or implied. see the license for the specific language governing permissions -// and limitations under the license. -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -#include "tgfx/svg/shaper/Shaper.h" -#include -#include -#include -#include "core/utils/Log.h" -#include "svg/shaper/ShaperPrimitive.h" -#include "tgfx/core/Font.h" -#include "tgfx/core/FontManager.h" -#include "tgfx/core/UTF.h" -#include "tgfx/svg/shaper/Shaper.h" - -namespace tgfx { -class FontManagerRunIterator final : public FontRunIterator { - public: - FontManagerRunIterator(const char* utf8, size_t utf8Bytes, const Font& inputfont, - std::shared_ptr fallbackMgr, const char* requestName, - FontStyle requestStyle, const LanguageRunIterator* lang) - : current(utf8), begin(utf8), end(current + utf8Bytes), - fallbackManager(std::move(fallbackMgr)), font(inputfont), fallbackFont(inputfont), - requestName(requestName), requestStyle(requestStyle), languageIter(lang) { - // If fallback is not wanted, clients should use TrivialFontRunIterator. - fallbackFont.setTypeface(nullptr); - } - - void consume() override { - ASSERT(current < end); - Unichar unichar = UTF::NextUTF8(¤t, end); - // If the starting typeface can handle this character, use it. - if (font.getGlyphID(unichar)) { - _currentFont = &font; - // If the current fallback can handle this character, use it. - } else if (fallbackFont.getTypeface() && fallbackFont.getGlyphID(unichar)) { - _currentFont = &fallbackFont; - // If not, try to find a fallback typeface - } else { - std::vector languages; - if (languageIter) { - languages.push_back(languageIter->currentLanguage()); - } - auto candidate = - fallbackManager->matchFamilyStyleCharacter(requestName, requestStyle, languages, unichar); - if (candidate) { - fallbackFont.setTypeface(std::move(candidate)); - _currentFont = &fallbackFont; - } else { - _currentFont = &font; - } - } - - while (current < end) { - const char* prev = current; - unichar = UTF::NextUTF8(¤t, end); - - // End run if not using initial typeface and initial typeface has this character. - if (_currentFont->getTypeface() != font.getTypeface() && font.getGlyphID(unichar)) { - current = prev; - return; - } - - // End run if current typeface does not have this character and some other font does. - if (!_currentFont->getGlyphID(unichar)) { - std::vector languages; - if (languageIter) { - languages.push_back(languageIter->currentLanguage()); - } - auto candidate = fallbackManager->matchFamilyStyleCharacter(requestName, requestStyle, - languages, unichar); - if (candidate) { - current = prev; - return; - } - } - } - } - - size_t endOfCurrentRun() const override { - return static_cast(current - begin); - } - - bool atEnd() const override { - return current == end; - } - - const Font& currentFont() const override { - return *_currentFont; - } - - private: - const char* current = nullptr; - const char* const begin = nullptr; - const char* const end = nullptr; - std::shared_ptr fallbackManager; - Font font; - Font fallbackFont; - Font* _currentFont = nullptr; - const char* const requestName; - const FontStyle requestStyle; - LanguageRunIterator const* const languageIter; -}; - -std::unique_ptr Shaper::MakeFontMgrRunIterator( - const char* utf8, size_t utf8Bytes, const Font& font, std::shared_ptr fallback) { - return std::make_unique(utf8, utf8Bytes, font, std::move(fallback), - nullptr, font.getTypeface()->getFontStyle(), - nullptr); -} - -std::unique_ptr Shaper::MakeStdLanguageRunIterator(const char* /*utf8*/, - size_t utf8Bytes) { - return std::make_unique(std::locale().name().c_str(), utf8Bytes); -} - -} // namespace tgfx \ No newline at end of file diff --git a/src/svg/shaper/ShaperPrimitive.cpp b/src/svg/shaper/ShaperPrimitive.cpp deleted file mode 100644 index 9ae30571..00000000 --- a/src/svg/shaper/ShaperPrimitive.cpp +++ /dev/null @@ -1,199 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making tgfx available. -// -// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// unless required by applicable law or agreed to in writing, software distributed under the -// license is distributed on an "as is" basis, without warranties or conditions of any kind, -// either express or implied. see the license for the specific language governing permissions -// and limitations under the license. -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -#include "svg/shaper/ShaperPrimitive.h" -#include "core/utils/Log.h" -#include "tgfx/core/Font.h" -#include "tgfx/core/Point.h" -#include "tgfx/core/Typeface.h" -#include "tgfx/core/UTF.h" - -namespace tgfx { - -namespace { - -inline bool is_breaking_whitespace(Unichar c) { - switch (c) { - case 0x0020: // SPACE - //case 0x00A0: // NO-BREAK SPACE - case 0x1680: // OGHAM SPACE MARK - case 0x180E: // MONGOLIAN VOWEL SEPARATOR - case 0x2000: // EN QUAD - case 0x2001: // EM QUAD - case 0x2002: // EN SPACE (nut) - case 0x2003: // EM SPACE (mutton) - case 0x2004: // THREE-PER-EM SPACE (thick space) - case 0x2005: // FOUR-PER-EM SPACE (mid space) - case 0x2006: // SIX-PER-EM SPACE - case 0x2007: // FIGURE SPACE - case 0x2008: // PUNCTUATION SPACE - case 0x2009: // THIN SPACE - case 0x200A: // HAIR SPACE - case 0x200B: // ZERO WIDTH SPACE - case 0x202F: // NARROW NO-BREAK SPACE - case 0x205F: // MEDIUM MATHEMATICAL SPACE - case 0x3000: // IDEOGRAPHIC SPACE - //case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE - return true; - default: - return false; - } -} - -size_t linebreak(const char text[], const char stop[], float width, const float* advance, - size_t* trailing) { - float accumulatedWidth = 0; - int glyphIndex = 0; - const char* start = text; - const char* wordStart = text; - bool prevWS = true; - *trailing = 0; - - while (text < stop) { - const char* prevText = text; - Unichar uni = UTF::NextUTF8(&text, stop); - accumulatedWidth += advance[glyphIndex++]; - bool currWS = is_breaking_whitespace(uni); - - if (!currWS && prevWS) { - wordStart = prevText; - } - prevWS = currWS; - - if (width < accumulatedWidth) { - bool consumeWhitespace = false; - if (currWS) { - // previous fit, put this and following whitespace in trailing - if (prevText == start) { - // don't put this in trailing if it's the first thing - prevText = text; - } - consumeWhitespace = true; - } else if (wordStart != start) { - // backup to the last whitespace that fit - text = wordStart; - } else if (prevText > start) { - // backup to just before the glyph that didn't fit - text = prevText; - } else { - // let it overflow, put any following whitespace in trailing - prevText = text; - consumeWhitespace = true; - } - if (consumeWhitespace) { - const char* next = text; - while (next < stop && is_breaking_whitespace(UTF::NextUTF8(&next, stop))) { - text = next; - } - if (trailing) { - *trailing = static_cast(text - prevText); - } - } - break; - } - } - return static_cast(text - start); -} - -} // namespace - -void ShaperPrimitive::shape(const char* utf8, size_t utf8Bytes, FontRunIterator& fontIter, - BiDiRunIterator& /*bidiIter*/, ScriptRunIterator& /*scriptIter*/, - LanguageRunIterator& /*langIter*/, const RunFeature* /*features*/, - size_t /*featuresSize*/, float width, RunHandler* handler) const { - Font font; - if (!fontIter.atEnd()) { - fontIter.consume(); - font = fontIter.currentFont(); - } - DEBUG_ASSERT(font.getTypeface()); - - std::vector glyphs; - std::vector advances; - - { - const char* textStart = utf8; - const char* textStop = utf8 + utf8Bytes; - while (textStart < textStop) { - auto unichar = UTF::NextUTF8(&textStart, textStop); - auto glyphID = font.getGlyphID(unichar); - glyphs.push_back(glyphID); - advances.push_back(font.getAdvance(glyphID)); - } - } - - int glyphOffset = 0; - size_t utf8Offset = 0; - do { - size_t bytesCollapsed = 0; - size_t bytesConsumed = - linebreak(utf8, utf8 + utf8Bytes, width, advances.data() + glyphOffset, &bytesCollapsed); - size_t bytesVisible = bytesConsumed - bytesCollapsed; - - auto numGlyphs = static_cast(UTF::CountUTF8(utf8, bytesVisible)); - - const char* textStart = utf8; - const char* textStop = utf8 + utf8Bytes; - float runWidth = 0.0f; - while (textStart < textStop) { - auto unichar = UTF::NextUTF8(&textStart, textStop); - auto glyphID = font.getGlyphID(unichar); - runWidth += font.getAdvance(glyphID) + font.getBounds(glyphID).width(); - } - - const RunHandler::RunInfo info = {font, - 0, - {runWidth, 0}, - numGlyphs, - RunHandler::Range(utf8Offset, bytesVisible)}; - - handler->beginLine(); - if (info.glyphCount) { - handler->runInfo(info); - } - handler->commitRunInfo(); - if (info.glyphCount) { - const auto buffer = handler->runBuffer(info); - - memcpy(buffer.glyphs, glyphs.data() + glyphOffset, info.glyphCount * sizeof(GlyphID)); - auto position = buffer.point; - for (size_t i = 0; i < info.glyphCount; ++i) { - buffer.positions[i] = position; - position.x += advances[i + static_cast(glyphOffset)]; - } - if (buffer.clusters) { - const auto* textPointer = utf8; - for (size_t i = 0; i < info.glyphCount; ++i) { - // Each character maps to exactly one glyph. - buffer.clusters[i] = - static_cast(static_cast(textPointer - utf8) + utf8Offset); - UTF::NextUTF8(&textPointer, utf8 + utf8Bytes); - } - } - handler->commitRunBuffer(info); - } - handler->commitLine(); - - glyphOffset += UTF::CountUTF8(utf8, bytesConsumed); - utf8Offset += bytesConsumed; - utf8 += bytesConsumed; - utf8Bytes -= bytesConsumed; - } while (0 < utf8Bytes); -} - -} // namespace tgfx \ No newline at end of file diff --git a/src/svg/shaper/ShaperPrimitive.h b/src/svg/shaper/ShaperPrimitive.h deleted file mode 100644 index 785d0ad4..00000000 --- a/src/svg/shaper/ShaperPrimitive.h +++ /dev/null @@ -1,109 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// Tencent is pleased to support the open source community by making tgfx available. -// -// Copyright (C) 2025 THL A29 Limited, a Tencent company. All rights reserved. -// -// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except -// in compliance with the License. You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// unless required by applicable law or agreed to in writing, software distributed under the -// license is distributed on an "as is" basis, without warranties or conditions of any kind, -// either express or implied. see the license for the specific language governing permissions -// and limitations under the license. -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "tgfx/svg/shaper/Shaper.h" - -namespace tgfx { - -template -class TrivialRunIterator : public RunIteratorSubclass { - public: - explicit TrivialRunIterator(size_t utf8Bytes) : end(utf8Bytes), _atEnd(end == 0) { - } - void consume() override { - _atEnd = true; - } - size_t endOfCurrentRun() const override { - return _atEnd ? end : 0; - } - bool atEnd() const override { - return _atEnd; - } - - private: - size_t end; - bool _atEnd; -}; - -class TrivialFontRunIterator : public TrivialRunIterator { - public: - TrivialFontRunIterator(Font font, size_t utf8Bytes) - : TrivialRunIterator(utf8Bytes), font(std::move(font)) { - } - const Font& currentFont() const override { - return font; - } - - private: - Font font; -}; - -class TrivialBiDiRunIterator : public TrivialRunIterator { - public: - TrivialBiDiRunIterator(uint8_t bidiLevel, size_t utf8Bytes) - : TrivialRunIterator(utf8Bytes), bidiLevel(bidiLevel) { - } - uint8_t currentLevel() const override { - return bidiLevel; - } - - private: - uint8_t bidiLevel; -}; - -class TrivialScriptRunIterator : public TrivialRunIterator { - public: - TrivialScriptRunIterator(FourByteTag script, size_t utf8Bytes) - : TrivialRunIterator(utf8Bytes), script(script) { - } - FourByteTag currentScript() const override { - return script; - } - - private: - FourByteTag script; -}; - -class TrivialLanguageRunIterator : public TrivialRunIterator { - public: - TrivialLanguageRunIterator(const char* language, size_t utf8Bytes) - : TrivialRunIterator(utf8Bytes), language(language) { - } - - std::string currentLanguage() const override { - return language; - } - - private: - std::string language; -}; - -class ShaperPrimitive : public Shaper { - public: - ShaperPrimitive() = default; - - private: - void shape(const char* utf8, size_t utf8Bytes, FontRunIterator& fontIter, - BiDiRunIterator& bidiIter, ScriptRunIterator& scriptIter, - LanguageRunIterator& langIter, const RunFeature* features, size_t featuresSize, - float width, RunHandler* handler) const override; -}; - -} // namespace tgfx \ No newline at end of file diff --git a/test/src/SVGRenderTest.cpp b/test/src/SVGRenderTest.cpp index 8fa59a8b..33d02e93 100644 --- a/test/src/SVGRenderTest.cpp +++ b/test/src/SVGRenderTest.cpp @@ -21,7 +21,6 @@ #include "tgfx/core/Data.h" #include "tgfx/core/FontStyle.h" #include "tgfx/core/Stream.h" -#include "tgfx/svg/FontManagerCustom.h" #include "tgfx/svg/SVGDOM.h" #include "tgfx/svg/xml/XMLDOM.h" #include "utils/TestUtils.h" @@ -193,34 +192,28 @@ TGFX_TEST(SVGRenderTest, BlurSVG) { EXPECT_TRUE(Baseline::Compare(surface, "SVGTest/blur")); } -class FontLoaderTest : public FontManagerCustom::FontLoader { - public: - void loadFonts(std::vector>& families) const override { - auto family = std::make_shared("Noto Sans SC"); - auto typeface = MakeTypeface("resources/font/NotoSansSC-Regular.otf"); - typeface->setFontStyle(FontStyle::Normal()); - family->appendTypeface(typeface); - families.push_back(family); - - family = std::make_shared("Noto Serif SC"); - typeface = MakeTypeface("resources/font/NotoSerifSC-Regular.otf"); - typeface->setFontStyle(FontStyle::Normal()); - family->appendTypeface(typeface); - families.push_back(family); - } -}; +// class FontLoaderTest : public FontManagerCustom::FontLoader { +// public: +// void loadFonts(std::vector>& families) const override { +// auto family = std::make_shared("Noto Sans SC"); +// auto typeface = MakeTypeface("resources/font/NotoSansSC-Regular.otf"); +// typeface->setFontStyle(FontStyle::Normal()); +// family->appendTypeface(typeface); +// families.push_back(family); + +// family = std::make_shared("Noto Serif SC"); +// typeface = MakeTypeface("resources/font/NotoSerifSC-Regular.otf"); +// typeface->setFontStyle(FontStyle::Normal()); +// family->appendTypeface(typeface); +// families.push_back(family); +// } +// }; TGFX_TEST(SVGRenderTest, TextSVG) { auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/text.svg")); ASSERT_TRUE(stream != nullptr); - FontLoaderTest loader; - std::shared_ptr fontManager = std::make_shared(loader); - - SVGDOMOptions options; - options.fontManager = fontManager; - - auto SVGDom = SVGDOM::Make(*stream, options); + auto SVGDom = SVGDOM::Make(*stream); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -239,13 +232,7 @@ TGFX_TEST(SVGRenderTest, TextFontSVG) { auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/textFont.svg")); ASSERT_TRUE(stream != nullptr); - FontLoaderTest loader; - std::shared_ptr fontManager = std::make_shared(loader); - - SVGDOMOptions options; - options.fontManager = fontManager; - - auto SVGDom = SVGDOM::Make(*stream, options); + auto SVGDom = SVGDOM::Make(*stream); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -261,11 +248,6 @@ TGFX_TEST(SVGRenderTest, TextFontSVG) { } TGFX_TEST(SVGRenderTest, ComplexSVG) { - FontLoaderTest loader; - std::shared_ptr fontManager = std::make_shared(loader); - - SVGDOMOptions options; - options.fontManager = fontManager; ContextScope scope; auto* context = scope.getContext(); @@ -275,7 +257,7 @@ TGFX_TEST(SVGRenderTest, ComplexSVG) { auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/complex1.svg")); ASSERT_TRUE(stream != nullptr); - auto SVGDom = SVGDOM::Make(*stream, options); + auto SVGDom = SVGDOM::Make(*stream); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -290,7 +272,7 @@ TGFX_TEST(SVGRenderTest, ComplexSVG) { auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/complex2.svg")); ASSERT_TRUE(stream != nullptr); - auto SVGDom = SVGDOM::Make(*stream, options); + auto SVGDom = SVGDOM::Make(*stream); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -306,7 +288,7 @@ TGFX_TEST(SVGRenderTest, ComplexSVG) { auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/complex3.svg")); ASSERT_TRUE(stream != nullptr); - auto SVGDom = SVGDOM::Make(*stream, options); + auto SVGDom = SVGDOM::Make(*stream); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -322,7 +304,7 @@ TGFX_TEST(SVGRenderTest, ComplexSVG) { auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/complex4.svg")); ASSERT_TRUE(stream != nullptr); - auto SVGDom = SVGDOM::Make(*stream, options); + auto SVGDom = SVGDOM::Make(*stream); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -337,7 +319,7 @@ TGFX_TEST(SVGRenderTest, ComplexSVG) { auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/complex5.svg")); ASSERT_TRUE(stream != nullptr); - auto SVGDom = SVGDOM::Make(*stream, options); + auto SVGDom = SVGDOM::Make(*stream); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -352,7 +334,7 @@ TGFX_TEST(SVGRenderTest, ComplexSVG) { auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/complex6.svg")); ASSERT_TRUE(stream != nullptr); - auto SVGDom = SVGDOM::Make(*stream, options); + auto SVGDom = SVGDOM::Make(*stream); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -367,7 +349,7 @@ TGFX_TEST(SVGRenderTest, ComplexSVG) { auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/complex7.svg")); ASSERT_TRUE(stream != nullptr); - auto SVGDom = SVGDOM::Make(*stream, options); + auto SVGDom = SVGDOM::Make(*stream); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); From 2dcbdc773e778ca912f6491f229aadf9ac70710e Mon Sep 17 00:00:00 2001 From: YGauroa Date: Sat, 25 Jan 2025 14:50:54 +0800 Subject: [PATCH 31/31] . --- include/tgfx/svg/ResourceLoader.h | 2 +- src/core/vectors/freetype/SystemFont.cpp | 4 +- test/src/SVGRenderTest.cpp | 72 +++++++++++++++++++++--- 3 files changed, 65 insertions(+), 13 deletions(-) diff --git a/include/tgfx/svg/ResourceLoader.h b/include/tgfx/svg/ResourceLoader.h index 146fcd22..c513cad4 100644 --- a/include/tgfx/svg/ResourceLoader.h +++ b/include/tgfx/svg/ResourceLoader.h @@ -26,7 +26,7 @@ namespace tgfx { /** - * LoadResourceProvider is an interface for loading resources (e.g. images, font) from + * ResourceLoader is an interface for loading resources (e.g. images, font) from * external sources. */ class ResourceLoader { diff --git a/src/core/vectors/freetype/SystemFont.cpp b/src/core/vectors/freetype/SystemFont.cpp index 48aec333..b9bd1df6 100644 --- a/src/core/vectors/freetype/SystemFont.cpp +++ b/src/core/vectors/freetype/SystemFont.cpp @@ -23,6 +23,7 @@ #ifdef _WIN32 #include #include +#include #include #endif @@ -154,9 +155,6 @@ static constexpr std::array FontSlantMap = { DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STYLE_ITALIC, DWRITE_FONT_STYLE_OBLIQUE}; DWriteFontStyle ToDWriteFontStyle(FontStyle fontStyle) { - std::string key; - key.resize(fontStyle.length()); - std::transform(fontStyle.begin(), fontStyle.end(), key.begin(), ::tolower); DWriteFontStyle dWriteFontStyle{}; dWriteFontStyle.weight = FontWeightMap[static_cast(fontStyle.weight())]; dWriteFontStyle.stretch = FontWidthMap[static_cast(fontStyle.width())]; diff --git a/test/src/SVGRenderTest.cpp b/test/src/SVGRenderTest.cpp index 33d02e93..c324da72 100644 --- a/test/src/SVGRenderTest.cpp +++ b/test/src/SVGRenderTest.cpp @@ -17,10 +17,14 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include +#include +#include #include "gtest/gtest.h" #include "tgfx/core/Data.h" #include "tgfx/core/FontStyle.h" #include "tgfx/core/Stream.h" +#include "tgfx/core/Typeface.h" +#include "tgfx/svg/FontManager.h" #include "tgfx/svg/SVGDOM.h" #include "tgfx/svg/xml/XMLDOM.h" #include "utils/TestUtils.h" @@ -209,11 +213,55 @@ TGFX_TEST(SVGRenderTest, BlurSVG) { // } // }; +class FontManagerTest : public FontManager { + public: + static std::shared_ptr Make() { + return std::shared_ptr(new FontManagerTest); + } + + protected: + FontManagerTest() { + auto typeface = + Typeface::MakeFromPath(ProjectPath::Absolute("resources/font/NotoSansSC-Regular.otf"), 0); + if (typeface) { + typefaces["Noto Sans SC"] = typeface; + defaultTypeface = typeface; + } + + typeface = + Typeface::MakeFromPath(ProjectPath::Absolute("resources/font/NotoSerifSC-Regular.otf"), 0); + if (typeface) { + typefaces["Noto Serif SC"] = typeface; + } + }; + + std::shared_ptr onMatchTypeface(const std::string& familyName, + FontStyle) const override { + auto iter = typefaces.find(familyName); + if (iter != typefaces.end()) { + return iter->second; + } + return defaultTypeface; + } + + std::shared_ptr onGetFallbackTypeface(const std::string& familyName, FontStyle style, + Unichar) const override { + return onMatchTypeface(familyName, style); + } + + private: + std::unordered_map> typefaces; + std::shared_ptr defaultTypeface; +}; + TGFX_TEST(SVGRenderTest, TextSVG) { auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/text.svg")); ASSERT_TRUE(stream != nullptr); - auto SVGDom = SVGDOM::Make(*stream); + SVGDOMOptions options; + options.fontManager = FontManagerTest::Make(); + + auto SVGDom = SVGDOM::Make(*stream, options); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -232,7 +280,10 @@ TGFX_TEST(SVGRenderTest, TextFontSVG) { auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/textFont.svg")); ASSERT_TRUE(stream != nullptr); - auto SVGDom = SVGDOM::Make(*stream); + SVGDOMOptions options; + options.fontManager = FontManagerTest::Make(); + + auto SVGDom = SVGDOM::Make(*stream, options); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -253,11 +304,14 @@ TGFX_TEST(SVGRenderTest, ComplexSVG) { auto* context = scope.getContext(); ASSERT_TRUE(context != nullptr); + SVGDOMOptions options; + options.fontManager = FontManagerTest::Make(); + { auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/complex1.svg")); ASSERT_TRUE(stream != nullptr); - auto SVGDom = SVGDOM::Make(*stream); + auto SVGDom = SVGDOM::Make(*stream, options); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -272,7 +326,7 @@ TGFX_TEST(SVGRenderTest, ComplexSVG) { auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/complex2.svg")); ASSERT_TRUE(stream != nullptr); - auto SVGDom = SVGDOM::Make(*stream); + auto SVGDom = SVGDOM::Make(*stream, options); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -288,7 +342,7 @@ TGFX_TEST(SVGRenderTest, ComplexSVG) { auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/complex3.svg")); ASSERT_TRUE(stream != nullptr); - auto SVGDom = SVGDOM::Make(*stream); + auto SVGDom = SVGDOM::Make(*stream, options); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -304,7 +358,7 @@ TGFX_TEST(SVGRenderTest, ComplexSVG) { auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/complex4.svg")); ASSERT_TRUE(stream != nullptr); - auto SVGDom = SVGDOM::Make(*stream); + auto SVGDom = SVGDOM::Make(*stream, options); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -319,7 +373,7 @@ TGFX_TEST(SVGRenderTest, ComplexSVG) { auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/complex5.svg")); ASSERT_TRUE(stream != nullptr); - auto SVGDom = SVGDOM::Make(*stream); + auto SVGDom = SVGDOM::Make(*stream, options); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -334,7 +388,7 @@ TGFX_TEST(SVGRenderTest, ComplexSVG) { auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/complex6.svg")); ASSERT_TRUE(stream != nullptr); - auto SVGDom = SVGDOM::Make(*stream); + auto SVGDom = SVGDOM::Make(*stream, options); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr); @@ -349,7 +403,7 @@ TGFX_TEST(SVGRenderTest, ComplexSVG) { auto stream = Stream::MakeFromFile(ProjectPath::Absolute("resources/apitest/SVG/complex7.svg")); ASSERT_TRUE(stream != nullptr); - auto SVGDom = SVGDOM::Make(*stream); + auto SVGDom = SVGDOM::Make(*stream, options); auto rootNode = SVGDom->getRoot(); ASSERT_TRUE(rootNode != nullptr);