Add optional OT-SVG support to FreeType
Bug: skia:12290 Change-Id: I064bee781d3a714e46f102cb48494fbe8f3e46e8 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/516436 Reviewed-by: Florin Malita <fmalita@google.com> Reviewed-by: Derek Sollenberger <djsollen@google.com> Commit-Queue: Ben Wagner <bungeman@google.com>
This commit is contained in:
parent
4f33f5bef8
commit
9cbadcd928
@ -57,6 +57,10 @@
|
||||
#include "src/utils/mac/SkUniqueCFRef.h"
|
||||
#endif
|
||||
|
||||
#if defined(SK_ENABLE_SVG)
|
||||
#include "modules/svg/include/SkSVGOpenTypeSVGDecoder.h"
|
||||
#endif
|
||||
|
||||
extern bool gSkForceRasterPipelineBlitter;
|
||||
extern bool gUseSkVMBlitter;
|
||||
extern bool gSkVMAllowJIT;
|
||||
@ -1530,6 +1534,9 @@ int main(int argc, char** argv) {
|
||||
dump_json(); // It's handy for the bots to assume this is ~never missing.
|
||||
|
||||
SkAutoGraphics ag;
|
||||
#if defined(SK_ENABLE_SVG)
|
||||
SkGraphics::SetOpenTypeSVGDecoderFactory(SkSVGOpenTypeSVGDecoder::Make);
|
||||
#endif
|
||||
SkTaskGroup::Enabler enabled(FLAGS_threads);
|
||||
|
||||
if (nullptr == GetResourceAsData("images/color_wheel.png")) {
|
||||
|
@ -39,10 +39,11 @@ protected:
|
||||
};
|
||||
static constexpr char const * const sampleText = ToolUtils::emoji_sample_text();
|
||||
static constexpr const Test tests[] = {
|
||||
{ Test::Source::Resource, "fonts/colr.ttf", sampleText },
|
||||
{ Test::Source::Resource, "fonts/sbix.ttf", sampleText },
|
||||
{ Test::Source::Resource, "fonts/cbdt.ttf", sampleText },
|
||||
{ Test::Source::Portable, "Emoji" , sampleText },
|
||||
{ Test::Source::Resource, "fonts/colr.ttf" , sampleText },
|
||||
{ Test::Source::Resource, "fonts/sbix.ttf" , sampleText },
|
||||
{ Test::Source::Resource, "fonts/cbdt.ttf" , sampleText },
|
||||
{ Test::Source::Portable, "Emoji" , sampleText },
|
||||
{ Test::Source::Resource, "fonts/SampleSVG.ttf", "abcdefghi" },
|
||||
};
|
||||
sk_sp<SkTypeface> typefaces[SK_ARRAY_COUNT(tests)];
|
||||
void onOnceBeforeDraw() override {
|
||||
|
@ -54,6 +54,7 @@ skia_core_public = [
|
||||
"$_include/core/SkMath.h",
|
||||
"$_include/core/SkMatrix.h",
|
||||
"$_include/core/SkMilestone.h",
|
||||
"$_include/core/SkOpenTypeSVGDecoder.h",
|
||||
"$_include/core/SkOverdrawCanvas.h",
|
||||
"$_include/core/SkPaint.h",
|
||||
"$_include/core/SkPath.h",
|
||||
|
@ -959,3 +959,14 @@ generated_cc_atom(
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [":SkTypes_hdr"],
|
||||
)
|
||||
|
||||
generated_cc_atom(
|
||||
name = "SkOpenTypeSVGDecoder_hdr",
|
||||
hdrs = ["SkOpenTypeSVGDecoder.h"],
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [
|
||||
":SkColor_hdr",
|
||||
":SkSpan_hdr",
|
||||
":SkTypes_hdr",
|
||||
],
|
||||
)
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
class SkData;
|
||||
class SkImageGenerator;
|
||||
class SkOpenTypeSVGDecoder;
|
||||
class SkTraceMemoryDump;
|
||||
|
||||
class SK_API SkGraphics {
|
||||
@ -137,6 +138,18 @@ public:
|
||||
static ImageGeneratorFromEncodedDataFactory
|
||||
SetImageGeneratorFromEncodedDataFactory(ImageGeneratorFromEncodedDataFactory);
|
||||
|
||||
/**
|
||||
* To draw OpenType SVG data, Skia will look at this runtime function pointer. If this function
|
||||
* pointer is set, the SkTypeface implementations which support OpenType SVG will call this
|
||||
* function to create an SkOpenTypeSVGDecoder to decode the OpenType SVG and draw it as needed.
|
||||
* If this function is not set, the SkTypeface implementations will generally not support
|
||||
* OpenType SVG and attempt to use other glyph representations if available.
|
||||
*/
|
||||
using OpenTypeSVGDecoderFactory =
|
||||
std::unique_ptr<SkOpenTypeSVGDecoder> (*)(const uint8_t* svg, size_t length);
|
||||
static OpenTypeSVGDecoderFactory SetOpenTypeSVGDecoderFactory(OpenTypeSVGDecoderFactory);
|
||||
static OpenTypeSVGDecoderFactory GetOpenTypeSVGDecoderFactory();
|
||||
|
||||
/**
|
||||
* Call early in main() to allow Skia to use a JIT to accelerate CPU-bound operations.
|
||||
*/
|
||||
|
30
include/core/SkOpenTypeSVGDecoder.h
Normal file
30
include/core/SkOpenTypeSVGDecoder.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2022 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkOpenTypeSVGDecoder_DEFINED
|
||||
#define SkOpenTypeSVGDecoder_DEFINED
|
||||
|
||||
#include "include/core/SkColor.h"
|
||||
#include "include/core/SkSpan.h"
|
||||
#include "include/core/SkTypes.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class SkCanvas;
|
||||
|
||||
class SkOpenTypeSVGDecoder {
|
||||
public:
|
||||
/** Each instance probably owns an SVG DOM.
|
||||
* The instance may be cached so needs to report how much memory it retains.
|
||||
*/
|
||||
virtual size_t approximateSize() = 0;
|
||||
virtual bool render(SkCanvas&, int upem, SkGlyphID glyphId,
|
||||
SkColor foregroundColor, SkSpan<SkColor> palette) = 0;
|
||||
virtual ~SkOpenTypeSVGDecoder() = default;
|
||||
};
|
||||
|
||||
#endif // SkOpenTypeSVGDecoder_DEFINED
|
@ -484,3 +484,15 @@ generated_cc_atom(
|
||||
"//include/private:SkNoncopyable_hdr",
|
||||
],
|
||||
)
|
||||
|
||||
generated_cc_atom(
|
||||
name = "SkSVGOpenTypeSVGDecoder_hdr",
|
||||
hdrs = ["SkSVGiOpenTypeSVGDecoder.h"],
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [
|
||||
"//include/core:SkColor_hdr",
|
||||
"//include/core:SkOpenTypeSVGDecoder_hdr",
|
||||
"//include/core:SkSpan_hdr",
|
||||
"//include/core:SkTypes_hdr",
|
||||
],
|
||||
)
|
||||
|
@ -19,6 +19,7 @@ class SkCanvas;
|
||||
class SkDOM;
|
||||
class SkStream;
|
||||
class SkSVGNode;
|
||||
struct SkSVGPresentationContext;
|
||||
class SkSVGSVG;
|
||||
|
||||
class SkSVGDOM : public SkRefCnt {
|
||||
@ -81,6 +82,9 @@ public:
|
||||
|
||||
void render(SkCanvas*) const;
|
||||
|
||||
/** Render the node with the given id as if it were the only child of the root. */
|
||||
void renderNode(SkCanvas*, SkSVGPresentationContext&, const char* id) const;
|
||||
|
||||
private:
|
||||
SkSVGDOM(sk_sp<SkSVGSVG>, sk_sp<SkFontMgr>, sk_sp<skresources::ResourceProvider>,
|
||||
SkSVGIDMapper&&);
|
||||
|
32
modules/svg/include/SkSVGOpenTypeSVGDecoder.h
Normal file
32
modules/svg/include/SkSVGOpenTypeSVGDecoder.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2022 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkSVGOpenTypeSVGDecoder_DEFINED
|
||||
#define SkSVGOpenTypeSVGDecoder_DEFINED
|
||||
|
||||
#include "include/core/SkColor.h"
|
||||
#include "include/core/SkOpenTypeSVGDecoder.h"
|
||||
#include "include/core/SkSpan.h"
|
||||
#include "include/core/SkTypes.h"
|
||||
|
||||
class SkCanvas;
|
||||
class SkSVGDOM;
|
||||
|
||||
class SkSVGOpenTypeSVGDecoder : public SkOpenTypeSVGDecoder {
|
||||
public:
|
||||
static std::unique_ptr<SkOpenTypeSVGDecoder> Make(const uint8_t* svg, size_t svgLength);
|
||||
size_t approximateSize() override;
|
||||
bool render(SkCanvas&, int upem, SkGlyphID glyphId,
|
||||
SkColor foregroundColor, SkSpan<SkColor> palette) override;
|
||||
~SkSVGOpenTypeSVGDecoder() override;
|
||||
private:
|
||||
SkSVGOpenTypeSVGDecoder(sk_sp<SkSVGDOM> skSvg, size_t approximateSize);
|
||||
sk_sp<SkSVGDOM> fSkSvg;
|
||||
size_t fApproximateSize;
|
||||
};
|
||||
|
||||
#endif // SkSVGOpenTypeSVGDecoder_DEFINED
|
@ -32,6 +32,8 @@ public:
|
||||
|
||||
SkSize intrinsicSize(const SkSVGLengthContext&) const;
|
||||
|
||||
void renderNode(const SkSVGRenderContext&, const SkSVGIRI& iri) const;
|
||||
|
||||
protected:
|
||||
bool onPrepareToRender(SkSVGRenderContext*) const override;
|
||||
|
||||
|
@ -596,3 +596,24 @@ generated_cc_atom(
|
||||
srcs = ["SkSVGValue.cpp"],
|
||||
visibility = ["//:__subpackages__"],
|
||||
)
|
||||
|
||||
generated_cc_atom(
|
||||
name = "SkSVGOpenTypeSVGDecoder_src",
|
||||
srcs = ["SkSVGOpenTypeSVGDecoder.cpp"],
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [
|
||||
"//include/core:SkColor_hdr",
|
||||
"//include/core:SkOpenTypeSVGDecoder_hdr",
|
||||
"//include/core:SkSpan_hdr",
|
||||
"//include/core:SkStream_hdr",
|
||||
"//include/core:SkTypes_hdr",
|
||||
"//include/utils:SkBase64_hdr",
|
||||
"//modules/skresources/include:SkResources_hdr",
|
||||
"//modules/svg/include:SkSVGDOM_hdr",
|
||||
"//modules/svg/include:SkSVGNode_hdr",
|
||||
"//modules/svg/include:SkSVGOpenTypeSVGDecoder_hdr",
|
||||
"//modules/svg/include:SkSVGRenderContext_hdr",
|
||||
"//modules/svg/include:SkSVGSVG_hdr",
|
||||
"//modules/svg/include:SkSVGUse_hdr",
|
||||
],
|
||||
)
|
||||
|
@ -443,6 +443,17 @@ void SkSVGDOM::render(SkCanvas* canvas) const {
|
||||
}
|
||||
}
|
||||
|
||||
void SkSVGDOM::renderNode(SkCanvas* canvas, SkSVGPresentationContext& pctx, const char* id) const {
|
||||
TRACE_EVENT0("skia", TRACE_FUNC);
|
||||
|
||||
if (fRoot) {
|
||||
SkSVGLengthContext lctx(fContainerSize);
|
||||
fRoot->renderNode(SkSVGRenderContext(canvas, fFontMgr, fResourceProvider, fIDMapper,
|
||||
lctx, pctx, {nullptr, nullptr}),
|
||||
SkSVGIRI(SkSVGIRI::Type::kLocal, SkSVGStringType(id)));
|
||||
}
|
||||
}
|
||||
|
||||
const SkSize& SkSVGDOM::containerSize() const {
|
||||
return fContainerSize;
|
||||
}
|
||||
|
122
modules/svg/src/SkSVGOpenTypeSVGDecoder.cpp
Normal file
122
modules/svg/src/SkSVGOpenTypeSVGDecoder.cpp
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2022 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "include/core/SkColor.h"
|
||||
#include "include/core/SkOpenTypeSVGDecoder.h"
|
||||
#include "include/core/SkSpan.h"
|
||||
#include "include/core/SkStream.h"
|
||||
#include "include/core/SkTypes.h"
|
||||
#include "include/utils/SkBase64.h"
|
||||
#include "modules/skresources/include/SkResources.h"
|
||||
#include "modules/svg/include/SkSVGDOM.h"
|
||||
#include "modules/svg/include/SkSVGNode.h"
|
||||
#include "modules/svg/include/SkSVGOpenTypeSVGDecoder.h"
|
||||
#include "modules/svg/include/SkSVGRenderContext.h"
|
||||
#include "modules/svg/include/SkSVGSVG.h"
|
||||
#include "modules/svg/include/SkSVGUse.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace {
|
||||
class DataResourceProvider final : public skresources::ResourceProvider {
|
||||
public:
|
||||
static sk_sp<skresources::ResourceProvider> Make() {
|
||||
return sk_sp<skresources::ResourceProvider>(new DataResourceProvider());
|
||||
}
|
||||
|
||||
sk_sp<skresources::ImageAsset> loadImageAsset(const char rpath[],
|
||||
const char rname[],
|
||||
const char rid[]) const override {
|
||||
if (auto data = decode_datauri("data:image/", rname)) {
|
||||
return skresources::MultiFrameImageAsset::Make(std::move(data));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
DataResourceProvider() = default;
|
||||
|
||||
static sk_sp<SkData> decode_datauri(const char prefix[], const char uri[]) {
|
||||
// We only handle B64 encoded image dataURIs: data:image/<type>;base64,<data>
|
||||
// (https://en.wikipedia.org/wiki/Data_URI_scheme)
|
||||
static constexpr char kDataURIEncodingStr[] = ";base64,";
|
||||
|
||||
const size_t prefixLen = strlen(prefix);
|
||||
if (strncmp(uri, prefix, prefixLen) != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* encoding = strstr(uri + prefixLen, kDataURIEncodingStr);
|
||||
if (!encoding) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* b64Data = encoding + SK_ARRAY_COUNT(kDataURIEncodingStr) - 1;
|
||||
size_t b64DataLen = strlen(b64Data);
|
||||
size_t dataLen;
|
||||
if (SkBase64::Decode(b64Data, b64DataLen, nullptr, &dataLen) != SkBase64::kNoError) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sk_sp<SkData> data = SkData::MakeUninitialized(dataLen);
|
||||
void* rawData = data->writable_data();
|
||||
if (SkBase64::Decode(b64Data, b64DataLen, rawData, &dataLen) != SkBase64::kNoError) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
using INHERITED = ResourceProvider;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
SkSVGOpenTypeSVGDecoder::SkSVGOpenTypeSVGDecoder(sk_sp<SkSVGDOM> skSvg, size_t approximateSize)
|
||||
: fSkSvg(std::move(skSvg))
|
||||
, fApproximateSize(approximateSize)
|
||||
{}
|
||||
|
||||
SkSVGOpenTypeSVGDecoder::~SkSVGOpenTypeSVGDecoder() = default;
|
||||
|
||||
std::unique_ptr<SkOpenTypeSVGDecoder> SkSVGOpenTypeSVGDecoder::Make(const uint8_t* svg,
|
||||
size_t svgLength) {
|
||||
std::unique_ptr<SkStreamAsset> stream = SkMemoryStream::MakeDirect(svg, svgLength);
|
||||
if (!stream) {
|
||||
return nullptr;
|
||||
}
|
||||
SkSVGDOM::Builder builder;
|
||||
builder.setResourceProvider(DataResourceProvider::Make());
|
||||
sk_sp<SkSVGDOM> skSvg = builder.make(*stream);
|
||||
if (!skSvg) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::unique_ptr<SkOpenTypeSVGDecoder>(
|
||||
new SkSVGOpenTypeSVGDecoder(std::move(skSvg), svgLength));
|
||||
}
|
||||
|
||||
size_t SkSVGOpenTypeSVGDecoder::approximateSize() {
|
||||
// TODO
|
||||
return fApproximateSize;
|
||||
}
|
||||
|
||||
bool SkSVGOpenTypeSVGDecoder::render(SkCanvas& canvas, int upem, SkGlyphID glyphId,
|
||||
SkColor foregroundColor, SkSpan<SkColor> palette) {
|
||||
SkSize emSize = SkSize::Make(SkScalar(upem), SkScalar(upem));
|
||||
fSkSvg->setContainerSize(emSize);
|
||||
|
||||
SkSVGPresentationContext pctx;
|
||||
pctx.fInherited.fColor = SkSVGProperty<SkSVGColorType , true>(foregroundColor);
|
||||
|
||||
// TODO: get palette into the presentation context and SkSVG to parse "var".
|
||||
|
||||
constexpr const size_t glyphStringLen = sizeof("glyph") - 1;
|
||||
char glyphIdString[glyphStringLen + kSkStrAppendU32_MaxSize + 1] = "glyph";
|
||||
*SkStrAppendU32(glyphIdString + glyphStringLen, glyphId) = 0;
|
||||
|
||||
fSkSvg->renderNode(&canvas, pctx, glyphIdString);
|
||||
return true;
|
||||
}
|
@ -10,6 +10,22 @@
|
||||
#include "modules/svg/include/SkSVGSVG.h"
|
||||
#include "modules/svg/include/SkSVGValue.h"
|
||||
|
||||
void SkSVGSVG::renderNode(const SkSVGRenderContext& ctx, const SkSVGIRI& iri) const {
|
||||
SkSVGRenderContext localContext(ctx, this);
|
||||
SkSVGRenderContext::BorrowedNode node = localContext.findNodeById(iri);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->onPrepareToRender(&localContext)) {
|
||||
if (this == node.get()) {
|
||||
this->onRender(ctx);
|
||||
} else {
|
||||
node->render(localContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SkSVGSVG::onPrepareToRender(SkSVGRenderContext* ctx) const {
|
||||
// x/y are ignored for outermost svg elements
|
||||
const auto x = fType == Type::kInner ? fX : SkSVGLength(0);
|
||||
|
@ -40,6 +40,7 @@ skia_svg_public = [
|
||||
"$_include/SkSVGLine.h",
|
||||
"$_include/SkSVGMask.h",
|
||||
"$_include/SkSVGNode.h",
|
||||
"$_include/SkSVGOpenTypeSVGDecoder.h",
|
||||
"$_include/SkSVGPath.h",
|
||||
"$_include/SkSVGPattern.h",
|
||||
"$_include/SkSVGPoly.h",
|
||||
@ -85,6 +86,7 @@ skia_svg_sources = [
|
||||
"$_src/SkSVGLinearGradient.cpp",
|
||||
"$_src/SkSVGMask.cpp",
|
||||
"$_src/SkSVGNode.cpp",
|
||||
"$_src/SkSVGOpenTypeSVGDecoder.cpp",
|
||||
"$_src/SkSVGPath.cpp",
|
||||
"$_src/SkSVGPattern.cpp",
|
||||
"$_src/SkSVGPoly.cpp",
|
||||
|
BIN
resources/fonts/SampleSVG.ttf
Normal file
BIN
resources/fonts/SampleSVG.ttf
Normal file
Binary file not shown.
@ -2284,6 +2284,7 @@ generated_cc_atom(
|
||||
"//include/core:SkGraphics_hdr",
|
||||
"//include/core:SkMath_hdr",
|
||||
"//include/core:SkMatrix_hdr",
|
||||
"//include/core:SkOpenTypeSVGDecoder_hdr",
|
||||
"//include/core:SkPathEffect_hdr",
|
||||
"//include/core:SkPath_hdr",
|
||||
"//include/core:SkRefCnt_hdr",
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "include/core/SkCanvas.h"
|
||||
#include "include/core/SkMath.h"
|
||||
#include "include/core/SkMatrix.h"
|
||||
#include "include/core/SkOpenTypeSVGDecoder.h"
|
||||
#include "include/core/SkPath.h"
|
||||
#include "include/core/SkPathEffect.h"
|
||||
#include "include/core/SkRefCnt.h"
|
||||
@ -125,6 +126,19 @@ void SkGraphics::PurgeFontCache() {
|
||||
SkTypefaceCache::PurgeAll();
|
||||
}
|
||||
|
||||
static SkGraphics::OpenTypeSVGDecoderFactory gSVGDecoderFactory = nullptr;
|
||||
|
||||
SkGraphics::OpenTypeSVGDecoderFactory
|
||||
SkGraphics::SetOpenTypeSVGDecoderFactory(OpenTypeSVGDecoderFactory svgDecoderFactory) {
|
||||
OpenTypeSVGDecoderFactory old(gSVGDecoderFactory);
|
||||
gSVGDecoderFactory = svgDecoderFactory;
|
||||
return old;
|
||||
}
|
||||
|
||||
SkGraphics::OpenTypeSVGDecoderFactory SkGraphics::GetOpenTypeSVGDecoderFactory() {
|
||||
return gSVGDecoderFactory;
|
||||
}
|
||||
|
||||
extern bool gSkVMAllowJIT;
|
||||
|
||||
void SkGraphics::AllowJIT() {
|
||||
|
@ -287,6 +287,8 @@ generated_cc_atom(
|
||||
"//include/core:SkCanvas_hdr",
|
||||
"//include/core:SkColor_hdr",
|
||||
"//include/core:SkDrawable_hdr",
|
||||
"//include/core:SkGraphics_hdr",
|
||||
"//include/core:SkOpenTypeSVGDecoder_hdr",
|
||||
"//include/core:SkPath_hdr",
|
||||
"//include/effects:SkGradientShader_hdr",
|
||||
"//include/pathops:SkPathOps_hdr",
|
||||
@ -304,11 +306,13 @@ generated_cc_atom(
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [
|
||||
":SkFontHost_FreeType_common_hdr",
|
||||
"//include/core:SkBBHFactory_hdr",
|
||||
"//include/core:SkBitmap_hdr",
|
||||
"//include/core:SkCanvas_hdr",
|
||||
"//include/core:SkData_hdr",
|
||||
"//include/core:SkDrawable_hdr",
|
||||
"//include/core:SkFontMetrics_hdr",
|
||||
"//include/core:SkGraphics_hdr",
|
||||
"//include/core:SkPath_hdr",
|
||||
"//include/core:SkPictureRecorder_hdr",
|
||||
"//include/core:SkStream_hdr",
|
||||
|
@ -5,11 +5,13 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "include/core/SkBBHFactory.h"
|
||||
#include "include/core/SkBitmap.h"
|
||||
#include "include/core/SkCanvas.h"
|
||||
#include "include/core/SkData.h"
|
||||
#include "include/core/SkDrawable.h"
|
||||
#include "include/core/SkFontMetrics.h"
|
||||
#include "include/core/SkGraphics.h"
|
||||
#include "include/core/SkPath.h"
|
||||
#include "include/core/SkPictureRecorder.h"
|
||||
#include "include/core/SkStream.h"
|
||||
@ -967,8 +969,7 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(sk_sp<SkTypeface_FreeType> ty
|
||||
// FT_LOAD_COLOR with scalable fonts means allow SVG.
|
||||
// It also implies attempt to render COLR if available, but this is not used.
|
||||
#if defined(FT_CONFIG_OPTION_SVG)
|
||||
const bool svgSupport = false;
|
||||
if (svgSupport) {
|
||||
if (SkGraphics::GetOpenTypeSVGDecoderFactory()) {
|
||||
fLoadGlyphFlags |= FT_LOAD_COLOR;
|
||||
}
|
||||
#endif
|
||||
@ -1289,6 +1290,31 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph, SkArenaAlloc* all
|
||||
SkIntToScalar(fFace->glyph->bitmap.rows ));
|
||||
fMatrix22Scalar.mapRect(&bounds);
|
||||
setGlyphBounds(glyph, &bounds, this->shouldSubpixelBitmap(*glyph, fMatrix22Scalar));
|
||||
|
||||
#if defined(FT_CONFIG_OPTION_SVG)
|
||||
} else if (fFace->glyph->format == FT_GLYPH_FORMAT_SVG) {
|
||||
glyph->fScalerContextBits = ScalerContextBits::SVG;
|
||||
glyph->fMaskFormat = SkMask::kARGB32_Format;
|
||||
glyph->setPath(alloc, nullptr, false);
|
||||
|
||||
SkPictureRecorder recorder;
|
||||
SkRect infiniteRect = SkRect::MakeLTRB(-SK_ScalarInfinity, -SK_ScalarInfinity,
|
||||
SK_ScalarInfinity, SK_ScalarInfinity);
|
||||
sk_sp<SkBBoxHierarchy> bboxh = SkRTreeFactory()();
|
||||
SkSpan<SkColor> palette(fFaceRec->fSkPalette.get(), fFaceRec->fFTPaletteEntryCount);
|
||||
SkCanvas* recordingCanvas = recorder.beginRecording(infiniteRect, bboxh);
|
||||
if (!this->drawSVGGlyph(fFace, *glyph, fLoadGlyphFlags, palette, recordingCanvas)) {
|
||||
glyph->zeroMetrics();
|
||||
return;
|
||||
}
|
||||
sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture();
|
||||
SkRect bounds = pic->cullRect();
|
||||
SkASSERT(bounds.isFinite());
|
||||
|
||||
// drawSVGGlyph already applied the subpixel positioning.
|
||||
setGlyphBounds(glyph, &bounds, false);
|
||||
#endif // FT_CONFIG_OPTION_SVG
|
||||
|
||||
} else {
|
||||
SkDEBUGFAIL("unknown glyph format");
|
||||
glyph->zeroMetrics();
|
||||
@ -1330,9 +1356,9 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
|
||||
}
|
||||
|
||||
if (glyph.fScalerContextBits == ScalerContextBits::COLRv0 ||
|
||||
glyph.fScalerContextBits == ScalerContextBits::COLRv1)
|
||||
glyph.fScalerContextBits == ScalerContextBits::COLRv1 ||
|
||||
glyph.fScalerContextBits == ScalerContextBits::SVG )
|
||||
{
|
||||
#ifdef FT_COLOR_H
|
||||
SkASSERT(glyph.maskFormat() == SkMask::kARGB32_Format);
|
||||
SkBitmap dstBitmap;
|
||||
// TODO: mark this as sRGB when the blits will be sRGB.
|
||||
@ -1352,13 +1378,21 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
|
||||
|
||||
SkSpan<SkColor> palette(fFaceRec->fSkPalette.get(), fFaceRec->fFTPaletteEntryCount);
|
||||
if (glyph.fScalerContextBits == ScalerContextBits::COLRv0) {
|
||||
#ifdef FT_COLOR_H
|
||||
this->drawCOLRv0Glyph(fFace, glyph, fLoadGlyphFlags, palette, &canvas);
|
||||
#endif
|
||||
} else if (glyph.fScalerContextBits == ScalerContextBits::COLRv1) {
|
||||
#ifdef TT_SUPPORT_COLRV1
|
||||
this->drawCOLRv1Glyph(fFace, glyph, fLoadGlyphFlags, palette, &canvas);
|
||||
#endif
|
||||
} else if (glyph.fScalerContextBits == ScalerContextBits::SVG) {
|
||||
#if defined(FT_CONFIG_OPTION_SVG)
|
||||
if (FT_Load_Glyph(fFace, glyph.getGlyphID(), fLoadGlyphFlags)) {
|
||||
return;
|
||||
}
|
||||
this->drawSVGGlyph(fFace, glyph, fLoadGlyphFlags, palette, &canvas);
|
||||
#endif
|
||||
}
|
||||
#endif // FT_COLOR_H
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1394,17 +1428,22 @@ sk_sp<SkDrawable> SkScalerContext_FreeType::generateDrawable(const SkGlyph& glyp
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if defined(FT_COLOR_H) || defined(TT_SUPPORT_COLRV1) || defined(FT_CONFIG_OPTION_SVG)
|
||||
if (glyph.fScalerContextBits == ScalerContextBits::COLRv0 ||
|
||||
glyph.fScalerContextBits == ScalerContextBits::COLRv1)
|
||||
glyph.fScalerContextBits == ScalerContextBits::COLRv1 ||
|
||||
glyph.fScalerContextBits == ScalerContextBits::SVG )
|
||||
{
|
||||
#ifdef FT_COLOR_H
|
||||
SkSpan<SkColor> palette(fFaceRec->fSkPalette.get(), fFaceRec->fFTPaletteEntryCount);
|
||||
SkPictureRecorder recorder;
|
||||
SkCanvas* recordingCanvas = recorder.beginRecording(SkRect::Make(glyph.mask().fBounds));
|
||||
if (glyph.fScalerContextBits == ScalerContextBits::COLRv0) {
|
||||
#ifdef FT_COLOR_H
|
||||
if (!this->drawCOLRv0Glyph(fFace, glyph, fLoadGlyphFlags, palette, recordingCanvas)) {
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
} else if (glyph.fScalerContextBits == ScalerContextBits::COLRv1) {
|
||||
#ifdef TT_SUPPORT_COLRV1
|
||||
if (!this->drawCOLRv1Glyph(fFace, glyph, fLoadGlyphFlags, palette, recordingCanvas)) {
|
||||
@ -1412,11 +1451,22 @@ sk_sp<SkDrawable> SkScalerContext_FreeType::generateDrawable(const SkGlyph& glyp
|
||||
}
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
} else if (glyph.fScalerContextBits == ScalerContextBits::SVG) {
|
||||
#if defined(FT_CONFIG_OPTION_SVG)
|
||||
if (FT_Load_Glyph(fFace, glyph.getGlyphID(), fLoadGlyphFlags)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!this->drawSVGGlyph(fFace, glyph, fLoadGlyphFlags, palette, recordingCanvas)) {
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
return recorder.finishRecordingAsDrawable();
|
||||
#endif // FT_COLOR_H
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -1600,8 +1650,15 @@ void SkScalerContext_FreeType::generateFontMetrics(SkFontMetrics* metrics) {
|
||||
metrics->fStrikeoutThickness = strikeoutThickness * fScale.y();
|
||||
metrics->fStrikeoutPosition = strikeoutPosition * fScale.y();
|
||||
|
||||
if (face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
|
||||
// The bounds are only valid for the default variation.
|
||||
if (face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS
|
||||
#if defined(FT_CONFIG_OPTION_SVG)
|
||||
|| face->face_flags & FT_FACE_FLAG_SVG
|
||||
#endif // FT_CONFIG_OPTION_SVG
|
||||
) {
|
||||
// The bounds are only valid for the default variation of variable glyphs.
|
||||
// https://docs.microsoft.com/en-us/typography/opentype/spec/head
|
||||
// For SVG glyphs this number is often incorrect for its non-`glyf` points.
|
||||
// https://github.com/fonttools/fonttools/issues/2566
|
||||
metrics->fFlags |= SkFontMetrics::kBoundsInvalid_Flag;
|
||||
}
|
||||
}
|
||||
@ -1732,6 +1789,10 @@ bool SkTypeface_FreeType::onGlyphMaskNeedsCurrentColor() const {
|
||||
fGlyphMasksMayNeedCurrentColorOnce([this]{
|
||||
static constexpr SkFourByteTag COLRTag = SkSetFourByteTag('C', 'O', 'L', 'R');
|
||||
fGlyphMasksMayNeedCurrentColor = this->getTableSize(COLRTag) > 0;
|
||||
#if defined(FT_CONFIG_OPTION_SVG)
|
||||
static constexpr SkFourByteTag SVGTag = SkSetFourByteTag('S', 'V', 'G', ' ');
|
||||
fGlyphMasksMayNeedCurrentColor |= this->getTableSize(SVGTag) > 0 ;
|
||||
#endif // FT_CONFIG_OPTION_SVG
|
||||
});
|
||||
return fGlyphMasksMayNeedCurrentColor;
|
||||
}
|
||||
|
@ -10,6 +10,8 @@
|
||||
#include "include/core/SkCanvas.h"
|
||||
#include "include/core/SkColor.h"
|
||||
#include "include/core/SkDrawable.h"
|
||||
#include "include/core/SkGraphics.h"
|
||||
#include "include/core/SkOpenTypeSVGDecoder.h"
|
||||
#include "include/core/SkPath.h"
|
||||
#include "include/effects/SkGradientShader.h"
|
||||
#include "include/pathops/SkPathOps.h"
|
||||
@ -37,6 +39,10 @@ namespace {
|
||||
[[maybe_unused]] static inline const constexpr bool kSkShowTextBlitCoverage = false;
|
||||
}
|
||||
|
||||
#if defined(FT_CONFIG_OPTION_SVG)
|
||||
# include <freetype/otsvg.h>
|
||||
#endif
|
||||
|
||||
#ifdef TT_SUPPORT_COLRV1
|
||||
// FT_ClipBox and FT_Get_Color_Glyph_ClipBox introduced VER-2-11-0-18-g47cf8ebf4
|
||||
// FT_COLR_COMPOSITE_PLUS and renumbering introduced VER-2-11-0-21-ge40ae7569
|
||||
@ -1294,6 +1300,43 @@ bool SkScalerContext_FreeType_Base::drawCOLRv0Glyph(FT_Face face,
|
||||
}
|
||||
#endif // FT_COLOR_H
|
||||
|
||||
#if defined(FT_CONFIG_OPTION_SVG)
|
||||
bool SkScalerContext_FreeType_Base::drawSVGGlyph(FT_Face face,
|
||||
const SkGlyph& glyph,
|
||||
uint32_t loadGlyphFlags,
|
||||
SkSpan<SkColor> palette,
|
||||
SkCanvas* canvas) {
|
||||
SkASSERT(face->glyph->format == FT_GLYPH_FORMAT_SVG);
|
||||
|
||||
FT_SVG_Document ftSvg = (FT_SVG_Document)face->glyph->other;
|
||||
SkMatrix m;
|
||||
FT_Matrix ftMatrix = ftSvg->transform;
|
||||
FT_Vector ftOffset = ftSvg->delta;
|
||||
m.setAll(
|
||||
SkFixedToFloat(ftMatrix.xx), -SkFixedToFloat(ftMatrix.xy), SkFixedToFloat(ftOffset.x),
|
||||
-SkFixedToFloat(ftMatrix.yx), SkFixedToFloat(ftMatrix.yy), -SkFixedToFloat(ftOffset.y),
|
||||
0 , 0 , 1 );
|
||||
m.postScale(SkFixedToFloat(ftSvg->metrics.x_scale) / 64.0f,
|
||||
SkFixedToFloat(ftSvg->metrics.y_scale) / 64.0f);
|
||||
if (this->isSubpixel()) {
|
||||
m.postTranslate(SkFixedToScalar(glyph.getSubXFixed()),
|
||||
SkFixedToScalar(glyph.getSubYFixed()));
|
||||
}
|
||||
canvas->concat(m);
|
||||
|
||||
SkGraphics::OpenTypeSVGDecoderFactory svgFactory = SkGraphics::GetOpenTypeSVGDecoderFactory();
|
||||
if (!svgFactory) {
|
||||
return false;
|
||||
}
|
||||
auto svgDecoder = svgFactory(ftSvg->svg_document, ftSvg->svg_document_length);
|
||||
if (!svgDecoder) {
|
||||
return false;
|
||||
}
|
||||
return svgDecoder->render(*canvas, ftSvg->units_per_EM, glyph.getGlyphID(),
|
||||
fRec.fForegroundColor, palette);
|
||||
}
|
||||
#endif // FT_CONFIG_OPTION_SVG
|
||||
|
||||
void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face,
|
||||
const SkGlyph& glyph,
|
||||
const SkMatrix& bitmapTransform)
|
||||
@ -1520,7 +1563,6 @@ void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face,
|
||||
src += dstBitmap.rowBytes();
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
default:
|
||||
|
@ -52,6 +52,8 @@ protected:
|
||||
SkSpan<SkColor> palette, SkCanvas*);
|
||||
bool drawCOLRv1Glyph(FT_Face, const SkGlyph&, uint32_t loadGlyphFlags,
|
||||
SkSpan<SkColor> palette, SkCanvas*);
|
||||
bool drawSVGGlyph(FT_Face, const SkGlyph&, uint32_t loadGlyphFlags,
|
||||
SkSpan<SkColor> palette, SkCanvas*);
|
||||
void generateGlyphImage(FT_Face, const SkGlyph&, const SkMatrix& bitmapTransform);
|
||||
bool generateGlyphPath(FT_Face, SkPath*);
|
||||
bool generateFacePath(FT_Face, SkGlyphID, uint32_t loadGlyphFlags, SkPath*);
|
||||
@ -67,6 +69,7 @@ protected:
|
||||
struct ScalerContextBits {
|
||||
static const constexpr uint32_t COLRv0 = 1;
|
||||
static const constexpr uint32_t COLRv1 = 2;
|
||||
static const constexpr uint32_t SVG = 3;
|
||||
};
|
||||
private:
|
||||
using INHERITED = SkScalerContext;
|
||||
|
5
third_party/BUILD.bazel
vendored
5
third_party/BUILD.bazel
vendored
@ -948,6 +948,9 @@ cc_library(
|
||||
"externals/freetype/src/smooth/ftsmerrs.h",
|
||||
"externals/freetype/src/smooth/ftsmooth.h",
|
||||
"externals/freetype/src/smooth/smooth.c",
|
||||
"externals/freetype/src/svg/ftsvg.h",
|
||||
"externals/freetype/src/svg/svg.c",
|
||||
"externals/freetype/src/svg/svgtypes.h",
|
||||
"externals/freetype/src/truetype/truetype.c",
|
||||
"externals/freetype/src/truetype/ttdriver.h",
|
||||
"externals/freetype/src/truetype/tterrors.h",
|
||||
@ -1021,6 +1024,7 @@ cc_library(
|
||||
"externals/freetype/include/freetype/fttrigon.h",
|
||||
"externals/freetype/include/freetype/fttypes.h",
|
||||
"externals/freetype/include/freetype/ftwinfnt.h",
|
||||
"externals/freetype/include/freetype/otsvg.h",
|
||||
"externals/freetype/include/freetype/t1tables.h",
|
||||
"externals/freetype/include/freetype/ttnameid.h",
|
||||
"externals/freetype/include/freetype/tttables.h",
|
||||
@ -1127,6 +1131,7 @@ cc_library(
|
||||
"externals/freetype/src/sfnt/woff2tags.c",
|
||||
"externals/freetype/src/smooth/ftgrays.c",
|
||||
"externals/freetype/src/smooth/ftsmooth.c",
|
||||
"externals/freetype/src/svg/ftsvg.c",
|
||||
"externals/freetype/src/truetype/ttdriver.c",
|
||||
"externals/freetype/src/truetype/ttgload.c",
|
||||
"externals/freetype/src/truetype/ttgxvar.c",
|
||||
|
12
third_party/freetype2/BUILD.gn
vendored
12
third_party/freetype2/BUILD.gn
vendored
@ -9,6 +9,7 @@ declare_args() {
|
||||
(is_official_build || !(is_android || sanitize == "MSAN")) &&
|
||||
!is_fuchsia && !is_wasm
|
||||
skia_use_freetype_woff2 = is_wasm
|
||||
skia_use_freetype_svg = true
|
||||
}
|
||||
|
||||
import("../third_party.gni")
|
||||
@ -21,11 +22,13 @@ if (skia_use_system_freetype2) {
|
||||
} else {
|
||||
third_party("freetype2") {
|
||||
public_defines = [ "SK_FREETYPE_MINIMUM_RUNTIME_VERSION=(((FREETYPE_MAJOR) << 24) | ((FREETYPE_MINOR) << 16) | ((FREETYPE_PATCH) << 8))" ]
|
||||
public_include_dirs = [ "../externals/freetype/include" ]
|
||||
public_include_dirs = [
|
||||
".",
|
||||
"../externals/freetype/include",
|
||||
]
|
||||
|
||||
deps = [ "//third_party/libpng" ]
|
||||
|
||||
include_dirs = [ "." ]
|
||||
defines = [ "FT2_BUILD_LIBRARY" ]
|
||||
if (target_cpu == "wasm") {
|
||||
defines += [
|
||||
@ -73,5 +76,10 @@ if (skia_use_system_freetype2) {
|
||||
"../externals/freetype/src/truetype/truetype.c",
|
||||
"../externals/freetype/src/type1/type1.c",
|
||||
]
|
||||
|
||||
if (skia_use_freetype_svg) {
|
||||
public_defines += [ "FT_CONFIG_OPTION_SVG" ]
|
||||
sources += [ "../externals/freetype/src/svg/svg.c" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,8 @@ FT_USE_MODULE( FT_Renderer_Class, ft_smooth_renderer_class )
|
||||
FT_USE_MODULE( FT_Renderer_Class, ft_raster1_renderer_class )
|
||||
//FT_USE_MODULE( FT_Renderer_Class, ft_sdf_renderer_class )
|
||||
//FT_USE_MODULE( FT_Renderer_Class, ft_bitmap_sdf_renderer_class )
|
||||
//FT_USE_MODULE( FT_Renderer_Class, ft_svg_renderer_class )
|
||||
#if defined(FT_CONFIG_OPTION_SVG)
|
||||
FT_USE_MODULE( FT_Renderer_Class, ft_svg_renderer_class )
|
||||
#endif
|
||||
|
||||
/* EOF */
|
||||
|
@ -22,6 +22,7 @@ generated_cc_atom(
|
||||
"//modules/skresources/include:SkResources_hdr",
|
||||
"//modules/svg/include:SkSVGDOM_hdr",
|
||||
"//modules/svg/include:SkSVGNode_hdr",
|
||||
"//modules/svg/include:SkSVGOpenTypeSVGDecoder_hdr",
|
||||
"//src/core:SkColorSpacePriv_hdr",
|
||||
"//src/core:SkMD5_hdr",
|
||||
"//src/core:SkOSFile_hdr",
|
||||
|
@ -41,6 +41,7 @@
|
||||
#if defined(SK_ENABLE_SVG)
|
||||
#include "modules/svg/include/SkSVGDOM.h"
|
||||
#include "modules/svg/include/SkSVGNode.h"
|
||||
#include "modules/svg/include/SkSVGOpenTypeSVGDecoder.h"
|
||||
#endif
|
||||
|
||||
#if defined(SK_ENABLE_SKOTTIE)
|
||||
@ -387,6 +388,10 @@ int main(int argc, char** argv) {
|
||||
if (FLAGS_cpuDetect) {
|
||||
SkGraphics::Init();
|
||||
}
|
||||
#if defined(SK_ENABLE_SVG)
|
||||
SkGraphics::SetOpenTypeSVGDecoderFactory(SkSVGOpenTypeSVGDecoder::Make);
|
||||
#endif
|
||||
|
||||
gUseSkVMBlitter = FLAGS_skvm;
|
||||
gSkVMAllowJIT = FLAGS_jit;
|
||||
gSkVMJITViaDylib = FLAGS_dylib;
|
||||
|
@ -436,6 +436,7 @@ generated_cc_atom(
|
||||
"//include/private:SkTo_hdr",
|
||||
"//include/utils:SkPaintFilterCanvas_hdr",
|
||||
"//misc/cpp:imgui_stdlib_hdr",
|
||||
"//modules/svg/include:SkSVGOpenTypeSVGDecoder_hdr",
|
||||
"//src/core:SkAutoPixmapStorage_hdr",
|
||||
"//src/core:SkColorSpacePriv_hdr",
|
||||
"//src/core:SkGlyphRun_hdr",
|
||||
|
@ -76,6 +76,10 @@
|
||||
#include "tools/viewer/SkottieSlide.h"
|
||||
#endif
|
||||
|
||||
#if defined(SK_ENABLE_SVG)
|
||||
#include "modules/svg/include/SkSVGOpenTypeSVGDecoder.h"
|
||||
#endif
|
||||
|
||||
class CapturingShaderErrorHandler : public GrContextOptions::ShaderErrorHandler {
|
||||
public:
|
||||
void compileError(const char* shader, const char* errors) override {
|
||||
@ -354,6 +358,9 @@ Viewer::Viewer(int argc, char** argv, void* platformData)
|
||||
, fPerspectiveMode(kPerspective_Off)
|
||||
{
|
||||
SkGraphics::Init();
|
||||
#if defined(SK_ENABLE_SVG)
|
||||
SkGraphics::SetOpenTypeSVGDecoderFactory(SkSVGOpenTypeSVGDecoder::Make);
|
||||
#endif
|
||||
|
||||
gPathRendererNames[GpuPathRenderers::kDefault] = "Default Path Renderers";
|
||||
gPathRendererNames[GpuPathRenderers::kAtlas] = "Atlas (tessellation)";
|
||||
|
Loading…
Reference in New Issue
Block a user