PDA

View Full Version : vector fonts? (freetype for pascal?)



noeska
19-08-2008, 05:11 PM
Is it possible to use vector fonts with opengl? I know of wgl, but that is windows only.
Now there is also freetype2. It is supposed to be able to give access to the vector data of the glyph. That give me 3 questions:
1. Is there a delphi/pascal unit for freetype2?
2. How does rendering a glyph work? Bezier->Polygon->Triangulate?
3. Has someone else already done the hard work?

With knowing how point 2 works. The font can also be save in like a milkshape ascii format. So the original font is not needed.

JernejL
19-08-2008, 06:23 PM
Is it possible to use vector fonts with opengl? I know of wgl, but that is windows only.
Now there is also freetype2. It is supposed to be able to give access to the vector data of the glyph. That give me 3 questions:
1. Is there a delphi/pascal unit for freetype2?
2. How does rendering a glyph work? Bezier->Polygon->Triangulate?
3. Has someone else already done the hard work?

With knowing how point 2 works. The font can also be save in like a milkshape ascii format. So the original font is not needed.

1. no
2. yes
3. no

I was considering doing this a while ago, but i did experiments (using WGL font outlines and converting them to mesh using feedback buffer), but the renderings were, well let me put it simply, the renderings looked like crap at low scale without AA, and you can't force users to use AA.. so they weren't useable for anything else but large text (it was totally unsuitable for hud text etc..).

Brainer
19-08-2008, 08:41 PM
Maybe this could be of any help: http://nopaste.info/51922ec7d4.html.

noeska
20-08-2008, 06:04 PM
As always torry is verry helpful as it provides a ttf2vector component. Now this solutions is also windows only. But gives access to the font coords. And allowed me to render with GL_LINES.
http://www.torry.net/quicksearchd.php?String=ttf+vector&Title=Yes
so with this i should also be able to extrude etc. I have yet to test it with my experimental 2dpolygon render to see if it can render it solid also, but i expect no problems there. It just needs a good cleanup.
And more importantly it is possible to save the font coords. So it is possible to render a font that is not available on the end user system. The ony thing to worry about is precission.

Only the font generator application would only run on windows only.

But for linux support freetype(2) is really needed.

Example:

Init


TTF2Vector := TTTFToVectorConverter.Create(DGLForm);
TTF2Vector.Font := TFont.Create();
TTF2Vector.Font.Name := 'Arial';
// Setup spline precision (1 min, 100 max)
TTF2Vector.Precision := 1;


Render


// Get glyphs' strokes
glyphs := TTF2Vector.GetCharacterGlyphs( ord('m') );
if glyphs = nil then // May be a "empty" glyph !!!!
beep;
// Get character bounds
cbounds := glyphs.Bounds;
xm := (cbounds.Right-cbounds.Left+1) div 2;
ym := (cbounds.Bottom-cbounds.Top+1) div 2;
// Compute the scaling factors
sx := 0.5;//(PaintBox.Width-20)/(cbounds.Right-cbounds.Left+1);
sy := 0.5;//(PaintBox.Height-20)/(cbounds.Bottom-cbounds.Top+1);

for i := 0 to glyphs.Count-1 do
begin
// Get a stroke
stroke := glyphs.Stroke[i];

x1 := 10 + round( (stroke.Pt1.X-cbounds.Left) * sx );
x2 := 10 + round( (stroke.Pt2.X-cbounds.Left) * sx );
y1 := 10 + round( (stroke.Pt1.Y-cbounds.Top) * sy );
y2 := 10 + round( (stroke.Pt2.Y-cbounds.Top) * sy );

// Draw the stroke
glBegin(GL_LINES);
glVertex3f(x1/1000, (y1-1)/1000, 0);
glVertex3f(x2/1000, (y2-1)/1000,0);
glEnd();

end;
// Free the glyphs
glyphs.Free;

JernejL
20-08-2008, 08:03 PM
Any pictures of how the font looks rendered?

