| /* | |
| * Copyright 2020 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "include/utils/SkCustomTypeface.h" | |
| #include "include/core/SkCanvas.h" | |
| #include "include/core/SkColor.h" | |
| #include "include/core/SkData.h" | |
| #include "include/core/SkDrawable.h" | |
| #include "include/core/SkFontArguments.h" | |
| #include "include/core/SkFontMetrics.h" | |
| #include "include/core/SkFontParameters.h" | |
| #include "include/core/SkFontStyle.h" | |
| #include "include/core/SkFontTypes.h" | |
| #include "include/core/SkMatrix.h" | |
| #include "include/core/SkPaint.h" | |
| #include "include/core/SkPath.h" | |
| #include "include/core/SkPathBuilder.h" | |
| #include "include/core/SkPoint.h" | |
| #include "include/core/SkRect.h" | |
| #include "include/core/SkRefCnt.h" | |
| #include "include/core/SkScalar.h" | |
| #include "include/core/SkSerialProcs.h" | |
| #include "include/core/SkSpan.h" | |
| #include "include/core/SkStream.h" | |
| #include "include/core/SkString.h" | |
| #include "include/core/SkTypeface.h" | |
| #include "include/core/SkTypes.h" | |
| #include "include/private/SkAlign.h" | |
| #include "include/private/SkFixed.h" | |
| #include "include/private/SkMalloc.h" | |
| #include "include/private/SkTo.h" | |
| #include "src/core/SkAdvancedTypefaceMetrics.h" // IWYU pragma: keep | |
| #include "src/core/SkFontDescriptor.h" | |
| #include "src/core/SkGlyph.h" | |
| #include "src/core/SkMask.h" | |
| #include "src/core/SkScalerContext.h" | |
| #include "src/core/SkStreamPriv.h" | |
| #include <algorithm> | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <memory> | |
| #include <optional> | |
| #include <utility> | |
| #include <vector> | |
| class SkArenaAlloc; | |
| class SkDescriptor; | |
| namespace { | |
| static inline const constexpr bool kSkShowTextBlitCoverage = false; | |
| } | |
| static SkFontMetrics scale_fontmetrics(const SkFontMetrics& src, float sx, float sy) { | |
| SkFontMetrics dst = src; | |
| #define SCALE_X(field) dst.field *= sx | |
| #define SCALE_Y(field) dst.field *= sy | |
| SCALE_X(fAvgCharWidth); | |
| SCALE_X(fMaxCharWidth); | |
| SCALE_X(fXMin); | |
| SCALE_X(fXMax); | |
| SCALE_Y(fTop); | |
| SCALE_Y(fAscent); | |
| SCALE_Y(fDescent); | |
| SCALE_Y(fBottom); | |
| SCALE_Y(fLeading); | |
| SCALE_Y(fXHeight); | |
| SCALE_Y(fCapHeight); | |
| SCALE_Y(fUnderlineThickness); | |
| SCALE_Y(fUnderlinePosition); | |
| SCALE_Y(fStrikeoutThickness); | |
| SCALE_Y(fStrikeoutPosition); | |
| #undef SCALE_X | |
| #undef SCALE_Y | |
| return dst; | |
| } | |
| class SkUserTypeface final : public SkTypeface { | |
| private: | |
| friend class SkCustomTypefaceBuilder; | |
| friend class SkUserScalerContext; | |
| explicit SkUserTypeface(SkFontStyle style, const SkFontMetrics& metrics, | |
| std::vector<SkCustomTypefaceBuilder::GlyphRec>&& recs) | |
| : SkTypeface(style) | |
| , fGlyphRecs(std::move(recs)) | |
| , fMetrics(metrics) | |
| {} | |
| const std::vector<SkCustomTypefaceBuilder::GlyphRec> fGlyphRecs; | |
| const SkFontMetrics fMetrics; | |
| std::unique_ptr<SkScalerContext> onCreateScalerContext(const SkScalerContextEffects&, | |
| const SkDescriptor* desc) const override; | |
| void onFilterRec(SkScalerContextRec* rec) const override; | |
| void getGlyphToUnicodeMap(SkSpan<SkUnichar>) const override; | |
| std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override; | |
| void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const override; | |
| void onCharsToGlyphs(SkSpan<const SkUnichar>, SkSpan<SkGlyphID>) const override; | |
| void onGetFamilyName(SkString* familyName) const override; | |
| bool onGetPostScriptName(SkString*) const override; | |
| SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override; | |
| std::unique_ptr<SkStreamAsset> onOpenStream(int*) const override; | |
| // trivial | |
| std::unique_ptr<SkStreamAsset> onOpenExistingStream(int*) const override { return nullptr; } | |
| sk_sp<SkTypeface> onMakeClone(const SkFontArguments& args) const override { | |
| return sk_ref_sp(this); | |
| } | |
| int onCountGlyphs() const override { return this->glyphCount(); } | |
| int onGetUPEM() const override { return 2048; /* ?? */ } | |
| bool onComputeBounds(SkRect* bounds) const override { | |
| bounds->setLTRB(fMetrics.fXMin, fMetrics.fTop, fMetrics.fXMax, fMetrics.fBottom); | |
| return true; | |
| } | |
| // noops | |
| void getPostScriptGlyphNames(SkString*) const override {} | |
| bool onGlyphMaskNeedsCurrentColor() const override { return false; } | |
| int onGetVariationDesignPosition( | |
| SkSpan<SkFontArguments::VariationPosition::Coordinate>) const override { return 0; } | |
| int onGetVariationDesignParameters(SkSpan<SkFontParameters::Variation::Axis>) const override { | |
| return 0; | |
| } | |
| int onGetTableTags(SkSpan<SkFontTableTag>) const override { return 0; } | |
| size_t onGetTableData(SkFontTableTag, size_t, size_t, void*) const override { return 0; } | |
| int glyphCount() const { | |
| return SkToInt(fGlyphRecs.size()); | |
| } | |
| }; | |
| SkCustomTypefaceBuilder::SkCustomTypefaceBuilder() { | |
| sk_bzero(&fMetrics, sizeof(fMetrics)); | |
| } | |
| void SkCustomTypefaceBuilder::setMetrics(const SkFontMetrics& fm, float scale) { | |
| fMetrics = scale_fontmetrics(fm, scale, scale); | |
| } | |
| void SkCustomTypefaceBuilder::setFontStyle(SkFontStyle style) { | |
| fStyle = style; | |
| } | |
| SkCustomTypefaceBuilder::GlyphRec& SkCustomTypefaceBuilder::ensureStorage(SkGlyphID index) { | |
| if (index >= fGlyphRecs.size()) { | |
| fGlyphRecs.resize(SkToSizeT(index) + 1); | |
| } | |
| return fGlyphRecs[index]; | |
| } | |
| void SkCustomTypefaceBuilder::setGlyph(SkGlyphID index, float advance, const SkPath& path) { | |
| auto& rec = this->ensureStorage(index); | |
| rec.fAdvance = advance; | |
| rec.fPath = path; | |
| rec.fDrawable = nullptr; | |
| } | |
| void SkCustomTypefaceBuilder::setGlyph(SkGlyphID index, float advance, | |
| sk_sp<SkDrawable> drawable, const SkRect& bounds) { | |
| auto& rec = this->ensureStorage(index); | |
| rec.fAdvance = advance; | |
| rec.fDrawable = std::move(drawable); | |
| rec.fBounds = bounds; | |
| rec.fPath = SkPath(); | |
| } | |
| sk_sp<SkTypeface> SkCustomTypefaceBuilder::detach() { | |
| if (fGlyphRecs.empty()) return nullptr; | |
| // initially inverted, so that any "union" will overwrite the first time | |
| SkRect bounds = {SK_ScalarMax, SK_ScalarMax, -SK_ScalarMax, -SK_ScalarMax}; | |
| for (const auto& rec : fGlyphRecs) { | |
| bounds.join(rec.isDrawable() | |
| ? rec.fBounds | |
| : rec.fPath.getBounds()); | |
| } | |
| fMetrics.fTop = bounds.top(); | |
| fMetrics.fBottom = bounds.bottom(); | |
| fMetrics.fXMin = bounds.left(); | |
| fMetrics.fXMax = bounds.right(); | |
| return sk_sp<SkUserTypeface>(new SkUserTypeface(fStyle, fMetrics, std::move(fGlyphRecs))); | |
| } | |
| ///////////// | |
| void SkUserTypeface::onFilterRec(SkScalerContextRec* rec) const { | |
| rec->useStrokeForFakeBold(); | |
| rec->setHinting(SkFontHinting::kNone); | |
| } | |
| void SkUserTypeface::getGlyphToUnicodeMap(SkSpan<SkUnichar> glyphToUnicode) const { | |
| const int n = std::min(this->glyphCount(), (int)glyphToUnicode.size()); | |
| for (int gid = 0; gid < n; ++gid) { | |
| glyphToUnicode[gid] = SkTo<SkUnichar>(gid); | |
| } | |
| } | |
| std::unique_ptr<SkAdvancedTypefaceMetrics> SkUserTypeface::onGetAdvancedMetrics() const { | |
| return nullptr; | |
| } | |
| void SkUserTypeface::onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const { | |
| desc->setFactoryId(SkCustomTypefaceBuilder::FactoryId); | |
| *isLocal = true; | |
| } | |
| void SkUserTypeface::onCharsToGlyphs(SkSpan<const SkUnichar> chars, | |
| SkSpan<SkGlyphID> glyphs) const { | |
| SkASSERT(chars.size() == glyphs.size()); | |
| const int glyphCount = this->glyphCount(); | |
| for (size_t i = 0; i < chars.size(); ++i) { | |
| glyphs[i] = chars[i] < glyphCount ? SkTo<SkGlyphID>(chars[i]) : 0; | |
| } | |
| } | |
| void SkUserTypeface::onGetFamilyName(SkString* familyName) const { | |
| *familyName = ""; | |
| } | |
| bool SkUserTypeface::onGetPostScriptName(SkString*) const { | |
| return false; | |
| } | |
| SkTypeface::LocalizedStrings* SkUserTypeface::onCreateFamilyNameIterator() const { | |
| return nullptr; | |
| } | |
| ////////////// | |
| class SkUserScalerContext : public SkScalerContext { | |
| public: | |
| SkUserScalerContext(SkUserTypeface& face, | |
| const SkScalerContextEffects& effects, | |
| const SkDescriptor* desc) | |
| : SkScalerContext(face, effects, desc) | |
| , fMatrix(fRec.getSingleMatrix()) | |
| {} | |
| const SkUserTypeface* userTF() const { | |
| return static_cast<SkUserTypeface*>(this->getTypeface()); | |
| } | |
| protected: | |
| GlyphMetrics generateMetrics(const SkGlyph& glyph, SkArenaAlloc*) override { | |
| GlyphMetrics mx(glyph.maskFormat()); | |
| const SkUserTypeface* tf = this->userTF(); | |
| const SkGlyphID gid = glyph.getGlyphID(); | |
| if (gid >= tf->fGlyphRecs.size()) { | |
| mx.neverRequestPath = true; | |
| return mx; | |
| } | |
| const auto& rec = tf->fGlyphRecs[gid]; | |
| mx.advance = fMatrix.mapPoint({rec.fAdvance, 0}); | |
| if (rec.isDrawable()) { | |
| mx.maskFormat = SkMask::kARGB32_Format; | |
| SkRect bounds = fMatrix.mapRect(rec.fBounds); | |
| bounds.offset(SkFixedToScalar(glyph.getSubXFixed()), | |
| SkFixedToScalar(glyph.getSubYFixed())); | |
| bounds.roundOut(&mx.bounds); | |
| // These do not have an outline path. | |
| mx.neverRequestPath = true; | |
| } else { | |
| mx.computeFromPath = true; | |
| } | |
| return mx; | |
| } | |
| void generateImage(const SkGlyph& glyph, void* imageBuffer) override { | |
| const auto& rec = this->userTF()->fGlyphRecs[glyph.getGlyphID()]; | |
| if (!rec.isDrawable()) { | |
| this->generateImageFromPath(glyph, imageBuffer); | |
| return; | |
| } | |
| auto canvas = SkCanvas::MakeRasterDirectN32(glyph.width(), glyph.height(), | |
| static_cast<SkPMColor*>(imageBuffer), | |
| glyph.rowBytes()); | |
| if constexpr (kSkShowTextBlitCoverage) { | |
| canvas->clear(0x33FF0000); | |
| } else { | |
| canvas->clear(SK_ColorTRANSPARENT); | |
| } | |
| canvas->translate(-glyph.left(), -glyph.top()); | |
| canvas->translate(SkFixedToScalar(glyph.getSubXFixed()), | |
| SkFixedToScalar(glyph.getSubYFixed())); | |
| canvas->drawDrawable(rec.fDrawable.get(), &fMatrix); | |
| } | |
| std::optional<SkScalerContext::GeneratedPath> generatePath(const SkGlyph& glyph) override { | |
| const auto& rec = this->userTF()->fGlyphRecs[glyph.getGlyphID()]; | |
| SkASSERT(!rec.isDrawable()); | |
| return {{rec.fPath.makeTransform(fMatrix), false}}; | |
| } | |
| sk_sp<SkDrawable> generateDrawable(const SkGlyph& glyph) override { | |
| class DrawableMatrixWrapper final : public SkDrawable { | |
| public: | |
| DrawableMatrixWrapper(sk_sp<SkDrawable> drawable, const SkMatrix& m) | |
| : fDrawable(std::move(drawable)) | |
| , fMatrix(m) | |
| {} | |
| SkRect onGetBounds() override { | |
| return fMatrix.mapRect(fDrawable->getBounds()); | |
| } | |
| size_t onApproximateBytesUsed() override { | |
| return fDrawable->approximateBytesUsed() + sizeof(DrawableMatrixWrapper); | |
| } | |
| void onDraw(SkCanvas* canvas) override { | |
| if constexpr (kSkShowTextBlitCoverage) { | |
| SkPaint paint; | |
| paint.setColor(0x3300FF00); | |
| paint.setStyle(SkPaint::kFill_Style); | |
| canvas->drawRect(this->onGetBounds(), paint); | |
| } | |
| canvas->drawDrawable(fDrawable.get(), &fMatrix); | |
| } | |
| private: | |
| const sk_sp<SkDrawable> fDrawable; | |
| const SkMatrix fMatrix; | |
| }; | |
| const auto& rec = this->userTF()->fGlyphRecs[glyph.getGlyphID()]; | |
| return rec.fDrawable | |
| ? sk_make_sp<DrawableMatrixWrapper>(rec.fDrawable, fMatrix) | |
| : nullptr; | |
| } | |
| void generateFontMetrics(SkFontMetrics* metrics) override { | |
| auto [sx, sy] = fMatrix.mapPoint({1, 1}); | |
| *metrics = scale_fontmetrics(this->userTF()->fMetrics, sx, sy); | |
| } | |
| private: | |
| const SkMatrix fMatrix; | |
| }; | |
| std::unique_ptr<SkScalerContext> SkUserTypeface::onCreateScalerContext( | |
| const SkScalerContextEffects& effects, const SkDescriptor* desc) const | |
| { | |
| return std::make_unique<SkUserScalerContext>(*const_cast<SkUserTypeface*>(this), effects, desc); | |
| } | |
| /////////////////////////////////////////////////////////////////////////////////////////////////// | |
| static constexpr int kMaxGlyphCount = 65536; | |
| static constexpr size_t kHeaderSize = 16; | |
| static const char gHeaderString[] = "SkUserTypeface01"; | |
| static_assert(sizeof(gHeaderString) == 1 + kHeaderSize, "need header to be 16 bytes"); | |
| enum GlyphType : uint32_t { kPath, kDrawable }; | |
| std::unique_ptr<SkStreamAsset> SkUserTypeface::onOpenStream(int* ttcIndex) const { | |
| SkDynamicMemoryWStream wstream; | |
| wstream.write(gHeaderString, kHeaderSize); | |
| wstream.write(&fMetrics, sizeof(fMetrics)); | |
| SkFontStyle style = this->fontStyle(); | |
| wstream.write(&style, sizeof(style)); | |
| wstream.write32(this->glyphCount()); | |
| for (const auto& rec : fGlyphRecs) { | |
| wstream.write32(GlyphType::kPath); | |
| wstream.writeScalar(rec.fAdvance); | |
| wstream.write(&rec.fBounds, sizeof(rec.fBounds)); | |
| sk_sp<SkData> data; | |
| if (rec.isDrawable()) { | |
| // We simplify drawables on serialization to make deserializing easier/safer. | |
| SkPath empty = SkPath::Rect(rec.fBounds); | |
| data = empty.serialize(); | |
| } else { | |
| data = rec.fPath.serialize(); | |
| } | |
| const size_t sz = data->size(); | |
| SkASSERT(SkIsAlign4(sz)); | |
| wstream.write(&sz, sizeof(sz)); | |
| wstream.write(data->data(), sz); | |
| } | |
| *ttcIndex = 0; | |
| return wstream.detachAsStream(); | |
| } | |
| class AutoRestorePosition { | |
| SkStream* fStream; | |
| size_t fPosition; | |
| public: | |
| AutoRestorePosition(SkStream* stream) : fStream(stream) { | |
| fPosition = stream->getPosition(); | |
| } | |
| ~AutoRestorePosition() { | |
| if (fStream) { | |
| fStream->seek(fPosition); | |
| } | |
| } | |
| // So we don't restore the position | |
| void markDone() { fStream = nullptr; } | |
| }; | |
| sk_sp<SkTypeface> SkCustomTypefaceBuilder::Deserialize(SkStream* stream) { | |
| AutoRestorePosition arp(stream); | |
| char header[kHeaderSize]; | |
| if (stream->read(header, kHeaderSize) != kHeaderSize || | |
| 0 != memcmp(header, gHeaderString, kHeaderSize)) | |
| { | |
| return nullptr; | |
| } | |
| SkFontMetrics metrics; | |
| if (stream->read(&metrics, sizeof(metrics)) != sizeof(metrics)) { | |
| return nullptr; | |
| } | |
| SkFontStyle style; | |
| if (stream->read(&style, sizeof(style)) != sizeof(style)) { | |
| return nullptr; | |
| } | |
| int glyphCount; | |
| if (!stream->readS32(&glyphCount) || glyphCount < 0 || glyphCount > kMaxGlyphCount) { | |
| return nullptr; | |
| } | |
| SkCustomTypefaceBuilder builder; | |
| builder.setMetrics(metrics); | |
| builder.setFontStyle(style); | |
| for (int i = 0; i < glyphCount; ++i) { | |
| uint32_t gtype; | |
| // We don't support deserializing GlyphType::kDrawable because drawables can be *anything* | |
| if (!stream->readU32(>ype) || (gtype != GlyphType::kPath)) { | |
| return nullptr; | |
| } | |
| float advance; | |
| if (!stream->readScalar(&advance)) { | |
| return nullptr; | |
| } | |
| SkRect bounds; | |
| if (stream->read(&bounds, sizeof(bounds)) != sizeof(bounds) || !bounds.isFinite()) { | |
| return nullptr; | |
| } | |
| // SkPath cannot be read from a stream, so we have to page them into ram | |
| size_t sz; | |
| if (stream->read(&sz, sizeof(sz)) != sizeof(sz)) { | |
| return nullptr; | |
| } | |
| // The amount of bytes in the stream must be at least as big as sz, otherwise | |
| // sz is invalid. | |
| if (SkStreamPriv::RemainingLengthIsBelow(stream, sz)) { | |
| return nullptr; | |
| } | |
| auto data = SkData::MakeUninitialized(sz); | |
| if (stream->read(data->writable_data(), sz) != sz) { | |
| return nullptr; | |
| } | |
| size_t bytesRead = 0; | |
| auto path = SkPath::ReadFromMemory(data->data(), data->size(), &bytesRead); | |
| if (path.has_value() && (bytesRead == data->size())) { | |
| builder.setGlyph(i, advance, *path); | |
| } else { | |
| return nullptr; | |
| } | |
| } | |
| arp.markDone(); | |
| return builder.detach(); | |
| } | |
| sk_sp<SkTypeface> SkCustomTypefaceBuilder::MakeFromStream(std::unique_ptr<SkStreamAsset> stream, | |
| const SkFontArguments&) { | |
| return Deserialize(stream.get()); | |
| } |