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:
Ben Wagner 2022-04-20 17:52:50 -04:00 committed by SkCQ
parent 4f33f5bef8
commit 9cbadcd928
29 changed files with 457 additions and 18 deletions

View File

@ -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")) {

View File

@ -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 {

View File

@ -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",

View File

@ -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",
],
)

View File

@ -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.
*/

View 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

View File

@ -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",
],
)

View File

@ -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&&);

View 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

View File

@ -32,6 +32,8 @@ public:
SkSize intrinsicSize(const SkSVGLengthContext&) const;
void renderNode(const SkSVGRenderContext&, const SkSVGIRI& iri) const;
protected:
bool onPrepareToRender(SkSVGRenderContext*) const override;

View File

@ -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",
],
)

View File

@ -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;
}

View 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;
}

View File

@ -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);

View File

@ -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",

Binary file not shown.

View File

@ -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",

View File

@ -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() {

View File

@ -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",

View File

@ -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;
}

View File

@ -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:

View File

@ -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;

View File

@ -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",

View File

@ -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" ]
}
}
}

View File

@ -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 */

View File

@ -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",

View File

@ -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;

View File

@ -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",

View File

@ -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)";