summaryrefslogtreecommitdiffstats
path: root/kernel/kls_ttf/ftcommon.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/kls_ttf/ftcommon.cpp')
-rw-r--r--kernel/kls_ttf/ftcommon.cpp1333
1 files changed, 1333 insertions, 0 deletions
diff --git a/kernel/kls_ttf/ftcommon.cpp b/kernel/kls_ttf/ftcommon.cpp
new file mode 100644
index 0000000..d162452
--- /dev/null
+++ b/kernel/kls_ttf/ftcommon.cpp
@@ -0,0 +1,1333 @@
+/****************************************************************************/
+/* */
+/* The FreeType project -- a free and portable quality TrueType renderer. */
+/* */
+/* Copyright 2005, 2006 by */
+/* D. Turner, R.Wilhelm, and W. Lemberg */
+/* */
+/* */
+/* ftcommon.c - common routines for the FreeType demo programs. */
+/* */
+/****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#include FT_CACHE_H
+#include FT_CACHE_MANAGER_H
+
+#include FT_BITMAP_H
+
+#include "ftcommon.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+ FT_Error error;
+
+ /* PanicZ */
+ void
+ PanicZ( const char * )
+ {
+ /*fprintf( stderr, "%s\n error = 0x%04x\n", message, error );*/
+ exit( 1 );
+ }
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** DISPLAY-SPECIFIC DEFINITIONS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#define DIM_X 600
+#define DIM_Y 450
+
+ grBitmap*
+ FTDemo_Display_New(void)
+ {
+ grBitmap *bit = (grBitmap *)malloc(sizeof(grBitmap));
+
+ if(!bit)
+ return NULL;
+
+ bit->mode = gr_pixel_mode_rgb24;
+ bit->width = DIM_X;
+ bit->rows = DIM_Y;
+ bit->grays = 256;
+
+ grNewBitmap(bit->mode, bit->grays, bit->width, bit->rows, bit);
+
+ grSetGlyphGamma(1.0);
+
+ return bit;
+ }
+
+
+ void
+ FTDemo_Display_Done(grBitmap *bit)
+ {
+ grDoneBitmap(bit);
+ }
+
+ void
+ FTDemo_Display_Clear(grBitmap *bit)
+ {
+ int image_size = bit->width * bit->rows * 3;
+
+ memset(bit->buffer, 255, image_size);
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FREETYPE-SPECIFIC DEFINITIONS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+#define FLOOR( x ) ( (x) & -64 )
+#define CEIL( x ) ( ( (x) + 63 ) & -64 )
+#define ROUND( x ) ( ( (x) + 32 ) & -64 )
+#define TRUNC( x ) ( (x) >> 6 )
+
+
+
+ static
+ const char* file_suffixes[] =
+ {
+ ".ttf",
+ ".ttc",
+ ".otf",
+ ".pfa",
+ ".pfb",
+ 0
+ };
+
+
+ /*************************************************************************/
+ /* */
+ /* The face requester is a function provided by the client application */
+ /* to the cache manager, whose role is to translate an `abstract' face */
+ /* ID into a real FT_Face object. */
+ /* */
+ /* In this program, the face IDs are simply pointers to TFont objects. */
+ /* */
+ static FT_Error
+ my_face_requester( FTC_FaceID face_id,
+ FT_Library lib,
+ FT_Pointer request_data,
+ FT_Face* aface )
+ {
+ PFont font = (PFont)face_id;
+
+ FT_UNUSED( request_data );
+
+ if ( font->file_address != NULL )
+ error = FT_New_Memory_Face( lib, (FT_Byte*)font->file_address, font->file_size,
+ font->face_index, aface );
+ else
+ error = FT_New_Face( lib,
+ font->filepathname,
+ font->face_index,
+ aface );
+ if ( !error )
+ {
+ const char* suffix_rchr;
+ char orig[4];
+
+ suffix_rchr = strrchr( font->filepathname, '.' );
+ char* suffix = new char[strlen(font->filepathname) + 1];
+ strcpy(suffix, suffix_rchr);
+
+ if ( suffix && ( strcasecmp( suffix, ".pfa" ) == 0 ||
+ strcasecmp( suffix, ".pfb" ) == 0 ) )
+ {
+ suffix++;
+
+ memcpy( orig, suffix, 4 );
+ memcpy( suffix, "afm", 4 );
+ FT_Attach_File( *aface, font->filepathname );
+
+ memcpy( suffix, "pfm", 4 );
+ FT_Attach_File( *aface, font->filepathname );
+ memcpy( suffix, orig, 4 );
+ }
+
+ if ( (*aface)->charmaps )
+ (*aface)->charmap = (*aface)->charmaps[font->cmap_index];
+ }
+
+ return error;
+ }
+
+
+ FTDemo_Handle*
+ FTDemo_New( FT_Encoding encoding )
+ {
+ FTDemo_Handle* handle;
+
+
+ handle = (FTDemo_Handle *)malloc( sizeof( FTDemo_Handle ) );
+ if ( !handle )
+ return NULL;
+
+ memset( handle, 0, sizeof( FTDemo_Handle ) );
+
+ error = FT_Init_FreeType( &handle->library );
+ if ( error )
+ PanicZ( "could not initialize FreeType" );
+
+ error = FTC_Manager_New( handle->library, 0, 0, 0,
+ my_face_requester, 0, &handle->cache_manager );
+ if ( error )
+ PanicZ( "could not initialize cache manager" );
+
+ error = FTC_SBitCache_New( handle->cache_manager, &handle->sbits_cache );
+ if ( error )
+ PanicZ( "could not initialize small bitmaps cache" );
+
+ error = FTC_ImageCache_New( handle->cache_manager, &handle->image_cache );
+ if ( error )
+ PanicZ( "could not initialize glyph image cache" );
+
+ error = FTC_CMapCache_New( handle->cache_manager, &handle->cmap_cache );
+ if ( error )
+ PanicZ( "could not initialize charmap cache" );
+
+
+ FT_Bitmap_New( &handle->bitmap );
+
+ handle->encoding = encoding;
+
+ handle->hinted = 1;
+ handle->antialias = 1;
+ handle->use_sbits = 1;
+ handle->low_prec = 0;
+ handle->autohint = 0;
+ handle->lcd_mode = 0;
+
+ handle->use_sbits_cache = 1;
+
+ /* string_init */
+ memset( handle->string, 0, sizeof( TGlyph ) * MAX_GLYPHS );
+ handle->string_length = 0;
+ handle->string_reload = 1;
+
+ return handle;
+ }
+
+
+ void
+ FTDemo_Done( FTDemo_Handle* handle )
+ {
+ int i;
+
+
+ for ( i = 0; i < handle->max_fonts; i++ )
+ {
+ if ( handle->fonts[i] )
+ {
+ if ( handle->fonts[i]->filepathname )
+ free( (void*)handle->fonts[i]->filepathname );
+ free( handle->fonts[i] );
+ }
+ }
+ free( handle->fonts );
+
+ /* string_done */
+ for ( i = 0; i < MAX_GLYPHS; i++ )
+ {
+ PGlyph glyph = handle->string + i;
+
+
+ if ( glyph->image )
+ FT_Done_Glyph( glyph->image );
+ }
+
+ FT_Bitmap_Done( handle->library, &handle->bitmap );
+ FTC_Manager_Done( handle->cache_manager );
+ FT_Done_FreeType( handle->library );
+
+ free( handle );
+ }
+
+
+ FT_Error
+ FTDemo_Install_Font( FTDemo_Handle* handle,
+ const char* filepath )
+ {
+ static char filename[1024 + 5];
+ int i, len, num_faces;
+ FT_Face face;
+
+
+ len = strlen( filepath );
+ if ( len > 1024 )
+ len = 1024;
+
+ strncpy( filename, filepath, len );
+ filename[len] = 0;
+
+ error = FT_New_Face( handle->library, filename, 0, &face );
+
+#ifndef macintosh
+ /* could not open the file directly; we will now try various */
+ /* suffixes like `.ttf' or `.pfb' */
+ if ( error )
+ {
+ const char** suffix;
+ char* p;
+ int found = 0;
+
+ suffix = file_suffixes;
+ p = filename + len - 1;
+
+ while ( p >= filename && *p != '\\' && *p != '/' )
+ {
+ if ( *p == '.' )
+ break;
+
+ p--;
+ }
+
+ /* no suffix found */
+ if ( p < filename || *p != '.' )
+ p = filename + len;
+
+ for ( suffix = file_suffixes; suffix[0]; suffix++ )
+ {
+ /* try with current suffix */
+ strcpy( p, suffix[0] );
+
+ error = FT_New_Face( handle->library, filename, 0, &face );
+ if ( !error )
+ {
+ found = 1;
+
+ break;
+ }
+ }
+
+ /* really couldn't open this file */
+ if ( !found )
+ return error;
+ }
+#endif /* !macintosh */
+
+ /* allocate new font object */
+ num_faces = face->num_faces;
+ for ( i = 0; i < num_faces; i++ )
+ {
+ PFont font;
+
+
+ if ( i > 0 )
+ {
+ error = FT_New_Face( handle->library, filename, i, &face );
+ if ( error )
+ continue;
+ }
+
+ if ( handle->encoding != FT_ENCODING_NONE )
+ {
+ error = FT_Select_Charmap( face, handle->encoding );
+ if ( error )
+ {
+ FT_Done_Face( face );
+ return error;
+ }
+ }
+
+ font = (PFont)malloc( sizeof ( *font ) );
+
+ font->filepathname = (char*)malloc( strlen( filename ) + 1 );
+ strcpy( (char*)font->filepathname, filename );
+
+ font->face_index = i;
+ font->cmap_index = face->charmap ? FT_Get_Charmap_Index( face->charmap )
+ : 0;
+
+ if ( handle->preload )
+ {
+ FILE* file = fopen( filename, "rb" );
+ size_t file_size;
+
+ if ( file == NULL ) /* shouldn't happen */
+ {
+ free( font );
+ return FT_Err_Invalid_Argument;
+ }
+
+ fseek( file, 0, SEEK_END );
+ file_size = ftell( file );
+ fseek( file, 0, SEEK_SET );
+
+ font->file_address = malloc( file_size );
+ fread( font->file_address, 1, file_size, file );
+
+ font->file_size = file_size;
+
+ fclose( file );
+ }
+ else
+ {
+ font->file_address = NULL;
+ font->file_size = 0;
+ }
+
+ switch ( handle->encoding )
+ {
+ case FT_ENCODING_NONE:
+ font->num_indices = face->num_glyphs;
+ break;
+
+ case FT_ENCODING_UNICODE:
+ font->num_indices = 0x110000L;
+ break;
+
+ case FT_ENCODING_MS_SYMBOL:
+ case FT_ENCODING_ADOBE_LATIN_1:
+ case FT_ENCODING_ADOBE_STANDARD:
+ case FT_ENCODING_ADOBE_EXPERT:
+ case FT_ENCODING_ADOBE_CUSTOM:
+ case FT_ENCODING_APPLE_ROMAN:
+ font->num_indices = 0x100L;
+ break;
+
+ default:
+ font->num_indices = 0x10000L;
+ }
+
+ FT_Done_Face( face );
+ face = NULL;
+
+ if ( handle->max_fonts == 0 )
+ {
+ handle->max_fonts = 16;
+ handle->fonts = (PFont*)calloc( handle->max_fonts,
+ sizeof ( PFont ) );
+ }
+ else if ( handle->num_fonts >= handle->max_fonts )
+ {
+ handle->max_fonts *= 2;
+ handle->fonts = (PFont*)realloc( handle->fonts,
+ handle->max_fonts *
+ sizeof ( PFont ) );
+
+ memset( &handle->fonts[handle->num_fonts], 0,
+ ( handle->max_fonts - handle->num_fonts ) *
+ sizeof ( PFont ) );
+ }
+
+ handle->fonts[handle->num_fonts++] = font;
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ void
+ FTDemo_Set_Current_Font( FTDemo_Handle* handle,
+ PFont font )
+ {
+ handle->current_font = font;
+ handle->image_type.face_id = (FTC_FaceID)font;
+
+ handle->string_reload = 1;
+ }
+
+
+ void
+ FTDemo_Set_Current_Size( FTDemo_Handle* handle,
+ int pixel_size )
+ {
+ if ( pixel_size > 0xFFFF )
+ pixel_size = 0xFFFF;
+
+ handle->image_type.width = (FT_UShort)pixel_size;
+ handle->image_type.height = (FT_UShort)pixel_size;
+
+ handle->string_reload = 1;
+ }
+
+ void
+ FTDemo_Set_Preload( FTDemo_Handle* handle,
+ int preload )
+ {
+ handle->preload = !!preload;
+ }
+
+ void
+ FTDemo_Set_Current_Pointsize( FTDemo_Handle* handle,
+ int point_size,
+ int res )
+ {
+ FTDemo_Set_Current_Size( handle, ( point_size * res + 36 ) / 72 );
+ }
+
+
+ void
+ FTDemo_Update_Current_Flags( FTDemo_Handle* handle )
+ {
+ FT_UInt32 flags, target;
+
+ flags = FT_LOAD_DEFAULT; /* really 0 */
+
+ flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
+
+ if ( handle->autohint )
+ flags |= FT_LOAD_FORCE_AUTOHINT;
+
+ if ( !handle->use_sbits )
+ flags |= FT_LOAD_NO_BITMAP;
+
+ if ( handle->hinted )
+ {
+ target = 0;
+
+ if ( handle->antialias )
+ {
+ switch ( handle->lcd_mode )
+ {
+ case LCD_MODE_LIGHT:
+ target = FT_LOAD_TARGET_LIGHT;
+ break;
+
+ case LCD_MODE_RGB:
+ case LCD_MODE_BGR:
+ target = FT_LOAD_TARGET_LCD;
+ break;
+
+ case LCD_MODE_VRGB:
+ case LCD_MODE_VBGR:
+ target = FT_LOAD_TARGET_LCD_V;
+ break;
+
+ default:
+ target = FT_LOAD_TARGET_NORMAL;
+ }
+ }
+ else
+ target = FT_LOAD_TARGET_MONO;
+
+ flags |= target;
+ }
+ else
+ flags |= FT_LOAD_NO_HINTING;
+
+ handle->image_type.flags = flags;
+ handle->string_reload = 1;
+ }
+
+
+ FT_UInt
+ FTDemo_Get_Index( FTDemo_Handle* handle,
+ FT_UInt32 charcode )
+ {
+ FTC_FaceID face_id = handle->image_type.face_id;
+ PFont font = handle->current_font;
+
+
+ return FTC_CMapCache_Lookup( handle->cmap_cache, face_id,
+ font->cmap_index, charcode );
+ }
+
+
+ FT_Error
+ FTDemo_Get_Size( FTDemo_Handle* handle,
+ FT_Size* asize )
+ {
+ FTC_ScalerRec scaler;
+ FT_Size size;
+
+ scaler.face_id = handle->image_type.face_id;
+ scaler.width = handle->image_type.width;
+ scaler.height = handle->image_type.height;
+ scaler.pixel = 1;
+
+ error = FTC_Manager_LookupSize( handle->cache_manager, &scaler, &size );
+
+ if ( !error )
+ *asize = size;
+
+ return error;
+ }
+
+
+ FT_Error
+ FTDemo_Glyph_To_Bitmap( FTDemo_Handle* handle,
+ FT_Glyph glyf,
+ grBitmap* target,
+ int* left,
+ int* top,
+ int* x_advance,
+ int* y_advance,
+ FT_Glyph* aglyf )
+ {
+ FT_BitmapGlyph bitmap;
+ FT_Bitmap* source;
+
+
+ *aglyf = NULL;
+
+ error = FT_Err_Ok;
+
+ if ( glyf->format == FT_GLYPH_FORMAT_OUTLINE )
+ {
+ FT_Render_Mode render_mode = FT_RENDER_MODE_MONO;
+
+
+ if ( handle->antialias )
+ {
+ if ( handle->lcd_mode == 0 )
+ render_mode = FT_RENDER_MODE_NORMAL;
+ else if ( handle->lcd_mode == 1 )
+ render_mode = FT_RENDER_MODE_LIGHT;
+ else if ( handle->lcd_mode <= 3 )
+ render_mode = FT_RENDER_MODE_LCD;
+ else
+ render_mode = FT_RENDER_MODE_LCD_V;
+ }
+
+ /* render the glyph to a bitmap, don't destroy original */
+ error = FT_Glyph_To_Bitmap( &glyf, render_mode, NULL, 0 );
+ if ( error )
+ return error;
+
+ *aglyf = glyf;
+ }
+
+ if ( glyf->format != FT_GLYPH_FORMAT_BITMAP )
+ PanicZ( "invalid glyph format returned!" );
+
+ bitmap = (FT_BitmapGlyph)glyf;
+ source = &bitmap->bitmap;
+
+ target->rows = source->rows;
+ target->width = source->width;
+ target->pitch = source->pitch;
+ target->buffer = source->buffer;
+ target->grays = source->num_grays;
+
+ switch ( source->pixel_mode )
+ {
+ case FT_PIXEL_MODE_MONO:
+ target->mode = gr_pixel_mode_mono;
+ break;
+
+ case FT_PIXEL_MODE_GRAY:
+ target->mode = gr_pixel_mode_gray;
+ target->grays = source->num_grays;
+ break;
+
+ case FT_PIXEL_MODE_GRAY2:
+ case FT_PIXEL_MODE_GRAY4:
+ (void)FT_Bitmap_Convert( handle->library, source, &handle->bitmap, 1 );
+ target->pitch = handle->bitmap.pitch;
+ target->buffer = handle->bitmap.buffer;
+ target->mode = gr_pixel_mode_gray;
+ target->grays = handle->bitmap.num_grays;
+ break;
+
+ case FT_PIXEL_MODE_LCD:
+ target->mode = handle->lcd_mode == 2 ? gr_pixel_mode_lcd
+ : gr_pixel_mode_lcd2;
+ target->grays = source->num_grays;
+ break;
+
+ case FT_PIXEL_MODE_LCD_V:
+ target->mode = handle->lcd_mode == 4 ? gr_pixel_mode_lcdv
+ : gr_pixel_mode_lcdv2;
+ target->grays = source->num_grays;
+ break;
+
+ default:
+ return FT_Err_Invalid_Glyph_Format;
+ }
+
+ *left = bitmap->left;
+ *top = bitmap->top;
+
+ *x_advance = ( glyf->advance.x + 0x8000 ) >> 16;
+ *y_advance = ( glyf->advance.y + 0x8000 ) >> 16;
+
+ return error;
+ }
+
+
+ FT_Error
+ FTDemo_Index_To_Bitmap( FTDemo_Handle* handle,
+ FT_ULong Index,
+ grBitmap* target,
+ int* left,
+ int* top,
+ int* x_advance,
+ int* y_advance,
+ FT_Glyph* aglyf )
+ {
+ int cached_bitmap = 1;
+
+ *aglyf = NULL;
+
+ /* use the SBits cache to store small glyph bitmaps; this is a lot */
+ /* more memory-efficient */
+ /* */
+ if ( handle->use_sbits_cache &&
+ handle->image_type.width < 48 &&
+ handle->image_type.height < 48 )
+ {
+ FTC_SBit sbit;
+ FT_Bitmap source;
+
+
+ error = FTC_SBitCache_Lookup( handle->sbits_cache,
+ &handle->image_type,
+ Index,
+ &sbit,
+ NULL );
+ if ( error )
+ goto Exit;
+
+ if ( sbit->buffer )
+ {
+ target->rows = sbit->height;
+ target->width = sbit->width;
+ target->pitch = sbit->pitch;
+ target->buffer = sbit->buffer;
+ target->grays = sbit->max_grays + 1;
+
+ switch ( sbit->format )
+ {
+ case FT_PIXEL_MODE_MONO:
+ target->mode = gr_pixel_mode_mono;
+ break;
+
+ case FT_PIXEL_MODE_GRAY:
+ target->mode = gr_pixel_mode_gray;
+ target->grays = sbit->max_grays + 1;
+ break;
+
+ case FT_PIXEL_MODE_GRAY2:
+ case FT_PIXEL_MODE_GRAY4:
+ source.rows = sbit->height;
+ source.width = sbit->width;
+ source.pitch = sbit->pitch;
+ source.buffer = sbit->buffer;
+ source.pixel_mode = sbit->format;
+
+ (void)FT_Bitmap_Convert( handle->library, &source,
+ &handle->bitmap, 1 );
+
+ target->pitch = handle->bitmap.pitch;
+ target->buffer = handle->bitmap.buffer;
+ target->mode = gr_pixel_mode_gray;
+ target->grays = handle->bitmap.num_grays;
+
+ cached_bitmap = 0;
+ break;
+
+ case FT_PIXEL_MODE_LCD:
+ target->mode = handle->lcd_mode == 2 ? gr_pixel_mode_lcd
+ : gr_pixel_mode_lcd2;
+ target->grays = sbit->max_grays + 1;
+ break;
+
+ case FT_PIXEL_MODE_LCD_V:
+ target->mode = handle->lcd_mode == 4 ? gr_pixel_mode_lcdv
+ : gr_pixel_mode_lcdv2;
+ target->grays = sbit->max_grays + 1;
+ break;
+
+ default:
+ return FT_Err_Invalid_Glyph_Format;
+ }
+
+
+ *left = sbit->left;
+ *top = sbit->top;
+ *x_advance = sbit->xadvance;
+ *y_advance = sbit->yadvance;
+
+ goto Exit;
+ }
+ }
+
+ /* otherwise, use an image cache to store glyph outlines, and render */
+ /* them on demand. we can thus support very large sizes easily.. */
+ {
+ FT_Glyph glyf;
+
+ error = FTC_ImageCache_Lookup( handle->image_cache,
+ &handle->image_type,
+ Index,
+ &glyf,
+ NULL );
+
+ if ( !error )
+ error = FTDemo_Glyph_To_Bitmap( handle, glyf, target, left, top,
+ x_advance, y_advance, aglyf );
+ }
+
+ Exit:
+
+#ifdef FT_RGB_FILTER_H
+ /* note that we apply the RGB filter to each cached glyph, which is
+ * a performance killer, but that's better than modifying the cache
+ * at the moment
+ */
+ if ( !error )
+ {
+ if ( target->mode == gr_pixel_mode_lcd ||
+ target->mode == gr_pixel_mode_lcdv )
+ {
+ /* copy the bitmap before filtering it, we don't want to touch
+ * the content of cache nodes at all
+ */
+ {
+ }
+ }
+ }
+
+#endif /* FT_RGB_FILTER_H */
+
+ /* don't accept a `missing' character with zero or negative width */
+ if ( Index == 0 && *x_advance <= 0 )
+ *x_advance = 1;
+
+ return error;
+ }
+
+
+ FT_Error
+ FTDemo_Draw_Index( FTDemo_Handle* handle,
+ grBitmap* bitmap,
+ int gindex,
+ int* pen_x,
+ int* pen_y )
+ {
+ int left, top, x_advance, y_advance;
+ grBitmap bit3;
+ FT_Glyph glyf;
+
+ grColor c;
+ memset(&c, 0, sizeof(grColor));
+
+ error = FTDemo_Index_To_Bitmap(handle, gindex, &bit3, &left, &top,
+ &x_advance, &y_advance, &glyf);
+ if(error)
+ return error;
+
+ /* now render the bitmap into the display surface */
+ grBlitGlyphToBitmap( bitmap, &bit3, *pen_x + left,
+ *pen_y - top, c );
+
+ if ( glyf )
+ FT_Done_Glyph( glyf );
+
+ *pen_x += x_advance + 1;
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_Error
+ FTDemo_Draw_Glyph( FTDemo_Handle* handle,
+ FTDemo_Display* display,
+ FT_Glyph glyph,
+ int* pen_x,
+ int* pen_y )
+ {
+ int left, top, x_advance, y_advance;
+ grBitmap bit3;
+ FT_Glyph glyf;
+
+
+ error = FTDemo_Glyph_To_Bitmap( handle, glyph, &bit3, &left, &top,
+ &x_advance, &y_advance, &glyf );
+ if ( error )
+ {
+ FT_Done_Glyph( glyph );
+
+ return error;
+ }
+
+ /* now render the bitmap into the display surface */
+ grBlitGlyphToBitmap( display->bitmap, &bit3, *pen_x + left,
+ *pen_y - top, display->fore_color );
+
+ if ( glyf )
+ FT_Done_Glyph( glyf );
+
+ *pen_x += x_advance + 1;
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_Error
+ FTDemo_Draw_Slot( FTDemo_Handle* handle,
+ FTDemo_Display* display,
+ FT_GlyphSlot slot,
+ int* pen_x,
+ int* pen_y )
+ {
+ FT_Glyph glyph;
+
+
+ error = FT_Get_Glyph( slot, &glyph );
+ if ( error )
+ return error;
+
+ error = FTDemo_Draw_Glyph( handle, display, glyph, pen_x, pen_y );
+
+ FT_Done_Glyph( glyph );
+
+ return error;
+ }
+
+
+ void
+ FTDemo_String_Set( FTDemo_Handle* handle,
+ const unsigned char* string )
+ {
+ const unsigned char* p = string;
+ unsigned long codepoint;
+ unsigned char in_code;
+ int expect;
+ PGlyph glyph = handle->string;
+
+
+ handle->string_length = 0;
+ codepoint = expect = 0;
+
+ while ( *p )
+ {
+ in_code = *p++ ;
+
+ if ( in_code >= 0xC0 )
+ {
+ if ( in_code < 0xE0 ) /* U+0080 - U+07FF */
+ {
+ expect = 1;
+ codepoint = in_code & 0x1F;
+ }
+ else if ( in_code < 0xF0 ) /* U+0800 - U+FFFF */
+ {
+ expect = 2;
+ codepoint = in_code & 0x0F;
+ }
+ else if ( in_code < 0xF8 ) /* U+10000 - U+10FFFF */
+ {
+ expect = 3;
+ codepoint = in_code & 0x07;
+ }
+ continue;
+ }
+ else if ( in_code >= 0x80 )
+ {
+ --expect;
+
+ if ( expect >= 0 )
+ {
+ codepoint <<= 6;
+ codepoint += in_code & 0x3F;
+ }
+ if ( expect > 0 )
+ continue;
+
+ expect = 0;
+ }
+ else /* ASCII, U+0000 - U+007F */
+ codepoint = in_code;
+
+ if ( handle->encoding != FT_ENCODING_NONE )
+ glyph->glyph_index = FTDemo_Get_Index( handle, codepoint );
+ else
+ glyph->glyph_index = codepoint;
+
+ glyph++;
+ handle->string_length++;
+
+ if ( handle->string_length >= MAX_GLYPHS )
+ break;
+ }
+
+ handle->string_reload = 1;
+ }
+
+
+ static FT_Error
+ string_load( FTDemo_Handle* handle )
+ {
+ int n;
+ FT_Size size;
+ FT_Face face;
+ FT_Pos prev_rsb_delta = 0;
+
+
+ error = FTDemo_Get_Size( handle, &size );
+ if ( error )
+ return error;
+
+ face = size->face;
+
+ for ( n = 0; n < handle->string_length; n++ )
+ {
+ PGlyph glyph = handle->string + n;
+
+
+ /* clear existing image if there is one */
+ if ( glyph->image )
+ {
+ FT_Done_Glyph( glyph->image );
+ glyph->image = NULL;
+ }
+
+ /* load the glyph and get the image */
+ if ( !FT_Load_Glyph( face, glyph->glyph_index,
+ handle->image_type.flags ) &&
+ !FT_Get_Glyph( face->glyph, &glyph->image ) )
+ {
+ FT_Glyph_Metrics* metrics = &face->glyph->metrics;
+
+
+ /* note that in vertical layout, y-positive goes downwards */
+
+ glyph->vvector.x = metrics->vertBearingX - metrics->horiBearingX;
+ glyph->vvector.y = -metrics->vertBearingY - metrics->horiBearingY;
+
+ glyph->vadvance.x = 0;
+ glyph->vadvance.y = -metrics->vertAdvance;
+
+ if ( prev_rsb_delta - face->glyph->lsb_delta >= 32 )
+ glyph->delta = -1 << 6;
+ else if ( prev_rsb_delta - face->glyph->lsb_delta < -32 )
+ glyph->delta = 1 << 6;
+ else
+ glyph->delta = 0;
+ }
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_Error
+ string_render_prepare( FTDemo_Handle* handle,
+ FTDemo_String_Context* sc,
+ FT_Vector* advances )
+ {
+ FT_Face face;
+ FT_Size size;
+ PGlyph glyph;
+ FT_Pos track_kern = 0;
+ FT_UInt prev_index = 0;
+ FT_Vector* prev_advance = NULL;
+ FT_Vector extent = {0, 0};
+ FT_Int i;
+
+
+ error = FTDemo_Get_Size( handle, &size );
+ if ( error )
+ return error;
+
+ face = size->face;
+
+ if ( !sc->vertical && sc->kerning_degree )
+ {
+ FT_Fixed ptsize;
+
+
+ ptsize = FT_MulFix( face->units_per_EM, face->size->metrics.x_scale );
+
+ if ( FT_Get_Track_Kerning( face, ptsize << 10,
+ -sc->kerning_degree,
+ &track_kern ) )
+ track_kern = 0;
+ else
+ track_kern >>= 10;
+ }
+
+ for ( i = 0; i < handle->string_length; i++ )
+ {
+ glyph = handle->string + i;
+
+ if ( !glyph->image )
+ continue;
+
+ if ( sc->vertical )
+ advances[i] = glyph->vadvance;
+ else
+ {
+ advances[i] = glyph->image->advance;
+ advances[i].x >>= 10;
+ advances[i].y >>= 10;
+
+ if ( prev_advance )
+ {
+ prev_advance->x += track_kern;
+
+ if ( sc->kerning_mode )
+ {
+ FT_Vector kern;
+
+
+ FT_Get_Kerning( face, prev_index, glyph->glyph_index,
+ FT_KERNING_UNFITTED, &kern );
+
+ prev_advance->x += kern.x;
+ prev_advance->y += kern.y;
+
+ if ( sc->kerning_mode > KERNING_MODE_NORMAL )
+ prev_advance->x += glyph->delta;
+ }
+ }
+ }
+
+ if ( prev_advance )
+ {
+ if ( handle->hinted )
+ {
+ prev_advance->x = ROUND( prev_advance->x );
+ prev_advance->y = ROUND( prev_advance->y );
+ }
+
+ extent.x += prev_advance->x;
+ extent.y += prev_advance->y;
+ }
+
+ prev_index = glyph->glyph_index;
+ prev_advance = advances + i;
+ }
+
+ if ( prev_advance )
+ {
+ if ( handle->hinted )
+ {
+ prev_advance->x = ROUND( prev_advance->x );
+ prev_advance->y = ROUND( prev_advance->y );
+ }
+
+ extent.x += prev_advance->x;
+ extent.y += prev_advance->y;
+
+ /*store the extent in the last slot */
+ i = handle->string_length - 1;
+ advances[i] = extent;
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ static void
+ gamma_ramp_apply( FT_Byte gamma_ramp[256],
+ grBitmap* bitmap )
+ {
+ int i, j;
+ FT_Byte* p = (FT_Byte*)bitmap->buffer;
+
+ if ( bitmap->pitch < 0 )
+ p += -bitmap->pitch * ( bitmap->rows - 1 );
+
+ for ( i = 0; i < bitmap->rows; i++ )
+ {
+ for ( j = 0; j < bitmap->width; j++ )
+ p[j] = gamma_ramp[p[j]];
+
+ p += bitmap->pitch;
+ }
+ }
+
+
+ FT_Error
+ FTDemo_String_Draw( FTDemo_Handle* handle,
+ FTDemo_Display* display,
+ FTDemo_String_Context* sc,
+ int x,
+ int y )
+ {
+ int n;
+ FT_Vector pen, advances[MAX_GLYPHS];
+ FT_Size size;
+ FT_Face face;
+
+
+ if ( !sc ||
+ x < 0 ||
+ y < 0 ||
+ x > display->bitmap->width ||
+ y > display->bitmap->rows )
+ return FT_Err_Invalid_Argument;
+
+ error = FTDemo_Get_Size( handle, &size );
+ if ( error )
+ return error;
+
+ face = size->face;
+
+ if ( handle->string_reload )
+ {
+ error = string_load( handle );
+ if ( error )
+ return error;
+
+ handle->string_reload = 0;
+ }
+
+ error = string_render_prepare( handle, sc, advances );
+ if ( error )
+ return error;
+
+ /* change to Cartesian coordinates */
+ y = display->bitmap->rows - y;
+
+ /* get the extent, which we store in the last slot */
+ pen = advances[handle->string_length - 1];
+
+ pen.x = FT_MulFix( pen.x, sc->center );
+ pen.y = FT_MulFix( pen.y, sc->center );
+
+ /* XXX sbits */
+ /* get pen position */
+ if ( sc->matrix && FT_IS_SCALABLE( face ) )
+ {
+ FT_Vector_Transform( &pen, sc->matrix );
+ pen.x = ( x << 6 ) - pen.x;
+ pen.y = ( y << 6 ) - pen.y;
+ }
+ else
+ {
+ pen.x = ROUND( ( x << 6 ) - pen.x );
+ pen.y = ROUND( ( y << 6 ) - pen.y );
+ }
+
+ for ( n = 0; n < handle->string_length; n++ )
+ {
+ PGlyph glyph = handle->string + n;
+ FT_Glyph image;
+ FT_BBox bbox;
+
+
+ if ( !glyph->image )
+ continue;
+
+ /* copy image */
+ error = FT_Glyph_Copy( glyph->image, &image );
+ if ( error )
+ continue;
+
+ if ( image->format != FT_GLYPH_FORMAT_BITMAP )
+ {
+ if ( sc->vertical )
+ error = FT_Glyph_Transform( image, NULL, &glyph->vvector );
+
+ if ( !error )
+ error = FT_Glyph_Transform( image, sc->matrix, &pen );
+
+ if ( error )
+ {
+ FT_Done_Glyph( image );
+ continue;
+ }
+ }
+ else
+ {
+ FT_BitmapGlyph bitmap = (FT_BitmapGlyph)image;
+
+
+ if ( sc->vertical )
+ {
+ bitmap->left += ( glyph->vvector.x + pen.x ) >> 6;
+ bitmap->top += ( glyph->vvector.x + pen.y ) >> 6;
+ }
+ else
+ {
+ bitmap->left += pen.x >> 6;
+ bitmap->top += pen.y >> 6;
+ }
+ }
+
+ if ( sc->matrix )
+ FT_Vector_Transform( advances + n, sc->matrix );
+
+ pen.x += advances[n].x;
+ pen.y += advances[n].y;
+
+ FT_Glyph_Get_CBox( image, FT_GLYPH_BBOX_PIXELS, &bbox );
+
+#if 0
+ if ( n == 0 )
+ {
+ fprintf( stderr, "bbox = [%ld %ld %ld %ld]\n",
+ bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax );
+ }
+#endif
+
+ /* check bounding box; if it is completely outside the */
+ /* display surface, we don't need to render it */
+ if ( bbox.xMax > 0 &&
+ bbox.yMax > 0 &&
+ bbox.xMin < display->bitmap->width &&
+ bbox.yMin < display->bitmap->rows )
+ {
+ int left, top, dummy1, dummy2;
+ grBitmap bit3;
+ FT_Glyph glyf;
+
+
+ error = FTDemo_Glyph_To_Bitmap( handle, image, &bit3, &left, &top,
+ &dummy1, &dummy2, &glyf );
+ if ( !error )
+ {
+ if ( sc->gamma_ramp )
+ gamma_ramp_apply( sc->gamma_ramp, &bit3 );
+
+ /* change back to the usual coordinates */
+ top = display->bitmap->rows - top;
+
+ /* now render the bitmap into the display surface */
+ grBlitGlyphToBitmap( display->bitmap, &bit3, left, top,
+ display->fore_color );
+
+ if ( glyf )
+ FT_Done_Glyph( glyf );
+ }
+ }
+
+ FT_Done_Glyph( image );
+ }
+
+ return error;
+ }
+
+
+ FT_Encoding
+ FTDemo_Make_Encoding_Tag( const char* s )
+ {
+ int i;
+ unsigned long l = 0;
+
+
+ for ( i = 0; i < 4; i++ )
+ {
+ if ( !s[i] )
+ break;
+ l <<= 8;
+ l += (unsigned long)s[i];
+ }
+
+ return (FT_Encoding)l;
+ }
+
+
+/* End */