/****************************************************************************/ /* */ /* 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 #include #include #include #include FT_FREETYPE_H #include FT_CACHE_H #include FT_CACHE_MANAGER_H #include FT_BITMAP_H #include "ftcommon.h" #include #include #include #include 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 */