freetype/demos/src/ftview.c
David Turner f0df85ba2a - MAJOR INTERNAL REDESIGN:
A lot of internal modifications have been performed lately on the
    source in order to provide the following enhancements:

      - more generic module support:

        The FT_Module type is now defined to represent a handle to a given
        module. The file <freetype/ftmodule.h> contains the FT_Module_Class
        definition, as well as the module-loading public API

        The FT_Driver type is still defined, and still represents a pointer
        to a font driver. Note that FT_Add_Driver is replaced by FT_Add_Module,
        FT_Get_Driver by FT_Get_Module, etc..


      - support for generic glyph image types:

        The FT_Renderer type is a pointer to a module used to perform various
        operations on glyph image.

        Each renderer is capable of handling images in a single format
        (e.g. ft_glyph_format_outline). Its functions are used to:

           - transform an glyph image
           - render a glyph image into a bitmap
           - return the control box (dimensions) of a given glyph image


        The scan converters "ftraster.c" and "ftgrays.c" have been moved
        to the new directory "src/renderer", and are used to provide two
        default renderer modules.

        One corresponds to the "standard" scan-converter, the other to the
        "smooth" one.

        The current renderer can be set through the new function
        FT_Set_Renderer.

        The old raster-related function FT_Set_Raster, FT_Get_Raster and
        FT_Set_Raster_Mode have now disappeared, in favor of the new:

           FT_Get_Renderer
           FT_Set_Renderer

        see the file <freetype/ftrender.h> for more details..

        These changes were necessary to properly support different scalable
        formats in the future, like bi-color glyphs, etc..


      - glyph loader object:

        A new internal object, called a 'glyph loader' has been introduced
        in the base layer. It is used by all scalable format font drivers
        to load glyphs and composites.

        This object has been created to reduce the code size of each driver,
        as each one of them basically re-implemented its functionality.

        See <freetype/internal/ftobjs.h> and the FT_GlyphLoader type for
        more information..



      - FT_GlyphSlot had new fields:

        In order to support extended features (see below), the FT_GlyphSlot
        structure has a few new fields:

           linearHoriAdvance:  this field gives the linearly scaled (i.e.
                               scaled but unhinted) advance width for the glyph,
                               expressed as a 16.16 fixed pixel value. This
                               is useful to perform WYSIWYG text.

           linearVertAdvance:  this field gives the linearly scaled advance
                               height for the glyph (relevant in vertical glyph
                               layouts only). This is useful to perform
                               WYSIWYG text.

        Note that the two above field replace the removed "metrics2" field
        in the glyph slot.

           advance:   this field is a vector that gives the transformed
                      advance for the glyph. By default, it corresponds
                      to the advance width, unless FT_LOAD_VERTICAL_LAYOUT
                      was specified when calling FT_Load_Glyph or FT_Load_Char

           bitmap_left: this field gives the distance in integer pixels from
                        the current pen position to the left-most pixel of
                        a glyph image WHEN IT IS A BITMAP. It is only valid
                        when the "format" field is set to
                        "ft_glyph_format_bitmap", for example, after calling
                        the new function FT_Render_Glyph.

           bitmap_top:  this field gives the distance in integer pixels from
                        the current pen position (located on the baseline) to
                        the top-most pixel of the glyph image WHEN IT IS A
                        BITMAP. Positive values correspond to upwards Y.

           loader:  this is a new private field for the glyph slot. Client
                    applications should not touch it..


      - support for transforms and direct rendering in FT_Load_Glyph:

        Most of the functionality found in <freetype/ftglyph.h> has been
        moved to the core library. Hence, the following:

          - a transform can be specified for a face through FT_Set_Transform.
            this transform is applied by FT_Load_Glyph to scalable glyph images
            (i.e. NOT TO BITMAPS) before the function returns, unless the
            bit flag FT_LOAD_IGNORE_TRANSFORM was set in the load flags..


          - once a glyph image has been loaded, it can be directly converted to
            a bitmap by using the new FT_Render_Glyph function. Note that this
            function takes the glyph image from the glyph slot, and converts
            it to a bitmap whose properties are returned in "face.glyph.bitmap",
            "face.glyph.bitmap_left" and "face.glyph.bitmap_top". The original
            native image might be lost after the conversion.


          - when using the new bit flag FT_LOAD_RENDER, the FT_Load_Glyph
            and FT_Load_Char functions will call FT_Render_Glyph automatically
            when needed.
