Files
GTASource/rage/scaleform/Src/GFxPlayer/GFxFT2Helper.cpp

450 lines
14 KiB
C++
Raw Permalink Normal View History

2025-02-23 17:40:52 +08:00
/**********************************************************************
Filename : GFxFT2Helper.cpp
Content : Helper for FreeType2 font provider
Removes dependency on nonpublic headers from GFxFontProviderFT2
Created : 3/18/2009
Authors : Dmitry Polenur, Maxim Shemanarev
Copyright : (c) 2001-2009 Scaleform Corp. All Rights Reserved.
Notes :
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.
----------------------------------------------------------------------
The code of these classes was taken from the Anti-Grain Geometry
Project and modified for the use by Scaleform.
Permission to use without restrictions is hereby granted to
Scaleform Corporation by the author of Anti-Grain Geometry Project.
See http://antigtain.com for details.
**********************************************************************/
#include "GFxFT2Helper.h"
#include "GFxShape.h"
#include "GMath2D.h"
static const GCoordType GFxExternalFontFT2_CubicTolerance = 2.0f;
namespace GMath2D
{
// The code of Cubic to Quadratic approximation was taken from the
// Anti-Grain Geometry research works and modified for the use by Scaleform.
// Permission to use without restrictions is hereby granted to
// Scaleform Corporation by the author of Anti-Grain Geometry Project.
// See http://antigtain.com for details.
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
template<class Path>
void SubdivideCubicToQuadratic(
GCoordType x1, GCoordType y1,
GCoordType x2, GCoordType y2,
GCoordType x3, GCoordType y3,
GCoordType x4, GCoordType y4,
Path& path, GCoordType tolerance)
{
GCoordType xc;
GCoordType yc;
if(!CalcIntersection(x1, y1, x2, y2, x3, y3, x4, y4, &xc, &yc, tolerance))
{
xc = (x2 + x3) / 2;
yc = (y2 + y3) / 2;
}
GCoordType x12 = (x1 + xc ) / 2;
GCoordType y12 = (y1 + yc ) / 2;
GCoordType x23 = (xc + x4 ) / 2;
GCoordType y23 = (yc + y4 ) / 2;
GCoordType xq = (x12+ x23) / 2;
GCoordType yq = (y12+ y23) / 2;
x12 = (x1 + x2 ) / 2;
y12 = (y1 + y2 ) / 2;
x23 = (x2 + x3 ) / 2;
y23 = (y2 + y3 ) / 2;
GCoordType x34 = (x3 + x4 ) / 2;
GCoordType y34 = (y3 + y4 ) / 2;
GCoordType x123 = (x12 + x23 ) / 2;
GCoordType y123 = (y12 + y23 ) / 2;
GCoordType x234 = (x23 + x34 ) / 2;
GCoordType y234 = (y23 + y34 ) / 2;
GCoordType x1234 = (x123+ x234) / 2;
GCoordType y1234 = (y123+ y234) / 2;
GCoordType d;
d = fabsf(fabsf(CalcLinePointDistance(x1, y1, x4, y4, xq, yq)) -
fabsf(CalcLinePointDistance(x1, y1, x4, y4, x1234, y1234))) +
fabsf(CalcLinePointDistance(x123, y123, x234, y234, xq, yq));
if(d < tolerance)
{
path.CurveToAbs(SInt(xc), SInt(yc), SInt(x4), SInt(y4));
return;
}
SubdivideCubicToQuadratic(x1, y1, x12, y12, x123, y123, x1234, y1234, path, tolerance);
SubdivideCubicToQuadratic(x1234, y1234, x234, y234, x34, y34, x4, y4, path, tolerance);
}
//-----------------------------------------------------------------------
struct CubicCoordType
{
GCoordType x1, y1, x2, y2, x3, y3, x4, y4;
};
//-----------------------------------------------------------------------
template<class CurveType>
void SubdivideCubicCurve(
const CurveType& c, GCoordType t, CurveType* c1, CurveType* c2)
{
// Local variables are necessary in case of reuse curve c,
// that is, when c1 or c2 points to the same physical c.
//---------------------
GCoordType x1 = c.x1;
GCoordType y1 = c.y1;
GCoordType x12 = c.x1 + t*(c.x2 - c.x1);
GCoordType y12 = c.y1 + t*(c.y2 - c.y1);
GCoordType x23 = c.x2 + t*(c.x3 - c.x2);
GCoordType y23 = c.y2 + t*(c.y3 - c.y2);
GCoordType x34 = c.x3 + t*(c.x4 - c.x3);
GCoordType y34 = c.y3 + t*(c.y4 - c.y3);
GCoordType x123 = x12 + t*(x23 - x12 );
GCoordType y123 = y12 + t*(y23 - y12 );
GCoordType x234 = x23 + t*(x34 - x23 );
GCoordType y234 = y23 + t*(y34 - y23 );
GCoordType x1234 = x123 + t*(x234 - x123);
GCoordType y1234 = y123 + t*(y234 - y123);
GCoordType x4 = c.x4;
GCoordType y4 = c.y4;
c1->x1 = x1;
c1->y1 = y1;
c1->x2 = x12;
c1->y2 = y12;
c1->x3 = x123;
c1->y3 = y123;
c1->x4 = x1234;
c1->y4 = y1234;
c2->x1 = x1234;
c2->y1 = y1234;
c2->x2 = x234;
c2->y2 = y234;
c2->x3 = x34;
c2->y3 = y34;
c2->x4 = x4;
c2->y4 = y4;
}
//-----------------------------------------------------------------------
template<class Path>
void CubicToQuadratic(
GCoordType x1, GCoordType y1,
GCoordType x2, GCoordType y2,
GCoordType x3, GCoordType y3,
GCoordType x4, GCoordType y4,
Path& path,
GCoordType tolerance)
{
GCoordType ax = -x1 + 3*x2 - 3*x3 + x4;
GCoordType ay = -y1 + 3*y2 - 3*y3 + y4;
GCoordType bx = 3*x1 - 6*x2 + 3*x3;
GCoordType by = 3*y1 - 6*y2 + 3*y3;
GCoordType cx = -3*x1 + 3*x2;
GCoordType cy = -3*y1 + 3*y2;
GCoordType den = ay*bx - ax*by;
GCoordType t1 = -1;
GCoordType t2 = -1;
if (den != 0)
{
GCoordType tc = -0.5f * (ay*cx - ax*cy) / den;
GCoordType d = sqrtf(tc*tc - (by*cx - bx*cy) / (3*den));
t1 = tc - d;
t2 = tc + d;
}
unsigned numSubcurves = 1;
CubicCoordType cc;
CubicCoordType sc[3];
cc.x1 = x1; cc.y1 = y1;
cc.x2 = x2; cc.y2 = y2;
cc.x3 = x3; cc.y3 = y3;
cc.x4 = x4; cc.y4 = y4;
switch(int(t2 > 0 && t2 < 1) * 2 + int(t1 > 0 && t1 < 1))
{
case 0:
sc[0] = cc;
numSubcurves = 1;
break;
case 1:
SubdivideCubicCurve(cc, t1, &sc[0], &sc[1]);
numSubcurves = 2;
break;
case 2:
SubdivideCubicCurve(cc, t2, &sc[0], &sc[1]);
numSubcurves = 2;
break;
case 3:
if(t2 < t1)
{
GCoordType t = t1; t1 = t2; t2 = t;
}
SubdivideCubicCurve(cc, t1, &sc[0], &sc[1]);
SubdivideCubicCurve(sc[1], (t2 - t1) / (1 - t1), &sc[1], &sc[2]);
numSubcurves = 3;
break;
}
unsigned i;
for(i = 0; i < numSubcurves; ++i)
{
const CubicCoordType& c = sc[i];
SubdivideCubicToQuadratic(c.x1, c.y1, c.x2, c.y2, c.x3, c.y3, c.x4, c.y4, path, tolerance);
}
}
} // namespace GMath2D
void GFxFT2Helper::cubicToQuadratic(
GFxPathPacker& path,
int hintedGlyphSize,
UInt fontHeight,
int x2, int y2, int x3, int y3, int x4, int y4)
{
int x1, y1;
path.GetLastVertex(&x1, &y1);
GCoordType k = 1;
if (hintedGlyphSize)
{
x2 = FtToTwips(x2); y2 = FtToTwips(y2);
x3 = FtToTwips(x3); y3 = FtToTwips(y3);
x4 = FtToTwips(x4); y4 = FtToTwips(y4);
k = GCoordType(hintedGlyphSize * 20) / GCoordType(fontHeight);
}
else
{
x2 = FtToS1024(x2); y2 = FtToS1024(y2);
x3 = FtToS1024(x3); y3 = FtToS1024(y3);
x4 = FtToS1024(x4); y4 = FtToS1024(y4);
}
GMath2D::CubicToQuadratic(
GCoordType(x1), GCoordType(y1),
GCoordType(x2), GCoordType(y2),
GCoordType(x3), GCoordType(y3),
GCoordType(x4), GCoordType(y4),
path, GFxExternalFontFT2_CubicTolerance * k);
}
bool GFxFT2Helper::decomposeGlyphOutline(const GFxFTOutline& outline, GFxShapeBase* shape, UInt fontHeight)
{
GFxFTVector v_last;
GFxFTVector v_control;
GFxFTVector v_start;
GFxPathPacker path;
bool hinted = shape->GetHintedGlyphSize() > 0;
GFxFTVector* point;
GFxFTVector* limit;
char* tags;
int n; // index of contour in outline
int first = 0; // index of first point in contour
char tag; // current point's state
bool shapeValid = false;
for (n = 0; n < outline.NContours; n++)
{
int last; // index of last point in contour
last = outline.Contours[n];
limit = outline.Points + last;
v_start = outline.Points[first];
v_last = outline.Points[last];
v_control = v_start;
point = outline.Points + first;
tags = outline.Tags + first;
tag = GetCurveTag(tags[0]);
// A contour cannot start with a cubic control point!
if(tag == GFxFTCurveTagCubic) return false;
// check first point to determine origin
if (tag == GFxFTCurveTagConic)
{
// first point is conic control. Yes, this happens.
if (GetCurveTag(outline.Tags[last]) == GFxFTCurveTagOn)
{
// start at last point if it is on the curve
v_start = v_last;
limit--;
}
else
{
// if both first and last points are conic,
// start at their middle and record its position
// for closure
v_start.x = (v_start.x + v_last.x) / 2;
v_start.y = (v_start.y + v_last.y) / 2;
v_last = v_start;
}
point--;
tags--;
}
path.Reset();
path.SetFill0(1);
path.SetFill1(0);
if (hinted)
path.SetMoveTo(FtToTwips(v_start.x), -FtToTwips(v_start.y));
else
path.SetMoveTo(FtToS1024(v_start.x), -FtToS1024(v_start.y));
while (point < limit)
{
point++;
tags++;
tag = GetCurveTag(tags[0]);
switch(tag)
{
case GFxFTCurveTagOn: // emit a single line_to
{
if (hinted)
path.LineToAbs(FtToTwips(point->x), -FtToTwips(point->y));
else
path.LineToAbs(FtToS1024(point->x), -FtToS1024(point->y));
continue;
}
case GFxFTCurveTagConic: // consume conic arcs
{
v_control.x = point->x;
v_control.y = point->y;
Do_Conic:
if (point < limit)
{
GFxFTVector vec;
GFxFTVector v_middle;
point++;
tags++;
tag = GetCurveTag(tags[0]);
vec.x = point->x;
vec.y = point->y;
if(tag == GFxFTCurveTagOn)
{
if (hinted)
path.CurveToAbs(FtToTwips(v_control.x), -FtToTwips(v_control.y),
FtToTwips(vec.x), -FtToTwips(vec.y));
else
path.CurveToAbs(FtToS1024(v_control.x), -FtToS1024(v_control.y),
FtToS1024(vec.x), -FtToS1024(vec.y));
continue;
}
if (tag != GFxFTCurveTagConic) return false;
v_middle.x = (v_control.x + vec.x) / 2;
v_middle.y = (v_control.y + vec.y) / 2;
if (hinted)
path.CurveToAbs(FtToTwips(v_control.x), -FtToTwips(v_control.y),
FtToTwips(v_middle.x), -FtToTwips(v_middle.y));
else
path.CurveToAbs(FtToS1024(v_control.x), -FtToS1024(v_control.y),
FtToS1024(v_middle.x), -FtToS1024(v_middle.y));
v_control = vec;
goto Do_Conic;
}
if (hinted)
path.CurveToAbs(FtToTwips(v_control.x), -FtToTwips(v_control.y),
FtToTwips(v_start.x), -FtToTwips(v_start.y));
else
path.CurveToAbs(FtToS1024(v_control.x), -FtToS1024(v_control.y),
FtToS1024(v_start.x), -FtToS1024(v_start.y));
goto Close;
}
default: // GFxFTCurveTagCubic
{
GFxFTVector vec1, vec2;
if (point + 1 > limit || GetCurveTag(tags[1]) != GFxFTCurveTagCubic)
{
return false;
}
vec1.x = point[0].x;
vec1.y = point[0].y;
vec2.x = point[1].x;
vec2.y = point[1].y;
point += 2;
tags += 2;
if (point <= limit)
{
GFxFTVector vec;
vec.x = point->x;
vec.y = point->y;
cubicToQuadratic(path, shape->GetHintedGlyphSize(), fontHeight,
vec1.x, -vec1.y, vec2.x, -vec2.y, vec.x, -vec.y);
continue;
}
cubicToQuadratic(path, shape->GetHintedGlyphSize(), fontHeight,
vec1.x, -vec1.y, vec2.x, -vec2.y, v_start.x, -v_start.y);
goto Close;
}
}
}
Close:
if (!path.IsEmpty())
{
path.ClosePath();
static_cast<GFxShapeNoStyles*>(shape)->AddPath(&path);
shapeValid = true;
}
first = last + 1;
}
return shapeValid;
}
GFxShapeBase* GFxFT2Helper::CreateShape(UInt shapePageSize, UInt glyphSize)
{
GFxShapeNoStyles* pshape = new GFxShapeNoStyles(shapePageSize);
pshape->SetHintedGlyphSize(glyphSize);
return pshape;
}
void GFxFT2Helper::ReleaseShape(GFxShapeBase* pshape)
{
pshape->Release();
}