You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libksquirrel/kernel/kls_ttf/ftcommon.cpp

1334 lines
33 KiB

/****************************************************************************/
/* */
/* 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 */