2000-06-22 00:17:42 +00:00

844 lines
20 KiB
C

/****************************************************************************/
/* */
/* The FreeType project -- a free and portable quality TrueType renderer. */
/* */
/* Copyright 1996-2000 by */
/* D. Turner, R.Wilhelm, and W. Lemberg */
/* */
/* */
/* FTView - a simple font viewer. */
/* */
/* This is a new version using the MiGS graphics subsystem for */
/* blitting and display. */
/* */
/* Press F1 when running this program to have a list of key-bindings */
/* */
/****************************************************************************/
#include <freetype/freetype.h>
#include <freetype/ftrender.h>
/* the following header shouldn't be used in normal programs */
#include <freetype/internal/ftdebug.h>
#include "common.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "graph.h"
#include "grfont.h"
#define DIM_X 500
#define DIM_Y 400
#define CENTER_X ( bit.width / 2 )
#define CENTER_Y ( bit.rows / 2 )
#define MAXPTSIZE 500 /* dtp */
char Header[128];
char* new_header = 0;
const unsigned char* Text = (unsigned char*)
"The quick brown fox jumped over the lazy dog 0123456789 "
"\342\352\356\373\364\344\353\357\366\374\377\340\371\351\350\347 "
"&#~\"\'(-`_^@)=+\260 ABCDEFGHIJKLMNOPQRSTUVWXYZ "
"$\243^\250*\265\371%!\247:/;.,?<>";
FT_Library library; /* the FreeType library */
FT_Face face; /* the font face */
FT_Size size; /* the font size */
FT_GlyphSlot glyph; /* the glyph slot */
FT_Error error; /* error code returned by FreeType */
grSurface* surface; /* current display surface */
grBitmap bit; /* current display bitmap */
int num_glyphs; /* number of glyphs */
int ptsize; /* current point size */
int hinted = 1; /* is glyph hinting active? */
int antialias = 0; /* is anti-aliasing active? */
int use_sbits = 1; /* do we use embedded bitmaps? */
int low_prec = 0; /* force low precision */
int Num; /* current first glyph index */
int res = 72;
static grColor fore_color = { 255 };
int Fail;
int graph_init = 0;
int render_mode = 1;
int use_grays = 1;
int debug = 0;
int trace_level = 0;
/* the standard raster's interface */
FT_Renderer std_renderer;
FT_Renderer smooth_renderer;
#define RASTER_BUFF_SIZE 32768
char raster_buff[RASTER_BUFF_SIZE];
#define LOG( x ) LogMessage##x
static
void LogMessage( const char* fmt, ... )
{
va_list ap;
va_start( ap, fmt );
vfprintf( stderr, fmt, ap );
va_end( ap );
}
/* PanicZ */
static
void PanicZ( const char* message )
{
fprintf( stderr, "%s\n error = 0x%04x\n", message, error );
exit( 1 );
}
/* Clears the Bit bitmap/pixmap */
static
void Clear_Display( void )
{
long size = (long)bit.pitch * bit.rows;
if ( size < 0 )
size = -size;
memset( bit.buffer, 0, size );
}
/* Initialize the display bitmap `bit' */
static
void Init_Display( void )
{
grInitDevices();
bit.mode = gr_pixel_mode_gray;
bit.width = DIM_X;
bit.rows = DIM_Y;
bit.grays = 256;
surface = grNewSurface( 0, &bit );
if ( !surface )
PanicZ( "could not allocate display surface\n" );
graph_init = 1;
}
#define MAX_BUFFER 300000
#define FLOOR( x ) ( (x) & -64 )
#define CEIL( x ) ( ( (x) + 63 ) & -64 )
#define TRUNC( x ) ( (x) >> 6 )
static
char bit_buffer[MAX_BUFFER];
/* Render a single glyph with the `grays' component */
static
FT_Error Render_Glyph( int x_offset,
int y_offset )
{
/* first, render the glyph into an intermediate buffer */
FT_Bitmap bit2;
grBitmap bit3;
int width, height, pitch, size;
int left, right, top, bottom;
int x_top, y_top;
left = FLOOR( glyph->metrics.horiBearingX );
right = CEIL( glyph->metrics.horiBearingX + glyph->metrics.width );
width = TRUNC( right - left );
top = CEIL( glyph->metrics.horiBearingY );
bottom = FLOOR( glyph->metrics.horiBearingY - glyph->metrics.height );
height = TRUNC( top - bottom );
if ( glyph->format == ft_glyph_format_outline )
{
pitch = antialias ? ( width + 3 ) & -4
: ( width + 7 ) >> 3;
size = pitch * height;
if ( size > MAX_BUFFER )
return FT_Err_Out_Of_Memory;
bit2.width = width;
bit2.rows = height;
bit2.pitch = pitch;
bit2.pixel_mode = antialias ? ft_pixel_mode_grays : ft_pixel_mode_mono;
bit2.buffer = bit_buffer;
bit3.rows = bit2.rows;
bit3.width = bit2.width;
bit3.pitch = bit2.pitch;
bit3.mode = antialias ? bit.mode : gr_pixel_mode_mono;
bit3.buffer = bit_buffer;
bit3.grays = 256;
FT_Outline_Translate( &glyph->outline, -left, -bottom );
memset( bit_buffer, 0, size );
if ( low_prec )
glyph->outline.flags &= ~ft_outline_high_precision;
error = FT_Outline_Get_Bitmap( library, &glyph->outline, &bit2 );
}
else
{
bit3.rows = glyph->bitmap.rows;
bit3.width = glyph->bitmap.width;
bit3.pitch = glyph->bitmap.pitch;
bit3.mode = gr_pixel_mode_mono;
bit3.buffer = glyph->bitmap.buffer;
bit3.grays = 0;
}
/* Then, blit the image to the target surface */
x_top = x_offset + TRUNC( left );
y_top = y_offset - TRUNC( top );
#if 0
if ( bit.pitch < 0 )
y_top = bit.rows - y_top;
#endif
grBlitGlyphToBitmap( &bit, &bit3, x_top, y_top, fore_color );
return 0;
}
static
FT_Error Reset_Scale( int pointSize )
{
FT_Error error;
error = FT_Set_Char_Size( face, pointSize << 6,
pointSize << 6,
res,
res );
if ( error )
{
/* to be written */
}
return FT_Err_Ok;
}
static
FT_Error LoadChar( int idx,
int hint )
{
int flags;
flags = FT_LOAD_DEFAULT;
if ( !hint )
flags |= FT_LOAD_NO_HINTING;
if ( !use_sbits )
flags |= FT_LOAD_NO_BITMAP;
return FT_Load_Glyph( face, idx, flags );
}
static
FT_Error Render_All( int first_glyph,
int ptsize )
{
FT_F26Dot6 start_x, start_y, step_x, step_y, x, y;
int i;
FT_Error error;
start_x = 4;
start_y = 16 + ptsize ;
step_x = size->metrics.x_ppem + 4;
step_y = size->metrics.y_ppem + 10;
x = start_x;
y = start_y;
i = first_glyph;
#if 0
while ( i < first_glyph + 1 )
#else
while ( i < num_glyphs )
#endif
{
if ( !( error = LoadChar( i, hinted ) ) )
{
if ( debug && trace_level > 1 )
{
if ( i <= first_glyph + 6 )
{
LOG(( "metrics[%02d] = [%x %x]\n",
i,
glyph->metrics.horiBearingX,
glyph->metrics.horiAdvance ));
if ( i == first_glyph + 6 )
LOG(( "-------------------------\n" ));
}
}
Render_Glyph( x, y );
x += ( glyph->metrics.horiAdvance >> 6 ) + 1;
if ( x + size->metrics.x_ppem > bit.width )
{
x = start_x;
y += step_y;
if ( y >= bit.rows )
return FT_Err_Ok;
}
}
else
Fail++;
i++;
}
return FT_Err_Ok;
}
static
FT_Error Render_Text( int first_glyph,
int ptsize )
{
FT_F26Dot6 start_x, start_y, step_x, step_y, x, y;
int i;
FT_Error error;
const unsigned char* p;
start_x = 4;
start_y = 12 + size->metrics.y_ppem;
step_x = size->metrics.x_ppem + 4;
step_y = size->metrics.y_ppem + 10;
x = start_x;
y = start_y;
i = first_glyph;
p = Text;
while ( i > 0 && *p )
{
p++;
i--;
}
while ( *p )
{
if ( !( error = LoadChar( FT_Get_Char_Index( face,
(unsigned char)*p ),
hinted ) ) )
{
if ( debug && trace_level > 1 )
{
if ( i <= first_glyph + 6 )
{
LOG(( "metrics[%02d] = [%x %x]\n",
i,
glyph->metrics.horiBearingX,
glyph->metrics.horiAdvance ));
if ( i == first_glyph + 6 )
LOG(( "-------------------------\n" ));
}
}
Render_Glyph( x, y );
x += ( glyph->metrics.horiAdvance >> 6 ) + 1;
if ( x + size->metrics.x_ppem > bit.width )
{
x = start_x;
y += step_y;
if ( y >= bit.rows )
return FT_Err_Ok;
}
}
else
Fail++;
i++;
p++;
}
return FT_Err_Ok;
}
static
void Help( void )
{
grEvent dummy_event;
Clear_Display();
grGotoxy( 0, 0 );
grSetMargin( 2, 1 );
grGotobitmap( &bit );
grWriteln("FreeType Glyph Viewer - part of the FreeType test suite" );
grLn();
grWriteln("This program is used to display all glyphs from one or" );
grWriteln("several font files, with the FreeType library.");
grLn();
grWriteln("Use the following keys :");
grLn();
grWriteln(" F1 or ? : display this help screen" );
grWriteln(" a : toggle anti-aliasing" );
grWriteln(" h : toggle outline hinting" );
grWriteln(" b : toggle embedded bitmaps" );
grWriteln(" l : toggle low precision rendering" );
grWriteln(" g : toggle between `smooth' and `standard' anti-aliaser" );
grWriteln(" space : toggle rendering mode" );
grLn();
grWriteln(" Up : increase pointsize by 1 unit" );
grWriteln(" Down : decrease pointsize by 1 unit" );
grWriteln(" Page Up : increase pointsize by 10 units" );
grWriteln(" Page Down : decrease pointsize by 10 units" );
grLn();
grWriteln(" Right : increment first glyph index" );
grWriteln(" Left : decrement first glyph index" );
grLn();
grWriteln(" F7 : decrement first glyph index by 10" );
grWriteln(" F8 : increment first glyph index by 10" );
grWriteln(" F9 : decrement first glyph index by 100");
grWriteln(" F10 : increment first glyph index by 100");
grWriteln(" F11 : decrement first glyph index by 1000");
grWriteln(" F12 : increment first glyph index by 1000");
grLn();
grWriteln("press any key to exit this help screen");
grRefreshSurface( surface );
grListenSurface( surface, gr_event_key, &dummy_event );
}
static
void reset_raster( void )
{
if ( antialias && use_grays && smooth_renderer )
FT_Set_Renderer( library, smooth_renderer, 0, 0 );
else
FT_Set_Renderer( library, std_renderer, 0, 0 );
}
static
int Process_Event( grEvent* event )
{
int i;
switch ( event->key )
{
case grKeyEsc: /* ESC or q */
case grKEY( 'q' ):
return 0;
case grKEY( 'a' ):
antialias = !antialias;
new_header = antialias ? "anti-aliasing is now on"
: "anti-aliasing is now off";
reset_raster();
return 1;
case grKEY( 'b' ):
use_sbits = !use_sbits;
new_header = use_sbits
? "embedded bitmaps are now used when available"
: "embedded bitmaps are now ignored";
return 1;
case grKEY( 'n' ):
case grKEY( 'p' ):
return (int)event->key;
case grKEY( 'g' ):
use_grays = !use_grays;
new_header = use_grays
? "now using the smooth anti-aliaser"
: "now using the standard anti-aliaser";
reset_raster();
break;
case grKEY( 'l' ):
low_prec = !low_prec;
new_header = low_prec
? "rendering precision is now forced to low"
: "rendering precision is now normal";
break;
case grKEY( 'h' ):
hinted = !hinted;
new_header = hinted ? "glyph hinting is now active"
: "glyph hinting is now ignored";
break;
case grKEY( ' ' ):
render_mode ^= 1;
new_header = render_mode ? "rendering all glyphs in font"
: "rendering test text string" ;
break;
case grKeyF1:
case grKEY( '?' ):
Help();
return 1;
#if 0
case grKeyF3: i = 16; goto Do_Rotate;
case grKeyF4: i = -16; goto Do_Rotate;
case grKeyF5: i = 1; goto Do_Rotate;
case grKeyF6: i = -1; goto Do_Rotate;
#endif
case grKeyPageUp: i = 10; goto Do_Scale;
case grKeyPageDown: i = -10; goto Do_Scale;
case grKeyUp: i = 1; goto Do_Scale;
case grKeyDown: i = -1; goto Do_Scale;
case grKeyLeft: i = -1; goto Do_Glyph;
case grKeyRight: i = 1; goto Do_Glyph;
case grKeyF7: i = -10; goto Do_Glyph;
case grKeyF8: i = 10; goto Do_Glyph;
case grKeyF9: i = -100; goto Do_Glyph;
case grKeyF10: i = 100; goto Do_Glyph;
case grKeyF11: i = -1000; goto Do_Glyph;
case grKeyF12: i = 1000; goto Do_Glyph;
default:
;
}
return 1;
#if 0
Do_Rotate:
Rotation = ( Rotation + i ) & 1023;
return 1;
#endif
Do_Scale:
ptsize += i;
if ( ptsize < 1 ) ptsize = 1;
if ( ptsize > MAXPTSIZE ) ptsize = MAXPTSIZE;
return 1;
Do_Glyph:
Num += i;
if ( Num < 0 ) Num = 0;
if ( Num >= num_glyphs ) Num = num_glyphs - 1;
return 1;
}
static
void usage( char* execname )
{
fprintf( stderr, "\n" );
fprintf( stderr, "ftview: simple glyph viewer -- part of the FreeType project\n" );
fprintf( stderr, "-----------------------------------------------------------\n" );
fprintf( stderr, "\n" );
fprintf( stderr, "Usage: %s [options below] ppem fontname[.ttf|.ttc] ...\n",
execname );
fprintf( stderr, "\n" );
fprintf( stderr, " -d enable debugging messages\n" );
fprintf( stderr, " -l N set debugging trace level to N (default: 0, max: 7)\n" );
fprintf( stderr, " -r R use resolution R dpi (default: 72 dpi)\n" );
fprintf( stderr, " -f index specify first glyph index to display\n" );
fprintf( stderr, "\n" );
exit( 1 );
}
int main( int argc,
char* argv[] )
{
int i, old_ptsize, orig_ptsize, file;
int first_glyph = 0;
int XisSetup = 0;
char filename[128 + 4];
char alt_filename[128 + 4];
char* execname;
int option;
int file_loaded;
FT_Error error;
grEvent event;
execname = ft_basename( argv[0] );
while ( 1 )
{
option = getopt( argc, argv, "df:l:r:" );
if ( option == -1 )
break;
switch ( option )
{
case 'd':
debug = 1;
break;
case 'f':
first_glyph = atoi( optarg );
break;
case 'l':
trace_level = atoi( optarg );
if ( trace_level < 1 || trace_level > 7 )
usage( execname );
break;
case 'r':
res = atoi( optarg );
if ( res < 1 )
usage( execname );
break;
default:
usage( execname );
break;
}
}
argc -= optind;
argv += optind;
if ( argc <= 1 )
usage( execname );
if ( sscanf( argv[0], "%d", &orig_ptsize ) != 1 )
orig_ptsize = 64;
file = 1;
if ( debug )
{
#ifdef FT_DEBUG_LEVEL_TRACE
FT_SetTraceLevel( trace_any, trace_level );
#else
trace_level = 0;
#endif
}
/* Initialize engine */
error = FT_Init_FreeType( &library );
if ( error )
PanicZ( "Could not initialize FreeType library" );
/* retrieve the standard raster's interface */
std_renderer = (FT_Renderer)FT_Get_Module( library, "standard renderer" );
if (!std_renderer)
PanicZ( "Could not retrieve standard renderer" );
smooth_renderer = (FT_Renderer)FT_Get_Module( library, "smooth renderer" );
NewFile:
ptsize = orig_ptsize;
hinted = 1;
file_loaded = 0;
filename[128] = '\0';
alt_filename[128] = '\0';
strncpy( filename, argv[file], 128 );
strncpy( alt_filename, argv[file], 128 );
/* try to load the file name as is, first */
error = FT_New_Face( library, argv[file], 0, &face );
if ( !error )
goto Success;
#ifndef macintosh
i = strlen( argv[file] );
while ( i > 0 && argv[file][i] != '\\' && argv[file][i] != '/' )
{
if ( argv[file][i] == '.' )
i = 0;
i--;
}
if ( i >= 0 )
{
strncpy( filename + strlen( filename ), ".ttf", 4 );
strncpy( alt_filename + strlen( alt_filename ), ".ttc", 4 );
}
#endif
/* Load face */
error = FT_New_Face( library, filename, 0, &face );
if ( error )
goto Display_Font;
Success:
file_loaded++;
error = Reset_Scale( ptsize );
if ( error )
goto Display_Font;
num_glyphs = face->num_glyphs;
glyph = face->glyph;
size = face->size;
Display_Font:
/* initialize graphics if needed */
if ( !XisSetup )
{
XisSetup = 1;
Init_Display();
}
grSetTitle( surface, "FreeType Glyph Viewer - press F1 for help" );
old_ptsize = ptsize;
if ( file_loaded >= 1 )
{
Fail = 0;
Num = first_glyph;
if ( Num >= num_glyphs )
Num = num_glyphs - 1;
if ( Num < 0 )
Num = 0;
}
for ( ;; )
{
int key;
Clear_Display();
if ( file_loaded >= 1 )
{
switch ( render_mode )
{
case 0:
Render_Text( Num, ptsize );
break;
default:
Render_All( Num, ptsize );
}
sprintf( Header, "%s %s (file `%s')",
face->family_name,
face->style_name,
ft_basename( filename ) );
if ( !new_header )
new_header = Header;
grWriteCellString( &bit, 0, 0, new_header, fore_color );
new_header = 0;
sprintf( Header, "at %d points, first glyph = %d",
ptsize,
Num );
}
else
sprintf( Header, "`%s': not a font file or could not be opened",
ft_basename( filename ) );
grWriteCellString( &bit, 0, 8, Header, fore_color );
grRefreshSurface( surface );
grListenSurface( surface, 0, &event );
if ( !( key = Process_Event( &event ) ) )
goto End;
if ( key == 'n' )
{
if (file_loaded >= 1)
FT_Done_Face( face );
if ( file < argc - 1 )
file++;
goto NewFile;
}
if ( key == 'p' )
{
if ( file_loaded >= 1 )
FT_Done_Face( face );
if ( file > 1 )
file--;
goto NewFile;
}
if ( ptsize != old_ptsize )
{
if ( Reset_Scale( ptsize ) )
PanicZ( "Could not resize font." );
old_ptsize = ptsize;
}
}
End:
#if 0
grDoneSurface(surface);
grDone();
#endif
printf( "Execution completed successfully.\n" );
printf( "Fails = %d\n", Fail );
exit( 0 ); /* for safety reasons */
return 0; /* never reached */
}
/* End */