Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SVG export module, converting drawing commands to SVG text #367

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
d420b9f
export to svg
YGaurora Nov 8, 2024
8aecd42
update code
YGaurora Nov 27, 2024
044dcc7
Merge remote-tracking branch 'origin/main' into feature/yg_drawcall_t…
YGaurora Nov 28, 2024
29d6acd
modify method
YGaurora Nov 28, 2024
89e3111
format code
YGaurora Nov 28, 2024
f2cb00d
format
YGaurora Nov 28, 2024
5e695ba
blur and shadow export
YGaurora Dec 4, 2024
5172467
remove unused code
YGaurora Dec 4, 2024
46eff0a
modify state stack
YGaurora Dec 4, 2024
89d2962
save
YGaurora Dec 4, 2024
cd1b226
...
YGaurora Dec 6, 2024
ea79d6f
...
YGaurora Dec 6, 2024
03351f9
Merge remote-tracking branch 'origin/main' into feature/yg_drawcall_t…
YGaurora Dec 6, 2024
ba97d37
refactor
YGaurora Dec 6, 2024
e45554f
remove unused code
YGaurora Dec 6, 2024
6559c09
unit test
YGaurora Dec 6, 2024
2f5cb4d
format
YGaurora Dec 6, 2024
578fb4e
fix code problem and export text as text
YGaurora Dec 11, 2024
7c1349b
Merge remote-tracking branch 'origin/main' into feature/yg_drawcall_t…
YGaurora Dec 11, 2024
d19f59c
Merge remote-tracking branch 'origin/main' into feature/yg_drawcall_t…
YGaurora Dec 11, 2024
ecf6dfd
...
YGaurora Dec 11, 2024
049d00c
format
YGaurora Dec 12, 2024
92f7863
optimize code and remove mistake
YGaurora Dec 17, 2024
e775c30
。。。
YGaurora Dec 17, 2024
93ce0a3
Merge remote-tracking branch 'origin/main' into feature/yg_drawcall_t…
YGaurora Dec 17, 2024
db206f9
format code
YGaurora Dec 17, 2024
922820d
fix compile error
YGaurora Dec 17, 2024
776637e
fix
YGaurora Dec 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ if (TGFX_USE_ZLIB)
list(APPEND TGFX_STATIC_VENDORS zlib)
endif ()

if (TGFX_BUILD_SVG)
# SVG and PDF exports require glyphs to be convertible back to Unicode.
list(APPEND TGFX_DEFINES TGFX_USE_GLYPH_TO_UNICODE)
endif ()

if (TGFX_USE_FREETYPE)
list(APPEND TGFX_DEFINES TGFX_USE_FREETYPE)
list(APPEND TGFX_STATIC_VENDORS freetype)
Expand Down
2 changes: 2 additions & 0 deletions include/tgfx/core/Canvas.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "tgfx/core/SamplingOptions.h"
#include "tgfx/core/Shape.h"
#include "tgfx/core/TextBlob.h"
#include "tgfx/svg/SVGExporter.h"

