program glfont;
{
glfont: An example of using the SDL_ttf library with OpenGL.
Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
The SDL_GL_* functions in this file are available in the public domain.
Sam Lantinga
slouken@libsdl.org
}
{ $Id: glfont.dpr,v 1.3 2005/05/23 22:35:39 savage Exp $ }
{ A simple program to test the text rendering feature of the TTF library }
uses
SysUtils,
sdl,
sdl_ttf,
gl;
(* Undefine this if you want a flat cube instead of a rainbow cube *)
{$DEFINE SHADED_CUBE}
const
DEFAULT_PTSIZE = 18;
DEFAULT_TEXT = 'The quick brown fox jumped over the lazy dog';
NUM_COLORS = 256;
Usage = 'Usage: %s <font>.ttf [-solid] [-utf8 or -unicode] [-b] [-i] [-u] [-fgcol r g b] [-bgcol r g b] [-ptsize nn] [-text "message"]';
SCREEN_WIDTH = 640;
SCREEN_HEIGHT = 480;
SCREEN_BPP = 0;
procedure ShutDownApplication( HaltStatus : integer );
begin
TTF_Quit;
SDL_Quit;
Halt( HaltStatus );
end;
procedure SDL_GL_Enter2DMode;
var
screen : PSDL_Surface;
begin
screen := SDL_GetVideoSurface;
(* Note, there may be other things you need to change,
depending on how you have your OpenGL state set up.
*)
glPushAttrib( GL_ENABLE_BIT );
glDisable( GL_DEPTH_TEST );
glDisable( GL_CULL_FACE );
glEnable( GL_TEXTURE_2D );
glViewport( 0, 0, screen.w, screen.h );
glMatrixMode( GL_PROJECTION );
glPushMatrix;
glLoadIdentity;
glOrtho( 0.0, screen.w, screen.h, 0.0, 0.0, 1.0 );
glMatrixMode( GL_MODELVIEW );
glPushMatrix;
glLoadIdentity;
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL );
end;
procedure SDL_GL_Leave2DMode;
begin
glMatrixMode( GL_MODELVIEW );
glPopMatrix;
glMatrixMode( GL_PROJECTION );
glPopMatrix;
glPopAttrib;
end;
(* Quick utility function for texture creation *)
function power_of_two( input : integer ) : integer;
var
value : integer;
begin
value := 1;
while ( value < input ) do
begin
value := value shl 1;
end;
result := value;
end;
function SDL_GL_LoadTexture( surface : PSDL_Surface; var texcoord : array of GlFloat ) : GLuint;
var
texture : GLuint;
w, h : integer;
image : PSDL_Surface;
area : TSDL_Rect;
saved_flags : Uint32;
saved_alpha : Uint8;
begin
(* Use the surface width and height expanded to powers of 2 *)
w := power_of_two( surface.w );
h := power_of_two( surface.h );
texcoord[ 0 ] := 0.0; (* Min X *)
texcoord[ 1 ] := 0.0; (* Min Y *)
texcoord[ 2 ] := surface.w / w; (* Max X *)
texcoord[ 3 ] := surface.h / h; (* Max Y *)
image := SDL_CreateRGBSurface(
SDL_SWSURFACE,
w, h,
32,
{$IFNDEF IA32} (* OpenGL RGBA masks *)
$000000FF,
$0000FF00,
$00FF0000,
$FF000000
{$ELSE}
$FF000000,
$00FF0000,
$0000FF00,
$000000FF
{$ENDIF}
);
if ( image = nil ) then
begin
result := 0;
exit;
end;
(* Save the alpha blending attributes *)
saved_flags := surface.flags and ( SDL_SRCALPHA or SDL_RLEACCELOK );
saved_alpha := surface.format.alpha;
if ( ( saved_flags and SDL_SRCALPHA ) = SDL_SRCALPHA ) then
begin
SDL_SetAlpha( surface, 0, 0 );
end;
(* Copy the surface into the GL texture image *)
area.x := 0;
area.y := 0;
area.w := surface.w;
area.h := surface.h;
SDL_BlitSurface( surface, @area, image, @area );
(* Restore the alpha blending attributes *)
if ( ( saved_flags and SDL_SRCALPHA ) = SDL_SRCALPHA ) then
begin
SDL_SetAlpha( surface, saved_flags, saved_alpha );
end;
(* Create an OpenGL texture for the image *)
glGenTextures( 1, @texture );
glBindTexture( GL_TEXTURE_2D, texture );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexImage2D( GL_TEXTURE_2D,
0,
GL_RGBA,
w, h,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
image.pixels );
SDL_FreeSurface( image ); (* No longer needed *)
result := texture;
end;
type
TRenderType = ( rtLatin1, rtUTF8, rtUnicode );
var
//fontfile : string;
//gl_error : GLenum;
texture : GLuint;
x, y, w, h : integer;
//texcoord : array[ 0..4 ] of TGLfloat;
texMinX, texMinY : GLfloat;
texMaxX, texMaxY : GLfloat;
color : array[ 0..7, 0..2 ] of single = ( ( 1.0, 1.0, 0.0 ),
( 1.0, 0.0, 0.0 ),
( 0.0, 0.0, 0.0 ),
( 0.0, 1.0, 0.0 ),
( 0.0, 1.0, 1.0 ),
( 1.0, 1.0, 1.0 ),
( 1.0, 0.0, 1.0 ),
( 0.0, 0.0, 1.0 ) );
cube : array[ 0..7, 0..2 ] of single = ( ( 0.5, 0.5, -0.5 ),
( 0.5, -0.5, -0.5 ),
( -0.5, -0.5, -0.5 ),
( -0.5, 0.5, -0.5 ),
( -0.5, 0.5, 0.5 ),
( 0.5, 0.5, 0.5 ),
( 0.5, -0.5, 0.5 ),
( -0.5, -0.5, 0.5 ) );
screen : PSDL_Surface = nil;
font : PTTF_Font;
temp : PSDL_Surface = nil;
ptsize : integer = 0;
i : integer;
done : Boolean = false;
//rdiff, gdiff, bdiff : integer;
//colors : array[ 0..NUM_COLORS - 1 ] of TSDL_Color;
white : TSDL_Color = ( r : $FF; g : $FF; b : $FF; unused : 0 );
black : TSDL_Color = ( r : $00; g : $00; b : $00; unused : 0 );
forecol : TSDL_Color;
backcol : TSDL_Color;
//dstrect : TSDL_Rect;
event : TSDL_Event;
rendersolid : Boolean = false;
renderstyle : integer = TTF_STYLE_NORMAL;
rendertype : TRenderType = rtLatin1;
dump : Boolean;
message : string;
glyph : PSDL_Surface = nil;
r, g, b : integer;
outname : string;
//texcoord : array[ 0..3 ] of TGLfloat;
aspect : single;
procedure CreateTextTexture;
var
//screen : PSDL_Surface;
image : PSDL_Surface;
texcoord : array[ 0..3 ] of GlFloat;
begin
//screen := SDL_GetVideoSurface;
if ( texture = 0 ) then
begin
case rendertype of
rtLatin1 :
begin
if ( rendersolid ) then
begin
image := TTF_RenderText_Solid( font, PChar( message ), forecol );
end
else
begin
image := TTF_RenderText_Shaded( font, PChar( message ), forecol, backcol );
end;
end;
rtUTF8 :
begin
if ( rendersolid ) then
begin
image := TTF_RenderUTF8_Solid( font, PChar( message ), forecol );
end
else
begin
image := TTF_RenderUTF8_Shaded( font, PChar( message ), forecol, backcol );
end;
end;
rtUNICODE :
begin
{ This doesn't actually work because you can't pass UNICODE text in via command line, AFAIK, but...}
{Uint16 unicode_text[BUFSIZ];
int index;
for index := 0 to Lneght(message) do
begin
unicode_text[index] := ((Uint8 *)message)[0];
unicode_text[index] := unicode_text[index] shl 8;
unicode_text[index] := unicode_text[index] or ((Uint8 *)message)[1];
message := message + 2;
end;
if ( rendersolid ) then
begin
text := TTF_RenderUNICODE_Solid(font, unicode_text, @forecol);
end
else
begin
text := TTF_RenderUNICODE_Shaded( font, unicode_text, @forecol, @backcol);
end;}
end;
else
begin
image := nil;
{ This shouldn't happen }
end;
end;
if ( image = nil ) then
begin
//fprintf(stderr, 'Couldn't render text: %s', [SDL_GetError]);
TTF_CloseFont( font );
ShutDownApplication( 2 );
end;
w := image.w;
h := image.h;
(* Convert the image into an OpenGL texture *)
texture := SDL_GL_LoadTexture( image, texcoord );
(* Make texture coordinates easy to understand *)
texMinX := texcoord[ 0 ];
texMinY := texcoord[ 1 ];
texMaxX := texcoord[ 2 ];
texMaxY := texcoord[ 3 ];
(* We don't need the original image anymore *)
SDL_FreeSurface( image );
(* Make sure that the texture conversion is okay *)
if ( texture = 0 ) then
begin
exit;
end;
end;
end;
procedure DrawTextTexture;
begin
(* Show the image on the screen *)
SDL_GL_Enter2DMode;
glBindTexture( GL_TEXTURE_2D, texture );
glBegin( GL_TRIANGLE_STRIP );
glTexCoord2f( texMinX, texMinY );
glVertex2i( x, y );
glTexCoord2f( texMaxX, texMinY );
glVertex2i( x + w, y );
glTexCoord2f( texMinX, texMaxY );
glVertex2i( x, y + h );
glTexCoord2f( texMaxX, texMaxY );
glVertex2i( x + w, y + h );
glEnd;
SDL_GL_Leave2DMode;
end;
begin
{ Default is black and white }
forecol := black;
backcol := white;
for i := 1 to ParamCount - 1 do
begin
if ( ParamStr( i ) = '-solid' ) then
begin
rendersolid := true;
end
else if ( ParamStr( i ) = '-utf8' ) then
begin
rendertype := rtUTF8;
end
else if ( ParamStr( i ) = '-unicode' ) then
begin
rendertype := rtUnicode;
end
else if ( ParamStr( i ) = '-b' ) then
begin
renderstyle := renderstyle or TTF_STYLE_BOLD;
end
else if ( ParamStr( i ) = '-i' ) then
begin
renderstyle := renderstyle or TTF_STYLE_ITALIC;
end
else if ( ParamStr( i ) = '-u' ) then
begin
renderstyle := renderstyle or TTF_STYLE_UNDERLINE;
end
else if ( ParamStr( i ) = '-dump' ) then
begin
dump := true;
end
else if ( ParamStr( i ) = '-fgcol' ) then
begin
try
r := StrToInt( ParamStr( i + 1 ) );
g := StrToInt( ParamStr( i + 2 ) );
b := StrToInt( ParamStr( i + 3 ) );
except
ShutDownApplication( 1 )
end;
forecol.r := r;
forecol.g := g;
forecol.b := b;
end
else if ( ParamStr( i ) = '-bgcol' ) then
begin
try
r := StrToInt( ParamStr( i + 1 ) );
g := StrToInt( ParamStr( i + 2 ) );
b := StrToInt( ParamStr( i + 3 ) );
except
ShutDownApplication( 1 )
end;
forecol.r := r;
forecol.g := g;
forecol.b := b;
end
else if ( ParamStr( i ) = '-ptsize' ) then
begin
ptsize := StrToInt( ParamStr( i + 1 ) );
end
else if ( ParamStr( i ) = '-text' ) then
begin
message := ParamStr( i + 1 );
end
else
begin
//fprintf(stderr, Usage, argv0);
end;
end;
{ Check usage }
{if ( ! argv[ 0 ] ) {
fprintf(stderr, Usage, argv0);
return(1);
}
{ Initialize SDL }
if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) then
begin
//fprintf(stderr, 'Couldn't initialize SDL: %s',SDL_GetError);
SDL_Quit;
end;
{ Initialize the TTF library }
if ( TTF_Init < 0 ) then
begin
//fprintf(stderr, 'Couldn't initialize TTF: %s',SDL_GetError);
SDL_Quit;
end;
//atexit( TTF_Quit );
{ Open the font file with the requested point size }
if ( ptsize = 0 ) then
begin
//i := 2;
ptsize := DEFAULT_PTSIZE;
end
else
begin
//i := 3;
end;
font := TTF_OpenFont( PChar( ParamStr( 1 ) ), ptsize );
if ( font = nil ) then
begin
//fprintf(stderr, 'Couldn''t load %d pt font from %s: %s',[ptsize, ParamStr(0), SDL_GetError]);
ShutDownApplication( 2 )
end;
TTF_SetFontStyle( font, renderstyle );
if ( dump ) then
begin
for i := 48 to 122 do
begin
glyph := TTF_RenderGlyph_Shaded( font, i, forecol, backcol );
if ( glyph <> nil ) then
begin
outname := 'glyph-' + IntToStr( i ) + '.bmp';
SDL_SaveBMP( glyph, PChar( outname ) );
end;
end;
ShutDownApplication( 0 );
exit;
end;
{ Set a 640x480x8 video mode }
screen := SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_OPENGL );
if ( screen = nil ) then
begin
//fprintf(stderr, 'Couldn''t set 640x480 OpenGL mode: %s', SDL_GetError);
ShutDownApplication( 2 );
end;
// Set the window manager title bar
SDL_WM_SetCaption( 'JEDI-SDL 3D TTF Demo', 'glfont' );
{ Render and center the message }
if ( message = '' ) then
begin
message := DEFAULT_TEXT;
end;
{ Initialize the GL state }
glViewport( 0, 0, screen.w, screen.h );
glMatrixMode( GL_PROJECTION );
glLoadIdentity;
// This makes the cube rectangular on screen */
//glOrtho( -2.0, 2.0, -2.0, 2.0, -20.0, 20.0 );
// This makes the cube equisided on screen */
aspect := 2.0 / ( screen.w / screen.h );
glOrtho( -2.0, 2.0, -aspect, aspect, -20.0, 20.0 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity;
glEnable( GL_DEPTH_TEST );
glDepthFunc( GL_LESS );
glShadeModel( GL_SMOOTH );
CreateTextTexture;
{ Wait for a keystroke, and blit text on mouse press }
done := false;
while ( not done ) do
begin
while ( SDL_PollEvent( @event ) <> 0 ) do
begin
case event.type_ of
SDL_MOUSEBUTTONDOWN :
begin
x := event.motion.x - w div 2;
y := event.motion.y - h div 2;
end;
SDL_KEYDOWN, SDL_QUITEV :
begin
done := true;
end;
end;
end;
{ Clear the screen }
glClearColor( 1.0, 1.0, 1.0, 1.0 );
glClear( GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT );
{ Draw the spinning cube }
glBegin( GL_QUADS );
{$IFDEF SHADED_CUBE}
glColor3fv( @color[ 0 ] );
glVertex3fv( @cube[ 0 ] );
glColor3fv( @color[ 1 ] );
glVertex3fv( @cube[ 1 ] );
glColor3fv( @color[ 2 ] );
glVertex3fv( @cube[ 2 ] );
glColor3fv( @color[ 3 ] );
glVertex3fv( @cube[ 3 ] );
glColor3fv( @color[ 3 ] );
glVertex3fv( @cube[ 3 ] );
glColor3fv( @color[ 4 ] );
glVertex3fv( @cube[ 4 ] );
glColor3fv( @color[ 7 ] );
glVertex3fv( @cube[ 7 ] );
glColor3fv( @color[ 2 ] );
glVertex3fv( @cube[ 2 ] );
glColor3fv( @color[ 0 ] );
glVertex3fv( @cube[ 0 ] );
glColor3fv( @color[ 5 ] );
glVertex3fv( @cube[ 5 ] );
glColor3fv( @color[ 6 ] );
glVertex3fv( @cube[ 6 ] );
glColor3fv( @color[ 1 ] );
glVertex3fv( @cube[ 1 ] );
glColor3fv( @color[ 5 ] );
glVertex3fv( @cube[ 5 ] );
glColor3fv( @color[ 4 ] );
glVertex3fv( @cube[ 4 ] );
glColor3fv( @color[ 7 ] );
glVertex3fv( @cube[ 7 ] );
glColor3fv( @color[ 6 ] );
glVertex3fv( @cube[ 6 ] );
glColor3fv( @color[ 5 ] );
glVertex3fv( @cube[ 5 ] );
glColor3fv( @color[ 0 ] );
glVertex3fv( @cube[ 0 ] );
glColor3fv( @color[ 3 ] );
glVertex3fv( @cube[ 3 ] );
glColor3fv( @color[ 4 ] );
glVertex3fv( @cube[ 4 ] );
glColor3fv( @color[ 6 ] );
glVertex3fv( @cube[ 6 ] );
glColor3fv( @color[ 1 ] );
glVertex3fv( @cube[ 1 ] );
glColor3fv( @color[ 2 ] );
glVertex3fv( @cube[ 2 ] );
glColor3fv( @color[ 7 ] );
glVertex3fv( @cube[ 7 ] );
{$ELSE} // flat cube
glColor3f( 1.0, 0.0, 0.0 );
glVertex3fv( @cube[ 0 ] );
glVertex3fv( @cube[ 1 ] );
glVertex3fv( @cube[ 2 ] );
glVertex3fv( @cube[ 3 ] );
glColor3f( 0.0, 1.0, 0.0 );
glVertex3fv( @cube[ 3 ] );
glVertex3fv( @cube[ 4 ] );
glVertex3fv( @cube[ 7 ] );
glVertex3fv( @cube[ 2 ] );
glColor3f( 0.0, 0.0, 1.0 );
glVertex3fv( @cube[ 0 ] );
glVertex3fv( @cube[ 5 ] );
glVertex3fv( @cube[ 6 ] );
glVertex3fv( @cube[ 1 ] );
glColor3f( 0.0, 1.0, 1.0 );
glVertex3fv( @cube[ 5 ] );
glVertex3fv( @cube[ 4 ] );
glVertex3fv( @cube[ 7 ] );
glVertex3fv( @cube[ 6 ] );
glColor3f( 1.0, 1.0, 0.0 );
glVertex3fv( @cube[ 5 ] );
glVertex3fv( @cube[ 0 ] );
glVertex3fv( @cube[ 3 ] );
glVertex3fv( @cube[ 4 ] );
glColor3f( 1.0, 0.0, 1.0 );
glVertex3fv( @cube[ 6 ] );
glVertex3fv( @cube[ 1 ] );
glVertex3fv( @cube[ 2 ] );
glVertex3fv( @cube[ 7 ] );
{$ENDIF} (* SHADED_CUBE *)
glEnd;
{ Rotate the cube }
glMatrixMode( GL_MODELVIEW );
glRotatef( 5.0, 1.0, 1.0, 1.0 );
{ Show the text on the screen }
DrawTextTexture;
{SDL_GL_Enter2DMode;
glBindTexture( GL_TEXTURE_2D, texture );
glBegin( GL_TRIANGLE_STRIP );
glTexCoord2f( texMinX, texMinY );
glVertex2i( x, y );
glTexCoord2f( texMaxX, texMinY );
glVertex2i( x + w, y );
glTexCoord2f( texMinX, texMaxY );
glVertex2i( x, y + h );
glTexCoord2f( texMaxX, texMaxY );
glVertex2i( x + w, y + h );
glEnd;
SDL_GL_Leave2DMode;}
{ Swap the buffers so everything is visible }
SDL_GL_SwapBuffers;
end;
TTF_CloseFont( font );
ShutDownApplication( 0 );
end.