From b5c1a4e5673a7e80abc8b59d7bf87a9b123f5f2a Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 28 Mar 2000 11:15:37 +0000 Subject: [PATCH] a new program to demonstrate the new convenience glyph API (see include/ftglyph.h). Supports kerning, rotation, sub-pixel rendering.. Could be easily modified to reach the level of strtto when we have the relevant OpenType module handy.. --- demos/src/ftstring.c | 835 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 835 insertions(+) create mode 100644 demos/src/ftstring.c diff --git a/demos/src/ftstring.c b/demos/src/ftstring.c new file mode 100644 index 000000000..cd232ad16 --- /dev/null +++ b/demos/src/ftstring.c @@ -0,0 +1,835 @@ +/****************************************************************************/ +/* */ +/* The FreeType project -- a free and portable quality TrueType renderer. */ +/* */ +/* Copyright 1996-1999 by */ +/* D. Turner, R.Wilhelm, and W. Lemberg */ +/* */ +/* */ +/* FTString.c - simple text string display */ +/* */ +/****************************************************************************/ + +#include "freetype.h" +#include "ftglyph.h" +#include "common.h" + +#include +#include +#include +#include +#include + +#include "graph.h" +#include "grfont.h" + +#include "ftgrays.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 */ + + static char Header[128]; + static char* new_header = 0; + + static unsigned char* Text = "The quick brown fox jumps over the lazy dog"; + + static FT_Library library; /* the FreeType library */ + static FT_Face face; /* the font face */ + static FT_Error error; /* error returned by FreeType ? */ + + static grSurface* surface; /* current display surface */ + static grBitmap bit; /* current display bitmap */ + + static int ptsize; /* current point size */ + static int Num; + static int Rotation = 0; + static int Fail; + + static int hinted = 1; /* is glyph hinting active ? */ + static int antialias = 1; /* is anti-aliasing active ? */ + static int use_sbits = 1; /* do we use embedded bitmaps ? */ + static int kerning = 1; + + static int res = 72; /* default resolution in dpi */ + + static grColor fore_color = { 127 }; + + static int graph_init = 0; + static int render_mode = 1; + static int use_grays = 0; + + /* the standard raster's interface */ + static FT_Raster_Funcs std_raster; + + static FT_Matrix trans_matrix; + static int transform = 0; + + static FT_Vector string_center; + + typedef struct TGlyph_ + { + FT_UInt glyph_index; /* glyph index in face */ + FT_Vector pos; /* position of glyph origin */ + FT_Glyph image; /* glyph image */ + + } TGlyph, *PGlyph; + +#define FLOOR(x) ((x) & -64) +#define CEIL(x) (((x)+63) & -64) +#define TRUNC(x) ((x) >> 6) + + + +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ +/**** ****/ +/**** U T I L I T Y F U N C T I O N S ****/ +/**** ****/ +/**** ****/ +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ + +#define DEBUGxxx + +#ifdef DEBUG +#define LOG(x) LogMessage##x +#else +#define LOG(x) /* rien */ +#endif + +#ifdef DEBUG + static void LogMessage( const char* fmt, ... ) + { + va_list ap; + + va_start( ap, fmt ); + vfprintf( stderr, fmt, ap ); + va_end( ap ); + } +#endif + + /* PanicZ */ + static void PanicZ( const char* message ) + { + fprintf( stderr, "%s\n error = 0x%04x\n", message, error ); + exit(1); + } + + +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ +/**** ****/ +/**** D I S P L A Y M A N A G E M E N T ****/ +/**** ****/ +/**** ****/ +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ + +#define MAX_GLYPHS 512 + + /*********************************************************************** + * + * The following arrays are used to store the glyph set that makes + * up a string of text.. + * + */ + static TGlyph glyphs[ MAX_GLYPHS ]; + static int num_glyphs; + + + /************************************************************** + * + * Initialise the display surface + * + */ + static int init_display( void ) + { + grInitDevices(); + + bit.mode = gr_pixel_mode_gray; + bit.width = DIM_X; + bit.rows = DIM_Y; + bit.grays = 128; + + surface = grNewSurface( 0, &bit ); + if (!surface) + PanicZ( "could not allocate display surface\n" ); + + graph_init = 1; + return 0; + } + + /************************************************************** + * + * Clears the display surface + * + */ + static void clear_display( void ) + { + long size = (long)bit.pitch * bit.rows; + + if (size < 0) size = -size; + memset( bit.buffer, 0, size ); + } + + + static FT_Error reset_scale( int pointSize ) + { + FT_Error error; + + error = FT_Set_Char_Size( face, pointSize << 6, + pointSize << 6, + res, + res ); + return FT_Err_Ok; + } + + + /************************************************************** + * + * Compute the dimension of a string of glyphs in pixels + * + */ + static void compute_bbox( FT_BBox* abbox ) + { + PGlyph glyph = glyphs; + FT_BBox bbox; + int n; + + bbox.xMin = 32000; bbox.xMax = -32000; + bbox.yMin = 32000; bbox.yMax = -32000; + + for ( n = 0; n < num_glyphs; n++, glyph++ ) + { + FT_BBox cbox; + FT_Pos x, y; + + if (!glyph->image) continue; + + x = glyph->pos.x >> 6; + y = glyph->pos.y >> 6; + + FT_Glyph_Get_Box( glyph->image, &cbox ); + + cbox.xMin += x; + cbox.yMin += y; + cbox.xMax += x; + cbox.yMax += y; + + if (cbox.xMin < bbox.xMin) bbox.xMin = cbox.xMin; + if (cbox.xMax > bbox.xMax) bbox.xMax = cbox.xMax; + if (cbox.yMin < bbox.yMin) bbox.yMin = cbox.yMin; + if (cbox.yMax > bbox.yMax) bbox.yMax = cbox.yMax; + } + *abbox = bbox; + } + + + /************************************************************** + * + * Layout a string of glyphs + * + */ + static void layout_glyphs( void ) + { + PGlyph glyph = glyphs; + FT_Error error; + int n; + FT_Vector origin; + FT_Pos origin_x = 0; + FT_UInt load_flags; + FT_UInt num_grays; + FT_UInt prev_index = 0; + + load_flags = FT_LOAD_DEFAULT; + if( !hinted ) + load_flags |= FT_LOAD_NO_HINTING; + + num_grays = 128; + if (!antialias) + num_grays = 0; + + for ( n = 0; n < num_glyphs; n++, glyph++ ) + { + /* compute glyph origin */ + if (kerning) + { + if (prev_index) + { + FT_Vector kern; + + FT_Get_Kerning( face, prev_index, glyph->glyph_index, &kern ); + kern.x = FT_MulFix( kern.x, face->size->metrics.x_scale ); + if (hinted) kern.x = (kern.x+32) & -64; + + origin_x += kern.x; + } + prev_index = glyph->glyph_index; + } + + origin.x = origin_x; + origin.y = 0; + + if (transform) + FT_Vector_Transform( &origin, &trans_matrix ); + + /* clear existing image if there is one */ + if (glyph->image) + FT_Done_Glyph(glyph->image); + + /* load the glyph image */ + /* for now, we take a monochrome glyph bitmap */ + error = FT_Get_Glyph_Bitmap( face, glyph->glyph_index, + load_flags, + num_grays, + &origin, + (FT_BitmapGlyph*)&glyph->image ); + if (error) continue; + + glyph->pos = origin; + + origin_x += glyph->image->advance; + } + string_center.x = origin_x / 2; + string_center.y = 0; + if (transform) + FT_Vector_Transform( &string_center, &trans_matrix ); + } + + /************************************************************** + * + * Renders a given glyph vector set + * + */ + static void render_string( FT_Pos x, FT_Pos y ) + { + PGlyph glyph = glyphs; + grBitmap bit3; + int n; + + for ( n = 0; n < num_glyphs; n++, glyph++ ) + { + if (!glyph->image) + continue; + + switch (glyph->image->glyph_type) + { + case ft_glyph_type_bitmap: + { + /* this is a bitmap, we simply blit it to our target surface */ + FT_BitmapGlyph bitm = (FT_BitmapGlyph)glyph->image; + FT_Bitmap* source = &bitm->bitmap; + FT_Pos x_top, y_top; + + bit3.rows = source->rows; + bit3.width = source->width; + bit3.pitch = source->pitch; + bit3.buffer = source->buffer; + + switch (source->pixel_mode) + { + case ft_pixel_mode_mono: + bit3.mode = gr_pixel_mode_mono; + break; + + case ft_pixel_mode_grays: + bit3.mode = gr_pixel_mode_gray; + bit3.grays = source->num_grays; + break; + + default: + continue; + } + + /* now render the bitmap into the display surface */ + x_top = x + (glyph->pos.x >> 6) + bitm->left; + y_top = y - (glyph->pos.y >> 6) - bitm->top; + grBlitGlyphToBitmap( &bit, &bit3, x_top, y_top, fore_color ); + } + break; +#if 0 + case ft_glyph_type_outline: + { + /* in the case of outlines, we directly render it into the */ + /* target surface with the smooth renderer.. */ + FT_OutlineGlyph out = (FT_OutlineGlyph)glyph->image; + + FT_Outline_Translate( (x+pen_pos[n]) << 6, (y+ + error = FT_Outline_Render( + } + break; +#endif + default: + ; + } + } + } + + + /************************************************************** + * + * Convert a string of text into a glyph vector + * + * XXX: For now, we perform a trivial conversion + * + */ + static void prepare_text( const char* string ) + { + const unsigned char* p = (const unsigned char*)string; + PGlyph glyph = glyphs; + FT_UInt glyph_index; + + num_glyphs = 0; + while (*p) + { + glyph_index = FT_Get_Char_Index( face, (FT_ULong)*p ); + glyph->glyph_index = glyph_index; + glyph++; + num_glyphs++; + if (num_glyphs >= MAX_GLYPHS) + break; + p++; + } + } + + + static void reset_transform( void ) + { + double angle = Rotation*3.14159/64.0; + FT_Fixed cosinus = (FT_Fixed)(cos(angle)*65536.0); + FT_Fixed sinus = (FT_Fixed)(sin(angle)*65536.0); + + transform = (angle != 0); + trans_matrix.xx = cosinus; + trans_matrix.xy = -sinus; + trans_matrix.yx = sinus; + trans_matrix.yy = cosinus; + + FT_Set_Transform(face,&trans_matrix, 0); + } + +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ +/**** ****/ +/**** E V E N T H A N D L I N G ****/ +/**** ****/ +/**** ****/ +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ + + + static void Help( ) + { + grEvent dummy_event; + + clear_display(); + grGotoxy( 0, 0 ); + grSetMargin( 2, 1 ); + grGotobitmap( &bit ); + + grWriteln("FreeType String Viewer - part of the FreeType test suite" ); + grLn(); + grWriteln("This program is used to display a string of text using" ); + grWriteln("the new convenience API of the FreeType 2 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(" k : toggle kerning" ); + grWriteln(" g : toggle between 'smooth' and 'standard' anti-aliaser" ); + 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 : rotate counter-clockwise" ); + grWriteln(" Left : rotate clockwise" ); + grWriteln(" F7 : big rotate counter-clockwise"); + grWriteln(" F8 : big rotate clockwise"); + grLn(); + grWriteln("press any key to exit this help screen"); + + grRefreshSurface( surface ); + grListenSurface( surface, gr_event_key, &dummy_event ); + } + + + static void reset_raster( void ) + { + FT_Error error; + + error = 1; + if ( use_grays && antialias ) + error = FT_Set_Raster( library, &ft_grays_raster ); + + if (error) + (void)FT_Set_Raster( library, &std_raster ); + } + + + static int Process_Event( grEvent* event ) + { + int i; + + switch ( event->key ) + { + case grKeyEsc: /* ESC or q */ + case grKEY('q'): + return 0; + + case grKEY('k'): + kerning = !kerning; + new_header = ( kerning + ? "kerning is now active" + : "kerning is now ignored" ); + return 1; + + 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('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_Rotate; + case grKeyRight: i = 1; goto Do_Rotate; + case grKeyF7: i = -10; goto Do_Rotate; + case grKeyF8: i = 10; goto Do_Rotate; + default: + ; + } + return 1; + + Do_Rotate: + Rotation = (Rotation + i) & 127; + return 1; + + Do_Scale: + ptsize += i; + if (ptsize < 1) ptsize = 1; + if (ptsize > MAXPTSIZE) ptsize = MAXPTSIZE; + return 1; + +#if 0 + Do_Glyph: + Num += i; + if (Num < 0) Num = 0; + if (Num >= num_glyphs) Num = num_glyphs-1; + return 1; +#endif + } + + +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ +/**** ****/ +/**** M A I N P R O G R A M ****/ +/**** ****/ +/**** ****/ +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ + + + static void usage( char* execname ) + { + fprintf( stderr, "\n" ); + fprintf( stderr, "ftview: simple string 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, " -r R use resolution R dpi (default: 72 dpi)\n" ); + fprintf( stderr, " -m message message 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, "m:r:" ); + + if ( option == -1 ) + break; + + switch ( option ) + { + case 'r': + res = atoi( optarg ); + if ( res < 1 ) + usage( execname ); + break; + + case 'm': + if (argc < 3) + usage( execname ); + Text = optarg; + 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; + + /* Initialize engine */ + error = FT_Init_FreeType( &library ); + if (error) PanicZ( "Could not initialise FreeType library" ); + + /* retrieve the standard raster's interface */ + (void)FT_Get_Raster( library, ft_glyph_format_outline, &std_raster ); + + NewFile: + ptsize = orig_ptsize; + hinted = 1; + file_loaded = 0; + +#ifndef macintosh + i = strlen( argv[file] ); + while ( i > 0 && argv[file][i] != '\\' && argv[file][i] != '/' ) + { + if ( argv[file][i] == '.' ) + i = 0; + i--; + } +#endif + + filename[128] = '\0'; + alt_filename[128] = '\0'; + + strncpy( filename, argv[file], 128 ); + strncpy( alt_filename, argv[file], 128 ); + +#ifndef macintosh + 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; + + /* prepare the text to be rendered */ + prepare_text( Text ); + + file_loaded++; + + error = reset_scale( ptsize ); + if (error) goto Display_Font; + + Display_Font: + /* initialise graphics if needed */ + if ( !XisSetup ) + { + XisSetup = 1; + init_display(); + } + + grSetTitle( surface, "FreeType String 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 ) + { + /* layout & render string */ + { + FT_BBox bbox; + + reset_transform(); + layout_glyphs(); + compute_bbox( &bbox ); + render_string( (bit.width-(string_center.x >> 5))/2, + (bit.rows +(string_center.y >> 5))/2 ); + } + + 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, rotation = %d", + ptsize, + Rotation ); + } + else + { + sprintf( Header, "%s : is 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 Fin; + + 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; + } + } + + Fin: +#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 */ +