// $Id: font.cpp,v 1.6 2004/11/23 18:19:27 philip Exp $ #include "stdafx.h" #include "wx/wx.h" #include "font.h" #include "freetype/ttunpat.h" #include #include "freetypedll.h" static char err[255]; // not exactly thread-safe FontRenderer::FontRenderer(const char* filename0, const char* filename1, double ptsize, bool unpatented_hinting, bool hinting) { char* err_str = SelectDLL(hinting ? 0 : 1); if (err_str) { sprintf(err, "Error loading FreeType DLL (freetype(a|b).dll (%d)): %s", hinting, err_str); throw err; } int error; error = DLLFT_Init_FreeType(&FontLibrary0); if (error) { throw "Error initialising FreeType"; } error = DLLFT_Init_FreeType(&FontLibrary1); if (error) { DLLFT_Done_FreeType(FontLibrary0); throw "Error initialising FreeType"; } FT_Parameter openparam = { FT_PARAM_TAG_UNPATENTED_HINTING, NULL }; FT_Open_Args args0 = { FT_OPEN_PATHNAME | (unpatented_hinting ? FT_OPEN_PARAMS : 0), NULL, NULL, (FT_String*)filename0, NULL, NULL, 1, &openparam }; FT_Open_Args args1 = { FT_OPEN_PATHNAME | (unpatented_hinting ? FT_OPEN_PARAMS : 0), NULL, NULL, (FT_String*)filename1, NULL, NULL, 1, &openparam }; error = DLLFT_Open_Face( FontLibrary0, &args0, 0, // index of face inside font file &FontFace0 ); if (error) { DLLFT_Done_FreeType(FontLibrary0); DLLFT_Done_FreeType(FontLibrary1); throw "Error loading primary font"; } error = DLLFT_Open_Face( FontLibrary1, &args1, 0, // index of face inside font file &FontFace1 ); if (error) { DLLFT_Done_Face(FontFace0); DLLFT_Done_FreeType(FontLibrary0); DLLFT_Done_FreeType(FontLibrary1); throw "Error loading secondary font face"; } error = DLLFT_Set_Char_Size( FontFace0, 0, // char_width in 1/64th of points (int)(ptsize*64.0 + 0.5), // char_height in 1/64th of points 96, // horizontal device resolution 96 // vertical device resolution ); if (error) { DLLFT_Done_Face(FontFace0); DLLFT_Done_Face(FontFace1); DLLFT_Done_FreeType(FontLibrary0); DLLFT_Done_FreeType(FontLibrary1); throw "Error loading scalable character from primary font - is this a TrueType font?"; } error = DLLFT_Set_Char_Size( FontFace1, 0, // char_width in 1/64th of points (int)(ptsize*64.0 + 0.5), // char_height in 1/64th of points 96, // horizontal device resolution 96 // vertical device resolution ); if (error) { DLLFT_Done_Face(FontFace0); DLLFT_Done_Face(FontFace1); DLLFT_Done_FreeType(FontLibrary0); DLLFT_Done_FreeType(FontLibrary1); throw "Error loading scalable character from secondary font - is this a TrueType font?"; } Boldness = 0; Italicness = 0; Tracking = 0; Leading = 0; } FontRenderer::~FontRenderer() { DLLFT_Done_Face(FontFace0); DLLFT_Done_Face(FontFace1); DLLFT_Done_FreeType(FontLibrary0); DLLFT_Done_FreeType(FontLibrary1); } #define deg2rad(a) ((double)a * 3.14159265358979323846 / 180.0) int FontRenderer::LoadGlyph(const int charcode) { FT_Face FontFace = FontFace0; int glyph_index = DLLFT_Get_Char_Index(FontFace0, charcode); Missing = false; if (glyph_index == 0) { // Can't find glyph in primary font - switch to secondary FontFace = FontFace1; glyph_index = DLLFT_Get_Char_Index(FontFace1, charcode); // Still can't find it - use the missing glyph symbol if (glyph_index == 0) { glyph_index = DLLFT_Get_Char_Index(FontFace1, 0xFFFD); Missing = true; } } int error; if (Italicness) { // Start with identity matrix (rotations could be added in here easily) FT_Matrix mat; mat.xx = mat.yy = (FT_Fixed)(0x10000L); mat.xy = mat.yx = (FT_Fixed)(0x00000L); // Apply shear FT_Fixed f = (FT_Fixed)(tan(deg2rad(Italicness)) * 0x10000L); mat.xy += DLLFT_MulFix(f, mat.xx); mat.yy += DLLFT_MulFix(f, mat.yx); DLLFT_Set_Transform(FontFace, &mat, 0); } error = DLLFT_Load_Glyph(FontFace, glyph_index, FT_LOAD_DEFAULT); if (error) throw "Error loading glyph"; if (Outline) { wxLogFatalError(wxT("Can't do this yet")); /* // Outline renderer which doesn't actually work: assert(FontFace->glyph->format == FT_GLYPH_FORMAT_OUTLINE); //FT_Outline *FontOutline = new FT_Outline; //error = FT_Outline_New(FontLibrary, 1024, 1024, FontOutline); //assert(!error); FT_Outline *FontOutline = &FontFace->glyph->outline; FT_BBox BBox; error = FT_Outline_Get_BBox(FontOutline, &BBox); assert(!error); // Translate back to (0,0) FT_Outline_Translate(FontOutline, -BBox.xMin, -BBox.yMin); FT_Bitmap *Bmp = new FT_Bitmap; Bmp->pixel_mode = FT_PIXEL_MODE_GRAY; Bmp->num_grays = 256; FontFace->glyph->bitmap_left = (BBox.xMin) >> 6; FontFace->glyph->bitmap_top = (BBox.yMax + 31) >> 6; Bmp->width = (BBox.xMax - BBox.xMin) >> 6; Bmp->pitch = 3*Bmp->width; Bmp->rows = (BBox.yMax - BBox.yMin) >> 6; Bmp->buffer = new unsigned char[Bmp->pitch * Bmp->rows]; memset(Bmp->buffer, 0, sizeof(unsigned char) * Bmp->pitch * Bmp->rows); error = FT_Outline_Get_Bitmap(FontLibrary, FontOutline, Bmp); assert(!error); FontFace->glyph->bitmap = *Bmp; error = FT_Outline_Done(FontLibrary, FontOutline); assert(!error); */ } else { // Simple version: error = DLLFT_Render_Glyph(FontFace->glyph, FT_RENDER_MODE_NORMAL); if (error) throw "Error rendering glyph"; } LastFontFace = FontFace; return glyph_index; } void FontRenderer::GetBoundingBox(int& width, int& height) { FT_Bitmap* bmp = &LastFontFace->glyph->bitmap; width = bmp->width + Boldness; height = bmp->rows; } void FontRenderer::RenderGlyph(unsigned char* rgb_buffer, int& pos_left, int& pos_top, int width, int height, int pitch, bool single_glyph) { FT_Face FontFace = LastFontFace; // This function only tries to work on 8-bit greyscale glyphs if (FontFace->glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY) throw "Error - invalid bitmap format"; // Add desired spacing, but not after zero-width combining glyphs if (!single_glyph && FontFace->glyph->advance.x) pos_left += Tracking; FT_Bitmap* bmp = &FontFace->glyph->bitmap; // Pointers to the appropriate row of pixel data unsigned char* ImageRow = rgb_buffer; unsigned char* GlyphRow = bmp->buffer; int Left = pos_left; int Top = pos_top; if (!single_glyph) { Left += FontFace->glyph->bitmap_left; Top -= FontFace->glyph->bitmap_top; } // Point to the first line that needs to be drawn if (Top >= 0) ImageRow += Top*pitch; for (int GlyphY = 0; GlyphY < bmp->rows; ++GlyphY) { // Clip vertically if (Top+GlyphY < 0 || Top+GlyphY >= height) { GlyphRow += bmp->pitch; continue; } for (int GlyphX = 0; GlyphX < bmp->width+Boldness; ++GlyphX) { // Clip horizontally if (Left+GlyphX < 0 || Left+GlyphX >= width) continue; int value = 0; if (! Boldness) { value = GlyphRow[GlyphX]; } else { // Glyph: 001122221100 // // Calc: 001122221100 // + 001122221100 // = 0012344432100 // ||__ __|| // row[0]+row[1] row[10]+row[11] // __| |__ // row[0] row[11] // // output[x] = sum row[x-boldness .. x] int min = GlyphX - Boldness; // inclusive if (min < 0) min = 0; int max = GlyphX+1; // not inclusive if (max > bmp->width) max = bmp->width; for (int x = min; x < max; ++x) value += GlyphRow[x]; } // Additive composition of pixel onto image buffer value += ImageRow[(Left+GlyphX)*3]; ImageRow[(Left+GlyphX)*3 ] = ImageRow[(Left+GlyphX)*3+1] = ImageRow[(Left+GlyphX)*3+2] = (unsigned char)(value > 255 ? 255 : value); } // Point everything at the next line ImageRow += pitch; GlyphRow += bmp->pitch; } if (!single_glyph) { // Advance the drawing-cursor ( >>6 due to 26.6-bit number format) pos_left += FontFace->glyph->advance.x >> 6; pos_top += FontFace->glyph->advance.y >> 6; } } int FontRenderer::GetLineSpacing() { return Leading + (FontFace0->size->metrics.height >> 6); } void FontRenderer::GetMetrics(int& offset_x, int& offset_y, int& advance) { offset_x = LastFontFace->glyph->bitmap_left; offset_y = LastFontFace->glyph->bitmap_top; advance = LastFontFace->glyph->advance.x >> 6; if (advance) offset_x += Tracking; } int FontRenderer::GetKerning(int left, int right) { FT_Vector vec; DLLFT_Get_Kerning(LastFontFace, left, right, FT_KERNING_DEFAULT, &vec); assert(vec.y == 0); return vec.x>>6; }