noeska
20-08-2008, 08:29 PM
for the moment not as good as expected:
http://www.noeska.com/downloads/vectorfont.jpg

But that is due to my polygon tesselation that goes haywire on the i character.

chronozphere
20-08-2008, 09:30 PM
You might want to triangulate the polygon's yourself using earclipping. As far as i can tell, it's not that hard to do. :)

It looks good so far. Great efforts. ;)

noeska
21-08-2008, 07:10 PM
I am using glu tesselation now. And with good results. Only how do i implement it as part of an class?

gluTessCallback(tess, GLU_TESS_BEGIN, @tessBeginCB);

@tessBeginCB cannot be part of the class and has to be an real procedure with stdcall.

JernejL
21-08-2008, 09:09 PM
I am using glu tesselation now. And with good results. Only how do i implement it as part of an class?

gluTessCallback(tess, GLU_TESS_BEGIN, @tessBeginCB);

@tessBeginCB cannot be part of the class and has to be an real procedure with stdcall.

sort of userdata pointer and global function?

noeska
22-08-2008, 03:28 PM
My TPolygon Class: (MPL license)



unit Polygon;

interface

uses DGLOpenGL;

type
TPoint = packed record
x: single;
y: single;
z: single;
r: single;
g: single;
b: single;
a: single;
end;

TPolygon = class
private
FPoints: array of TPoint; //polygon point
FVertex: array of TPoint; //triangulated data
FColor: TPoint;
FCount: integer;
FVertexCount: integer;
FTesselated: boolean;
procedure SetPoint(I: integer; Value: TPoint);
procedure AddVertex(x: single; y: single; z: single; r: single; g: single; b: single; a:single);
function GetPoint(I: integer): TPoint;
function GetCount(): integer;
procedure tessBegin(which: GLenum);
procedure tessEnd();
procedure tessVertex(x: single; y: single; z: single; r: single; g: single; b: single; a:single);
public
constructor Create();
destructor Destroy();
procedure SetColor(R: single; G: single; B: single;A: single);
procedure Add(X: single; Y: single); overload;
procedure Add(X: single; Y: single; Z: single); overload;
procedure Add(X: single; Y: single; Z: single; R: single; G: single; B: single; A: single); overload;
procedure Render();
procedure Tesselate();
property Points[I: integer]: TPoint read GetPoint write SetPoint;
property Count: integer read GetCount;
end;

implementation

type
TGLArrayd6 = array[0..5] of GLDouble;
PGLArrayd6 = ^TGLArrayd6;
TGLArrayvertex4 = array[0..3] of PGLArrayd6;
PGLArrayvertex4 = ^TGLArrayvertex4;
PGLArrayf4 = ^TGLArrayf4;

threadvar
PolygonClass: TPolygon;

procedure TPolygon.SetColor(R: single; G: single; B: single;A: single);
begin
FColor.r := R;
FColor.g := G;
FColor.b := B;
FColor.a := A;
end;

procedure TPolygon.tessBegin(which: GLenum);
begin
glBegin(which);
end;

procedure TPolygon.tessEnd();
begin
glEnd();
end;

procedure TPolygon.tessVertex(x: single; y: single; z: single; r: single; g: single; b: single; a:single);
begin
glcolor3f(r,g,b);
glVertex3f(x,y,z);
end;

procedure TPolygon.AddVertex(x: single; y: single; z: single; r: single; g: single; b: single; a:single);
begin
FVertexCount := FVertexCount + 1;
SetLength(FVertex, FVertexCount);

FVertex[FVertexCount-1].R := R;
FVertex[FVertexCount-1].G := G;
FVertex[FVertexCount-1].B := B;

FVertex[FVertexCount-1].X := X;
FVertex[FVertexCount-1].Y := Y;
FVertex[FVertexCount-1].Z := Z;
end;

constructor TPolygon.Create();
begin
inherited Create();
FCount := 0;
FVertexCount := 0;
FTesselated := false;
FColor.R := 0.0;
FColor.G := 0.0;
FColor.B := 0.0;
FColor.A := 0.0;
end;

