Files
GTASource/rage/scaleform/Src/GFxPlayer/GFxFontCompactor.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

635 lines
18 KiB
C++

/**********************************************************************
Filename : GFxFontCompactor.cpp
Content :
Created : 2007
Authors : Maxim Shemanarev
Copyright : (c) 2001-2007 Scaleform Corp. All Rights Reserved.
Notes : Compact font data storage
Licensees may use this file in accordance with the valid Scaleform
Commercial License Agreement provided with the software.
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR ANY PURPOSE.
For information regarding Commercial License Agreements go to:
online - http://www.scaleform.com/licensing.html or
email - sales@scaleform.com
**********************************************************************/
#include "GMath2D.h"
#include "GAlgorithm.h"
#include "GFxFontCompactor.h"
#ifndef GFC_NO_FONTCOMPACTOR_SUPPORT
//------------------------------------------------------------------------
const SInt GFxFontCompactor_CollinearCurveEpsilon = 5;
//------------------------------------------------------------------------
GFxFontCompactor::GFxFontCompactor(ContainerType& data) :
Encoder(data),
Decoder(data)
{
}
//------------------------------------------------------------------------
GFxFontCompactor::~GFxFontCompactor()
{
}
//------------------------------------------------------------------------
void GFxFontCompactor::Clear()
{
Encoder.Clear();
TmpVertices.Clear();
TmpContours.Clear();
GlyphCodes.Clear();
GlyphInfoTable.Clear();
KerningTable.Clear();
}
//------------------------------------------------------------------------
void GFxFontCompactor::normalizeLastContour()
{
ContourType& c = TmpContours.Back();
VertexType v2 = TmpVertices.Back();
VertexType v1;
if ((v2.x & 1) == 0)
{
v1 = TmpVertices[c.DataStart];
if (v1.x == v2.x && v1.y == v2.y)
{
c.DataSize--;
TmpVertices.PopBack();
}
}
if (c.DataSize < 3)
{
TmpVertices.CutAt(c.DataStart);
TmpContours.PopBack();
return;
}
UInt start = 0;
SInt minX = TmpVertices[c.DataStart].x >> 1;
SInt minY = TmpVertices[c.DataStart].y;
UInt i;
for (i = 1; i < c.DataSize; i++)
{
v1 = TmpVertices[c.DataStart + i];
if (v1.x & 1)
{
v1 = TmpVertices[++i + c.DataStart];
}
else
{
if (v1.y < minY)
{
minY = v1.y;
start = i;
}
else
if (v1.y == minY && (v1.x >> 1) < minX)
{
minX = v1.x;
start = i;
}
}
}
if (start == 0)
return;
TmpContour.Clear();
v1 = TmpVertices[c.DataStart + start];
v1.x &= ~1;
TmpContour.PushBack(v1);
for (i = 1; i < c.DataSize; i++)
{
++start;
v1 = TmpVertices[c.DataStart + start % c.DataSize];
if (v1.x & 1)
{
++i;
++start;
v2 = TmpVertices[c.DataStart + start % c.DataSize];
TmpContour.PushBack(v1);
TmpContour.PushBack(v2);
}
else
{
if ((v1.x >> 1) != (TmpContour.Back().x >> 1) ||
v1.y != TmpContour.Back().y)
{
TmpContour.PushBack(v1);
}
}
}
TmpVertices.CutAt(c.DataStart);
for (i = 0; i < TmpContour.GetSize(); i++)
TmpVertices.PushBack(TmpContour[i]);
c.DataSize = (UInt)TmpContour.GetSize();
}
//------------------------------------------------------------------------
inline void GFxFontCompactor::extendBounds(SInt* x1, SInt* y1, SInt* x2, SInt* y2,
SInt x, SInt y)
{
if (x < *x1) *x1 = x;
if (y < *y1) *y1 = y;
if (x > *x2) *x2 = x;
if (y > *y2) *y2 = y;
}
//------------------------------------------------------------------------
void GFxFontCompactor::computeBounds(SInt* x1, SInt* y1, SInt* x2, SInt* y2) const
{
*x1 = 16383;
*y1 = 16383;
*x2 = -16383;
*y2 = -16383;
UInt i, j;
for (i = 0; i < TmpContours.GetSize(); ++i)
{
const ContourType& c = TmpContours[i];
VertexType v1 = TmpVertices[c.DataStart];
v1.x >>= 1;
extendBounds(x1, y1, x2, y2, v1.x, v1.y);
for (j = 1; j < c.DataSize; ++j)
{
const VertexType& v2 = TmpVertices[c.DataStart + j];
if(v2.x & 1)
{
const VertexType& v3 = TmpVertices[++j + c.DataStart];
Float t, x, y;
t = GMath2D::CalcQuadCurveExtremum(Float(v1.x), Float(v2.x >> 1), Float(v3.x >> 1));
if (t > 0 && t < 1)
{
GMath2D::CalcPointOnQuadCurve(Float(v1.x), Float(v1.y),
Float(v2.x >> 1), Float(v2.y),
Float(v3.x >> 1), Float(v3.y),
t, &x, &y);
extendBounds(x1, y1, x2, y2, SInt(floorf(x) + 0.5f), SInt(floorf(y) + 0.5f));
}
t = GMath2D::CalcQuadCurveExtremum(Float(v1.y), Float(v2.y), Float(v3.y));
if (t > 0 && t < 1)
{
GMath2D::CalcPointOnQuadCurve(Float(v1.x), Float(v1.y),
Float(v2.x >> 1), Float(v2.y),
Float(v3.x >> 1), Float(v3.y),
t, &x, &y);
extendBounds(x1, y1, x2, y2, SInt(floorf(x) + 0.5f), SInt(floorf(y) + 0.5f));
}
v1 = v3;
}
else
v1 = v2;
v1.x >>= 1;
extendBounds(x1, y1, x2, y2, v1.x, v1.y);
}
}
}
//------------------------------------------------------------------------
void GFxFontCompactor::StartFont(const char* name, UInt flags, UInt nominalSize,
SInt ascent, SInt descent, SInt leading)
{
while(*name)
Encoder.WriteChar(*name++);
Encoder.WriteChar(0);
FontMetricsPos = (UInt)Encoder.GetSize();
Encoder.WriteUInt16fixlen(flags);
Encoder.WriteUInt16fixlen(nominalSize);
Encoder.WriteSInt16fixlen(ascent);
Encoder.WriteSInt16fixlen(descent);
Encoder.WriteSInt16fixlen(leading);
FontNumGlyphs = 0;
FontTotalGlyphBytes = 0;
FontStartGlyphs = (UInt)Encoder.GetSize();
Encoder.WriteUInt32fixlen(0); // NumGlyphs
Encoder.WriteUInt32fixlen(0); // TotalGlyphBytes
GlyphCodes.Clear();
GlyphInfoTable.Clear();
KerningTable.Clear();
}
//------------------------------------------------------------------------
void GFxFontCompactor::StartGlyph()
{
TmpVertices.Clear();
TmpContours.Clear();
}
//------------------------------------------------------------------------
void GFxFontCompactor::MoveTo(SInt16 x, SInt16 y)
{
if (TmpContours.GetSize())
normalizeLastContour();
ContourType c;
c.DataStart = (UInt)TmpVertices.GetSize();
c.DataSize = 1;
TmpContours.PushBack(c);
VertexType v;
v.x = x << 1;
v.y = y;
TmpVertices.PushBack(v);
}
//------------------------------------------------------------------------
void GFxFontCompactor::LineTo(SInt16 x, SInt16 y)
{
ContourType& c = TmpContours.Back();
VertexType v;
if (c.DataSize)
{
v = TmpVertices.Back();
if (x == (v.x >> 1) && y == v.y)
{
return;
}
}
v.x = x << 1;
v.y = y;
TmpVertices.PushBack(v);
TmpContours.Back().DataSize++;
}
//------------------------------------------------------------------------
void GFxFontCompactor::QuadTo(SInt16 cx, SInt16 cy, SInt16 ax, SInt16 ay)
{
ContourType& c = TmpContours.Back();
VertexType v;
if (c.DataSize)
{
v = TmpVertices.Back();
v.x >>= 1;
SInt cp = SInt(cx - ax) * SInt(ay - v.y) - SInt(cy - ay) * SInt(ax - v.x);
if (cp < 0) cp = -cp;
if (cp <= GFxFontCompactor_CollinearCurveEpsilon)
{
LineTo(ax, ay);
return;
}
}
v.x = (cx << 1) | 1;
v.y = cy;
TmpVertices.PushBack(v);
v.x = (ax << 1) | 1;
v.y = ay;
TmpVertices.PushBack(v);
TmpContours.Back().DataSize += 2;
}
//------------------------------------------------------------------------
UInt GFxFontCompactor::navigateToEndGlyph(UInt pos) const
{
SInt t1;
UInt numContours;
pos += Decoder.ReadSInt15(pos, &t1);
pos += Decoder.ReadSInt15(pos, &t1);
pos += Decoder.ReadSInt15(pos, &t1);
pos += Decoder.ReadSInt15(pos, &t1);
pos += Decoder.ReadUInt15(pos, &numContours);
while (numContours--)
{
UInt numEdges;
pos += Decoder.ReadSInt15(pos, &t1);
pos += Decoder.ReadSInt15(pos, &t1);
pos += Decoder.ReadUInt30(pos, &numEdges);
if ((numEdges & 1) == 0)
{
numEdges >>= 1;
while(numEdges--)
{
UInt8 edge[10];
pos += Decoder.ReadRawEdge(pos, edge);
}
}
}
return pos;
}
//------------------------------------------------------------------------
UInt32 GFxFontCompactor::ComputeGlyphHash(UInt pos) const
{
UInt32 h = 0;
UInt end = navigateToEndGlyph(pos);
for (; pos < end; ++pos)
h = ((h << 5) + h) ^ UInt8(Decoder.ReadChar(pos));
return h;
}
//------------------------------------------------------------------------
bool GFxFontCompactor::GlyphsEqual(UInt pos, const GFxFontCompactor& cmpFont, UInt cmpPos) const
{
UInt end1 = navigateToEndGlyph(pos);
UInt end2 = cmpFont.navigateToEndGlyph(cmpPos);
if (end1 - pos != end2 - cmpPos)
return false;
for (; pos < end1; ++pos, ++cmpPos)
{
if (UInt8(Decoder.ReadChar(pos)) != UInt8(cmpFont.Decoder.ReadChar(cmpPos)))
return false;
}
return true;
}
//------------------------------------------------------------------------
bool GFxFontCompactor::PathsEqual(UInt pos, const GFxFontCompactor& cmpPath, UInt cmpPos) const
{
UInt size1, size2;
UInt pos1 = pos;
UInt pos2 = cmpPos;
pos1 += Decoder.ReadUInt30(pos1, &size1);
pos2 += cmpPath.Decoder.ReadUInt30(pos2, &size2);
if (size1 != size2)
return false;
size1 >>= 1;
size2 >>= 1;
UInt8 edge1[10];
UInt8 edge2[10];
while (size1--)
{
UInt nb1 = Decoder.ReadRawEdge(pos1, edge1);
UInt nb2 = cmpPath.Decoder.ReadRawEdge(pos2, edge2);
if (nb1 != nb2)
return false;
if (memcmp(edge1, edge2, nb1))
return false;
pos1 += nb1;
pos2 += nb2;
}
return true;
}
//------------------------------------------------------------------------
UInt32 GFxFontCompactor::ComputePathHash(UInt pos) const
{
UInt size;
UInt32 h = 0;
UInt8 edge[10];
pos += Decoder.ReadUInt30(pos, &size);
size >>= 1;
while (size--)
{
UInt nb = Decoder.ReadRawEdge(pos, edge);
pos += nb;
for (UInt i = 0; i < nb; ++i)
h = ((h << 5) + h) ^ (UInt32)edge[i];
}
return h;
}
//------------------------------------------------------------------------
void GFxFontCompactor::EndGlyph(bool mergeContours)
{
GlyphInfoType glyphInfo;
glyphInfo.GlyphCode = UInt16(FontNumGlyphs);
glyphInfo.AdvanceX = 0;
glyphInfo.GlobalOffset = (UInt)Encoder.GetSize();
if (TmpContours.GetSize())
normalizeLastContour();
SInt x1, y1, x2, y2;
computeBounds(&x1, &y1, &x2, &y2);
Encoder.WriteSInt15(x1);
Encoder.WriteSInt15(y1);
Encoder.WriteSInt15(x2);
Encoder.WriteSInt15(y2);
Encoder.WriteUInt15((UInt)TmpContours.GetSize());
bool newShapesAdded = false;
if (TmpContours.GetSize())
{
UInt i, j;
SInt x, y, cx, cy, ax, ay;
for (i = 0; i < TmpContours.GetSize(); ++i)
{
const ContourType& c = TmpContours[i];
const VertexType* v1;
UInt numEdges = 0;
for (j = 1; j < c.DataSize; ++j)
{
++numEdges;
v1 = &TmpVertices[c.DataStart + j];
if(v1->x & 1)
++j;
}
v1 = &TmpVertices[c.DataStart];
x = v1->x >> 1;
y = v1->y;
Encoder.WriteSInt15(x);
Encoder.WriteSInt15(y);
UInt startPath = (UInt)Encoder.GetSize();
Encoder.WriteUInt30(numEdges << 1);
for (j = 1; j < c.DataSize; ++j)
{
v1 = &TmpVertices[c.DataStart + j];
if(v1->x & 1)
{
const VertexType* v2 = &TmpVertices[++j + c.DataStart];
cx = v1->x >> 1;
cy = v1->y;
ax = v2->x >> 1;
ay = v2->y;
Encoder.WriteQuad(cx-x, cy-y, ax-cx, ay-cy);
x = ax;
y = ay;
}
else
{
ax = v1->x >> 1;
ay = v1->y;
if (ax == x)
Encoder.WriteVLine(ay-y);
else
if (ay == y)
Encoder.WriteHLine(ax-x);
else
Encoder.WriteLine(ax-x, ay-y);
x = ax;
y = ay;
}
}
if (mergeContours)
{
UInt32 hash = ComputePathHash(startPath);
ContourKeyType key(this, hash, startPath);
const ContourKeyType* found = ContourHash.Get(key);
if (found)
{
Encoder.CutAt(startPath);
Encoder.WriteUInt30((found->DataStart << 1) | 1);
}
else
{
ContourHash.Add(key);
newShapesAdded = true;
}
}
}
}
++FontNumGlyphs;
UInt startGlyph = glyphInfo.GlobalOffset;
if (mergeContours && !newShapesAdded)
{
UInt32 hash = ComputeGlyphHash(glyphInfo.GlobalOffset);
GlyphKeyType key(this, hash, glyphInfo.GlobalOffset);
const GlyphKeyType* found = GlyphHash.Get(key);
if (found)
{
Encoder.CutAt(glyphInfo.GlobalOffset);
glyphInfo.GlobalOffset = found->DataStart;
}
else
{
GlyphHash.Add(key);
}
}
FontTotalGlyphBytes += (UInt)Encoder.GetSize() - startGlyph;
Encoder.UpdateUInt32fixlen(FontStartGlyphs, FontNumGlyphs);
Encoder.UpdateUInt32fixlen(FontStartGlyphs+4, FontTotalGlyphBytes);
GlyphInfoTable.PushBack(glyphInfo);
}
//------------------------------------------------------------------------
void GFxFontCompactor::AssignGlyphInfo(UInt glyphIndex, UInt glyphCode, SInt advanceX)
{
if (glyphIndex < GlyphInfoTable.GetSize())
{
GlyphInfoType& glyphInfo = GlyphInfoTable[glyphIndex];
glyphInfo.GlyphCode = UInt16(glyphCode);
glyphInfo.AdvanceX = SInt16(advanceX);
if (GlyphCodes.Get(UInt16(glyphCode)) == 0)
GlyphCodes.Add(UInt16(glyphCode));
}
}
//------------------------------------------------------------------------
void GFxFontCompactor::AssignGlyphCode(UInt glyphIndex, UInt glyphCode)
{
if (glyphIndex < GlyphInfoTable.GetSize())
{
GlyphInfoTable[glyphIndex].GlyphCode = UInt16(glyphCode);
if (GlyphCodes.Get(UInt16(glyphCode)) == 0)
GlyphCodes.Add(UInt16(glyphCode));
}
}
//------------------------------------------------------------------------
void GFxFontCompactor::AssignGlyphAdvance(UInt glyphIndex, SInt advanceX)
{
if (glyphIndex < GlyphInfoTable.GetSize())
{
GlyphInfoTable[glyphIndex].AdvanceX = SInt16(advanceX);
}
}
//------------------------------------------------------------------------
void GFxFontCompactor::EndGlyph(UInt glyphCode, SInt advanceX, bool mergeContours)
{
EndGlyph(mergeContours);
AssignGlyphInfo((UInt)GlyphInfoTable.GetSize() - 1, glyphCode, advanceX);
}
//------------------------------------------------------------------------
void GFxFontCompactor::AddKerningPair(UInt char1, UInt char2, SInt adjustment)
{
if (GlyphCodes.Get(UInt16(char1)) && GlyphCodes.Get(UInt16(char2)))
{
KerningPairType kp;
kp.Char1 = UInt16(char1);
kp.Char2 = UInt16(char2);
kp.Adjustment = adjustment;
KerningTable.PushBack(kp);
}
}
//------------------------------------------------------------------------
void GFxFontCompactor::UpdateFlags(UInt flags)
{
Encoder.UpdateSInt16fixlen(FontMetricsPos + 0, flags);
}
//------------------------------------------------------------------------
void GFxFontCompactor::UpdateMetrics(SInt ascent, SInt descent, SInt leading)
{
Encoder.UpdateSInt16fixlen(FontMetricsPos + 4, ascent);
Encoder.UpdateSInt16fixlen(FontMetricsPos + 6, descent);
Encoder.UpdateSInt16fixlen(FontMetricsPos + 8, leading);
}
//------------------------------------------------------------------------
void GFxFontCompactor::EndFont()
{
UInt i;
//bool ordered = true;
//for (i = 1; i < GlyphInfoTable.GetSize(); ++i)
//{
// if (GlyphInfoTable[i-1].GlyphCode > GlyphInfoTable[i].GlyphCode)
// {
// ordered = false;
// break;
// }
//}
//
//if (!ordered)
// G_QuickSort(GlyphInfoTable, cmpGlyphCodes);
for (i = 0; i < GlyphInfoTable.GetSize(); ++i)
{
const GlyphInfoType& glyphInfo = GlyphInfoTable[i];
Encoder.WriteUInt16fixlen(glyphInfo.GlyphCode);
Encoder.WriteSInt16fixlen(glyphInfo.AdvanceX);
Encoder.WriteUInt32fixlen(glyphInfo.GlobalOffset);
}
G_QuickSort(KerningTable, cmpKerningPairs);
Encoder.WriteUInt30((UInt)KerningTable.GetSize());
for (i = 0; i < KerningTable.GetSize(); ++i)
{
const KerningPairType& kerningPair = KerningTable[i];
Encoder.WriteUInt16fixlen(kerningPair.Char1);
Encoder.WriteUInt16fixlen(kerningPair.Char2);
Encoder.WriteSInt16fixlen(kerningPair.Adjustment);
}
}
#endif //GFC_NO_FONTCOMPACTOR_SUPPORT