namespace tgfx {
class Surface;
Expand Down Expand Up @@ -430,5 +431,6 @@ class Canvas {
friend class Surface;
friend class Picture;
friend class Recorder;
friend class SVGExporter;
};
} // namespace tgfx
1 change: 1 addition & 0 deletions include/tgfx/core/ImageFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,5 +169,6 @@ class ImageFilter {
friend class InnerShadowImageFilter;
friend class ComposeImageFilter;
friend class FilterImage;
friend class ImageFilterCaster;
};
} // namespace tgfx
2 changes: 2 additions & 0 deletions include/tgfx/core/Picture.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ namespace tgfx {
class Record;
class Canvas;
class DrawContext;
class SVGExportingContext;
class MCState;
class Image;

Expand Down Expand Up @@ -61,6 +62,7 @@ class Picture {
friend class MeasureContext;
friend class RenderContext;
friend class RecordingContext;
friend class SVGExportingContext;
friend class Image;
friend class PictureImage;
friend class Canvas;
Expand Down
1 change: 1 addition & 0 deletions include/tgfx/core/Shader.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,5 +151,6 @@ class Shader {

friend class FragmentProcessor;
friend class Canvas;
friend class ShaderCaster;
};
} // namespace tgfx
10 changes: 10 additions & 0 deletions include/tgfx/core/Typeface.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include <mutex>
#include <unordered_map>
#include <vector>
#include "tgfx/core/Data.h"

namespace tgfx {
Expand Down Expand Up @@ -131,11 +132,20 @@ class Typeface {
virtual std::shared_ptr<Data> 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.
* The length of the array is not required, it will be resized within the method.
*/
virtual std::vector<Unichar> getGlyphToUnicodeMap() const = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里的vector表示了两层含义,替代了map的作用,理解上不是太直观

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

它这里要兼顾性能,GlyphID是连续线性,直接构造vector的查性能最高,用map会很慢。Skia也是这么处理。


mutable std::mutex locker = {};

private:
std::unordered_map<float, std::weak_ptr<ScalerContext>> scalerContexts = {};

friend class ScalerContext;
friend class GlyphConverter;
};
} // namespace tgfx
91 changes: 91 additions & 0 deletions include/tgfx/svg/SVGExporter.h
Original file line number Diff line number Diff line change
@@ -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.
//
/////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#include <memory>
#include <sstream>
#include "tgfx/core/Canvas.h"
#include "tgfx/core/Rect.h"
#include "tgfx/gpu/Context.h"

namespace tgfx {
class SVGExportingContext;

struct ExportingOptions {
YGaurora marked this conversation as resolved.
Show resolved Hide resolved
ExportingOptions() = default;
ExportingOptions(bool convertTextToPaths, bool prettyXML)
: convertTextToPaths(convertTextToPaths), prettyXML(prettyXML) {
}
YGaurora marked this conversation as resolved.
Show resolved Hide resolved
/**
* Convert text to paths in the exported SVG text, only applicable to outline fonts. Emoji and
YGaurora marked this conversation as resolved.
Show resolved Hide resolved
* web fonts will only be exported as text.
*/
bool convertTextToPaths = false;
/**
* Format SVG text with spaces('/t') and newlines('/n').
*/
bool prettyXML = true;
};

/**
* SVGExporter is a class used to export SVG text, converting drawing commands in the Canvas to
* SVG text.
*/
class SVGExporter {
YGaurora marked this conversation as resolved.
Show resolved Hide resolved
public:
/**
* Creates an SVGExporter object pointer, which can be used to export SVG text.
*
* @param svgStream The string stream to store the SVG text.
* @param context used to convert some rendering commands into image data.
* @param viewBox viewBox of SVG, and content that exceeds the display area in the
* SVG will be clipped.
* @param options Options for exporting SVG text.
*/
static std::shared_ptr<SVGExporter> Make(std::stringstream& svgStream, Context* context,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里不能直接用std::stringsteam, 这个会一直在内存里,我们之所以要支持stream就是为了可以减少内存占用,背后可以包装一个FILE对象,直接写入磁盘。这里应该像Skia一样定义一个WStream基类,但是提供一些基本的实现,有FileWStream,也可以创建MemoryWStream,内部实现再包装std::stringstream

const Rect& viewBox, ExportingOptions options);

/**
* Destroys the SVGExporter object, equivalent to calling close().
YGaurora marked this conversation as resolved.
Show resolved Hide resolved
*/
~SVGExporter();

/**
* Get the canvas if the SVGExporter is not closed.if closed, return nullptr.
YGaurora marked this conversation as resolved.
Show resolved Hide resolved
*/
Canvas* getCanvas() const;

/**
* Closes the SVGExporter.Finalizing any unfinished drawing commands and writing the SVG end tag.
YGaurora marked this conversation as resolved.
Show resolved Hide resolved
*/
void close();

private:
/**
* Construct a SVGExporter object
*/
SVGExporter(std::stringstream& svgStream, Context* context, const Rect& viewBox,
ExportingOptions options);

bool closed = false;
SVGExportingContext* drawContext = nullptr;
Canvas* canvas = nullptr;
};

} // namespace tgfx
2 changes: 2 additions & 0 deletions src/core/Canvas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ void Canvas::restore() {
*mcState = canvasState->mcState;
auto layer = std::move(canvasState->savedLayer);
stateStack.pop();
drawContext->onRestore();
YGaurora marked this conversation as resolved.
Show resolved Hide resolved
if (layer != nullptr) {
drawContext = layer->drawContext;
auto layerContext = reinterpret_cast<RecordingContext*>(layer->layerContext.get());
Expand Down Expand Up @@ -188,6 +189,7 @@ void Canvas::clipPath(const Path& path) {
auto clipPath = path;
clipPath.transform(mcState->matrix);
mcState->clip.addPath(clipPath, PathOp::Intersect);
drawContext->onClipPath(*mcState);
}

void Canvas::resetStateStack() {
Expand Down
4 changes: 4 additions & 0 deletions src/core/DrawContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,5 +95,9 @@ class DrawContext {
*/
virtual void drawLayer(std::shared_ptr<Picture> picture, const MCState& state,
const FillStyle& style, std::shared_ptr<ImageFilter> filter) = 0;

virtual void onClipPath(const MCState&){};

virtual void onRestore(){};
};
} // namespace tgfx
6 changes: 6 additions & 0 deletions src/core/Typeface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
/////////////////////////////////////////////////////////////////////////////////////////////////

#include "tgfx/core/Typeface.h"
#include <vector>
#include "core/utils/UniqueID.h"
#include "tgfx/core/UTF.h"

Expand Down Expand Up @@ -63,6 +64,11 @@ class EmptyTypeface : public Typeface {
return nullptr;
}

protected:
std::vector<Unichar> getGlyphToUnicodeMap() const override {
return {};
}

private:
uint32_t _uniqueID = UniqueID::Next();
};
Expand Down
69 changes: 69 additions & 0 deletions src/core/utils/Caster.cpp
Original file line number Diff line number Diff line change
@@ -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 "Caster.h"

namespace tgfx {
std::shared_ptr<const ColorShader> ShaderCaster::AsColorShader(
const std::shared_ptr<Shader>& shader) {
if (shader->type() == Shader::Type::Color) {
return std::static_pointer_cast<const ColorShader>(shader);
}
return nullptr;
}

std::shared_ptr<const ImageShader> ShaderCaster::AsImageShader(
const std::shared_ptr<Shader>& shader) {
if (shader->type() == Shader::Type::Image) {
return std::static_pointer_cast<const ImageShader>(shader);
}
return nullptr;
}

std::shared_ptr<const GradientShader> ShaderCaster::AsGradientShader(
const std::shared_ptr<Shader>& shader) {
if (shader->type() == Shader::Type::Gradient) {
return std::static_pointer_cast<const GradientShader>(shader);
}
return nullptr;
}

std::shared_ptr<const BlurImageFilter> ImageFilterCaster::AsBlurImageFilter(
const std::shared_ptr<ImageFilter>& imageFilter) {
if (imageFilter->type() == ImageFilter::Type::Blur) {
return std::static_pointer_cast<const BlurImageFilter>(imageFilter);
}
return nullptr;
}
std::shared_ptr<const DropShadowImageFilter> ImageFilterCaster::AsDropShadowImageFilter(
const std::shared_ptr<ImageFilter>& imageFilter) {
if (imageFilter->type() == ImageFilter::Type::DropShadow) {
return std::static_pointer_cast<const DropShadowImageFilter>(imageFilter);
}
return nullptr;
}

std::shared_ptr<const InnerShadowImageFilter> ImageFilterCaster::AsInnerShadowImageFilter(
const std::shared_ptr<ImageFilter>& imageFilter) {
if (imageFilter->type() == ImageFilter::Type::InnerShadow) {
return std::static_pointer_cast<const InnerShadowImageFilter>(imageFilter);
}
return nullptr;
}

} // namespace tgfx
53 changes: 53 additions & 0 deletions src/core/utils/Caster.h
Original file line number Diff line number Diff line change
@@ -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.
//
/////////////////////////////////////////////////////////////////////////////////////////////////

#include <memory>
#include "core/filters/BlurImageFilter.h"
#include "core/filters/DropShadowImageFilter.h"
#include "core/filters/InnerShadowImageFilter.h"
#include "core/shaders/ColorShader.h"
#include "core/shaders/GradientShader.h"
#include "core/shaders/ImageShader.h"

#pragma once

namespace tgfx {

class ShaderCaster {
public:
static std::shared_ptr<const ColorShader> AsColorShader(const std::shared_ptr<Shader>& shader);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个方法有地方使用吗?没搜索到调用的地方,下面的几个方法都看看是在哪些地方使用的。

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

都是在ElementWriter中使用
Clipboard_Screenshot_1734333874


static std::shared_ptr<const ImageShader> AsImageShader(const std::shared_ptr<Shader>& shader);

static std::shared_ptr<const GradientShader> AsGradientShader(
const std::shared_ptr<Shader>& shader);
};

class ImageFilterCaster {
public:
static std::shared_ptr<const BlurImageFilter> AsBlurImageFilter(
const std::shared_ptr<ImageFilter>& imageFilter);

static std::shared_ptr<const DropShadowImageFilter> AsDropShadowImageFilter(
const std::shared_ptr<ImageFilter>& imageFilter);

static std::shared_ptr<const InnerShadowImageFilter> AsInnerShadowImageFilter(
const std::shared_ptr<ImageFilter>& imageFilter);
};

} // namespace tgfx
Loading
Loading