destructor TPolygon.Destroy();
begin
FTesselated := false;
FCount := 0;
FVertexCount := 0;
SetLength(FPoints, FCount);
SetLength(FVertex, FVertexCount);
inherited Destroy;
end;

procedure TPolygon.SetPoint(I: integer; Value: TPoint);
begin
FTesselated := false; //check first on changed values
FPoints[I] := Value;
end;

function TPolygon.GetPoint(I: integer): TPoint;
begin
result := FPoints[I];
end;

function TPolygon.GetCount(): integer;
begin
result := FCount;
end;

procedure TPolygon.Add(X: single; Y: single);
begin
FTesselated := false;
FCount := FCount + 1;
SetLength(FPoints, FCount);
FPoints[FCount-1].X := X;
FPoints[FCount-1].Y := Y;
FPoints[FCount-1].Z := 0.0;
FPoints[FCount-1].R := FColor.R;
FPoints[FCount-1].G := FColor.G;
FPoints[FCount-1].B := FColor.B;
FPoints[FCount-1].A := FColor.A;
end;

procedure TPolygon.Add(X: single; Y: single; Z: single);
begin
FTesselated := false;
FCount := FCount + 1;
SetLength(FPoints, FCount);
FPoints[FCount-1].X := X;
FPoints[FCount-1].Y := Y;
FPoints[FCount-1].Z := Z;
FPoints[FCount-1].R := FColor.R;
FPoints[FCount-1].G := FColor.G;
FPoints[FCount-1].B := FColor.B;
FPoints[FCount-1].A := FColor.A;
end;

procedure TPolygon.Add(X: single; Y: single; Z: single; R: single; G: single; B: single; A: single);
begin
FTesselated := false;
FCount := FCount + 1;
SetLength(FPoints, FCount);
FPoints[FCount-1].X := X;
FPoints[FCount-1].Y := Y;
FPoints[FCount-1].Z := Z;
FPoints[FCount-1].R := R;
FPoints[FCount-1].G := G;
FPoints[FCount-1].B := B;
FPoints[FCount-1].A := A;
end;

Procedure TPolygon.Render();
var
loop: integer;
begin
if FTesselated = false then Tesselate;

glbegin(GL_TRIANGLES);
for loop:=0 to FVertexCount-1 do
begin
glcolor3f(FVertex[loop].R,FVertex[loop].G,FVertex[ loop].B);
glvertex3f(FVertex[loop].X,FVertex[loop].Y,FVertex [loop].Z);
end;
glend;
end;

procedure TPolygon.Tesselate();
var
loop: integer;
tess: pointer;
test: TGLArrayd3;
pol: PGLArrayd6;

MyTest: string;

procedure iTessBeginCB(which: GLenum); {$IFDEF Win32}stdcall; {$ELSE}cdecl; {$ENDIF}
begin
//PolygonClass.tessBegin(which);
end;

procedure iTessEndCB(); {$IFDEF Win32}stdcall; {$ELSE}cdecl; {$ENDIF}
begin
//PolygonClass.tessEnd();
end;

procedure iTessEdgeCB(flag: GLboolean; lpContext: pointer); {$IFDEF Win32}stdcall; {$ELSE}cdecl; {$ENDIF}
begin
//just do nothing to force GL_TRIANGLES !!!
end;

procedure iTessVertexCB(data: PGLArrayd6); {$IFDEF Win32}stdcall; {$ELSE}cdecl; {$ENDIF}
begin
//PolygonClass.tessVertex(data[0], data[1], data[2], data[3], data[4], data[5],0);
PolygonClass.AddVertex(data[0], data[1], data[2], data[3], data[4], data[5],0);
end;


procedure iTessCombineCB(newVertex : PGLArrayd6; neighborVertex : Pointer;
neighborWeight : Pointer; var outData : Pointer); {$IFDEF Win32}stdcall; {$ELSE}cdecl; {$ENDIF}
var
vertex: PGLArrayd6;
loop: integer;
colorloop: integer;
color: double;
begin
new(vertex);

