mirror of
https://github.com/elasota/Aerofoil.git
synced 2025-12-14 12:09:36 +00:00
Add synthetic bold hack to better support Geneva CY
This commit is contained in:
@@ -8,6 +8,7 @@ struct GpRenderedFontMetrics;
|
|||||||
struct IGpFont
|
struct IGpFont
|
||||||
{
|
{
|
||||||
virtual void Destroy() = 0;
|
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 GetLineSpacing(unsigned int size, int32_t &outSpacing) = 0;
|
||||||
|
virtual bool SupportScaling() const = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -40,8 +40,9 @@ class GpFont_FreeType2 final : public IGpFont
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void Destroy() override;
|
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 GetLineSpacing(unsigned int size, int32_t &outSpacing) override;
|
||||||
|
bool SupportScaling() const override;
|
||||||
|
|
||||||
static GpFont_FreeType2 *Create(const FT_StreamRec_ &streamRec, GpIOStream *stream);
|
static GpFont_FreeType2 *Create(const FT_StreamRec_ &streamRec, GpIOStream *stream);
|
||||||
|
|
||||||
@@ -107,7 +108,7 @@ void GpFont_FreeType2::Destroy()
|
|||||||
free(this);
|
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)
|
if (m_currentSize != size)
|
||||||
{
|
{
|
||||||
@@ -117,6 +118,14 @@ IGpFontRenderedGlyph *GpFont_FreeType2::Render(uint32_t unicodeCodePoint, unsign
|
|||||||
m_currentSize = size;
|
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);
|
FT_UInt glyphIndex = FT_Get_Char_Index(m_face, unicodeCodePoint);
|
||||||
if (!glyphIndex)
|
if (!glyphIndex)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -247,6 +256,10 @@ bool GpFont_FreeType2::GetLineSpacing(unsigned int size, int32_t &outSpacing)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GpFont_FreeType2::SupportScaling() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
GpFont_FreeType2 *GpFont_FreeType2::Create(const FT_StreamRec_ &streamRec, GpIOStream *stream)
|
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)
|
if (errorCode != 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
FT_Matrix transform;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,5 +6,6 @@ namespace PortabilityLayer
|
|||||||
{
|
{
|
||||||
FontHacks_None,
|
FontHacks_None,
|
||||||
FontHacks_Roboto,
|
FontHacks_Roboto,
|
||||||
|
FontHacks_SyntheticBold,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,15 +140,7 @@ namespace PortabilityLayer
|
|||||||
*outVariationFlags = ms_fontPresets[preset].m_variationFlags;
|
*outVariationFlags = ms_fontPresets[preset].m_variationFlags;
|
||||||
|
|
||||||
if (outAA)
|
if (outAA)
|
||||||
{
|
*outAA = ms_fontPresets[preset].m_aa;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outFamilyID)
|
if (outFamilyID)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -101,9 +101,31 @@ namespace PortabilityLayer
|
|||||||
static FontRendererImpl *GetInstance();
|
static FontRendererImpl *GetInstance();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static void SynthesizeBoldAA(IGpFontRenderedGlyph *&glyph, unsigned int xScale, unsigned int yScale, bool aa);
|
||||||
|
|
||||||
static FontRendererImpl ms_instance;
|
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
|
bool RenderedFontImpl::GetGlyph(unsigned int character, const GpRenderedGlyphMetrics *&outMetricsPtr, const void *&outData) const
|
||||||
{
|
{
|
||||||
const size_t dataOffset = m_dataOffsets[character];
|
const size_t dataOffset = m_dataOffsets[character];
|
||||||
@@ -347,13 +369,38 @@ namespace PortabilityLayer
|
|||||||
for (unsigned int i = 0; i < numCharacters; i++)
|
for (unsigned int i = 0; i < numCharacters; i++)
|
||||||
glyphs[i] = nullptr;
|
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++)
|
for (unsigned int i = 0; i < numCharacters; i++)
|
||||||
{
|
{
|
||||||
uint16_t unicodeCodePoint = MacRoman::ToUnicode(i);
|
uint16_t unicodeCodePoint = MacRoman::ToUnicode(i);
|
||||||
if (unicodeCodePoint == 0xffff)
|
if (unicodeCodePoint == 0xffff)
|
||||||
continue;
|
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
|
size_t glyphDataSize = GP_SYSTEM_MEMORY_ALIGNMENT; // So we can use 0 to mean no data
|
||||||
@@ -465,6 +512,97 @@ namespace PortabilityLayer
|
|||||||
return true;
|
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<uint16_t*>(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<uint8_t*>(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<const uint8_t*>(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<uint32_t>(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()
|
FontRendererImpl *FontRendererImpl::GetInstance()
|
||||||
{
|
{
|
||||||
return &ms_instance;
|
return &ms_instance;
|
||||||
@@ -472,6 +610,67 @@ namespace PortabilityLayer
|
|||||||
|
|
||||||
FontRendererImpl FontRendererImpl::ms_instance;
|
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<uint8_t*>(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()
|
FontRenderer *FontRenderer::GetInstance()
|
||||||
{
|
{
|
||||||
return FontRendererImpl::GetInstance();
|
return FontRendererImpl::GetInstance();
|
||||||
|
|||||||
Reference in New Issue
Block a user