| /* |
| * 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/SkPoint.h" |
| #include "include/core/SkRect.h" |
| #include "include/core/SkRefCnt.h" |
| #include "include/core/SkScalar.h" |
| #include "include/core/SkStream.h" |
| #include "include/core/SkString.h" |
| #include "include/core/SkTypeface.h" |
| #include "include/core/SkTypes.h" |
| #include "include/private/base/SkAlign.h" |
| #include "include/private/base/SkFixed.h" |
| #include "include/private/base/SkMalloc.h" |
| #include "include/private/base/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 <cstdint> |
| #include <cstring> |
| #include <memory> |
| #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(SkUnichar* glyphToUnicode) const override; |
| std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override; |
| |
| void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const override; |
| |
| void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) 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(SkFontArguments::VariationPosition::Coordinate[], |
| int) const override { return 0; } |
| int onGetVariationDesignParameters(SkFontParameters::Variation::Axis[], |
| int) const override { return 0; } |
| int onGetTableTags(SkFontTableTag tags[]) 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.reset(); |
| } |
| |
| 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->setHinting(SkFontHinting::kNone); |
| } |
| |
| void SkUserTypeface::getGlyphToUnicodeMap(SkUnichar* glyphToUnicode) const { |
| for (int gid = 0; gid < this->glyphCount(); ++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(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const { |
| for (int i = 0; i < count; ++i) { |
| glyphs[i] = chars[i] < this->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(sk_sp<SkUserTypeface> face, |
| const SkScalerContextEffects& effects, |
| const SkDescriptor* desc) |
| : SkScalerContext(std::move(face), effects, desc) { |
| fRec.getSingleMatrix(&fMatrix); |
| this->forceGenerateImageFromPath(); |
| } |
| |
| const SkUserTypeface* userTF() const { |
| return static_cast<SkUserTypeface*>(this->getTypeface()); |
| } |
| |
| protected: |
| bool generateAdvance(SkGlyph* glyph) override { |
| const SkUserTypeface* tf = this->userTF(); |
| auto advance = fMatrix.mapXY(tf->fGlyphRecs[glyph->getGlyphID()].fAdvance, 0); |
| |
| glyph->fAdvanceX = advance.fX; |
| glyph->fAdvanceY = advance.fY; |
| return true; |
| } |
| |
| void generateMetrics(SkGlyph* glyph, SkArenaAlloc* alloc) override { |
| glyph->zeroMetrics(); |
| this->generateAdvance(glyph); |
| |
| const auto& rec = this->userTF()->fGlyphRecs[glyph->getGlyphID()]; |
| if (rec.isDrawable()) { |
| glyph->fMaskFormat = SkMask::kARGB32_Format; |
| |
| SkRect bounds = fMatrix.mapRect(rec.fBounds); |
| bounds.offset(SkFixedToScalar(glyph->getSubXFixed()), |
| SkFixedToScalar(glyph->getSubYFixed())); |
| |
| SkIRect ibounds; |
| bounds.roundOut(&ibounds); |
| glyph->fLeft = ibounds.fLeft; |
| glyph->fTop = ibounds.fTop; |
| glyph->fWidth = ibounds.width(); |
| glyph->fHeight = ibounds.height(); |
| |
| // These do not have an outline path. |
| glyph->setPath(alloc, nullptr, false); |
| } |
| } |
| |
| void generateImage(const SkGlyph& glyph) override { |
| const auto& rec = this->userTF()->fGlyphRecs[glyph.getGlyphID()]; |
| SkASSERTF(rec.isDrawable(), "Only drawable-backed glyphs should reach generateImage."); |
| |
| auto canvas = SkCanvas::MakeRasterDirectN32(glyph.fWidth, glyph.fHeight, |
| static_cast<SkPMColor*>(glyph.fImage), |
| glyph.rowBytes()); |
| if constexpr (kSkShowTextBlitCoverage) { |
| canvas->clear(0x33FF0000); |
| } else { |
| canvas->clear(SK_ColorTRANSPARENT); |
| } |
| |
| canvas->translate(-glyph.fLeft, -glyph.fTop); |
| canvas->translate(SkFixedToScalar(glyph.getSubXFixed()), |
| SkFixedToScalar(glyph.getSubYFixed())); |
| canvas->drawDrawable(rec.fDrawable.get(), &fMatrix); |
| } |
| |
| bool generatePath(const SkGlyph& glyph, SkPath* path) override { |
| const auto& rec = this->userTF()->fGlyphRecs[glyph.getGlyphID()]; |
| |
| SkASSERT(!rec.isDrawable()); |
| |
| rec.fPath.transform(fMatrix, path); |
| |
| return true; |
| } |
| |
| 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.mapXY(1, 1); |
| *metrics = scale_fontmetrics(this->userTF()->fMetrics, sx, sy); |
| } |
| |
| private: |
| SkMatrix fMatrix; |
| }; |
| |
| std::unique_ptr<SkScalerContext> SkUserTypeface::onCreateScalerContext( |
| const SkScalerContextEffects& effects, const SkDescriptor* desc) const |
| { |
| return std::make_unique<SkUserScalerContext>( |
| sk_ref_sp(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(rec.isDrawable() ? GlyphType::kDrawable : GlyphType::kPath); |
| |
| wstream.writeScalar(rec.fAdvance); |
| |
| wstream.write(&rec.fBounds, sizeof(rec.fBounds)); |
| |
| auto data = rec.isDrawable() |
| ? rec.fDrawable->serialize() |
| : 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; |
| if (!stream->readU32(>ype) || |
| (gtype != GlyphType::kDrawable && 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 and SkDrawable cannot 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 (StreamRemainingLengthIsBelow(stream, sz)) { |
| return nullptr; |
| } |
| |
| auto data = SkData::MakeUninitialized(sz); |
| if (stream->read(data->writable_data(), sz) != sz) { |
| return nullptr; |
| } |
| |
| switch (gtype) { |
| case GlyphType::kDrawable: { |
| auto drawable = SkDrawable::Deserialize(data->data(), data->size()); |
| if (!drawable) { |
| return nullptr; |
| } |
| builder.setGlyph(i, advance, std::move(drawable), bounds); |
| } break; |
| case GlyphType::kPath: { |
| SkPath path; |
| if (path.readFromMemory(data->data(), data->size()) != data->size()) { |
| return nullptr; |
| } |
| |
| builder.setGlyph(i, advance, path); |
| } break; |
| default: |
| return nullptr; |
| } |
| } |
| |
| arp.markDone(); |
| return builder.detach(); |
| } |
| |
| sk_sp<SkTypeface> SkCustomTypefaceBuilder::MakeFromStream(std::unique_ptr<SkStreamAsset> stream, |
| const SkFontArguments&) { |
| return Deserialize(stream.get()); |
| } |