vertex[0] := newVertex^[0];
vertex[1] := newVertex^[1];
vertex[2] := newVertex^[2];

for colorloop := 3 to 5 do
begin
vertex[colorloop] := 0.0;
for loop:=0 to 3 do
begin
if PGLArrayf4&#40;neighborWeight&#41;^&#91;loop&#93; <> 0 then
begin
vertex&#91;colorloop&#93; &#58;= vertex&#91;colorloop&#93; +
PGLArrayf4&#40;neighborWeight&#41;^&#91;loop&#93; *
PGLArrayvertex4&#40;neighborVertex&#41;^&#91;loop&#93;&#91;colorloop&#93;
end;
end;
end;

// return output data &#40;vertex coords and others&#41;
outData&#58;= vertex;
end;

begin
PolygonClass &#58;= Self;

tess &#58;= gluNewTess&#40;&#41;;

gluTessCallback&#40;tess, GLU_TESS_BEGIN, @iTessBeginCB &#41;;
gluTessCallback&#40;tess, GLU_TESS_END, @iTessEndCB&#41;;
gluTessCallback&#40;tess, GLU_TESS_VERTEX, @iTessVertexCB&#41;;
gluTessCallback&#40;tess, GLU_TESS_COMBINE, @iTessCombineCB&#41;; //does not work for font?
gluTessCallback&#40;tess, GLU_TESS_EDGE_FLAG_DATA, @iTessEdgeCB&#41;; //force triangles

gluTessProperty&#40;tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO &#41;;

gluTessBeginPolygon&#40;tess, nil&#41;; // with NULL data
gluTessBeginContour&#40;tess&#41;;

for loop &#58;= 0 to FCount-1 do
begin
new&#40;pol&#41;;
pol&#91;3&#93;&#58;=FPoints&#91;loop&#93;.R; //color
pol&#91;4&#93;&#58;=FPoints&#91;loop&#93;.G;
pol&#91;5&#93;&#58;=FPoints&#91;loop&#93;.B;

pol&#91;0&#93;&#58;=FPoints&#91;loop&#93;.X;
pol&#91;1&#93;&#58;=FPoints&#91;loop&#93;.Y;
pol&#91;2&#93;&#58;=0;

test&#91;0&#93; &#58;= pol&#91;0&#93;;
test&#91;1&#93; &#58;= pol&#91;1&#93;;
test&#91;2&#93; &#58;= pol&#91;2&#93;;
gluTessVertex&#40;tess, test, pol&#41;;
end;

gluTessEndContour&#40;tess&#41;;
gluTessEndPolygon&#40;tess&#41;;
gluDeleteTess&#40;tess&#41;; // delete after tessellation

PolygonClass &#58;= nil;
FTesselated &#58;= true;
end;

end.

noeska
22-08-2008, 05:30 PM
My PolygonFont class: (MPL license)

Needs to be improved. Also only does a..z lowercase only.
Also needs Freetype2 integration.



unit PolygonFont;

interface

uses Polygon;

type
TPolygonFont = class
private
FCharGlyph&#58; array&#91;0..255&#93; of TPolygon;
FCharWidth&#58; array&#91;0..255&#93; of integer;
FName&#58; string;
FPrecision&#58; integer;
FScale&#58; single;
public
procedure Generate&#40;&#41;;
procedure RenderChar&#40;value&#58; char&#41;;
procedure RenderString&#40;value&#58; string&#41;;
property Name&#58; string read FName write FName;
property Precision&#58; integer read FPrecision write FPrecision;
property Scale&#58; single read FScale write FScale;
end;

implementation

uses DGLOpenGL, VectorFont, Windows, Graphics;

procedure TPolygonFont.Generate&#40;&#41;;
var
loop&#58; integer;
glyphs&#58; TStrokeCollection;
cbounds&#58; TRect;
x1, y1, x2, y2, xm, ym&#58; integer;
sx, sy&#58; double; // Scaling factors
i, j, k, sog&#58; integer;
stroke&#58; TFontStroke;
//scale&#58; integer;

