mirror of
https://github.com/elasota/Aerofoil.git
synced 2025-09-23 06:53:43 +00:00
Add synthetic bold hack to better support Geneva CY
This commit is contained in:
@@ -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;
|
||||
};
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -6,5 +6,6 @@ namespace PortabilityLayer
|
||||
{
|
||||
FontHacks_None,
|
||||
FontHacks_Roboto,
|
||||
FontHacks_SyntheticBold,
|
||||
};
|
||||
}
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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<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()
|
||||
{
|
||||
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<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()
|
||||
{
|
||||
return FontRendererImpl::GetInstance();
|
||||
|
Reference in New Issue
Block a user