| /* |
| * 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/SkData.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/SkPath.h" |
| #include "include/core/SkPathTypes.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/SkFloatingPoint.h" |
| #include "include/private/SkMalloc.h" |
| #include "include/private/SkTo.h" |
| #include "src/core/SkAdvancedTypefaceMetrics.h" // IWYU pragma: keep |
| #include "src/core/SkAutoMalloc.h" |
| #include "src/core/SkGlyph.h" |
| #include "src/core/SkPathPriv.h" |
| #include "src/core/SkScalerContext.h" |
| |
| #include <string.h> |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| class SkArenaAlloc; |
| class SkDescriptor; |
| class SkFontDescriptor; |
| |
| 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) : SkTypeface(style) {} |
| |
| std::vector<SkPath> fPaths; |
| std::vector<float> fAdvances; |
| 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 |
| |
| 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 { |
| SkASSERT(fPaths.size() == fAdvances.size()); |
| return SkToInt(fPaths.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; |
| } |
| |
| void SkCustomTypefaceBuilder::setGlyph(SkGlyphID index, float advance, const SkPath& path) { |
| SkASSERT(fPaths.size() == fAdvances.size()); |
| if (index >= fPaths.size()) { |
| fPaths.resize(SkToSizeT(index) + 1); |
| fAdvances.resize(SkToSizeT(index) + 1); |
| } |
| fAdvances[index] = advance; |
| fPaths[index] = path; |
| } |
| |
| sk_sp<SkTypeface> SkCustomTypefaceBuilder::detach() { |
| SkASSERT(fPaths.size() == fAdvances.size()); |
| if (fPaths.empty()) return nullptr; |
| |
| sk_sp<SkUserTypeface> tf(new SkUserTypeface(fStyle)); |
| tf->fAdvances = std::move(fAdvances); |
| tf->fPaths = std::move(fPaths); |
| tf->fMetrics = fMetrics; |
| |
| // initially inverted, so that any "union" will overwrite the first time |
| SkRect bounds = {SK_ScalarMax, SK_ScalarMax, -SK_ScalarMax, -SK_ScalarMax}; |
| |
| for (const auto& path : tf->fPaths) { |
| if (!path.isEmpty()) { |
| bounds.join(path.getBounds()); |
| } |
| } |
| tf->fMetrics.fTop = bounds.top(); |
| tf->fMetrics.fBottom = bounds.bottom(); |
| tf->fMetrics.fXMin = bounds.left(); |
| tf->fMetrics.fXMax = bounds.right(); |
| |
| return std::move(tf); |
| } |
| |
| ///////////// |
| |
| 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 { |
| *isLocal = true; |
| } |
| |
| void SkUserTypeface::onCharsToGlyphs(const SkUnichar uni[], int count, SkGlyphID glyphs[]) const { |
| for (int i = 0; i < count; ++i) { |
| glyphs[i] = uni[i] < this->glyphCount() ? SkTo<SkGlyphID>(uni[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->fAdvances[glyph->getGlyphID()], 0); |
| |
| glyph->fAdvanceX = advance.fX; |
| glyph->fAdvanceY = advance.fY; |
| return true; |
| } |
| |
| void generateMetrics(SkGlyph* glyph, SkArenaAlloc*) override { |
| glyph->zeroMetrics(); |
| this->generateAdvance(glyph); |
| // Always generates from paths, so SkScalerContext::makeGlyph will figure the bounds. |
| } |
| |
| void generateImage(const SkGlyph&) override { SK_ABORT("Should have generated from path."); } |
| |
| bool generatePath(const SkGlyph& glyph, SkPath* path) override { |
| this->userTF()->fPaths[glyph.getGlyphID()].transform(fMatrix, path); |
| return true; |
| } |
| |
| 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 void write_scaled_float_to_16(SkWStream* stream, float x, float scale) { |
| stream->write16(SkToS16(sk_float_round2int(x * scale)) & 0xFFFF); |
| } |
| |
| enum PVerb { |
| kMove, |
| kLine, |
| kCurve, |
| kClose, |
| }; |
| |
| static void compress_write(SkWStream* stream, const SkPath& path, int upem) { |
| int pCount = 0; |
| std::vector<PVerb> verbs; |
| for (auto [v, p, w] : SkPathPriv::Iterate(path)) { |
| switch (v) { |
| default: break; |
| case SkPathVerb::kMove: verbs.push_back(kMove); pCount += 1; break; |
| case SkPathVerb::kQuad: verbs.push_back(kCurve); pCount += 2; break; |
| case SkPathVerb::kLine: verbs.push_back(kLine); pCount += 1; break; |
| case SkPathVerb::kClose: verbs.push_back(kClose); break; |
| } |
| } |
| |
| int vCount = verbs.size(); |
| |
| stream->write16(upem); // share w/ other paths? |
| stream->write16(vCount); |
| stream->write16(pCount); |
| for (int i = 0; i < (vCount & ~3); i += 4) { |
| stream->write8((verbs[i+0]<<6) | (verbs[i+1]<<4) | (verbs[i+2]<<2) | verbs[i+3]); |
| } |
| if (vCount & 3) { |
| uint8_t b = 0; |
| int shift = 6; |
| for (int i = vCount & ~3; i < vCount; ++i) { |
| b |= verbs[i] << shift; |
| shift >>= 2; |
| } |
| stream->write8(b); |
| } |
| if (vCount & 1) { |
| stream->write8(0); |
| } |
| |
| const float scale = (float)upem; |
| auto write_pts = [&](const SkPoint pts[], int count) { |
| for (int i = 0; i < count; ++i) { |
| write_scaled_float_to_16(stream, pts[i].fX, scale); |
| write_scaled_float_to_16(stream, pts[i].fY, scale); |
| } |
| }; |
| |
| for (auto [v, p, w] : SkPathPriv::Iterate(path)) { |
| switch (v) { |
| default: break; |
| case SkPathVerb::kMove: write_pts(&p[0], 1); break; |
| case SkPathVerb::kQuad: write_pts(&p[1], 2); break; |
| case SkPathVerb::kLine: write_pts(&p[1], 1); break; |
| case SkPathVerb::kClose: break; |
| } |
| } |
| } |
| |
| 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"); |
| |
| 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)); |
| |
| // just hacking around -- this makes the serialized font 1/2 size |
| const bool use_compression = false; |
| |
| wstream.write32(this->glyphCount()); |
| |
| if (use_compression) { |
| for (float a : fAdvances) { |
| write_scaled_float_to_16(&wstream, a, 2048); |
| } |
| } else { |
| wstream.write(fAdvances.data(), this->glyphCount() * sizeof(float)); |
| } |
| |
| for (const auto& p : fPaths) { |
| if (use_compression) { |
| compress_write(&wstream, p, 2048); |
| } else { |
| auto data = p.serialize(); |
| SkASSERT(SkIsAlign4(data->size())); |
| wstream.write(data->data(), data->size()); |
| } |
| } |
| // SkDebugf("%d glyphs, %d bytes\n", fGlyphCount, wstream.bytesWritten()); |
| *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); |
| |
| std::vector<float> advances(glyphCount); |
| if (stream->read(advances.data(), glyphCount * sizeof(float)) != glyphCount * sizeof(float)) { |
| return nullptr; |
| } |
| |
| // SkPath can read from a stream, so we have to page the rest into ram |
| const size_t offset = stream->getPosition(); |
| const size_t length = stream->getLength() - offset; |
| SkAutoMalloc ram(length); |
| char* buffer = (char*)ram.get(); |
| |
| if (stream->read(buffer, length) != length) { |
| return nullptr; |
| } |
| |
| size_t totalUsed = 0; |
| for (int i = 0; i < glyphCount; ++i) { |
| SkPath path; |
| size_t used = path.readFromMemory(buffer + totalUsed, length - totalUsed); |
| if (used == 0) { |
| return nullptr; |
| } |
| builder.setGlyph(i, advances[i], path); |
| totalUsed += used; |
| SkASSERT(length >= totalUsed); |
| } |
| |
| // all done, update the stream to only reflect the bytes we needed |
| stream->seek(offset + totalUsed); |
| |
| arp.markDone(); |
| return builder.detach(); |
| } |