TTF2Vector&#58; TTTFToVectorConverter;

begin

//vectorfont test
TTF2Vector &#58;= TTTFToVectorConverter.Create&#40;nil&#41;;
TTF2Vector.Font &#58;= TFont.Create&#40;&#41;;
TTF2Vector.Font.Name &#58;= FName;
// Setup spline precision &#40;1 min, 100 max&#41;
TTF2Vector.Precision &#58;= FPrecision;

for loop &#58;= 0 to 255 do
begin
FCharGlyph&#91;loop&#93; &#58;= TPolygon.Create&#40;&#41;;
//FCharGlyph&#91;loop&#93;.Outline &#58;= true;
FCharGlyph&#91;loop&#93;.SetColor&#40;0.0,1.0,0.0,0.0&#41;;

// Get glyphs' strokes
if &#40;loop >= ord&#40;'a'&#41;&#41; and &#40;loop <= ord&#40;'z'&#41;&#41; then
begin
glyphs &#58;= TTF2Vector.GetCharacterGlyphs&#40; loop &#41;;

// Get character bounds
cbounds &#58;= glyphs.Bounds;
xm &#58;= &#40;cbounds.Right-cbounds.Left+1&#41; div 2;
ym &#58;= &#40;cbounds.Bottom-cbounds.Top+1&#41; div 2;

// Compute the scaling factors
sx &#58;= fscale;
sy &#58;= fscale;

for i &#58;= 0 to glyphs.Count-1 do
begin
// Get a stroke
stroke &#58;= glyphs.Stroke&#91;i&#93;;

x1 &#58;= 10 + round&#40; &#40;stroke.Pt1.X-cbounds.Left&#41; * sx &#41;;
x2 &#58;= 10 + round&#40; &#40;stroke.Pt2.X-cbounds.Left&#41; * sx &#41;;
y1 &#58;= 10 + round&#40; &#40;stroke.Pt1.Y-cbounds.Top&#41; * sy &#41;;
y2 &#58;= 10 + round&#40; &#40;stroke.Pt2.Y-cbounds.Top&#41; * sy &#41;;

FCharGlyph&#91;loop&#93;.Add&#40;x1/1000, &#40;y1-1&#41;/1000&#41;;
FCharGlyph&#91;loop&#93;.Add&#40;x2/1000, &#40;y2-1&#41;/1000&#41;;
end;

// Free the glyphs
glyphs.Free;

FCharWidth&#91;loop&#93; &#58;= cbounds.Right;
FCharGlyph&#91;loop&#93;.Tesselate&#40;&#41;;
end;

end;

TTF2Vector.Free;
end;

procedure TPolygonFont.RenderChar&#40;value&#58; char&#41;;
begin
FCharGlyph&#91;ord&#40;value&#41;&#93;.Render&#40;&#41;;
glTranslatef&#40;&#40;FCharWidth&#91;ord&#40;value&#41;&#93;*fscale&#41;/1000, 0, 0&#41;;
end;

procedure TPolygonFont.RenderString&#40;value&#58; string&#41;;
var
i&#58; integer;
begin
for i &#58;=1 to length&#40;value&#41; do
begin
RenderChar&#40;value&#91;i&#93;&#41;;
end;
end;

end.

marcov
25-08-2008, 12:44 PM
Is it possible to use vector fonts with opengl? I know of wgl, but that is windows only.
Now there is also freetype2. It is supposed to be able to give access to the vector data of the glyph. That give me 3 questions:
1. Is there a delphi/pascal unit for freetype2?
2. How does rendering a glyph work? Bezier->Polygon->Triangulate?
3. Has someone else already done the hard work?

With knowing how point 2 works. The font can also be save in like a milkshape ascii format. So the original font is not needed.

1. no


Afaik Free Pascal has something freetype related in the fcl-image sub-package. Is a bit small, and might only contain necessary calls though.