From d3b933f3e03d5c22e9e7c56d2110469e41509720 Mon Sep 17 00:00:00 2001 From: Ben Wagner Date: Thu, 15 Feb 2018 10:20:04 -0500 Subject: Add color fonts. BUG=skia:7624 Change-Id: I1d0974282adce2fd7b13a003309e63593b6e1a9c Reviewed-on: https://skia-review.googlesource.com/109521 Commit-Queue: Ben Wagner Reviewed-by: Mike Klein Reviewed-by: Herb Derby Reviewed-on: https://skia-review.googlesource.com/116541 --- BUILD.gn | 18 +- gm/fontmgr.cpp | 7 +- public.bzl | 6 +- resources/fonts/cbdt.ttf | Bin 0 -> 18020 bytes resources/fonts/colr.ttf | Bin 0 -> 2996 bytes resources/fonts/sbix.ttf | Bin 0 -> 17956 bytes resources/fonts/svg/diamond.svg | 22 + resources/fonts/svg/empty.svg | 10 + resources/fonts/svg/notdef.svg | 29 + resources/fonts/svg/smile.svg | 44 ++ src/ports/SkFontHost_win.cpp | 7 + tools/fonts/SkTestFontMgr.cpp | 28 +- tools/fonts/SkTestSVGTypeface.cpp | 1300 ++++++++++++++++++++++++++++++++ tools/fonts/SkTestSVGTypeface.h | 135 ++++ tools/fonts/SkTestScalerContext.cpp | 267 ------- tools/fonts/SkTestScalerContext.h | 102 --- tools/fonts/SkTestTypeface.cpp | 274 +++++++ tools/fonts/SkTestTypeface.h | 116 +++ tools/fonts/create_test_font_color.cpp | 37 + tools/fonts/sk_tool_utils_font.cpp | 41 +- tools/sk_tool_utils.cpp | 24 +- tools/sk_tool_utils.h | 13 +- 22 files changed, 2066 insertions(+), 414 deletions(-) create mode 100644 resources/fonts/cbdt.ttf create mode 100644 resources/fonts/colr.ttf create mode 100644 resources/fonts/sbix.ttf create mode 100644 resources/fonts/svg/diamond.svg create mode 100644 resources/fonts/svg/empty.svg create mode 100644 resources/fonts/svg/notdef.svg create mode 100644 resources/fonts/svg/smile.svg create mode 100644 tools/fonts/SkTestSVGTypeface.cpp create mode 100644 tools/fonts/SkTestSVGTypeface.h delete mode 100644 tools/fonts/SkTestScalerContext.cpp delete mode 100644 tools/fonts/SkTestScalerContext.h create mode 100644 tools/fonts/SkTestTypeface.cpp create mode 100644 tools/fonts/SkTestTypeface.h create mode 100644 tools/fonts/create_test_font_color.cpp diff --git a/BUILD.gn b/BUILD.gn index 7786024f6a..6fc9b0bae4 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1278,7 +1278,11 @@ if (skia_enable_tools) { "tools/debugger/SkObjectParser.cpp", "tools/fonts/SkRandomScalerContext.cpp", "tools/fonts/SkTestFontMgr.cpp", - "tools/fonts/SkTestScalerContext.cpp", + "tools/fonts/SkTestFontMgr.h", + "tools/fonts/SkTestSVGTypeface.cpp", + "tools/fonts/SkTestSVGTypeface.h", + "tools/fonts/SkTestTypeface.cpp", + "tools/fonts/SkTestTypeface.h", "tools/fonts/sk_tool_utils_font.cpp", "tools/picture_utils.cpp", "tools/random_parse_path.cpp", @@ -1298,6 +1302,7 @@ if (skia_enable_tools) { } deps = [ ":common_flags", + ":experimental_svg_model", ":flags", "//third_party/libpng", ] @@ -1669,6 +1674,17 @@ if (skia_enable_tools) { ] } + test_app("create_test_font_color") { + sources = [ + "tools/fonts/create_test_font_color.cpp", + ] + deps = [ + ":flags", + ":skia", + ":tool_utils" + ] + } + test_app("get_images_from_skps") { sources = [ "tools/get_images_from_skps.cpp", diff --git a/gm/fontmgr.cpp b/gm/fontmgr.cpp index a0b2f5b44a..58c569c300 100644 --- a/gm/fontmgr.cpp +++ b/gm/fontmgr.cpp @@ -9,6 +9,7 @@ #include "sk_tool_utils.h" #include "SkCanvas.h" #include "SkFontMgr.h" +#include "SkPath.h" #include "SkGraphics.h" #include "SkTypeface.h" @@ -224,6 +225,7 @@ public: fontBounds.offset(x, y); SkPaint boundsPaint(glyphPaint); boundsPaint.setColor(boundsColor); + boundsPaint.setStyle(SkPaint::kStroke_Style); canvas->drawRect(fontBounds, boundsPaint); SkPaint::FontMetrics fm; @@ -264,6 +266,10 @@ public: } SkGlyphID str[] = { left, right, top, bottom }; for (size_t i = 0; i < SK_ARRAY_COUNT(str); ++i) { + SkPath path; + glyphPaint.getTextPath(&str[i], sizeof(str[0]), x, y, &path); + SkPaint::Style style = path.isEmpty() ? SkPaint::kFill_Style : SkPaint::kStroke_Style; + glyphPaint.setStyle(style); canvas->drawText(&str[i], sizeof(str[0]), x, y, glyphPaint); } } @@ -282,7 +288,6 @@ protected: paint.setAntiAlias(true); paint.setSubpixelText(true); paint.setTextSize(100); - paint.setStyle(SkPaint::kStroke_Style); paint.setTextScaleX(fScaleX); paint.setTextSkewX(fSkewX); diff --git a/public.bzl b/public.bzl index 9ae6e7d438..3e392d8f84 100644 --- a/public.bzl +++ b/public.bzl @@ -458,8 +458,10 @@ DM_SRCS_ALL = struct( "tools/fonts/SkRandomScalerContext.h", "tools/fonts/SkTestFontMgr.cpp", "tools/fonts/SkTestFontMgr.h", - "tools/fonts/SkTestScalerContext.cpp", - "tools/fonts/SkTestScalerContext.h", + "tools/fonts/SkTestSVGTypeface.cpp", + "tools/fonts/SkTestSVGTypeface.h", + "tools/fonts/SkTestTypeface.cpp", + "tools/fonts/SkTestTypeface.h", "tools/fonts/sk_tool_utils_font.cpp", "tools/fonts/test_font_monospace.inc", "tools/fonts/test_font_sans_serif.inc", diff --git a/resources/fonts/cbdt.ttf b/resources/fonts/cbdt.ttf new file mode 100644 index 0000000000..bed498f07e Binary files /dev/null and b/resources/fonts/cbdt.ttf differ diff --git a/resources/fonts/colr.ttf b/resources/fonts/colr.ttf new file mode 100644 index 0000000000..42e2a08096 Binary files /dev/null and b/resources/fonts/colr.ttf differ diff --git a/resources/fonts/sbix.ttf b/resources/fonts/sbix.ttf new file mode 100644 index 0000000000..5eed8a917b Binary files /dev/null and b/resources/fonts/sbix.ttf differ diff --git a/resources/fonts/svg/diamond.svg b/resources/fonts/svg/diamond.svg new file mode 100644 index 0000000000..826cb749f3 --- /dev/null +++ b/resources/fonts/svg/diamond.svg @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/resources/fonts/svg/empty.svg b/resources/fonts/svg/empty.svg new file mode 100644 index 0000000000..d470cc5a4c --- /dev/null +++ b/resources/fonts/svg/empty.svg @@ -0,0 +1,10 @@ + + + + diff --git a/resources/fonts/svg/notdef.svg b/resources/fonts/svg/notdef.svg new file mode 100644 index 0000000000..3fbb9a02ff --- /dev/null +++ b/resources/fonts/svg/notdef.svg @@ -0,0 +1,29 @@ + + + + + + + + + diff --git a/resources/fonts/svg/smile.svg b/resources/fonts/svg/smile.svg new file mode 100644 index 0000000000..41d26b5e29 --- /dev/null +++ b/resources/fonts/svg/smile.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp index f0eb7b7d9b..abe1550a01 100644 --- a/src/ports/SkFontHost_win.cpp +++ b/src/ports/SkFontHost_win.cpp @@ -830,6 +830,13 @@ uint16_t SkScalerContext_GDI::generateCharToGlyph(SkUnichar utf32) { HRZM(ScriptItemize(utf16, numWCHAR, maxItems, &sc, nullptr, si, &numItems), "Could not itemize character."); + // Disable any attempt at shaping. + // Without this ScriptShape may return 0x80040200 (USP_E_SCRIPT_NOT_IN_FONT) + // when all that is desired here is a simple cmap lookup. + for (SCRIPT_ITEM& item : si) { + item.a.eScript = SCRIPT_UNDEFINED; + } + // Sometimes ScriptShape cannot find a glyph for a non-BMP and returns 2 space glyphs. static const int maxGlyphs = 2; SCRIPT_VISATTR vsa[maxGlyphs]; diff --git a/tools/fonts/SkTestFontMgr.cpp b/tools/fonts/SkTestFontMgr.cpp index 3b5d1589f2..4513a2f8d1 100644 --- a/tools/fonts/SkTestFontMgr.cpp +++ b/tools/fonts/SkTestFontMgr.cpp @@ -8,6 +8,8 @@ #include "SkFontDescriptor.h" #include "SkTestFontMgr.h" #include "sk_tool_utils.h" +#include "SkTestTypeface.h" +#include "SkTestSVGTypeface.h" namespace { @@ -15,6 +17,28 @@ static constexpr const char* kFamilyNames[] = { "Toy Liberation Sans", "Toy Liberation Serif", "Toy Liberation Mono", + "Emoji", +}; + +class JustOneTypefaceStyleSet final : public SkFontStyleSet { +public: + explicit JustOneTypefaceStyleSet(sk_sp typeface) : fTypeface(std::move(typeface)) {} + int count() override { return 1; } + + void getStyle(int index, SkFontStyle* style, SkString* name) override { + if (style) { *style = SkFontStyle::Normal(); } + if (name) { *name = "Normal"; } + } + + SkTypeface* createTypeface(int index) override { + return SkRef(fTypeface.get()); + } + + SkTypeface* matchStyle(const SkFontStyle& pattern) override { + return this->matchStyleCSS3(pattern); + } +private: + sk_sp fTypeface; }; class FontStyleSet final : public SkFontStyleSet { @@ -69,6 +93,7 @@ public: fFamilies[0] = sk_make_sp(0); fFamilies[1] = sk_make_sp(1); fFamilies[2] = sk_make_sp(2); + fFamilies[3] = sk_make_sp(SkTestSVGTypeface::Default()); } int onCountFamilies() const override { return SK_ARRAY_COUNT(fFamilies); } @@ -86,6 +111,7 @@ public: if (strstr(familyName, "ans")) { return this->createStyleSet(0); } if (strstr(familyName, "erif")) { return this->createStyleSet(1); } if (strstr(familyName, "ono")) { return this->createStyleSet(2); } + if (strstr(familyName, "oji")) { return this->createStyleSet(3); } } return this->createStyleSet(0); } @@ -138,7 +164,7 @@ public: } private: - sk_sp fFamilies[3]; + sk_sp fFamilies[4]; }; } diff --git a/tools/fonts/SkTestSVGTypeface.cpp b/tools/fonts/SkTestSVGTypeface.cpp new file mode 100644 index 0000000000..715a28c5f0 --- /dev/null +++ b/tools/fonts/SkTestSVGTypeface.cpp @@ -0,0 +1,1300 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Resources.h" +#include "SkAdvancedTypefaceMetrics.h" +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkColor.h" +#include "SkData.h" +#include "SkEncodedImageFormat.h" +#include "SkFontDescriptor.h" +#include "SkFontStyle.h" +#include "SkGeometry.h" +#include "SkGlyph.h" +#include "SkImage.h" +#include "SkImageInfo.h" +#include "SkMask.h" +#include "SkMatrix.h" +#include "SkNoDrawCanvas.h" +#include "SkOTUtils.h" +#include "SkPaintPriv.h" +#include "SkPath.h" +#include "SkPathPriv.h" +#include "SkPathEffect.h" +#include "SkPathOps.h" +#include "SkPixmap.h" +#include "SkPointPriv.h" +#include "SkRRect.h" +#include "SkSVGDOM.h" +#include "SkScalerContext.h" +#include "SkSize.h" +#include "SkStream.h" +#include "SkSurface.h" +#include "SkTestSVGTypeface.h" +#include "SkTDArray.h" +#include "SkTemplates.h" +#include "SkUtils.h" + +#include + +class SkDescriptor; + +SkTestSVGTypeface::SkTestSVGTypeface(const char* name, + int upem, + const SkPaint::FontMetrics& fontMetrics, + const SkSVGTestTypefaceGlyphData* data, int dataCount, + const SkFontStyle& style) + : SkTypeface(style, false) + , fName(name) + , fUpem(upem) + , fFontMetrics(fontMetrics) + , fGlyphs(new Glyph[dataCount]) + , fGlyphCount(dataCount) +{ + for (int i = 0; i < dataCount; ++i) { + const SkSVGTestTypefaceGlyphData& datum = data[i]; + std::unique_ptr stream = GetResourceAsStream(datum.fSvgResourcePath); + fCMap.set(datum.fUnicode, i); + fGlyphs[i].fAdvance = datum.fAdvance; + fGlyphs[i].fOrigin = datum.fOrigin; + if (!stream) { + continue; + } + sk_sp svg = SkSVGDOM::MakeFromStream(*stream.get()); + if (!svg) { + continue; + } + + const SkSize& sz = svg->containerSize(); + if (sz.isEmpty()) { + continue; + } + + fGlyphs[i].fSvg = std::move(svg); + } +} + +SkTestSVGTypeface::~SkTestSVGTypeface() {} + +SkTestSVGTypeface::Glyph::Glyph() : fOrigin{0,0}, fAdvance(0) {} +SkTestSVGTypeface::Glyph::~Glyph() {} + +void SkTestSVGTypeface::getAdvance(SkGlyph* glyph) const { + SkGlyphID glyphID = glyph->getGlyphID(); + glyphID = glyphID < fGlyphCount ? glyphID : 0; + + glyph->fAdvanceX = fGlyphs[glyphID].fAdvance; + glyph->fAdvanceY = 0; +} + +void SkTestSVGTypeface::getFontMetrics(SkPaint::FontMetrics* metrics) const { + *metrics = fFontMetrics; +} + +void SkTestSVGTypeface::getPath(SkGlyphID glyphID, SkPath* path) const { + path->reset(); +} + +void SkTestSVGTypeface::onFilterRec(SkScalerContextRec* rec) const { + rec->setHinting(SkPaint::kNo_Hinting); +} + +std::unique_ptr SkTestSVGTypeface::onGetAdvancedMetrics() const { + std::unique_ptr info(new SkAdvancedTypefaceMetrics); + info->fFontName.set(fName); + + SkTDArray& toUnicode = info->fGlyphToUnicode; + toUnicode.setCount(fGlyphCount); + fCMap.foreach([&toUnicode](const SkUnichar& c, const SkGlyphID& g) { + toUnicode[g] = c; + }); + return info; +} + +void SkTestSVGTypeface::onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const { + desc->setFamilyName(fName.c_str()); + desc->setStyle(this->fontStyle()); + *isLocal = false; +} + +int SkTestSVGTypeface::onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const { + auto utf8 = (const char*)chars; + auto utf16 = (const uint16_t*)chars; + auto utf32 = (const SkUnichar*)chars; + + for (int i = 0; i < glyphCount; i++) { + SkUnichar ch; + switch (encoding) { + case kUTF8_Encoding: ch = SkUTF8_NextUnichar(&utf8 ); break; + case kUTF16_Encoding: ch = SkUTF16_NextUnichar(&utf16); break; + case kUTF32_Encoding: ch = *utf32++; break; + } + if (glyphs) { + SkGlyphID* g = fCMap.find(ch); + glyphs[i] = g ? *g : 0; + } + } + return glyphCount; +} + +void SkTestSVGTypeface::onGetFamilyName(SkString* familyName) const { + *familyName = fName; +} + +SkTypeface::LocalizedStrings* SkTestSVGTypeface::onCreateFamilyNameIterator() const { + SkString familyName(fName); + SkString language("und"); //undetermined + return new SkOTUtils::LocalizedStrings_SingleName(familyName, language); +} + +class SkTestSVGScalerContext : public SkScalerContext { +public: + SkTestSVGScalerContext(sk_sp face, const SkScalerContextEffects& effects, + const SkDescriptor* desc) + : SkScalerContext(std::move(face), effects, desc) + { + fRec.getSingleMatrix(&fMatrix); + SkScalar upem = this->geTestSVGTypeface()->fUpem; + fMatrix.preScale(1.f/upem, 1.f/upem); + } + +protected: + SkTestSVGTypeface* geTestSVGTypeface() const { + return static_cast(this->getTypeface()); + } + + unsigned generateGlyphCount() override { + return this->geTestSVGTypeface()->onCountGlyphs(); + } + + uint16_t generateCharToGlyph(SkUnichar u) override { + uint16_t g; + (void) this->geTestSVGTypeface()->onCharsToGlyphs(&u, SkTypeface::kUTF32_Encoding, &g, 1); + return g; + } + + void generateAdvance(SkGlyph* glyph) override { + this->geTestSVGTypeface()->getAdvance(glyph); + + const SkVector advance = fMatrix.mapXY(SkFloatToScalar(glyph->fAdvanceX), + SkFloatToScalar(glyph->fAdvanceY)); + glyph->fAdvanceX = SkScalarToFloat(advance.fX); + glyph->fAdvanceY = SkScalarToFloat(advance.fY); + } + + void generateMetrics(SkGlyph* glyph) override { + SkGlyphID glyphID = glyph->getGlyphID(); + glyphID = glyphID < this->geTestSVGTypeface()->fGlyphCount ? glyphID : 0; + + glyph->zeroMetrics(); + glyph->fMaskFormat = SkMask::kARGB32_Format; + this->generateAdvance(glyph); + + SkTestSVGTypeface::Glyph& glyphData = this->geTestSVGTypeface()->fGlyphs[glyphID]; + if (!glyphData.fSvg) { + return; + } + + SkSize containerSize = glyphData.fSvg->containerSize(); + SkRect newBounds = SkRect::MakeXYWH(glyphData.fOrigin.fX, -glyphData.fOrigin.fY, + containerSize.fWidth, containerSize.fHeight); + fMatrix.mapRect(&newBounds); + SkScalar dx = SkFixedToScalar(glyph->getSubXFixed()); + SkScalar dy = SkFixedToScalar(glyph->getSubYFixed()); + newBounds.offset(dx, dy); + + SkIRect ibounds; + newBounds.roundOut(&ibounds); + glyph->fLeft = ibounds.fLeft; + glyph->fTop = ibounds.fTop; + glyph->fWidth = ibounds.width(); + glyph->fHeight = ibounds.height(); + } + + void generateImage(const SkGlyph& glyph) override { + SkGlyphID glyphID = glyph.getGlyphID(); + glyphID = glyphID < this->geTestSVGTypeface()->fGlyphCount ? glyphID : 0; + + SkBitmap bm; + // TODO: this should be SkImageInfo::MakeS32 when that passes all the tests. + bm.installPixels(SkImageInfo::MakeN32(glyph.fWidth, glyph.fHeight, kPremul_SkAlphaType), + glyph.fImage, glyph.rowBytes()); + bm.eraseColor(0); + + SkTestSVGTypeface::Glyph& glyphData = this->geTestSVGTypeface()->fGlyphs[glyphID]; + + SkScalar dx = SkFixedToScalar(glyph.getSubXFixed()); + SkScalar dy = SkFixedToScalar(glyph.getSubYFixed()); + + SkCanvas canvas(bm); + canvas.translate(-glyph.fLeft, -glyph.fTop); + canvas.translate(dx, dy); + canvas.concat(fMatrix); + canvas.translate(glyphData.fOrigin.fX, -glyphData.fOrigin.fY); + + if (glyphData.fSvg) { + SkAutoExclusive lock(glyphData.fSvgMutex); + glyphData.fSvg->render(&canvas); + } + } + + void generatePath(SkGlyphID glyph, SkPath* path) override { + this->geTestSVGTypeface()->getPath(glyph, path); + path->transform(fMatrix); + } + + void generateFontMetrics(SkPaint::FontMetrics* metrics) override { + this->geTestSVGTypeface()->getFontMetrics(metrics); + SkPaintPriv::ScaleFontMetrics(metrics, fMatrix.getScaleY()); + } + +private: + SkMatrix fMatrix; +}; + +SkScalerContext* SkTestSVGTypeface::onCreateScalerContext( + const SkScalerContextEffects& e, const SkDescriptor* desc) const +{ + return new SkTestSVGScalerContext(sk_ref_sp(const_cast(this)), e, desc); +} + +// Recommended that the first four be .notdef, .null, CR, space +constexpr const static SkSVGTestTypefaceGlyphData gGlyphs[] = { + {"fonts/svg/notdef.svg", {100,800}, 800, 0x0}, // .notdef + {"fonts/svg/empty.svg", {0,0}, 800, 0x0020}, // space + {"fonts/svg/diamond.svg", {100, 800}, 800, 0x2662}, // β™’ + {"fonts/svg/smile.svg", {0,800}, 800, 0x1F600}, // πŸ˜€ +}; + +sk_sp SkTestSVGTypeface::Default() { + SkPaint::FontMetrics metrics; + metrics.fFlags = SkPaint::FontMetrics::kUnderlineThicknessIsValid_Flag | + SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag | + SkPaint::FontMetrics::kStrikeoutThicknessIsValid_Flag | + SkPaint::FontMetrics::kStrikeoutPositionIsValid_Flag; + metrics.fTop = -800; + metrics.fAscent = -800; + metrics.fDescent = 200; + metrics.fBottom = 200; + metrics.fLeading = 100; + metrics.fAvgCharWidth = 1000; + metrics.fMaxCharWidth = 1000; + metrics.fXMin = 0; + metrics.fXMax = 1000; + metrics.fXHeight = 500; + metrics.fCapHeight = 700; + metrics.fUnderlineThickness = 40; + metrics.fUnderlinePosition = 20; + metrics.fStrikeoutThickness = 20; + metrics.fStrikeoutPosition = -400; + return sk_make_sp("Emoji", 1000, metrics, gGlyphs, SK_ARRAY_COUNT(gGlyphs), + SkFontStyle::Normal()); +} + +void SkTestSVGTypeface::exportTtxCommon(SkWStream* out, const char* type, + const SkTArray* glyfInfo) const +{ + int totalGlyphs = fGlyphCount; + out->writeText(" \n"); + for (int i = 0; i < fGlyphCount; ++i) { + out->writeText(" writeHexAsText(i, 4); + out->writeText("\"/>\n"); + } + if (glyfInfo) { + for (int i = 0; i < fGlyphCount; ++i) { + for (int j = 0; j < (*glyfInfo)[i].fLayers.count(); ++j) { + out->writeText(" writeHexAsText(i, 4); + out->writeText("l"); + out->writeHexAsText(j, 4); + out->writeText("\"/>\n"); + ++totalGlyphs; + } + } + } + out->writeText(" \n"); + + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" writeDecAsText(fUpem); + out->writeText("\"/>\n"); + out->writeText(" \n"); + out->writeText(" \n"); + // TODO: not recalculated for bitmap fonts? + out->writeText(" writeScalarAsText(fFontMetrics.fXMin); + out->writeText("\"/>\n"); + out->writeText(" writeScalarAsText(-fFontMetrics.fBottom); + out->writeText("\"/>\n"); + out->writeText(" writeScalarAsText(fFontMetrics.fXMax); + out->writeText("\"/>\n"); + out->writeText(" writeScalarAsText(-fFontMetrics.fTop); + out->writeText("\"/>\n"); + + char macStyle[16] = {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; + if (this->fontStyle().weight() >= SkFontStyle::Bold().weight()) { + macStyle[0xF - 0x0] = '1'; // Bold + } + switch (this->fontStyle().slant()) { + case SkFontStyle::kUpright_Slant: + break; + case SkFontStyle::kItalic_Slant: + macStyle[0xF - 0x1] = '1'; // Italic + break; + case SkFontStyle::kOblique_Slant: + macStyle[0xF - 0x1] = '1'; // Italic + break; + default: + SK_ABORT("Unknown slant."); + } + if (this->fontStyle().width() <= SkFontStyle::kCondensed_Width) { + macStyle[0xF - 0x5] = '1'; // Condensed + } else if (this->fontStyle().width() >= SkFontStyle::kExpanded_Width) { + macStyle[0xF - 0x6] = '1'; // Extended + } + out->writeText(" write(macStyle, 8); + out->writeText(" "); + out->write(macStyle + 8, 8); + out->writeText("\"/>\n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" writeDecAsText(-fFontMetrics.fAscent); + out->writeText("\"/>\n"); + out->writeText(" writeDecAsText(-fFontMetrics.fDescent); + out->writeText("\"/>\n"); + out->writeText(" writeDecAsText(fFontMetrics.fLeading); + out->writeText("\"/>\n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" writeScalarAsText(fFontMetrics.fXMax - fFontMetrics.fXMin); + out->writeText("\"/>\n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + + // Some of this table is going to be re-calculated, but we have to write it out anyway. + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" writeDecAsText(totalGlyphs); + out->writeText("\"/>\n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" writeScalarAsText(fFontMetrics.fAvgCharWidth); + out->writeText("\"/>\n"); + out->writeText(" writeDecAsText(this->fontStyle().weight()); + out->writeText("\"/>\n"); + out->writeText(" writeDecAsText(this->fontStyle().width()); + out->writeText("\"/>\n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" writeScalarAsText(fFontMetrics.fStrikeoutThickness); + out->writeText("\"/>\n"); + out->writeText(" writeScalarAsText(-fFontMetrics.fStrikeoutPosition); + out->writeText("\"/>\n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + char fsSelection[16] = {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; + fsSelection[0xF - 0x7] = '1'; // Use typo metrics + if (this->fontStyle().weight() >= SkFontStyle::Bold().weight()) { + fsSelection[0xF - 0x5] = '1'; // Bold + } + switch (this->fontStyle().slant()) { + case SkFontStyle::kUpright_Slant: + if (this->fontStyle().weight() < SkFontStyle::Bold().weight()) { + fsSelection[0xF - 0x6] = '1'; // Not bold or italic, is regular + } + break; + case SkFontStyle::kItalic_Slant: + fsSelection[0xF - 0x0] = '1'; // Italic + break; + case SkFontStyle::kOblique_Slant: + fsSelection[0xF - 0x0] = '1'; // Italic + fsSelection[0xF - 0x9] = '1'; // Oblique + break; + default: + SK_ABORT("Unknown slant."); + } + out->writeText(" write(fsSelection, 8); + out->writeText(" "); + out->write(fsSelection + 8, 8); + out->writeText("\"/>\n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" writeScalarAsText(-fFontMetrics.fAscent); + out->writeText("\"/>\n"); + out->writeText(" writeScalarAsText(-fFontMetrics.fDescent); + out->writeText("\"/>\n"); + out->writeText(" writeScalarAsText(fFontMetrics.fLeading); + out->writeText("\"/>\n"); + out->writeText(" writeScalarAsText(-fFontMetrics.fAscent); + out->writeText("\"/>\n"); + out->writeText(" writeScalarAsText(fFontMetrics.fDescent); + out->writeText("\"/>\n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" writeScalarAsText(fFontMetrics.fXHeight); + out->writeText("\"/>\n"); + out->writeText(" writeScalarAsText(fFontMetrics.fCapHeight); + out->writeText("\"/>\n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + + out->writeText(" \n"); + for (int i = 0; i < fGlyphCount; ++i) { + out->writeText(" writeHexAsText(i, 4); + out->writeText("\" width=\""); + out->writeDecAsText(fGlyphs[i].fAdvance); + out->writeText("\" lsb=\""); + int lsb = fGlyphs[i].fOrigin.fX; + if (glyfInfo) { + lsb += (*glyfInfo)[i].fBounds.fLeft; + } + out->writeDecAsText(lsb); + out->writeText("\"/>\n"); + } + if (glyfInfo) { + for (int i = 0; i < fGlyphCount; ++i) { + for (int j = 0; j < (*glyfInfo)[i].fLayers.count(); ++j) { + out->writeText(" writeHexAsText(i, 4); + out->writeText("l"); + out->writeHexAsText(j, 4); + out->writeText("\" width=\""); + out->writeDecAsText(fGlyphs[i].fAdvance); + out->writeText("\" lsb=\""); + int32_t lsb = fGlyphs[i].fOrigin.fX + (*glyfInfo)[i].fLayers[j].fBounds.fLeft; + out->writeDecAsText(lsb); + out->writeText("\"/>\n"); + } + } + } + out->writeText(" \n"); + + bool hasNonBMP = false; + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + fCMap.foreach([&out, &hasNonBMP](const SkUnichar& c, const SkGlyphID& g) { + if (0xFFFF < c) { + hasNonBMP = true; + return; + } + out->writeText(" writeHexAsText(c, 4); + out->writeText("\" name=\"glyf"); + out->writeHexAsText(g, 4); + out->writeText("\"/>\n"); + }); + out->writeText(" \n"); + if (hasNonBMP) { + out->writeText(" \n"); + fCMap.foreach([&out](const SkUnichar& c, const SkGlyphID& g) { + out->writeText(" writeHexAsText(c, 6); + out->writeText("\" name=\"glyf"); + out->writeHexAsText(g, 4); + out->writeText("\"/>\n"); + }); + out->writeText(" \n"); + } + out->writeText(" \n"); + + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" "); + out->writeText(fName.c_str()); + out->writeText(" "); + out->writeText(type); + out->writeText("\n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" Regular\n"); + out->writeText(" \n"); + out->writeText(" \n"); + + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" writeScalarAsText(fFontMetrics.fUnderlinePosition); + out->writeText("\"/>\n"); + out->writeText(" writeScalarAsText(fFontMetrics.fUnderlineThickness); + out->writeText("\"/>\n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); +} + +void SkTestSVGTypeface::exportTtxCbdt(SkWStream* out) const { + out->writeText("\n"); + out->writeText("\n"); + this->exportTtxCommon(out, "CBDT"); + + int strikeSizes[3] = { 16, 64, 128 }; + + SkPaint paint; + paint.setTypeface(sk_ref_sp(const_cast(this))); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + out->writeText(" \n"); + out->writeText("
\n"); + for (size_t strikeIndex = 0; strikeIndex < SK_ARRAY_COUNT(strikeSizes); ++strikeIndex) { + paint.setTextSize(strikeSizes[strikeIndex]); + out->writeText(" writeDecAsText(strikeIndex); + out->writeText("\">\n"); + for (int i = 0; i < fGlyphCount; ++i) { + SkGlyphID gid = i; + SkScalar advance; + SkRect bounds; + paint.getTextWidths(&gid, sizeof(gid), &advance, &bounds); + SkIRect ibounds = bounds.roundOut(); + if (ibounds.isEmpty()) { + continue; + } + SkImageInfo image_info = SkImageInfo::MakeN32Premul(ibounds.width(), ibounds.height()); + sk_sp surface(SkSurface::MakeRaster(image_info)); + SkASSERT(surface); + SkCanvas* canvas = surface->getCanvas(); + canvas->clear(0); + SkPixmap pix; + surface->peekPixels(&pix); + canvas->drawText(&gid, sizeof(gid), -bounds.fLeft, -bounds.fTop, paint); + canvas->flush(); + sk_sp image = surface->makeImageSnapshot(); + sk_sp data = image->encodeToData(SkEncodedImageFormat::kPNG, 100); + + out->writeText(" writeHexAsText(i, 4); + out->writeText("\">\n"); + out->writeText(" \n"); + out->writeText(" writeDecAsText(image->height()); + out->writeText("\"/>\n"); + out->writeText(" writeDecAsText(image->width()); + out->writeText("\"/>\n"); + out->writeText(" writeDecAsText(bounds.fLeft); + out->writeText("\"/>\n"); + out->writeText(" writeScalarAsText(-bounds.fTop); + out->writeText("\"/>\n"); + out->writeText(" writeScalarAsText(advance); + out->writeText("\"/>\n"); + out->writeText(" \n"); + out->writeText(" "); + uint8_t const * bytes = data->bytes(); + for (size_t i = 0; i < data->size(); ++i) { + if ((i % 0x10) == 0x0) { + out->writeText("\n "); + } else if (((i - 1) % 0x4) == 0x3) { + out->writeText(" "); + } + out->writeHexAsText(bytes[i], 2); + } + out->writeText("\n"); + out->writeText(" \n"); + out->writeText(" \n"); + } + out->writeText(" \n"); + } + out->writeText(" \n"); + + SkPaint::FontMetrics fm; + out->writeText(" \n"); + out->writeText("
\n"); + for (size_t strikeIndex = 0; strikeIndex < SK_ARRAY_COUNT(strikeSizes); ++strikeIndex) { + paint.setTextSize(strikeSizes[strikeIndex]); + paint.getFontMetrics(&fm); + out->writeText(" writeDecAsText(strikeIndex); + out->writeText("\">\n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" writeScalarAsText(-fm.fTop); + out->writeText("\"/>\n"); + out->writeText(" writeScalarAsText(-fm.fBottom); + out->writeText("\"/>\n"); + out->writeText(" writeScalarAsText(fm.fXMax - fm.fXMin); + out->writeText("\"/>\n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" writeScalarAsText(-fm.fTop); + out->writeText("\"/>\n"); + out->writeText(" writeScalarAsText(-fm.fBottom); + out->writeText("\"/>\n"); + out->writeText(" writeScalarAsText(fm.fXMax - fm.fXMin); + out->writeText("\"/>\n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" writeDecAsText(strikeSizes[strikeIndex]); + out->writeText("\"/>\n"); + out->writeText(" writeDecAsText(strikeSizes[strikeIndex]); + out->writeText("\"/>\n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + for (int i = 0; i < fGlyphCount; ++i) { + SkGlyphID gid = i; + SkRect bounds; + paint.getTextWidths(&gid, sizeof(gid), nullptr, &bounds); + if (bounds.isEmpty()) { + continue; + } + out->writeText(" writeHexAsText(i, 4); + out->writeText("\"/>\n"); + } + out->writeText(" \n"); + out->writeText(" \n"); + } + out->writeText(" \n"); + + out->writeText("\n"); +} + +/** + * UnitsPerEm is generally 1000 here. Versions of macOS older than 10.13 + * have problems in CoreText determining the glyph bounds of bitmap glyphs + * with unitsPerEm set to 1024 or numbers not divisible by 100 when the + * contour is not closed. The bounds of sbix fonts on macOS appear to be those + * of the outline in the 'glyf' table. If this countour is closed it will be + * drawn, as the 'glyf' outline is to be drawn on top of any bitmap. (There is + * a bit which is supposed to control this, but it cannot be relied on.) So + * make the glyph contour a degenerate line with points at the edge of the + * bounding box of the glyph. + */ +void SkTestSVGTypeface::exportTtxSbix(SkWStream* out) const { + out->writeText("\n"); + out->writeText("\n"); + this->exportTtxCommon(out, "sbix"); + + SkPaint paint; + paint.setTypeface(sk_ref_sp(const_cast(this))); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + out->writeText(" \n"); + for (int i = 0; i < fGlyphCount; ++i) { + const SkTestSVGTypeface::Glyph& glyphData = this->fGlyphs[i]; + + SkSize containerSize = glyphData.fSvg ? glyphData.fSvg->containerSize() + : SkSize::MakeEmpty(); + SkRect bounds = SkRect::MakeXYWH(glyphData.fOrigin.fX, -glyphData.fOrigin.fY, + containerSize.fWidth, containerSize.fHeight); + SkIRect ibounds = bounds.roundOut(); + out->writeText(" writeHexAsText(i, 4); + out->writeText("\" xMin=\""); + out->writeDecAsText(ibounds.fLeft); + out->writeText("\" yMin=\""); + out->writeDecAsText(-ibounds.fBottom); + out->writeText("\" xMax=\""); + out->writeDecAsText(ibounds.fRight); + out->writeText("\" yMax=\""); + out->writeDecAsText(-ibounds.fTop); + out->writeText("\">\n"); + out->writeText(" \n"); + out->writeText(" writeDecAsText(ibounds.fLeft); + out->writeText("\" y=\""); + out->writeDecAsText(-ibounds.fBottom); + out->writeText("\" on=\"1\"/>\n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" writeDecAsText(ibounds.fRight); + out->writeText("\" y=\""); + out->writeDecAsText(-ibounds.fTop); + out->writeText("\" on=\"1\"/>\n"); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + } + out->writeText(" \n"); + + // The loca table will be re-calculated, but if we don't write one we don't get one. + out->writeText(" \n"); + + int strikeSizes[3] = { 16, 64, 128 }; + + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" \n"); + for (size_t strikeIndex = 0; strikeIndex < SK_ARRAY_COUNT(strikeSizes); ++strikeIndex) { + paint.setTextSize(strikeSizes[strikeIndex]); + out->writeText(" \n"); + out->writeText(" writeDecAsText(strikeSizes[strikeIndex]); + out->writeText("\"/>\n"); + out->writeText(" \n"); + for (int i = 0; i < fGlyphCount; ++i) { + SkGlyphID gid = i; + SkScalar advance; + SkRect bounds; + paint.getTextWidths(&gid, sizeof(gid), &advance, &bounds); + SkIRect ibounds = bounds.roundOut(); + if (ibounds.isEmpty()) { + continue; + } + SkImageInfo image_info = SkImageInfo::MakeN32Premul(ibounds.width(), ibounds.height()); + sk_sp surface(SkSurface::MakeRaster(image_info)); + SkASSERT(surface); + SkCanvas* canvas = surface->getCanvas(); + canvas->clear(0); + SkPixmap pix; + surface->peekPixels(&pix); + canvas->drawText(&gid, sizeof(gid), -bounds.fLeft, -bounds.fTop, paint); + canvas->flush(); + sk_sp image = surface->makeImageSnapshot(); + sk_sp data = image->encodeToData(SkEncodedImageFormat::kPNG, 100); + + out->writeText(" writeHexAsText(i, 4); + out->writeText("\" graphicType=\"png \" originOffsetX=\""); + out->writeDecAsText(bounds.fLeft); + out->writeText("\" originOffsetY=\""); + out->writeScalarAsText(bounds.fBottom); + out->writeText("\">\n"); + + out->writeText(" "); + uint8_t const * bytes = data->bytes(); + for (size_t i = 0; i < data->size(); ++i) { + if ((i % 0x10) == 0x0) { + out->writeText("\n "); + } else if (((i - 1) % 0x4) == 0x3) { + out->writeText(" "); + } + out->writeHexAsText(bytes[i], 2); + } + out->writeText("\n"); + out->writeText(" \n"); + out->writeText(" \n"); + } + out->writeText(" \n"); + } + out->writeText(" \n"); + out->writeText("\n"); +} + +namespace { + +void convert_noninflect_cubic_to_quads(const SkPoint p[4], + SkScalar toleranceSqd, + SkTArray* quads, + int sublevel = 0) +{ + // Notation: Point a is always p[0]. Point b is p[1] unless p[1] == p[0], in which case it is + // p[2]. Point d is always p[3]. Point c is p[2] unless p[2] == p[3], in which case it is p[1]. + + SkVector ab = p[1] - p[0]; + SkVector dc = p[2] - p[3]; + + if (SkPointPriv::LengthSqd(ab) < SK_ScalarNearlyZero) { + if (SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero) { + SkPoint* degQuad = quads->push_back_n(3); + degQuad[0] = p[0]; + degQuad[1] = p[0]; + degQuad[2] = p[3]; + return; + } + ab = p[2] - p[0]; + } + if (SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero) { + dc = p[1] - p[3]; + } + + static const SkScalar kLengthScale = 3 * SK_Scalar1 / 2; + static const int kMaxSubdivs = 10; + + ab.scale(kLengthScale); + dc.scale(kLengthScale); + + // e0 and e1 are extrapolations along vectors ab and dc. + SkVector c0 = p[0]; + c0 += ab; + SkVector c1 = p[3]; + c1 += dc; + + SkScalar dSqd = sublevel > kMaxSubdivs ? 0 : SkPointPriv::DistanceToSqd(c0, c1); + if (dSqd < toleranceSqd) { + SkPoint cAvg = c0; + cAvg += c1; + cAvg.scale(SK_ScalarHalf); + + SkPoint* pts = quads->push_back_n(3); + pts[0] = p[0]; + pts[1] = cAvg; + pts[2] = p[3]; + return; + } + SkPoint choppedPts[7]; + SkChopCubicAtHalf(p, choppedPts); + convert_noninflect_cubic_to_quads(choppedPts + 0, toleranceSqd, quads, sublevel + 1); + convert_noninflect_cubic_to_quads(choppedPts + 3, toleranceSqd, quads, sublevel + 1); +} + +void convertCubicToQuads(const SkPoint p[4], SkScalar tolScale, SkTArray* quads) { + if (!p[0].isFinite() || !p[1].isFinite() || !p[2].isFinite() || !p[3].isFinite()) { + return; + } + SkPoint chopped[10]; + int count = SkChopCubicAtInflections(p, chopped); + + const SkScalar tolSqd = SkScalarSquare(tolScale); + + for (int i = 0; i < count; ++i) { + SkPoint* cubic = chopped + 3*i; + convert_noninflect_cubic_to_quads(cubic, tolSqd, quads); + } +} + +void path_to_quads(const SkPath& path, SkPath* quadPath) { + quadPath->reset(); + SkTArray qPts; + SkAutoConicToQuads converter; + const SkPoint* quadPts; + SkPath::RawIter iter(path); + uint8_t verb; + SkPoint pts[4]; + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { + switch (verb) { + case SkPath::kMove_Verb: + quadPath->moveTo(pts[0].fX, pts[0].fY); + break; + case SkPath::kLine_Verb: + quadPath->lineTo(pts[1].fX, pts[1].fY); + break; + case SkPath::kQuad_Verb: + quadPath->quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY); + break; + case SkPath::kCubic_Verb: + qPts.reset(); + convertCubicToQuads(pts, SK_Scalar1, &qPts); + for (int i = 0; i < qPts.count(); i += 3) { + quadPath->quadTo(qPts[i+1].fX, qPts[i+1].fY, qPts[i+2].fX, qPts[i+2].fY); + } + break; + case SkPath::kConic_Verb: + quadPts = converter.computeQuads(pts, iter.conicWeight(), SK_Scalar1); + for (int i = 0; i < converter.countQuads(); ++i) { + quadPath->quadTo(quadPts[i*2+1].fX, quadPts[i*2+1].fY, + quadPts[i*2+2].fX, quadPts[i*2+2].fY); + } + break; + case SkPath::kClose_Verb: + quadPath->close(); + break; + default: + SkDEBUGFAIL("bad verb"); + return; + } + } +} + +class SkCOLRCanvas : public SkNoDrawCanvas { +public: + SkCOLRCanvas(SkRect glyphBounds, SkGlyphID glyphId, + SkTestSVGTypeface::GlyfInfo* glyf, SkTHashMap* colors, + SkWStream* out) + : SkNoDrawCanvas(glyphBounds.roundOut().width(), glyphBounds.roundOut().height()) + , fOut(out) + , fGlyphId(glyphId) + , fBaselineOffset(glyphBounds.top()) + , fLayerId(0) + , fGlyf(glyf) + , fColors(colors) + { } + + void writePoint(SkScalar x, SkScalar y, bool on) { + fOut->writeText(" writeDecAsText(SkScalarRoundToInt(x)); + fOut->writeText("\" y=\""); + fOut->writeDecAsText(SkScalarRoundToInt(y)); + fOut->writeText("\" on=\""); + fOut->write8(on ? '1' : '0'); + fOut->writeText("\"/>\n"); + } + SkIRect writePath(const SkPath& path, bool layer) { + // Convert to quads. + SkPath quads; + path_to_quads(path, &quads); + + SkRect bounds = quads.computeTightBounds(); + SkIRect ibounds = bounds.roundOut(); + // The bounds will be re-calculated anyway. + fOut->writeText(" writeHexAsText(fGlyphId, 4); + if (layer) { + fOut->writeText("l"); + fOut->writeHexAsText(fLayerId, 4); + } + fOut->writeText("\" xMin=\""); + fOut->writeDecAsText(ibounds.fLeft); + fOut->writeText("\" yMin=\""); + fOut->writeDecAsText(ibounds.fTop); + fOut->writeText("\" xMax=\""); + fOut->writeDecAsText(ibounds.fRight); + fOut->writeText("\" yMax=\""); + fOut->writeDecAsText(ibounds.fBottom); + fOut->writeText("\">\n"); + + SkPath::RawIter iter(quads); + uint8_t verb; + SkPoint pts[4]; + bool contourOpen = false; + while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { + switch (verb) { + case SkPath::kMove_Verb: + if (contourOpen) { + fOut->writeText(" \n"); + contourOpen = false; + } + break; + case SkPath::kLine_Verb: + if (!contourOpen) { + fOut->writeText(" \n"); + this->writePoint(pts[0].fX, pts[0].fY, true); + contourOpen = true; + } + this->writePoint(pts[1].fX, pts[1].fY, true); + break; + case SkPath::kQuad_Verb: + if (!contourOpen) { + fOut->writeText(" \n"); + this->writePoint(pts[0].fX, pts[0].fY, true); + contourOpen = true; + } + this->writePoint(pts[1].fX, pts[1].fY, false); + this->writePoint(pts[2].fX, pts[2].fY, true); + break; + case SkPath::kClose_Verb: + if (contourOpen) { + fOut->writeText(" \n"); + contourOpen = false; + } + break; + default: + SkDEBUGFAIL("bad verb"); + return ibounds; + } + } + if (contourOpen) { + fOut->writeText(" \n"); + } + + // Required to write out an instructions tag. + fOut->writeText(" \n"); + fOut->writeText(" \n"); + return ibounds; + } + + void onDrawRect(const SkRect& rect, const SkPaint& paint) override { + SkPath path; + path.addRect(rect); + this->drawPath(path, paint); + } + + void onDrawOval(const SkRect& oval, const SkPaint& paint) override { + SkPath path; + path.addOval(oval); + this->drawPath(path, paint); + } + + void onDrawArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool useCenter, + const SkPaint& paint) override + { + SkPath path; + bool fillNoPathEffect = SkPaint::kFill_Style == paint.getStyle() && !paint.getPathEffect(); + SkPathPriv::CreateDrawArcPath(&path, oval, startAngle, sweepAngle, useCenter, + fillNoPathEffect); + this->drawPath(path, paint); + } + + void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override { + SkPath path; + path.addRRect(rrect); + this->drawPath(path, paint); + } + + void onDrawPath(const SkPath& platonicPath, const SkPaint& originalPaint) override { + SkPaint paint = originalPaint; + SkPath path = platonicPath; + + // Apply the path effect. + if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) { + bool fill = paint.getFillPath(path, &path); + + paint.setPathEffect(nullptr); + if (fill) { + paint.setStyle(SkPaint::kFill_Style); + } else { + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(0); + } + } + + // Apply the matrix. + SkMatrix m = this->getTotalMatrix(); + // If done to the canvas then everything would get clipped out. + m.postTranslate(0, fBaselineOffset); // put the baseline at 0 + m.postScale(1, -1); // and flip it since OpenType is y-up. + path.transform(m); + + // While creating the default glyf, union with dark colors and intersect with bright colors. + SkColor color = paint.getColor(); + if ((SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color)) / 3 > 0x20) { + fBasePath.add(path, SkPathOp::kDifference_SkPathOp); + } else { + fBasePath.add(path, SkPathOp::kUnion_SkPathOp); + } + + SkIRect bounds = this->writePath(path, true); + + // The CPAL table has the concept of a 'current color' which is index 0xFFFF. + // Mark any layer drawn in 'currentColor' as having this special index. + // The value of 'currentColor' here should a color which causes this layer to union into the + // default glyf. + constexpr SkColor currentColor = 0xFF2B0000; + + int colorIndex; + if (color == currentColor) { + colorIndex = 0xFFFF; + } else { + int* colorIndexPtr = fColors->find(color); + if (colorIndexPtr) { + colorIndex = *colorIndexPtr; + } else { + colorIndex = fColors->count(); + fColors->set(color, colorIndex); + } + } + fGlyf->fLayers.emplace_back(colorIndex, bounds); + + ++fLayerId; + } + + void finishGlyph() { + SkPath baseGlyph; + fBasePath.resolve(&baseGlyph); + fGlyf->fBounds = this->writePath(baseGlyph, false); + } + +private: + SkWStream * const fOut; + SkGlyphID fGlyphId; + SkScalar fBaselineOffset; + int fLayerId; + SkOpBuilder fBasePath; + SkTestSVGTypeface::GlyfInfo* fGlyf; + SkTHashMap* fColors; +}; + +} // namespace + +void SkTestSVGTypeface::exportTtxColr(SkWStream* out) const { + out->writeText("\n"); + out->writeText("\n"); + + SkTHashMap colors; + SkTArray glyfInfos(fGlyphCount); + + // Need to know all the glyphs up front for the common tables. + SkDynamicMemoryWStream glyfOut; + glyfOut.writeText(" \n"); + for (int i = 0; i < fGlyphCount; ++i) { + const SkTestSVGTypeface::Glyph& glyphData = this->fGlyphs[i]; + + SkSize containerSize = glyphData.fSvg ? glyphData.fSvg->containerSize() + : SkSize::MakeEmpty(); + SkRect bounds = SkRect::MakeXYWH(glyphData.fOrigin.fX, -glyphData.fOrigin.fY, + containerSize.fWidth, containerSize.fHeight); + SkCOLRCanvas canvas(bounds, i, &glyfInfos.emplace_back(), &colors, &glyfOut); + if (glyphData.fSvg) { + glyphData.fSvg->render(&canvas); + } + canvas.finishGlyph(); + } + glyfOut.writeText(" \n"); + + this->exportTtxCommon(out, "COLR", &glyfInfos); + + // The loca table will be re-calculated, but if we don't write one we don't get one. + out->writeText(" \n"); + + std::unique_ptr glyfStream = glyfOut.detachAsStream(); + out->writeStream(glyfStream.get(), glyfStream->getLength()); + + out->writeText(" \n"); + out->writeText(" \n"); + for (int i = 0; i < fGlyphCount; ++i) { + if (glyfInfos[i].fBounds.isEmpty() || glyfInfos[i].fLayers.empty()) { + continue; + } + out->writeText(" writeHexAsText(i, 4); + out->writeText("\">\n"); + for (int j = 0; j < glyfInfos[i].fLayers.count(); ++j) { + const int colorIndex = glyfInfos[i].fLayers[j].fLayerColorIndex; + out->writeText(" writeDecAsText(colorIndex); + out->writeText("\" name=\"glyf"); + out->writeHexAsText(i, 4); + out->writeText("l"); + out->writeHexAsText(j, 4); + out->writeText("\"/>\n"); + } + out->writeText(" \n"); + } + out->writeText(" \n"); + + // The colors must be written in order, the 'index' is ignored by ttx. + SkAutoTMalloc colorsInOrder(colors.count()); + colors.foreach([&colorsInOrder](const SkColor& c, const int* i) { + colorsInOrder[*i] = c; + }); + out->writeText(" \n"); + out->writeText(" \n"); + out->writeText(" writeDecAsText(colors.count()); + out->writeText("\"/>\n"); + out->writeText(" \n"); + for (int i = 0; i < colors.count(); ++i) { + SkColor c = colorsInOrder[i]; + out->writeText(" writeDecAsText(i); + out->writeText("\" value=\"#"); + out->writeHexAsText(SkColorGetR(c), 2); + out->writeHexAsText(SkColorGetG(c), 2); + out->writeHexAsText(SkColorGetB(c), 2); + out->writeHexAsText(SkColorGetA(c), 2); + out->writeText("\"/>\n"); + } + out->writeText(" \n"); + out->writeText(" \n"); + + out->writeText("\n"); +} diff --git a/tools/fonts/SkTestSVGTypeface.h b/tools/fonts/SkTestSVGTypeface.h new file mode 100644 index 0000000000..c6e9f4e2cf --- /dev/null +++ b/tools/fonts/SkTestSVGTypeface.h @@ -0,0 +1,135 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTestSVGTypeface_DEFINED +#define SkTestSVGTypeface_DEFINED + +#include "SkFontArguments.h" +#include "SkMutex.h" +#include "SkPaint.h" +#include "SkPoint.h" +#include "SkRect.h" +#include "SkRefCnt.h" +#include "SkScalar.h" +#include "SkString.h" +#include "SkTArray.h" +#include "SkTHash.h" +#include "SkTypeface.h" +#include "SkTypes.h" + +#include + +class SkDescriptor; +class SkFontDescriptor; +class SkFontStyle; +class SkGlyph; +class SkPath; +class SkScalerContext; +class SkStreamAsset; +class SkSVGDOM; +class SkWStream; +struct SkAdvancedTypefaceMetrics; +struct SkScalerContextEffects; +struct SkScalerContextRec; + +struct SkSVGTestTypefaceGlyphData { + const char* fSvgResourcePath; + SkPoint fOrigin; + SkScalar fAdvance; + SkUnichar fUnicode; //TODO: this limits to 1:1 +}; + +class SkTestSVGTypeface : public SkTypeface { +public: + SkTestSVGTypeface(const char* name, + int upem, + const SkPaint::FontMetrics& metrics, + const SkSVGTestTypefaceGlyphData* data, int dataCount, + const SkFontStyle& style); + ~SkTestSVGTypeface() override; + void getAdvance(SkGlyph* glyph) const; + void getFontMetrics(SkPaint::FontMetrics* metrics) const; + void getPath(SkGlyphID glyph, SkPath* path) const; + + static sk_sp Default(); + void exportTtxCbdt(SkWStream*) const; + void exportTtxSbix(SkWStream*) const; + void exportTtxColr(SkWStream*) const; + + struct GlyfLayerInfo { + GlyfLayerInfo(int layerColorIndex, SkIRect bounds) + : fLayerColorIndex(layerColorIndex) + , fBounds(bounds) {} + int fLayerColorIndex; + SkIRect fBounds; + }; + struct GlyfInfo { + GlyfInfo() : fBounds(SkIRect::MakeEmpty()) {} + SkIRect fBounds; + SkTArray fLayers; + }; +protected: + void exportTtxCommon(SkWStream*, const char* type, const SkTArray* = nullptr) const; + + SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&, + const SkDescriptor* desc) const override; + void onFilterRec(SkScalerContextRec* rec) const override; + std::unique_ptr onGetAdvancedMetrics() const override; + + SkStreamAsset* onOpenStream(int* ttcIndex) const override { + return nullptr; + } + + void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const override; + + int onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const override; + + int onCountGlyphs() const override { + return fGlyphCount; + } + + int onGetUPEM() const override { + return fUpem; + } + + void onGetFamilyName(SkString* familyName) const override; + SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override; + + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override + { + return 0; + } + + int onGetTableTags(SkFontTableTag tags[]) const override { + return 0; + } + + size_t onGetTableData(SkFontTableTag tag, size_t offset, + size_t length, void* data) const override { + return 0; + } +private: + struct Glyph { + Glyph(); + ~Glyph(); + sk_sp fSvg; + SkMutex fSvgMutex; + SkPoint fOrigin; + SkScalar fAdvance; + }; + SkString fName; + int fUpem; + const SkPaint::FontMetrics fFontMetrics; + std::unique_ptr fGlyphs; + int fGlyphCount; + SkTHashMap fCMap; + friend class SkTestSVGScalerContext; +}; + +#endif diff --git a/tools/fonts/SkTestScalerContext.cpp b/tools/fonts/SkTestScalerContext.cpp deleted file mode 100644 index f74b1bd57a..0000000000 --- a/tools/fonts/SkTestScalerContext.cpp +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkAdvancedTypefaceMetrics.h" -#include "SkBitmap.h" -#include "SkCanvas.h" -#include "SkDescriptor.h" -#include "SkFontDescriptor.h" -#include "SkGlyph.h" -#include "SkMakeUnique.h" -#include "SkMask.h" -#include "SkOTUtils.h" -#include "SkPaintPriv.h" -#include "SkScalerContext.h" -#include "SkTestScalerContext.h" -#include "SkTypefaceCache.h" -#include "SkUtils.h" - -SkTestFont::SkTestFont(const SkTestFontData& fontData) - : INHERITED() - , fCharCodes(fontData.fCharCodes) - , fCharCodesCount(fontData.fCharCodes ? fontData.fCharCodesCount : 0) - , fWidths(fontData.fWidths) - , fMetrics(fontData.fMetrics) - , fName(fontData.fName) - , fPaths(nullptr) -{ - init(fontData.fPoints, fontData.fVerbs); -} - -SkTestFont::~SkTestFont() { - for (unsigned index = 0; index < fCharCodesCount; ++index) { - delete fPaths[index]; - } - delete[] fPaths; -} - -int SkTestFont::codeToIndex(SkUnichar charCode) const { - for (unsigned index = 0; index < fCharCodesCount; ++index) { - if (fCharCodes[index] == (unsigned) charCode) { - return (int) index; - } - } - return 0; -} - -void SkTestFont::init(const SkScalar* pts, const unsigned char* verbs) { - fPaths = new SkPath* [fCharCodesCount]; - for (unsigned index = 0; index < fCharCodesCount; ++index) { - SkPath* path = new SkPath; - SkPath::Verb verb; - while ((verb = (SkPath::Verb) *verbs++) != SkPath::kDone_Verb) { - switch (verb) { - case SkPath::kMove_Verb: - path->moveTo(pts[0], pts[1]); - pts += 2; - break; - case SkPath::kLine_Verb: - path->lineTo(pts[0], pts[1]); - pts += 2; - break; - case SkPath::kQuad_Verb: - path->quadTo(pts[0], pts[1], pts[2], pts[3]); - pts += 4; - break; - case SkPath::kCubic_Verb: - path->cubicTo(pts[0], pts[1], pts[2], pts[3], pts[4], pts[5]); - pts += 6; - break; - case SkPath::kClose_Verb: - path->close(); - break; - default: - SkDEBUGFAIL("bad verb"); - return; - } - } - // This should make SkPath::getBounds() queries threadsafe. - path->updateBoundsCache(); - fPaths[index] = path; - } -} - -SkTestTypeface::SkTestTypeface(sk_sp testFont, const SkFontStyle& style) - : SkTypeface(style, false) - , fTestFont(std::move(testFont)) { -} - -void SkTestTypeface::getAdvance(SkGlyph* glyph) { - // TODO(benjaminwagner): Update users to use floats. - glyph->fAdvanceX = SkFixedToFloat(fTestFont->fWidths[glyph->getGlyphID()]); - glyph->fAdvanceY = 0; -} - -void SkTestTypeface::getFontMetrics(SkPaint::FontMetrics* metrics) { - *metrics = fTestFont->fMetrics; -} - -void SkTestTypeface::getMetrics(SkGlyph* glyph) { - SkGlyphID glyphID = glyph->getGlyphID(); - glyphID = glyphID < fTestFont->fCharCodesCount ? glyphID : 0; - - // TODO(benjaminwagner): Update users to use floats. - glyph->fAdvanceX = SkFixedToFloat(fTestFont->fWidths[glyphID]); - glyph->fAdvanceY = 0; -} - -void SkTestTypeface::getPath(SkGlyphID glyphID, SkPath* path) { - glyphID = glyphID < fTestFont->fCharCodesCount ? glyphID : 0; - *path = *fTestFont->fPaths[glyphID]; -} - -void SkTestTypeface::onFilterRec(SkScalerContextRec* rec) const { - rec->setHinting(SkPaint::kNo_Hinting); -} - -std::unique_ptr SkTestTypeface::onGetAdvancedMetrics() const { // pdf only - std::unique_ptr info(new SkAdvancedTypefaceMetrics); - info->fFontName.set(fTestFont->fName); - int glyphCount = this->onCountGlyphs(); - - SkTDArray& toUnicode = info->fGlyphToUnicode; - toUnicode.setCount(glyphCount); - SkASSERT(glyphCount == SkToInt(fTestFont->fCharCodesCount)); - for (int gid = 0; gid < glyphCount; ++gid) { - toUnicode[gid] = SkToS32(fTestFont->fCharCodes[gid]); - } - return info; -} - -void SkTestTypeface::onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const { - desc->setFamilyName(fTestFont->fName); - desc->setStyle(this->fontStyle()); - *isLocal = false; -} - -int SkTestTypeface::onCharsToGlyphs(const void* chars, Encoding encoding, - uint16_t glyphs[], int glyphCount) const { - auto utf8 = (const char*)chars; - auto utf16 = (const uint16_t*)chars; - auto utf32 = (const SkUnichar*)chars; - - for (int i = 0; i < glyphCount; i++) { - SkUnichar ch; - switch (encoding) { - case kUTF8_Encoding: ch = SkUTF8_NextUnichar(&utf8 ); break; - case kUTF16_Encoding: ch = SkUTF16_NextUnichar(&utf16); break; - case kUTF32_Encoding: ch = *utf32++; break; - } - if (glyphs) { - glyphs[i] = fTestFont->codeToIndex(ch); - } - } - return glyphCount; -} - -void SkTestTypeface::onGetFamilyName(SkString* familyName) const { - *familyName = fTestFont->fName; -} - -SkTypeface::LocalizedStrings* SkTestTypeface::onCreateFamilyNameIterator() const { - SkString familyName(fTestFont->fName); - SkString language("und"); //undetermined - return new SkOTUtils::LocalizedStrings_SingleName(familyName, language); -} - -class SkTestScalerContext : public SkScalerContext { -public: - SkTestScalerContext(sk_sp face, const SkScalerContextEffects& effects, - const SkDescriptor* desc) - : SkScalerContext(std::move(face), effects, desc) - { - fRec.getSingleMatrix(&fMatrix); - this->forceGenerateImageFromPath(); - } - -protected: - SkTestTypeface* getTestTypeface() const { - return static_cast(this->getTypeface()); - } - - unsigned generateGlyphCount() override { - return this->getTestTypeface()->onCountGlyphs(); - } - - uint16_t generateCharToGlyph(SkUnichar uni) override { - uint16_t glyph; - (void) this->getTestTypeface()->onCharsToGlyphs((const void *) &uni, - SkTypeface::kUTF32_Encoding, &glyph, 1); - return glyph; - } - - void generateAdvance(SkGlyph* glyph) override { - this->getTestTypeface()->getAdvance(glyph); - - const SkVector advance = fMatrix.mapXY(SkFloatToScalar(glyph->fAdvanceX), - SkFloatToScalar(glyph->fAdvanceY)); - glyph->fAdvanceX = SkScalarToFloat(advance.fX); - glyph->fAdvanceY = SkScalarToFloat(advance.fY); - } - - void generateMetrics(SkGlyph* glyph) override { - this->getTestTypeface()->getMetrics(glyph); - - const SkVector advance = fMatrix.mapXY(SkFloatToScalar(glyph->fAdvanceX), - SkFloatToScalar(glyph->fAdvanceY)); - glyph->fAdvanceX = SkScalarToFloat(advance.fX); - glyph->fAdvanceY = SkScalarToFloat(advance.fY); - - SkPath path; - this->getTestTypeface()->getPath(glyph->getGlyphID(), &path); - path.transform(fMatrix); - - SkRect storage; - const SkPaint paint; - const SkRect& newBounds = paint.doComputeFastBounds(path.getBounds(), - &storage, - SkPaint::kFill_Style); - SkIRect ibounds; - newBounds.roundOut(&ibounds); - glyph->fLeft = ibounds.fLeft; - glyph->fTop = ibounds.fTop; - glyph->fWidth = ibounds.width(); - glyph->fHeight = ibounds.height(); - } - - void generateImage(const SkGlyph& glyph) override { - SkPath path; - this->getTestTypeface()->getPath(glyph.getGlyphID(), &path); - - SkBitmap bm; - bm.installPixels(SkImageInfo::MakeN32Premul(glyph.fWidth, glyph.fHeight), - glyph.fImage, glyph.rowBytes()); - bm.eraseColor(0); - - SkCanvas canvas(bm); - canvas.translate(-SkIntToScalar(glyph.fLeft), - -SkIntToScalar(glyph.fTop)); - canvas.concat(fMatrix); - SkPaint paint; - paint.setAntiAlias(true); - canvas.drawPath(path, paint); - } - - void generatePath(SkGlyphID glyph, SkPath* path) override { - this->getTestTypeface()->getPath(glyph, path); - path->transform(fMatrix); - } - - void generateFontMetrics(SkPaint::FontMetrics* metrics) override { - this->getTestTypeface()->getFontMetrics(metrics); - SkPaintPriv::ScaleFontMetrics(metrics, fMatrix.getScaleY()); - } - -private: - SkMatrix fMatrix; -}; - -SkScalerContext* SkTestTypeface::onCreateScalerContext( - const SkScalerContextEffects& effects, const SkDescriptor* desc) const -{ - return new SkTestScalerContext(sk_ref_sp(const_cast(this)), effects, desc); -} diff --git a/tools/fonts/SkTestScalerContext.h b/tools/fonts/SkTestScalerContext.h deleted file mode 100644 index e62210b2e3..0000000000 --- a/tools/fonts/SkTestScalerContext.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkTestScalerContext_DEFINED -#define SkTestScalerContext_DEFINED - -#include "SkFixed.h" -#include "SkPaint.h" -#include "SkPath.h" -#include "SkRefCnt.h" -#include "SkTDArray.h" -#include "SkTypeface.h" - -class SkTestFont; - -struct SkTestFontData { - const SkScalar* fPoints; - const unsigned char* fVerbs; - const unsigned* fCharCodes; - const size_t fCharCodesCount; - const SkFixed* fWidths; - const SkPaint::FontMetrics& fMetrics; - const char* fName; - SkFontStyle fStyle; - sk_sp fCachedFont; -}; - -class SkTestFont : public SkRefCnt { -public: - SkTestFont(const SkTestFontData& ); - virtual ~SkTestFont(); - int codeToIndex(SkUnichar charCode) const; - void init(const SkScalar* pts, const unsigned char* verbs); -private: - const unsigned* fCharCodes; - const size_t fCharCodesCount; - const SkFixed* fWidths; - const SkPaint::FontMetrics& fMetrics; - const char* fName; - SkPath** fPaths; - friend class SkTestTypeface; - typedef SkRefCnt INHERITED; -}; - - -class SkTestTypeface : public SkTypeface { -public: - SkTestTypeface(sk_sp, const SkFontStyle& style); - void getAdvance(SkGlyph* glyph); - void getFontMetrics(SkPaint::FontMetrics* metrics); - void getMetrics(SkGlyph* glyph); - void getPath(SkGlyphID glyph, SkPath* path); -protected: - SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&, - const SkDescriptor* desc) const override; - void onFilterRec(SkScalerContextRec* rec) const override; - std::unique_ptr onGetAdvancedMetrics() const override; - - SkStreamAsset* onOpenStream(int* ttcIndex) const override { - return nullptr; - } - - void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const override; - - int onCharsToGlyphs(const void* chars, Encoding encoding, - uint16_t glyphs[], int glyphCount) const override; - - int onCountGlyphs() const override { - return (int) fTestFont->fCharCodesCount; - } - - int onGetUPEM() const override { - return 2048; - } - - void onGetFamilyName(SkString* familyName) const override; - SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override; - - int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], - int coordinateCount) const override - { - return 0; - } - - int onGetTableTags(SkFontTableTag tags[]) const override { - return 0; - } - - size_t onGetTableData(SkFontTableTag tag, size_t offset, - size_t length, void* data) const override { - return 0; - } -private: - sk_sp fTestFont; - friend class SkTestScalerContext; -}; - -#endif diff --git a/tools/fonts/SkTestTypeface.cpp b/tools/fonts/SkTestTypeface.cpp new file mode 100644 index 0000000000..52751c9f59 --- /dev/null +++ b/tools/fonts/SkTestTypeface.cpp @@ -0,0 +1,274 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkAdvancedTypefaceMetrics.h" +#include "SkBitmap.h" +#include "SkCanvas.h" +#include "SkFontDescriptor.h" +#include "SkGlyph.h" +#include "SkImageInfo.h" +#include "SkMatrix.h" +#include "SkOTUtils.h" +#include "SkPaintPriv.h" +#include "SkPath.h" +#include "SkPoint.h" +#include "SkRect.h" +#include "SkScalerContext.h" +#include "SkString.h" +#include "SkTestTypeface.h" +#include "SkTDArray.h" +#include "SkUtils.h" + +#include + +class SkDescriptor; + +SkTestFont::SkTestFont(const SkTestFontData& fontData) + : INHERITED() + , fCharCodes(fontData.fCharCodes) + , fCharCodesCount(fontData.fCharCodes ? fontData.fCharCodesCount : 0) + , fWidths(fontData.fWidths) + , fMetrics(fontData.fMetrics) + , fName(fontData.fName) + , fPaths(nullptr) +{ + init(fontData.fPoints, fontData.fVerbs); +} + +SkTestFont::~SkTestFont() { + for (unsigned index = 0; index < fCharCodesCount; ++index) { + delete fPaths[index]; + } + delete[] fPaths; +} + +int SkTestFont::codeToIndex(SkUnichar charCode) const { + for (unsigned index = 0; index < fCharCodesCount; ++index) { + if (fCharCodes[index] == (unsigned) charCode) { + return (int) index; + } + } + return 0; +} + +void SkTestFont::init(const SkScalar* pts, const unsigned char* verbs) { + fPaths = new SkPath* [fCharCodesCount]; + for (unsigned index = 0; index < fCharCodesCount; ++index) { + SkPath* path = new SkPath; + SkPath::Verb verb; + while ((verb = (SkPath::Verb) *verbs++) != SkPath::kDone_Verb) { + switch (verb) { + case SkPath::kMove_Verb: + path->moveTo(pts[0], pts[1]); + pts += 2; + break; + case SkPath::kLine_Verb: + path->lineTo(pts[0], pts[1]); + pts += 2; + break; + case SkPath::kQuad_Verb: + path->quadTo(pts[0], pts[1], pts[2], pts[3]); + pts += 4; + break; + case SkPath::kCubic_Verb: + path->cubicTo(pts[0], pts[1], pts[2], pts[3], pts[4], pts[5]); + pts += 6; + break; + case SkPath::kClose_Verb: + path->close(); + break; + default: + SkDEBUGFAIL("bad verb"); + return; + } + } + // This should make SkPath::getBounds() queries threadsafe. + path->updateBoundsCache(); + fPaths[index] = path; + } +} + +SkTestTypeface::SkTestTypeface(sk_sp testFont, const SkFontStyle& style) + : SkTypeface(style, false) + , fTestFont(std::move(testFont)) { +} + +void SkTestTypeface::getAdvance(SkGlyph* glyph) { + // TODO(benjaminwagner): Update users to use floats. + glyph->fAdvanceX = SkFixedToFloat(fTestFont->fWidths[glyph->getGlyphID()]); + glyph->fAdvanceY = 0; +} + +void SkTestTypeface::getFontMetrics(SkPaint::FontMetrics* metrics) { + *metrics = fTestFont->fMetrics; +} + +void SkTestTypeface::getMetrics(SkGlyph* glyph) { + SkGlyphID glyphID = glyph->getGlyphID(); + glyphID = glyphID < fTestFont->fCharCodesCount ? glyphID : 0; + + // TODO(benjaminwagner): Update users to use floats. + glyph->fAdvanceX = SkFixedToFloat(fTestFont->fWidths[glyphID]); + glyph->fAdvanceY = 0; +} + +void SkTestTypeface::getPath(SkGlyphID glyphID, SkPath* path) { + glyphID = glyphID < fTestFont->fCharCodesCount ? glyphID : 0; + *path = *fTestFont->fPaths[glyphID]; +} + +void SkTestTypeface::onFilterRec(SkScalerContextRec* rec) const { + rec->setHinting(SkPaint::kNo_Hinting); +} + +std::unique_ptr SkTestTypeface::onGetAdvancedMetrics() const { // pdf only + std::unique_ptr info(new SkAdvancedTypefaceMetrics); + info->fFontName.set(fTestFont->fName); + int glyphCount = this->onCountGlyphs(); + + SkTDArray& toUnicode = info->fGlyphToUnicode; + toUnicode.setCount(glyphCount); + SkASSERT(glyphCount == SkToInt(fTestFont->fCharCodesCount)); + for (int gid = 0; gid < glyphCount; ++gid) { + toUnicode[gid] = SkToS32(fTestFont->fCharCodes[gid]); + } + return info; +} + +void SkTestTypeface::onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const { + desc->setFamilyName(fTestFont->fName); + desc->setStyle(this->fontStyle()); + *isLocal = false; +} + +int SkTestTypeface::onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const { + auto utf8 = (const char*)chars; + auto utf16 = (const uint16_t*)chars; + auto utf32 = (const SkUnichar*)chars; + + for (int i = 0; i < glyphCount; i++) { + SkUnichar ch; + switch (encoding) { + case kUTF8_Encoding: ch = SkUTF8_NextUnichar(&utf8 ); break; + case kUTF16_Encoding: ch = SkUTF16_NextUnichar(&utf16); break; + case kUTF32_Encoding: ch = *utf32++; break; + } + if (glyphs) { + glyphs[i] = fTestFont->codeToIndex(ch); + } + } + return glyphCount; +} + +void SkTestTypeface::onGetFamilyName(SkString* familyName) const { + *familyName = fTestFont->fName; +} + +SkTypeface::LocalizedStrings* SkTestTypeface::onCreateFamilyNameIterator() const { + SkString familyName(fTestFont->fName); + SkString language("und"); //undetermined + return new SkOTUtils::LocalizedStrings_SingleName(familyName, language); +} + +class SkTestScalerContext : public SkScalerContext { +public: + SkTestScalerContext(sk_sp face, const SkScalerContextEffects& effects, + const SkDescriptor* desc) + : SkScalerContext(std::move(face), effects, desc) + { + fRec.getSingleMatrix(&fMatrix); + this->forceGenerateImageFromPath(); + } + +protected: + SkTestTypeface* getTestTypeface() const { + return static_cast(this->getTypeface()); + } + + unsigned generateGlyphCount() override { + return this->getTestTypeface()->onCountGlyphs(); + } + + uint16_t generateCharToGlyph(SkUnichar uni) override { + uint16_t glyph; + (void) this->getTestTypeface()->onCharsToGlyphs((const void *) &uni, + SkTypeface::kUTF32_Encoding, &glyph, 1); + return glyph; + } + + void generateAdvance(SkGlyph* glyph) override { + this->getTestTypeface()->getAdvance(glyph); + + const SkVector advance = fMatrix.mapXY(SkFloatToScalar(glyph->fAdvanceX), + SkFloatToScalar(glyph->fAdvanceY)); + glyph->fAdvanceX = SkScalarToFloat(advance.fX); + glyph->fAdvanceY = SkScalarToFloat(advance.fY); + } + + void generateMetrics(SkGlyph* glyph) override { + this->getTestTypeface()->getMetrics(glyph); + + const SkVector advance = fMatrix.mapXY(SkFloatToScalar(glyph->fAdvanceX), + SkFloatToScalar(glyph->fAdvanceY)); + glyph->fAdvanceX = SkScalarToFloat(advance.fX); + glyph->fAdvanceY = SkScalarToFloat(advance.fY); + + SkPath path; + this->getTestTypeface()->getPath(glyph->getGlyphID(), &path); + path.transform(fMatrix); + + SkRect storage; + const SkPaint paint; + const SkRect& newBounds = paint.doComputeFastBounds(path.getBounds(), + &storage, + SkPaint::kFill_Style); + SkIRect ibounds; + newBounds.roundOut(&ibounds); + glyph->fLeft = ibounds.fLeft; + glyph->fTop = ibounds.fTop; + glyph->fWidth = ibounds.width(); + glyph->fHeight = ibounds.height(); + } + + void generateImage(const SkGlyph& glyph) override { + SkPath path; + this->getTestTypeface()->getPath(glyph.getGlyphID(), &path); + + SkBitmap bm; + bm.installPixels(SkImageInfo::MakeN32Premul(glyph.fWidth, glyph.fHeight), + glyph.fImage, glyph.rowBytes()); + bm.eraseColor(0); + + SkCanvas canvas(bm); + canvas.translate(-SkIntToScalar(glyph.fLeft), + -SkIntToScalar(glyph.fTop)); + canvas.concat(fMatrix); + SkPaint paint; + paint.setAntiAlias(true); + canvas.drawPath(path, paint); + } + + void generatePath(SkGlyphID glyph, SkPath* path) override { + this->getTestTypeface()->getPath(glyph, path); + path->transform(fMatrix); + } + + void generateFontMetrics(SkPaint::FontMetrics* metrics) override { + this->getTestTypeface()->getFontMetrics(metrics); + SkPaintPriv::ScaleFontMetrics(metrics, fMatrix.getScaleY()); + } + +private: + SkMatrix fMatrix; +}; + +SkScalerContext* SkTestTypeface::onCreateScalerContext( + const SkScalerContextEffects& effects, const SkDescriptor* desc) const +{ + return new SkTestScalerContext(sk_ref_sp(const_cast(this)), effects, desc); +} diff --git a/tools/fonts/SkTestTypeface.h b/tools/fonts/SkTestTypeface.h new file mode 100644 index 0000000000..b520fb6e5c --- /dev/null +++ b/tools/fonts/SkTestTypeface.h @@ -0,0 +1,116 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTestTypeface_DEFINED +#define SkTestTypeface_DEFINED + +#include "SkFixed.h" +#include "SkFontArguments.h" +#include "SkFontStyle.h" +#include "SkPaint.h" +#include "SkRefCnt.h" +#include "SkScalar.h" +#include "SkTypeface.h" +#include "SkTypes.h" + +#include + +class SkDescriptor; +class SkFontDescriptor; +class SkGlyph; +class SkPath; +class SkScalerContext; +class SkStreamAsset; +class SkString; +class SkTestFont; +struct SkAdvancedTypefaceMetrics; +struct SkScalerContextEffects; +struct SkScalerContextRec; + +struct SkTestFontData { + const SkScalar* fPoints; + const unsigned char* fVerbs; + const unsigned* fCharCodes; + const size_t fCharCodesCount; + const SkFixed* fWidths; + const SkPaint::FontMetrics& fMetrics; + const char* fName; + SkFontStyle fStyle; + sk_sp fCachedFont; +}; + +class SkTestFont : public SkRefCnt { +public: + SkTestFont(const SkTestFontData& ); + virtual ~SkTestFont(); + int codeToIndex(SkUnichar charCode) const; + void init(const SkScalar* pts, const unsigned char* verbs); +private: + const unsigned* fCharCodes; + const size_t fCharCodesCount; + const SkFixed* fWidths; + const SkPaint::FontMetrics& fMetrics; + const char* fName; + SkPath** fPaths; + friend class SkTestTypeface; + typedef SkRefCnt INHERITED; +}; + + +class SkTestTypeface : public SkTypeface { +public: + SkTestTypeface(sk_sp, const SkFontStyle& style); + void getAdvance(SkGlyph* glyph); + void getFontMetrics(SkPaint::FontMetrics* metrics); + void getMetrics(SkGlyph* glyph); + void getPath(SkGlyphID glyph, SkPath* path); +protected: + SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&, + const SkDescriptor* desc) const override; + void onFilterRec(SkScalerContextRec* rec) const override; + std::unique_ptr onGetAdvancedMetrics() const override; + + SkStreamAsset* onOpenStream(int* ttcIndex) const override { + return nullptr; + } + + void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const override; + + int onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const override; + + int onCountGlyphs() const override { + return (int) fTestFont->fCharCodesCount; + } + + int onGetUPEM() const override { + return 2048; + } + + void onGetFamilyName(SkString* familyName) const override; + SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override; + + int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[], + int coordinateCount) const override + { + return 0; + } + + int onGetTableTags(SkFontTableTag tags[]) const override { + return 0; + } + + size_t onGetTableData(SkFontTableTag tag, size_t offset, + size_t length, void* data) const override { + return 0; + } +private: + sk_sp fTestFont; + friend class SkTestScalerContext; +}; + +#endif diff --git a/tools/fonts/create_test_font_color.cpp b/tools/fonts/create_test_font_color.cpp new file mode 100644 index 0000000000..c3f2e08f3f --- /dev/null +++ b/tools/fonts/create_test_font_color.cpp @@ -0,0 +1,37 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// running create_test_font_color generates ./.ttx +// which are read by fonttools ttx to produce native fonts. + +#include "SkCommandLineFlags.h" +#include "SkRefCnt.h" +#include "SkStream.h" +#include "SkTestSVGTypeface.h" + +int main(int argc, char** argv) { + SkCommandLineFlags::Parse(argc, argv); + + sk_sp typeface = SkTestSVGTypeface::Default(); + + SkFILEWStream cbdt("cbdt.ttx"); + typeface->exportTtxCbdt(&cbdt); + cbdt.flush(); + cbdt.fsync(); + + SkFILEWStream sbix("sbix.ttx"); + typeface->exportTtxSbix(&sbix); + sbix.flush(); + sbix.fsync(); + + SkFILEWStream colr("colr.ttx"); + typeface->exportTtxColr(&colr); + colr.flush(); + colr.fsync(); + + return 0; +} diff --git a/tools/fonts/sk_tool_utils_font.cpp b/tools/fonts/sk_tool_utils_font.cpp index d2aac851bb..4d5b86aa0a 100644 --- a/tools/fonts/sk_tool_utils_font.cpp +++ b/tools/fonts/sk_tool_utils_font.cpp @@ -11,7 +11,7 @@ #include "SkFontStyle.h" #include "SkMutex.h" #include "SkOSFile.h" -#include "SkTestScalerContext.h" +#include "SkTestTypeface.h" #include "SkUtils.h" #include "sk_tool_utils.h" @@ -70,42 +70,23 @@ sk_sp create_font(const char* name, SkFontStyle style) { } sk_sp emoji_typeface() { + const char* filename; #if defined(SK_BUILD_FOR_WIN) - sk_sp fm(SkFontMgr::RefDefault()); - const char *colorEmojiFontName = "Segoe UI Emoji"; - sk_sp typeface(fm->matchFamilyStyle(colorEmojiFontName, SkFontStyle())); - if (typeface) { - return typeface; - } - sk_sp fallback(fm->matchFamilyStyleCharacter( - colorEmojiFontName, SkFontStyle(), nullptr /* bcp47 */, 0 /* bcp47Count */, - 0x1f4b0 /* character: πŸ’° */)); - if (fallback) { - return fallback; - } - // If we don't have Segoe UI Emoji and can't find a fallback, try Segoe UI Symbol. - // Windows 7 does not have Segoe UI Emoji; Segoe UI Symbol has the (non - color) emoji. - return SkTypeface::MakeFromName("Segoe UI Symbol", SkFontStyle()); - + filename = "fonts/colr.ttf"; #elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) - return SkTypeface::MakeFromName("Apple Color Emoji", SkFontStyle()); - + filename = "fonts/sbix.ttf"; #else - return MakeResourceAsTypeface("fonts/Funkster.ttf"); - + filename = "fonts/cbdt.ttf"; #endif + sk_sp typeface = MakeResourceAsTypeface(filename); + if (typeface) { + return typeface; + } + return SkTypeface::MakeFromName("Emoji", SkFontStyle()); } const char* emoji_sample_text() { -#if defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) - return "\xF0\x9F\x92\xB0" "\xF0\x9F\x8F\xA1" "\xF0\x9F\x8E\x85" // πŸ’°πŸ‘πŸŽ… - "\xF0\x9F\x8D\xAA" "\xF0\x9F\x8D\x95" "\xF0\x9F\x9A\x80" // πŸͺπŸ•πŸš€ - "\xF0\x9F\x9A\xBB" "\xF0\x9F\x92\xA9" "\xF0\x9F\x93\xB7" // πŸš»πŸ’©πŸ“· - "\xF0\x9F\x93\xA6" // πŸ“¦ - "\xF0\x9F\x87\xBA" "\xF0\x9F\x87\xB8" "\xF0\x9F\x87\xA6"; // πŸ‡ΊπŸ‡ΈπŸ‡¦ -#else - return "Hamburgefons"; -#endif + return "\xF0\x9F\x98\x80" " " "\xE2\x99\xA2"; // πŸ˜€ β™’ } static const char* platform_os_name() { diff --git a/tools/sk_tool_utils.cpp b/tools/sk_tool_utils.cpp index 2eb8dba833..99b03a4391 100644 --- a/tools/sk_tool_utils.cpp +++ b/tools/sk_tool_utils.cpp @@ -5,19 +5,29 @@ * found in the LICENSE file. */ -#include "sk_tool_utils.h" - -#include "Resources.h" #include "SkBitmap.h" +#include "SkBlendMode.h" #include "SkCanvas.h" +#include "SkColorData.h" +#include "SkColorPriv.h" +#include "SkFloatingPoint.h" #include "SkImage.h" -#include "SkPixelRef.h" +#include "SkMatrix.h" #include "SkPM4f.h" -#include "SkPoint3.h" +#include "SkPaint.h" +#include "SkPath.h" +#include "SkPixelRef.h" +#include "SkPixmap.h" +#include "SkPoint.h" +#include "SkRRect.h" #include "SkShader.h" #include "SkSurface.h" -#include "SkTestScalerContext.h" #include "SkTextBlob.h" +#include "sk_tool_utils.h" + +#include +#include +#include namespace sk_tool_utils { @@ -159,7 +169,7 @@ SkPath make_star(const SkRect& bounds, int numPts, int step) { #pragma optimize("", off) #endif void make_big_path(SkPath& path) { - #include "BigPathBench.inc" + #include "BigPathBench.inc" // IWYU pragma: keep } static float gaussian2d_value(int x, int y, float sigma) { diff --git a/tools/sk_tool_utils.h b/tools/sk_tool_utils.h index 6c1adf8e26..30d195d479 100644 --- a/tools/sk_tool_utils.h +++ b/tools/sk_tool_utils.h @@ -9,26 +9,33 @@ #define sk_tool_utils_DEFINED #include "SkColor.h" +#include "SkData.h" +#include "SkEncodedImageFormat.h" +#include "SkFontStyle.h" #include "SkImageEncoder.h" #include "SkImageInfo.h" #include "SkRandom.h" +#include "SkRect.h" #include "SkRefCnt.h" +#include "SkScalar.h" #include "SkStream.h" +#include "SkTArray.h" #include "SkTDArray.h" -#include "SkTypeface.h" +#include "SkTypes.h" class SkBitmap; class SkCanvas; -class SkColorFilter; +class SkFontStyle; class SkImage; class SkPaint; class SkPath; +class SkPixmap; class SkRRect; class SkShader; class SkSurface; class SkSurfaceProps; -class SkTestFont; class SkTextBlobBuilder; +class SkTypeface; namespace sk_tool_utils { -- cgit v1.2.3