From 3d3f8398016e59d2e99a1058d94ddaebe3f79e60 Mon Sep 17 00:00:00 2001 From: elasota Date: Fri, 16 Apr 2021 00:25:30 -0400 Subject: [PATCH] Add synthetic bold hack to better support Geneva CY --- GpCommon/IGpFont.h | 3 +- .../GpFontHandler_FreeType2.cpp | 19 +- PortabilityLayer/FontHacks.h | 1 + PortabilityLayer/FontManager.cpp | 10 +- PortabilityLayer/FontRenderer.cpp | 201 +++++++++++++++++- 5 files changed, 221 insertions(+), 13 deletions(-) diff --git a/GpCommon/IGpFont.h b/GpCommon/IGpFont.h index a1f8157..50caa1f 100644 --- a/GpCommon/IGpFont.h +++ b/GpCommon/IGpFont.h @@ -8,6 +8,7 @@ struct GpRenderedFontMetrics; struct IGpFont { virtual void Destroy() = 0; - virtual IGpFontRenderedGlyph *Render(uint32_t unicodeCodePoint, unsigned int size, bool aa) = 0; + virtual IGpFontRenderedGlyph *Render(uint32_t unicodeCodePoint, unsigned int size, unsigned int xScale, unsigned int yScale, bool aa) = 0; virtual bool GetLineSpacing(unsigned int size, int32_t &outSpacing) = 0; + virtual bool SupportScaling() const = 0; }; diff --git a/GpFontHandler_FreeType2/GpFontHandler_FreeType2.cpp b/GpFontHandler_FreeType2/GpFontHandler_FreeType2.cpp index b08e608..f09412b 100644 --- a/GpFontHandler_FreeType2/GpFontHandler_FreeType2.cpp +++ b/GpFontHandler_FreeType2/GpFontHandler_FreeType2.cpp @@ -40,8 +40,9 @@ class GpFont_FreeType2 final : public IGpFont { public: void Destroy() override; - IGpFontRenderedGlyph *Render(uint32_t unicodeCodePoint, unsigned int size, bool aa) override; + IGpFontRenderedGlyph *Render(uint32_t unicodeCodePoint, unsigned int size, unsigned int xScale, unsigned int yScale, bool aa) override; bool GetLineSpacing(unsigned int size, int32_t &outSpacing) override; + bool SupportScaling() const override; static GpFont_FreeType2 *Create(const FT_StreamRec_ &streamRec, GpIOStream *stream); @@ -107,7 +108,7 @@ void GpFont_FreeType2::Destroy() free(this); } -IGpFontRenderedGlyph *GpFont_FreeType2::Render(uint32_t unicodeCodePoint, unsigned int size, bool aa) +IGpFontRenderedGlyph *GpFont_FreeType2::Render(uint32_t unicodeCodePoint, unsigned int size, unsigned int xScale, unsigned int yScale, bool aa) { if (m_currentSize != size) { @@ -117,6 +118,14 @@ IGpFontRenderedGlyph *GpFont_FreeType2::Render(uint32_t unicodeCodePoint, unsign m_currentSize = size; } + FT_Matrix transform; + transform.xx = xScale << 16; + transform.xy = 0; + transform.yx = 0; + transform.yy = yScale << 16; + + FT_Set_Transform(m_face, &transform, nullptr); + FT_UInt glyphIndex = FT_Get_Char_Index(m_face, unicodeCodePoint); if (!glyphIndex) return nullptr; @@ -247,6 +256,10 @@ bool GpFont_FreeType2::GetLineSpacing(unsigned int size, int32_t &outSpacing) return true; } +bool GpFont_FreeType2::SupportScaling() const +{ + return true; +} GpFont_FreeType2 *GpFont_FreeType2::Create(const FT_StreamRec_ &streamRec, GpIOStream *stream) { @@ -268,6 +281,8 @@ bool GpFont_FreeType2::FTLoad(const FT_Library &library, int typeFaceIndex) if (errorCode != 0) return false; + FT_Matrix transform; + return true; } diff --git a/PortabilityLayer/FontHacks.h b/PortabilityLayer/FontHacks.h index 55a7444..4ffb2c8 100644 --- a/PortabilityLayer/FontHacks.h +++ b/PortabilityLayer/FontHacks.h @@ -6,5 +6,6 @@ namespace PortabilityLayer { FontHacks_None, FontHacks_Roboto, + FontHacks_SyntheticBold, }; } diff --git a/PortabilityLayer/FontManager.cpp b/PortabilityLayer/FontManager.cpp index bcc06cb..8039d12 100644 --- a/PortabilityLayer/FontManager.cpp +++ b/PortabilityLayer/FontManager.cpp @@ -140,15 +140,7 @@ namespace PortabilityLayer *outVariationFlags = ms_fontPresets[preset].m_variationFlags; if (outAA) - { - bool aa = ms_fontPresets[preset].m_aa; - FontFamilyID_t fontFamily = ms_fontPresets[preset].m_familyID; - - if (m_hasPreinstalledFonts && (fontFamily == FontFamilyIDs::kApplication || fontFamily == FontFamilyIDs::kSystem)) - *outAA = false; - else - *outAA = aa; - } + *outAA = ms_fontPresets[preset].m_aa; if (outFamilyID) { diff --git a/PortabilityLayer/FontRenderer.cpp b/PortabilityLayer/FontRenderer.cpp index 5cfef15..8c08927 100644 --- a/PortabilityLayer/FontRenderer.cpp +++ b/PortabilityLayer/FontRenderer.cpp @@ -101,9 +101,31 @@ namespace PortabilityLayer static FontRendererImpl *GetInstance(); private: + static void SynthesizeBoldAA(IGpFontRenderedGlyph *&glyph, unsigned int xScale, unsigned int yScale, bool aa); + static FontRendererImpl ms_instance; }; + class ReRenderedGlyph final : public IGpFontRenderedGlyph + { + public: + const GpRenderedGlyphMetrics &GetMetrics() const override; + const void *GetData() const override; + void Destroy() override; + + void *GetMutableData(); + GpRenderedGlyphMetrics &GetMutableMetrics(); + + static ReRenderedGlyph *Create(unsigned int width, unsigned int height, bool aa); + + private: + explicit ReRenderedGlyph(void *data, unsigned int width, unsigned int height, size_t pitch, size_t dataSize); + + GpRenderedGlyphMetrics m_metrics; + void *m_data; + size_t m_dataSize; + }; + bool RenderedFontImpl::GetGlyph(unsigned int character, const GpRenderedGlyphMetrics *&outMetricsPtr, const void *&outData) const { const size_t dataOffset = m_dataOffsets[character]; @@ -347,13 +369,38 @@ namespace PortabilityLayer for (unsigned int i = 0; i < numCharacters; i++) glyphs[i] = nullptr; + unsigned int xScale = 1; + unsigned int yScale = 1; + bool syntheticBoldAA = false; + if (fontHacks == FontHacks_SyntheticBold) + { + if (aa) + { + syntheticBoldAA = true; + xScale = 16; + yScale = 16; + } + } + for (unsigned int i = 0; i < numCharacters; i++) { uint16_t unicodeCodePoint = MacRoman::ToUnicode(i); if (unicodeCodePoint == 0xffff) continue; - glyphs[i] = font->Render(unicodeCodePoint, size, aa); + glyphs[i] = font->Render(unicodeCodePoint, size, xScale, yScale, aa); + } + + if (fontHacks == FontHacks_SyntheticBold) + { + if (syntheticBoldAA) + { + for (unsigned int i = 0; i < numCharacters; i++) + { + if (glyphs[i]) + SynthesizeBoldAA(glyphs[i], xScale, yScale, syntheticBoldAA); + } + } } size_t glyphDataSize = GP_SYSTEM_MEMORY_ALIGNMENT; // So we can use 0 to mean no data @@ -465,6 +512,97 @@ namespace PortabilityLayer return true; } + void FontRendererImpl::SynthesizeBoldAA(IGpFontRenderedGlyph *&glyph, unsigned int xScale, unsigned int yScale, bool aa) + { + GpRenderedGlyphMetrics metrics = glyph->GetMetrics(); + const void *existingData = glyph->GetData(); + + uint32_t existingWidth = metrics.m_glyphWidth; + uint32_t existingHeight = metrics.m_glyphHeight; + + uint32_t newWidth = (existingWidth + xScale - 1) / xScale + 1; + uint32_t newHeight = (existingHeight + yScale - 1) / yScale; + + ReRenderedGlyph *newGlyph = ReRenderedGlyph::Create(newWidth, newHeight, aa); + if (!newGlyph) + return; + + uint16_t *fattenAccumulateBuffer = static_cast(MemoryManager::GetInstance()->Alloc(newWidth * sizeof(uint16_t))); + if (!fattenAccumulateBuffer) + { + newGlyph->Destroy(); + return; + } + + GpRenderedGlyphMetrics &newMetrics = newGlyph->GetMutableMetrics(); + newMetrics.m_advanceX = metrics.m_advanceX + 1; + newMetrics.m_bearingX = metrics.m_bearingX; + newMetrics.m_bearingY = metrics.m_bearingY; + + const size_t existingDataPitch = metrics.m_glyphDataPitch; + const size_t newPitch = newMetrics.m_glyphDataPitch; + uint8_t *newData = static_cast(newGlyph->GetMutableData()); + + for (unsigned int outY = 0; outY < newHeight; outY++) + { + memset(fattenAccumulateBuffer, 0, newWidth * sizeof(uint16_t)); + + for (unsigned int subY = 0; subY < yScale; subY++) + { + unsigned int originalY = subY + outY * yScale; + if (originalY >= existingHeight) + break; + + const uint8_t *existingRow = static_cast(existingData) + originalY * existingDataPitch; + + unsigned int streakCounter = 0; + unsigned int outX = 0; + unsigned int outSubX = 0; + for (unsigned int originalX = 0; originalX < newWidth * xScale; originalX++) + { + if (originalX < existingWidth) + { + //if (existingRow[originalX / 8] & (1 << (originalX & 7))) + uint8_t v = ((existingRow[originalX / 2] >> ((originalX & 1) * 4)) & 0xf); + if (v >= 8) + streakCounter = xScale + 1; + } + + if (streakCounter > 0) + { + streakCounter--; + fattenAccumulateBuffer[outX]++; + } + + outSubX++; + if (outSubX == xScale) + { + outSubX = 0; + outX++; + } + } + } + + uint8_t *outRow = newData + outY * newPitch; + unsigned int divisor = xScale * yScale; + for (unsigned int x = 0; x < newWidth; x++) + { + uint32_t fraction = static_cast(fattenAccumulateBuffer[x]) * 15; + fraction = (fraction * 2 + divisor) / (divisor * 2); + + uint8_t *outByte = outRow + (x / 2); + (*outByte) |= fraction << ((x & 1) * 4); + + size_t outOffset = outByte - newData; + } + } + + MemoryManager::GetInstance()->Release(fattenAccumulateBuffer); + + glyph->Destroy(); + glyph = newGlyph; + } + FontRendererImpl *FontRendererImpl::GetInstance() { return &ms_instance; @@ -472,6 +610,67 @@ namespace PortabilityLayer FontRendererImpl FontRendererImpl::ms_instance; + const GpRenderedGlyphMetrics &ReRenderedGlyph::GetMetrics() const + { + return m_metrics; + } + + const void *ReRenderedGlyph::GetData() const + { + return m_data; + } + + void ReRenderedGlyph::Destroy() + { + this->~ReRenderedGlyph(); + PortabilityLayer::MemoryManager::GetInstance()->Release(this); + } + + void *ReRenderedGlyph::GetMutableData() + { + return m_data; + } + + GpRenderedGlyphMetrics &ReRenderedGlyph::GetMutableMetrics() + { + return m_metrics; + } + + ReRenderedGlyph *ReRenderedGlyph::Create(unsigned int width, unsigned int height, bool aa) + { + size_t pitchRequired = 0; + if (aa) + pitchRequired = (width + 1) / 2; + else + pitchRequired = (width + 7) / 8; + + pitchRequired = pitchRequired + (GP_SYSTEM_MEMORY_ALIGNMENT - 1); + pitchRequired -= pitchRequired % GP_SYSTEM_MEMORY_ALIGNMENT; + + size_t baseRequired = sizeof(ReRenderedGlyph); + baseRequired = baseRequired + (GP_SYSTEM_MEMORY_ALIGNMENT - 1); + baseRequired -= baseRequired % GP_SYSTEM_MEMORY_ALIGNMENT; + + size_t totalRequired = baseRequired + pitchRequired * height; + void *storage = MemoryManager::GetInstance()->Alloc(totalRequired); + if (!storage) + return nullptr; + + uint8_t *data = static_cast(storage) + baseRequired; + memset(data, 0, pitchRequired * height); + + return new (storage) ReRenderedGlyph(data, width, height, pitchRequired, totalRequired - baseRequired); + } + + ReRenderedGlyph::ReRenderedGlyph(void *data, unsigned int width, unsigned int height, size_t pitch, size_t dataSize) + : m_data(data) + , m_dataSize(dataSize) + { + m_metrics.m_glyphWidth = width; + m_metrics.m_glyphHeight = height; + m_metrics.m_glyphDataPitch = pitch; + } + FontRenderer *FontRenderer::GetInstance() { return FontRendererImpl::GetInstance();