diff --git a/source/tools/fontbuilder/FontBuilder.ico b/source/tools/fontbuilder/FontBuilder.ico new file mode 100755 index 0000000000..94eb6138c7 Binary files /dev/null and b/source/tools/fontbuilder/FontBuilder.ico differ diff --git a/source/tools/fontbuilder/FontBuilder.rc b/source/tools/fontbuilder/FontBuilder.rc new file mode 100755 index 0000000000..c770629f87 --- /dev/null +++ b/source/tools/fontbuilder/FontBuilder.rc @@ -0,0 +1,76 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON1 ICON "FontBuilder.ico" + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/source/tools/fontbuilder/FontBuilder.sln b/source/tools/fontbuilder/FontBuilder.sln new file mode 100755 index 0000000000..76b2c4254c --- /dev/null +++ b/source/tools/fontbuilder/FontBuilder.sln @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{EAF909A5-FA59-4C3D-9431-0FCC20D5BCF9}") = "FontBuilder", "FontBuilder.icproj", "{CFCF18FF-C3A4-4E76-BFA1-C5DB476E4687}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + OldRelease = OldRelease + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {CFCF18FF-C3A4-4E76-BFA1-C5DB476E4687}.Debug.ActiveCfg = Debug|Win32 + {CFCF18FF-C3A4-4E76-BFA1-C5DB476E4687}.Debug.Build.0 = Debug|Win32 + {CFCF18FF-C3A4-4E76-BFA1-C5DB476E4687}.OldRelease.ActiveCfg = Release|Win32 + {CFCF18FF-C3A4-4E76-BFA1-C5DB476E4687}.OldRelease.Build.0 = Release|Win32 + {CFCF18FF-C3A4-4E76-BFA1-C5DB476E4687}.Release.ActiveCfg = Release|Win32 + {CFCF18FF-C3A4-4E76-BFA1-C5DB476E4687}.Release.Build.0 = Release|Win32 + {277A0C35-0B44-48FC-81FD-119AD97194EA}.Release.Build.0 = Release|Win32 + {277A0C35-0B44-48FC-81FD-119AD97194EA}.Release.ActiveCfg = Release|Win32 + {277A0C35-0B44-48FC-81FD-119AD97194EA}.OldRelease.Build.0 = Release|Win32 + {277A0C35-0B44-48FC-81FD-119AD97194EA}.OldRelease.ActiveCfg = Release|Win32 + {277A0C35-0B44-48FC-81FD-119AD97194EA}.Debug.Build.0 = Debug|Win32 + {277A0C35-0B44-48FC-81FD-119AD97194EA}.Debug.ActiveCfg = Debug|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/source/tools/fontbuilder/FontBuilder.vcproj b/source/tools/fontbuilder/FontBuilder.vcproj new file mode 100755 index 0000000000..16d528ae62 --- /dev/null +++ b/source/tools/fontbuilder/FontBuilder.vcproj @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/tools/fontbuilder/fileformat.txt b/source/tools/fontbuilder/fileformat.txt new file mode 100755 index 0000000000..d93298c62d --- /dev/null +++ b/source/tools/fontbuilder/fileformat.txt @@ -0,0 +1,41 @@ +$Id: fileformat.txt,v 1.1 2004/06/17 19:32:04 philip Exp $ + + +Fonts consist of + fontname.fnt + fontname.tga +and you load fonts by just specifying the "path/fontname" part. + +The .tga is an 8-bit greyscale image, used for alpha-blending the text. +(DXTC allows 0.5 bytes/pixel at best, which isn't much better than +TGA's 1 byte/pixel, and DXTC's lossiness is almost noticeable.) + +The .fnt file is something like: + + 100 + 512 256 + 3 + 16 + 32 0 0 10 10 12 + 33 10 0 6 10 7 + 3300 16 0 6 10 7 + +First line = version number of file, for slightly better error +detection if people leave old ones hanging around. + +Second line = size of texture (width/height). + +Third line = number of glyphs in the font. + +Fourth line = line spacing (pixels between each baseline) + +Each subsequent line is + + + +0 <= Code point < 2^16. +x, y, width, height are in pixels on the texture. +x/y offset are the position of the texture relative to the glyph's origin. +advance is the number of pixels to move along for the next character. + +Currently no support for kerning. diff --git a/source/tools/fontbuilder/filemanip.cpp b/source/tools/fontbuilder/filemanip.cpp new file mode 100755 index 0000000000..20f5f89533 --- /dev/null +++ b/source/tools/fontbuilder/filemanip.cpp @@ -0,0 +1,126 @@ +// $Id: filemanip.cpp,v 1.1 2004/06/17 19:32:04 philip Exp $ + +#include "stdafx.h" + +#include "filemanip.h" + +// Ensure there's no padding added into the struct, +// in a very non-portable way +#pragma pack(push) +#pragma pack(1) +// For gcc, use "} TGA_HEADER __attribute__ ((packed));" instead of this + +typedef struct +{ + char identsize; // size of ID field that follows 18 byte header (0 usually) + char colourmaptype; // type of colour map 0=none, 1=has palette + char imagetype; // type of image 0=none,1=indexed,2=rgb,3=grey,+8=rle packed + + short colourmapstart; // first colour map entry in palette + short colourmaplength; // number of colours in palette + char colourmapbits; // number of bits per palette entry 15,16,24,32 + + short xstart; // image x origin + short ystart; // image y origin + short width; // image width in pixels + short height; // image height in pixels + char bits; // image bits per pixel 8,16,24,32 + char descriptor; // image descriptor bits (vh flip bits) +} TGA_HEADER; + +#pragma pack(pop) + +// Convert the RGB image to 8-bit greyscale and output +void RGB_OutputGreyscaleTGA(unsigned char* image_data, int width, int height, int pitch, wxFFile& file) +{ + assert(sizeof(TGA_HEADER) == 18); + + TGA_HEADER header; + + header.identsize = 0; + header.colourmaptype = 0; + header.imagetype = 3; + header.xstart = 0; + header.ystart = 0; + header.width = (short)width; + header.height = (short)height; + header.bits = 8; + header.descriptor = 0; + + file.Write(&header, sizeof(header)); + + // Start from the bottom, so things look the right way up + image_data += height*pitch; + + for (int y = 0; y < height; ++y) + { + image_data -= pitch; + + for (int x = 0; x < width; ++x) + file.Write(&image_data[x*3], 1); + } +} + +std::set AnalyseChars(wxString filename) +{ + wxFFile File (filename, "rb"); + if (! File.IsOpened()) + throw "Cannot open file"; + + enum { + UNKNOWN, + UTF8, + UTF16SE, // same-endian as this machine + UTF16DE // different-endian + } Format = UNKNOWN; + + unsigned short BOM; + File.Read(&BOM, 2); + if (BOM == 0xFEFF) + Format = UTF16SE; + else if (BOM == 0xFFFE) + Format = UTF16DE; + else { + // Make an educated guess based on the first byte + // If it's like "_\0" it's probably little-endian, + // if it's like "\0_" it's probably big-endian + if ((BOM & 0xFF00) == 0) + Format = UTF16SE; + else if ((BOM & 0x00FF) == 0) + Format = UTF16DE; + else + throw "Can't determine endianness of source file - make sure it's UTF16 and starts with a simple letter"; + // (Maybe it's UTF8, but I don't want to bother with that) + + File.Seek(0); // Make sure the first character is included in the list + } + + std::set Chars; + if (Format == UTF16SE) + { + wchar_t c; + while (! File.Eof()) + { + File.Read(&c, 2); + if (c != '\r' && c != '\n') + Chars.insert(c); + } + } + else if (Format == UTF16DE) + { + unsigned short c; + while (! File.Eof()) + { + if (File.Read(&c, 2) != 2) break; // abort if failed (i.e. eof) + c = (c>>8)|((c&0xff)<<8); // swap bytes + if (c != '\r' && c != '\n') + Chars.insert(c); + } + } + else + { + throw "Internal error - invalid file format"; + } + + return Chars; +} diff --git a/source/tools/fontbuilder/filemanip.h b/source/tools/fontbuilder/filemanip.h new file mode 100755 index 0000000000..fb0ad3ac02 --- /dev/null +++ b/source/tools/fontbuilder/filemanip.h @@ -0,0 +1,6 @@ +// $Id: filemanip.h,v 1.1 2004/06/17 19:32:04 philip Exp $ + +#include + +void RGB_OutputGreyscaleTGA(unsigned char* image_data, int width, int height, int pitch, wxFFile& file); +std::set AnalyseChars(wxString filename); diff --git a/source/tools/fontbuilder/font.cpp b/source/tools/fontbuilder/font.cpp new file mode 100755 index 0000000000..e31450635e --- /dev/null +++ b/source/tools/fontbuilder/font.cpp @@ -0,0 +1,305 @@ +// $Id: font.cpp,v 1.1 2004/06/17 19:32:04 philip Exp $ + +#include "stdafx.h" + +#include "font.h" +#include + +FontRenderer::FontRenderer(const char* filename0, const char* filename1, int ptsize) +{ + int error; + + error = FT_Init_FreeType(&FontLibrary0); + if (error) + { + throw "Error initialising FreeType"; + } + + error = FT_Init_FreeType(&FontLibrary1); + if (error) + { + FT_Done_FreeType(FontLibrary0); + throw "Error initialising FreeType"; + } + + error = FT_New_Face( + FontLibrary0, + filename0, + 0, // index of face inside font file + &FontFace0 + ); + if (error) + { + FT_Done_FreeType(FontLibrary0); + FT_Done_FreeType(FontLibrary1); + throw "Error loading primary font"; + } + + error = FT_New_Face( + FontLibrary1, + filename1, + 0, // index of face inside font file + &FontFace1 + ); + if (error) + { + FT_Done_Face(FontFace0); + FT_Done_FreeType(FontLibrary0); + FT_Done_FreeType(FontLibrary1); + throw "Error loading secondary font face"; + } + + error = FT_Set_Char_Size( + FontFace0, + 0, // char_width in 1/64th of points + ptsize*64, // char_height in 1/64th of points + 72, // horizontal device resolution + 72 // vertical device resolution + ); + if (error) + { + FT_Done_Face(FontFace0); + FT_Done_Face(FontFace1); + FT_Done_FreeType(FontLibrary0); + FT_Done_FreeType(FontLibrary1); + throw "Error loading scalable character from primary font - is this a TrueType font?"; + } + + error = FT_Set_Char_Size( + FontFace1, + 0, // char_width in 1/64th of points + ptsize*64, // char_height in 1/64th of points + 72, // horizontal device resolution + 72 // vertical device resolution + ); + if (error) + { + FT_Done_Face(FontFace0); + FT_Done_Face(FontFace1); + FT_Done_FreeType(FontLibrary0); + FT_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() +{ + FT_Done_Face(FontFace0); + FT_Done_Face(FontFace1); + FT_Done_FreeType(FontLibrary0); + FT_Done_FreeType(FontLibrary1); +} + +#define deg2rad(a) ((double)a * 3.14159265358979323846 / 180.0) + +void FontRenderer::LoadGlyph(const int charcode) +{ + FT_Face FontFace = FontFace0; + int glyph_index = FT_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 = FT_Get_Char_Index(FontFace1, charcode); + + // Still can't find it - use the missing glyph symbol + if (glyph_index == 0) + { + glyph_index = FT_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 += FT_MulFix(f, mat.xx); + mat.yy += FT_MulFix(f, mat.yx); + + FT_Set_Transform(FontFace, &mat, 0); + } + + error = FT_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 = FT_Render_Glyph(FontFace->glyph, FT_RENDER_MODE_NORMAL); + if (error) + throw "Error rendering glyph"; + } + + LastFontFace = FontFace; +} + + +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 = -Boldness; GlyphX < bmp->width+Boldness; ++GlyphX) + { + // Clip horizontally + if (Left+GlyphX < 0 || Left+GlyphX >= width) + continue; + + int value = 0; + + if (! Boldness) + { + value = GlyphRow[GlyphX]; + } + else + { + // Pretend to be bold - add lots of adjacent images + // Calculate value =~ sum(in[x-n] .. in[x+n]) + + // Work out valid limits for summing + // (with weird shifts so that it does precisely + // Boldness pixels) + int min = GlyphX - ( Boldness >> 1); + if (min < 0) min = 0; + int max = GlyphX + ( (Boldness+1) >> 1) + 1; + 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; +} diff --git a/source/tools/fontbuilder/font.h b/source/tools/fontbuilder/font.h new file mode 100755 index 0000000000..01e4baa280 --- /dev/null +++ b/source/tools/fontbuilder/font.h @@ -0,0 +1,90 @@ +// $Id: font.h,v 1.1 2004/06/17 19:32:04 philip Exp $ + +#ifndef _FONT_H_ +#define _FONT_H_ + +#include +#include FT_FREETYPE_H +#include FT_OUTLINE_H +#include FT_BBOX_H + + +// XXX: Kerning ( = hard? got to store giant table for OpenGL to use) + +// XXX: Right-to-left text + + + +// Make my IDE a little happier: +#if 0 + #include "freetype/freetype.h" + #include "freetype/freetype_defs.h" + #include "freetype/ftimage.h" + #include "freetype/fttypes.h" + #include "freetype/ftbbox.h" + #include "freetype/ftoutln.h" +#endif + +class FontRenderer +{ +public: + + // Two fonts are required - a primary (0) font which will be used first, + // and a secondary (1) font for filling in missing glyphs. + // (The secondary font should usually be Arial Unicode MS). + FontRenderer(const char* filename0, const char* filename1, int ptsize); + + ~FontRenderer(); + + // Generate the glyph for the given Unicode code point + void LoadGlyph(const int charcode); + + // Put the appropriate pixel sizes into width and height + void GetBoundingBox(int& width, int& height); + + // Copy the glyph onto a 24-bit RGB image buffer + // pos_left / pos_top: Position to put the origin (usually bottom left corner) of the glyph. + // Altered by the function to point towards the next character. + // width / height: of bitmap, to do clipping + // pitch: Bytes per row, including any padding + // single_glyph: If true, doesn't update the cursor and draws with the bounding box at pos_left/top + void RenderGlyph(unsigned char* rgb_buffer, int& pos_left, int& pos_top, int width, int height, int pitch, bool single_glyph); + + // Returns baseline-to-baseline distance, including the desired leading + int GetLineSpacing(); + + // Supplies some information for positioning glyphs + void GetMetrics(int& offset_x, int& offset_y, int& advance); + + // Set by LoadGlyph if it can't find the right glyph + bool Missing; + + ///////////////////////////////////////////////////////// + + // Font settings, set by users and used by this class: + + // Shear angle (in degrees) for fake italics + int Italicness; + + // 0 for standard; otherwise draws n+1 overlapping copies of the text + int Boldness; + + // Additional spacing between each non-zero-width glyph and the next + int Tracking; + + // Additional spacing between lines + int Leading; + + // Whether to draw an outlined font + bool Outline; + +private: + FT_Library FontLibrary0; + FT_Library FontLibrary1; + FT_Face FontFace0; + FT_Face FontFace1; + + FT_Face LastFontFace; // = FontFace(0|1), depending on the last LoadGlyph +}; + +#endif // _FONT_H_ diff --git a/source/tools/fontbuilder/fontbuilder.icproj b/source/tools/fontbuilder/fontbuilder.icproj new file mode 100755 index 0000000000..cbc2c0dfdc --- /dev/null +++ b/source/tools/fontbuilder/fontbuilder.icproj @@ -0,0 +1,224 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/tools/fontbuilder/imagemanip.cpp b/source/tools/fontbuilder/imagemanip.cpp new file mode 100755 index 0000000000..c463cc3857 --- /dev/null +++ b/source/tools/fontbuilder/imagemanip.cpp @@ -0,0 +1,24 @@ +// $Id: imagemanip.cpp,v 1.1 2004/06/17 19:32:04 philip Exp $ + +#include "stdafx.h" + +#include "math.h" + +unsigned char* GenerateImage(int width, int height) +{ + unsigned char* ImageData = new unsigned char[width*height*3]; + + for (int y = 0; y < height; ++y) + { + for (int x = 0; x < width; ++x) + { + // Slightly off black, so you can see the bounding boxes + // by playing in some paint program + ImageData[(x+y*width)*3+0] = 0; + ImageData[(x+y*width)*3+1] = 0; + ImageData[(x+y*width)*3+2] = 1; + } + } + + return ImageData; +} diff --git a/source/tools/fontbuilder/imagemanip.h b/source/tools/fontbuilder/imagemanip.h new file mode 100755 index 0000000000..66ef6e334f --- /dev/null +++ b/source/tools/fontbuilder/imagemanip.h @@ -0,0 +1,4 @@ +// $Id: imagemanip.h,v 1.1 2004/06/17 19:32:04 philip Exp $ + +unsigned char* GenerateImage(int width, int height); +unsigned char* GreyscaleToRGB(int width, int height, unsigned char* grey); diff --git a/source/tools/fontbuilder/packer.cpp b/source/tools/fontbuilder/packer.cpp new file mode 100755 index 0000000000..7a0df5f2c6 --- /dev/null +++ b/source/tools/fontbuilder/packer.cpp @@ -0,0 +1,415 @@ +// $Id: packer.cpp,v 1.1 2004/06/17 19:32:04 philip Exp $ + +#include "stdafx.h" + +#include "packer.h" + +#include +#include + +#include + +PackedFont::PackedFont(FontRenderer* font, std::set chars) +{ + TextureData = NULL; + TextureWidth = TextureHeight = 0; + Font = font; + Chars = chars; +} + + +PackedFont::~PackedFont() +{ + delete[] TextureData; +} + + +class GlyphInfo { +public: + GlyphInfo() { c = 0; w = h = size = -1; } + GlyphInfo(wchar_t _c, int _w, int _h) { c=_c; w=_w; h=_h; size=(w*w)+(h*h); } + wchar_t c; + int w, h; + int x, y; // of top-left corner + + bool operator<(GlyphInfo& b) { return size > b.size; } // sort biggest first + +private: + int size; // used for sorting +}; + +typedef std::vector GlyphsInfo; + +struct GlyphCharSort : public std::binary_function { + bool operator()(GlyphInfo& x, GlyphInfo& y) { return x.c < y.c; } +}; + +int next_power_of_two(int x) +{ + // See something like http://bob.allegronetwork.com/prog/tricks.html for an explanation + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + return x+1; +} + +// To avoid a few ugly gotos. Returns true on collision. +inline bool TryFittingInternal(const int &x, const int &y, GlyphsInfo& glyphs, const GlyphsInfo::iterator& ThisGlyph) +{ + for (GlyphsInfo::iterator OtherGlyph = glyphs.begin(); OtherGlyph != ThisGlyph; ++OtherGlyph) + { + // Test for collisions - effectively trying to find a line that + // can fit between the two rectangles + if (x <= OtherGlyph->x+OtherGlyph->w + && x+ThisGlyph->w >= OtherGlyph->x + && y <= OtherGlyph->y+OtherGlyph->h + && y+ThisGlyph->h >= OtherGlyph->y) return true; + } + return false; +} + + +bool TryFitting(const int packing_precision, const int texture_width, const int texture_height, const bool randomness, GlyphsInfo& glyphs, bool ProgressCallback(float, wxString, void*), void* CallbackData) +{ + // A roughly O(n^3) algorithm, but with lots of attempted + // optimisations that do occasionally work. + + int count = 0; + + int ProgressCurrent = 0; + int ProgressMax = (int)glyphs.size(); + + GlyphInfo* LastGlyph = NULL; + for (GlyphsInfo::iterator ThisGlyph = glyphs.begin(); ThisGlyph != glyphs.end(); ++ThisGlyph, ++count) + { + if (ProgressCallback != NULL) + { + // Squaring makes the bar increase at a more constant rate + float Progress = powf((float)ProgressCurrent++ / (float)ProgressMax, 2.0f); + if (ProgressCallback(Progress, wxT("Fitting"), CallbackData)) + throw "Aborted"; + } + + // After the first few easy ones, try sticking things in randomly + // and see if it turns out adequately. + + if (randomness && count > 32) + { + /* + // except this is quite rubbish, so don't bother + + bool fitted = false; + for (int i = 0; i < 16; ++i) // try several times + { + int x = 1 + rand() % (texture_width-ThisGlyph->w-1); + int y = 1 + rand() % (texture_height-ThisGlyph->h-1); + for (GlyphsInfo::iterator OtherGlyph = glyphs.begin(); OtherGlyph != ThisGlyph; ++OtherGlyph) + if (box_collide(x, y, x+ThisGlyphRef.w, y+ThisGlyphRef.h, OtherGlyph->x, OtherGlyph->y, OtherGlyph->x+OtherGlyph->w, OtherGlyph->y+OtherGlyph->h)) + goto randomly_collided; + + ThisGlyph->x = x; + ThisGlyph->y = y; + goto fitted; + + randomly_collided: ; + } + */ + } + + bool fitted = false; + + int starty, stepy; + + // Check whether this glyph is no smaller than the previous one + if (LastGlyph && LastGlyph->w <= ThisGlyph->w && LastGlyph->h <= ThisGlyph->h) + { + // If so, carry on the fitting from where the last glyph left off, + // because we've already discovered that there's no more + // space above. + --count; + if (count & 1) + stepy = -packing_precision; + else + stepy = packing_precision; + starty = LastGlyph->y; + } + else + { + + // Alternate between fitting glyphs at the top and bottom + // to make things go a bit faster by minimising collisions + if (count & 1) + starty = texture_height-ThisGlyph->h-1, stepy = -packing_precision; + else + starty = 1, stepy = packing_precision; + } + + for (int y = starty; y > 0 && y < texture_height-ThisGlyph->h; y += stepy) + { + // and alternate between left and right + int startx, stepx; + if (count & 2) + startx = 1, stepx = packing_precision; + else + startx = texture_width-ThisGlyph->w-1, stepx = -packing_precision; + + int x; + +/* If you have a multiple-processor machine and the Intel C++ compiler, +// this parallel version might be a little faster. (It's only a few +// tens of percents slower on a hyperthreaded computer) + +// [Assuming it works at all, which it probably doesn't because I haven't +// tried compiling it recently] + + if (startx == 1) + { + int max = texture_width-ThisGlyph->w; + #pragma omp parallel shared(startx, texture_width, ThisGlyph, stepx, glyphs) + { + #pragma omp for schedule(static) nowait + for (x = startx; x < max; x += stepx) + { + if (! TryFittingInternal(x, y, glyphs, ThisGlyph)) + { + ThisGlyph->x = x; + stepx = 1e9; // Ugly hack so the threads finish at the same time + } + } + } + } + else + { + #pragma omp parallel shared(startx, texture_width, ThisGlyph, stepx, glyphs) + { + #pragma omp for schedule(static) nowait + for (x = startx; x > 0; x += stepx) + { + if (! TryFittingInternal(x, y, glyphs, ThisGlyph)) + { + ThisGlyph->x = x; + stepx = -1e9; + } + } + } + + } + + if (stepx == 1e9 || stepx == -1e9) + { + ThisGlyph->y = y; + goto fitted; + } +*/ + if (startx == 1) + { + for (x = startx; x < texture_width-ThisGlyph->w; x += stepx) + { + if (! TryFittingInternal(x, y, glyphs, ThisGlyph)) + { + ThisGlyph->x = x; + fitted = true; + break; + } + } + } + else + { + for (x = startx; x > 0; x += stepx) + { + if (! TryFittingInternal(x, y, glyphs, ThisGlyph)) + { + ThisGlyph->x = x; + fitted = true; + } + } + } + + if (fitted) + { + ThisGlyph->y = y; + break; + } + + + } + + if (!fitted) + // Couldn't fit glyph anywhere - give up + return false; + + //fitted: ; // for lazy people who just want to goto + + LastGlyph = &*ThisGlyph; + } + return true; +} + +bool TryFittingLots(int& texture_width, int& texture_height, GlyphsInfo& glyphs, bool ProgressCallback(float, wxString, void*), void* CallbackData) +{ + // Try the fairly fast method + if (TryFitting(16, texture_width, texture_height, false, glyphs, ProgressCallback, CallbackData)) + return true; + + // Go a bit more carefully + if (TryFitting(8, texture_width, texture_height, false, glyphs, ProgressCallback, CallbackData)) + return true; + + // Maybe it just needs a little more precision? + if (TryFitting(2, texture_width, texture_height, false, glyphs, ProgressCallback, CallbackData)) + return true; + + // This takes a long time and doesn't usually achieve much, but try it anyway + if (TryFitting(1, texture_width, texture_height, false, glyphs, ProgressCallback, CallbackData)) + return true; + + // Try swapping the dimensions if it's currently rectangular + if (texture_width != texture_height) + { + std::swap(texture_width, texture_height); + if (TryFitting(1, texture_width, texture_height, false, glyphs, ProgressCallback, CallbackData)) + return true; + // Didn't work, swap back again + std::swap(texture_width, texture_height); + } + + // Double the size and try again + if (texture_height == texture_width) + texture_width *= 2; + else + texture_height *= 2; + + if (TryFitting(32, texture_width, texture_height, true, glyphs, ProgressCallback, CallbackData)) + return true; + + // How much space can it need?! + if (TryFitting(4, texture_width, texture_height, false, glyphs, ProgressCallback, CallbackData)) + return true; + + // Give up + return false; +} + +void PackedFont::Generate(bool ProgressCallback(float, wxString, void*) = NULL, void* CallbackData = NULL) +{ + GlyphsInfo glyphs; + + int total_area = 0; + + std::vector MissingGlyphs; + + int ProgressCurrent = 0; + int ProgressMax = (int)Chars.size(); + + std::list SortableGlyphs; + + for (std::set::iterator c = Chars.begin(); c != Chars.end(); ++c) + { + if (ProgressCallback != NULL) + if (ProgressCallback((float)ProgressCurrent++ / (float)ProgressMax, wxT("Measuring"), CallbackData)) + throw "Aborted"; + + int w, h; + Font->LoadGlyph(*c); + if (Font->Missing) + { + // Was the missing glyph missing? + if (*c == 0xFFFD) + { + // Just use a question mark. (Surely *every* font has a question mark...) + Font->LoadGlyph('?'); + if (Font->Missing) + throw "Can't find either a missing glyph symbol or a question mark!"; + Font->GetBoundingBox(w, h); + SortableGlyphs.push_back( GlyphInfo('?', w, h) ); + total_area += w*h; + } + else + { + MissingGlyphs.push_back(*c); + } + } + else + { + Font->GetBoundingBox(w, h); + SortableGlyphs.push_back( GlyphInfo(*c, w, h) ); + total_area += w*h; + } + } + SortableGlyphs.sort(); + // Convert into a vector for a little more speed + for (std::list::iterator i = SortableGlyphs.begin(); i != SortableGlyphs.end(); ++i) + { + glyphs.push_back(*i); + } + + // Calculate minimum size square to hold all glyphs + int texture_width, texture_height; + texture_width = texture_height = next_power_of_two(int( sqrt((double)total_area) )); + + // See if a smaller rectangle ought to still fit + if (texture_width*texture_height > total_area*2) + texture_height /= 2; + + if (! TryFittingLots(texture_width, texture_height, glyphs, ProgressCallback, CallbackData)) + throw "Failed to fit all the glyphs, too many times."; + + TextureData = new unsigned char[texture_width*texture_height*3]; + memset(TextureData, 0, texture_width*texture_height*3); + TextureWidth = texture_width; + TextureHeight = texture_height; + + FontDefinition = wxEmptyString; + FontDefinition += wxT("100\n"); // version number + FontDefinition << TextureWidth << wxT(" ") << TextureHeight << wxT("\n"); // size of texture + FontDefinition << (int)glyphs.size() << wxT("\n"); // number of glyphs + FontDefinition << Font->GetLineSpacing() << wxT("\n"); + + SortableGlyphs.clear(); + for (GlyphsInfo::iterator i = glyphs.begin(); i != glyphs.end(); ++i) + { + SortableGlyphs.push_back(*i); + } + SortableGlyphs.sort(GlyphCharSort()); + + ProgressCurrent = 0; + ProgressMax = (int)glyphs.size(); + for (std::list::iterator ThisGlyph = SortableGlyphs.begin(); ThisGlyph != SortableGlyphs.end(); ++ThisGlyph) + { + if (ProgressCallback != NULL) + if (ProgressCallback((float)ProgressCurrent++ / (float)ProgressMax, wxT("Rendering"), CallbackData)) + throw "Aborted"; + + Font->LoadGlyph(ThisGlyph->c); + Font->RenderGlyph(TextureData, ThisGlyph->x, ThisGlyph->y, texture_width, texture_height, texture_width*3, true); + int offset_x, offset_y, advance; + Font->GetMetrics(offset_x, offset_y, advance); + FontDefinition << (int)ThisGlyph->c + << wxT(" ") << ThisGlyph->x + << wxT(" ") << (TextureHeight - ThisGlyph->y) // because it's stored upside-down + << wxT(" ") << ThisGlyph->w + << wxT(" ") << ThisGlyph->h + << wxT(" ") << offset_x + << wxT(" ") << offset_y + << wxT(" ") << advance + << wxT("\n"); + } + + if (MissingGlyphs.size()) + { + wxString Error = wxString::Format(wxT("WARNING: %d glyphs were missing: "), MissingGlyphs.size()); + for (size_t i = 0; i < MissingGlyphs.size(); ++i) + { + if (i) Error += wxT(", "); + if (i == 20) + { + Error += wxT("..."); + break; + } + Error += wxString::Format(wxT("%d"), MissingGlyphs[i]); + } + wxLogWarning(Error); + } +} diff --git a/source/tools/fontbuilder/packer.h b/source/tools/fontbuilder/packer.h new file mode 100755 index 0000000000..7bdaeca0c6 --- /dev/null +++ b/source/tools/fontbuilder/packer.h @@ -0,0 +1,28 @@ +// $Id: packer.h,v 1.1 2004/06/17 19:32:04 philip Exp $ + +#include "font.h" + +#include + +class PackedFont +{ +public: + PackedFont(FontRenderer* font, std::set chars); + ~PackedFont(); + + void Generate(bool ProgressCallback(float, wxString, void*), void* CallbackData); + + // Sizes are always a power of two + int TextureWidth; + int TextureHeight; + + // 24-bit (RGB) but greyscale image + unsigned char* TextureData; + + // For the text file describing how to read all the glyphs + wxString FontDefinition; + +private: + FontRenderer* Font; + std::set Chars; +}; diff --git a/source/tools/fontbuilder/platform/fontselect.h b/source/tools/fontbuilder/platform/fontselect.h new file mode 100755 index 0000000000..c51bde2299 --- /dev/null +++ b/source/tools/fontbuilder/platform/fontselect.h @@ -0,0 +1,24 @@ +// $Id: fontselect.h,v 1.1 2004/06/17 19:32:04 philip Exp $ + +class FontSelectorDialog : public wxDialog +{ +public: + FontSelectorDialog(wxWindow* parent); + ~FontSelectorDialog(); + + wxString FontName; + wxString FontFilename; + + void OnFontSelect(wxCommandEvent& event); + void OnOK(wxCommandEvent& event); + + static void DefaultFonts(wxString& Name0, wxString& Filename0, wxString& Name1, wxString& Filename1); + +private: + + wxArrayString FontNames; + wxCharBuffer** FontFilenames; + + DECLARE_EVENT_TABLE() +}; + diff --git a/source/tools/fontbuilder/platform/msw/fontselect.cpp b/source/tools/fontbuilder/platform/msw/fontselect.cpp new file mode 100755 index 0000000000..3f73f2a24f --- /dev/null +++ b/source/tools/fontbuilder/platform/msw/fontselect.cpp @@ -0,0 +1,136 @@ +// $Id: fontselect.cpp,v 1.1 2004/06/17 19:32:04 philip Exp $ + +#include "stdafx.h" + +#include "../fontselect.h" + +#include "wx/msw/registry.h" + +enum +{ + ID_FontList = wxID_HIGHEST+1, + ID_FontPreview +}; + +BEGIN_EVENT_TABLE(FontSelectorDialog, wxDialog) + EVT_LISTBOX(ID_FontList, FontSelectorDialog::OnFontSelect) + EVT_BUTTON(wxID_OK, FontSelectorDialog::OnOK) +END_EVENT_TABLE() + + +FontSelectorDialog::FontSelectorDialog(wxWindow* parent) + : wxDialog(parent, -1, wxString(wxT("Font selector")), wxDefaultPosition, wxSize(400, 200), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +{ + wxBoxSizer* MainSizer = new wxBoxSizer(wxVERTICAL); + + wxString PreviewString; + PreviewString += wxT("Aa"); + PreviewString += (wchar_t)0x0105; // LATIN SMALL LETTER A WITH OGONEK + PreviewString += wxT("\nBb"); + PreviewString += (wchar_t)0x00DF; // LATIN SMALL LETTER SHARP S + PreviewString += wxT("\nCc"); + PreviewString += (wchar_t)0x00E7; // LATIN SMALL LETTER C WITH CEDILLA + PreviewString += (wchar_t)0x033F; // COMBINING DOUBLE OVERLINE + PreviewString += wxT("\n"); + PreviewString += (wchar_t)0xFB4E; // HEBREW LETTER PE WITH RAFE + PreviewString += (wchar_t)0xFB6B; // ARABIC LETTER VEH FINAL FORM + PreviewString += (wchar_t)0xF915; // CJK COMPATIBILITY IDEOGRAPH-F915 + PreviewString += (wchar_t)0x3342; // SQUARE HOON + + wxBoxSizer* TopSizer = new wxBoxSizer(wxHORIZONTAL); + wxListBox* FontListBox = new wxListBox(this, ID_FontList, wxDefaultPosition, wxSize(200, 100)); + TopSizer->Add(FontListBox, 0, wxGROW); + TopSizer->Add(new wxStaticText(this, ID_FontPreview, PreviewString), 1, wxGROW); + MainSizer->Add(TopSizer, 1); + + wxBoxSizer* ButtonSizer = new wxBoxSizer(wxHORIZONTAL); + ButtonSizer->Add(new wxButton(this, wxID_OK, wxT("OK")), 0, wxALL, 10); + ButtonSizer->Add(new wxButton(this, wxID_CANCEL, wxT("Cancel")), 0, wxALL, 10); + MainSizer->Add(ButtonSizer, 0, wxALIGN_CENTER); + + SetSizer(MainSizer); + + // Get a list of all the fonts installed on the system + + wxRegKey FontKey (wxT("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts")); + + // Get the list of keys (= font names) + wxString ValueName; + long ValueIndex; + FontKey.GetFirstValue(ValueName, ValueIndex); + do { + if (! ValueName.IsEmpty()) + FontNames.Add(ValueName); + } while (FontKey.GetNextValue(ValueName, ValueIndex)); + + FontNames.Sort(); + + // Get all the filenames, and store in the listbox's client data + + FontFilenames = new wxCharBuffer*[FontNames.Count()]; + + for (size_t i = 0; i < FontNames.Count(); ++i) + { + wxString t; + FontKey.QueryValue(FontNames[i], t); + wxCharBuffer* b = new wxCharBuffer(t.mb_str()); + FontFilenames[i] = b; + } + + FontListBox->Set(FontNames, (void**)FontFilenames); +} + +FontSelectorDialog::~FontSelectorDialog() +{ + for (size_t i = 0; i < FontNames.Count(); ++i) + delete FontFilenames[i]; + delete[] FontFilenames; +} + + +void FontSelectorDialog::OnOK(wxCommandEvent& WXUNUSED(event)) +{ + // Find where the fonts are on the disk + wxRegKey FontPathKey (wxT("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders")); + wxString FontPath; + FontPathKey.QueryValue(wxT("Fonts"), FontPath); + + wxListBox* FontList = (wxListBox*)wxWindow::FindWindowById(ID_FontList); + + int Selection = FontList->GetSelection(); + FontFilename = FontPath + wxT("\\"); + FontFilename += wxString::FromAscii( ((wxCharBuffer*) FontList->GetClientData(Selection))->data() ); + + FontName = FontList->GetStringSelection(); + + EndModal(wxID_OK); +} + + +void FontSelectorDialog::OnFontSelect(wxCommandEvent& event) +{ + wxString FontName = event.GetString(); + + // Translate "Arial (TrueType)" into "Arial", etc + wxRegEx ThingyRemove; + ThingyRemove.Compile(wxT(" \\(.+\\)$")); + ThingyRemove.Replace(&FontName, wxT("")); + + // Set the preview box to use that font + wxFont PreviewFont (18, wxDEFAULT, wxNORMAL, wxNORMAL, false, FontName, wxFONTENCODING_SYSTEM); + wxStaticText* PreviewText = (wxStaticText*)wxWindow::FindWindowById(ID_FontPreview); + PreviewText->SetFont(PreviewFont); +} + +void FontSelectorDialog::DefaultFonts(wxString& Name0, wxString& Filename0, wxString& Name1, wxString& Filename1) +{ + // Find where the fonts are on the disk + wxRegKey FontPathKey (wxT("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders")); + wxString FontPath; + FontPathKey.QueryValue(wxT("Fonts"), FontPath); + + Name0 = wxT("Verdana (TrueType)"); + Filename0 = FontPath + wxT("\\verdana.ttf"); + Name1 = wxT("Arial Unicode MS (TrueType)"); + Filename1 = FontPath + wxT("\\arialuni.ttf"); +} diff --git a/source/tools/fontbuilder/readme.html b/source/tools/fontbuilder/readme.html new file mode 100755 index 0000000000..da4f6ffb1a --- /dev/null +++ b/source/tools/fontbuilder/readme.html @@ -0,0 +1,78 @@ + + + Font Builder + + + +

Font Builder

+ +

Usage

+ +
    +
  1. Choose a primary font, which will be used for as much as + it can be used for. +
  2. Optionally, choose a different secondary font which is used + when the primary font is missing certain characters - for example, + Trebuchet doesn't include any Greek/Cyrillic/Hebrew characters, so + these will be taken from Arial Unicode MS (by default) instead. +
  3. Use the Preview font button to see what the font looks like + by rendering the contents of the textbox. Adjust the font settings + until perfection is attained. +
  4. Choose a desired character list - a text file containing + all the characters that must be in the final font. The standard files + are latin.txt (containing ASCII plus some accented characters, + mainly useful for non-localised text) and standard.txt + (containing Latin, Greek, Cyrillic and Hebrew characters, for text that + could be in any language). +
  5. Click Generate texture, and select a name for the .fnt + file. A .tga texture will also be generated, in the same directory + with the same name. Depending on several random coincidences, the texture + generation might be very fast or might take quite a while. When it finishes, the + excitingly patterned texture will be displayed. +
  6. Save your settings through the File menu so that you can + easily recreate the font later. +
+ +

Unicode fonts

+ +

+ +The font builder is intended to be used with Unicode characters. +Unfortunately, most fonts aren't. The font builder tries +to use Arial Unicode MS by default: this seems to +be installed by some versions of Microsoft Office, or can +be downloaded from locations such as +this (12MB +self-extracting 7-Zip). It's probably worth getting, since it +includes almost every Unicode glyph that you could wish for. + +

+ +Another potentially useful Unicode font is Bitstream Cyberbit +(available from Netscape's FTP server, +6MB). + +

+ +All fonts must be installed into Windows before you can use +them in the font builder. + +

+ +

Font settings

+ +
    +
  • Size - size of the font in arbitrary units. +
  • Boldness - if you don't have a proper bold variation of the + font, the boldness setting fakes it by drawing + the character several times. +
  • Italicness - if you don't have a proper italic variaton, + the italicness setting slants it in 5-degree increments. Negative + values slant the other way. +
  • Tracking - extra spacing in pixels added between every + non-zero-width glyph. +
  • Leading - extra spacing in pixels between lines of text. +
+ + + diff --git a/source/tools/fontbuilder/resource.h b/source/tools/fontbuilder/resource.h new file mode 100755 index 0000000000..0a52bb7148 --- /dev/null +++ b/source/tools/fontbuilder/resource.h @@ -0,0 +1,25 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by FontBuilder.rc +// +#define IDC_MYICON 2 +#define IDD_FONTBUILDER_DIALOG 102 +#define IDS_APP_TITLE 103 +#define IDM_ABOUT 104 +#define IDM_EXIT 105 +#define IDI_FONTBUILDER 107 +#define IDC_FONTBUILDER 109 +#define IDR_MAINFRAME 128 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 130 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/source/tools/fontbuilder/stdafx.cpp b/source/tools/fontbuilder/stdafx.cpp new file mode 100755 index 0000000000..ef0da50df1 --- /dev/null +++ b/source/tools/fontbuilder/stdafx.cpp @@ -0,0 +1,7 @@ +// $Id: stdafx.cpp,v 1.1 2004/06/17 19:32:04 philip Exp $ + +// stdafx.cpp : source file that includes just the standard includes +// FontBuilder.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" diff --git a/source/tools/fontbuilder/stdafx.h b/source/tools/fontbuilder/stdafx.h new file mode 100755 index 0000000000..a23297d757 --- /dev/null +++ b/source/tools/fontbuilder/stdafx.h @@ -0,0 +1,54 @@ +// $Id: stdafx.h,v 1.1 2004/06/17 19:32:04 philip Exp $ + +// Precompiled headers + +// Exclude rarely-used stuff from Windows headers +#define WIN32_LEAN_AND_MEAN + +// Disable some complaints in wx headers +#pragma warning (push) +#pragma warning (disable: 4267 4311 1125) + +#ifdef __INTEL_COMPILER +// Disable some of the excessively pedantic icl /W4 warnings (remarks) +#pragma warning (disable: 193 373 444 981 383 1418) +#endif + +// Include relevant wx headers +#include "wx/wxprec.h" +#include "wx/image.h" +#include "wx/spinctrl.h" +#include "wx/regex.h" +#include "wx/ffile.h" +#include "wx/utils.h" +#include "wx/progdlg.h" +#include "wx/wxexpr.h" +#include "wx/docview.h" +#include "wx/config.h" +#include "wx/filename.h" +#include "wx/config.h" + +#pragma warning (pop) + +#include +#include + + +//// Things other than precompiled headers which + // are just useful to include everywhere: + +// For nicer memory-leak detection +#ifdef _DEBUG + #include + #define new new(_NORMAL_BLOCK ,__FILE__, __LINE__) +#endif + +const wxString version = wxT("v0.9"); + +// Don't care about copy constructors / assignment operators +#pragma warning (disable: 4511 4512) + +#ifdef __INTEL_COMPILER +// Disable some of the excessively pedantic warnings again +#pragma warning (disable: 193 373 444 981 383 1418) +#endif diff --git a/source/tools/fontbuilder/todo.txt b/source/tools/fontbuilder/todo.txt new file mode 100755 index 0000000000..72cd0b61b3 --- /dev/null +++ b/source/tools/fontbuilder/todo.txt @@ -0,0 +1,16 @@ +$Id: todo.txt,v 1.1 2004/06/17 19:32:04 philip Exp $ + +* Optimise storage of duplicated bitmaps (e.g. e and e-with-some-accent, + comma and semicolon, anything and fullwidth versions, etc) + +* Optional antialiasing + +* Optional outlining (text with e.g. yellow fill and black outline + is made by drawing the black outline font and then the yellow + normal font) + +* Are the one-pixel black borders necessary? I don't trust graphics + cards not to leak a little bit, especially when they force texture + compression and filtering and antialiasing + +* Work out when I should say 'character', when 'code point', and when 'glyph' \ No newline at end of file diff --git a/source/tools/fontbuilder/wxapp.cpp b/source/tools/fontbuilder/wxapp.cpp new file mode 100755 index 0000000000..b62937095b --- /dev/null +++ b/source/tools/fontbuilder/wxapp.cpp @@ -0,0 +1,34 @@ +// $Id: wxapp.cpp,v 1.1 2004/06/17 19:32:04 philip Exp $ + +#include "stdafx.h" + +#include "wxframe.h" +#include "wxconfig.h" + +class FontBuilderApp : public wxApp +{ +public: + virtual bool OnInit(); + virtual int OnExit(); +}; + + +IMPLEMENT_APP(FontBuilderApp) + +bool FontBuilderApp::OnInit() +{ + ConfigInit(); + + MainFrame *frame = new MainFrame(wxString::Format(wxT("Font Builder %s"), version), wxDefaultPosition, wxSize(640,480)); + frame->SetIcon(wxIcon(wxT("IDI_ICON1"))); + frame->Show(true); + SetTopWindow(frame); + + return true; +} + +int FontBuilderApp::OnExit() +{ + ConfigDestroy(); + return 0; +} diff --git a/source/tools/fontbuilder/wxconfig.cpp b/source/tools/fontbuilder/wxconfig.cpp new file mode 100755 index 0000000000..9554adcf47 --- /dev/null +++ b/source/tools/fontbuilder/wxconfig.cpp @@ -0,0 +1,55 @@ +// $Id: wxconfig.cpp,v 1.1 2004/06/17 19:32:04 philip Exp $ + +#include "stdafx.h" + +#include "wxconfig.h" + +void ConfigInit() +{ + wxConfig* cfg = new wxConfig(wxT("WFG Font Builder")); + wxConfig::Set(cfg); + + // Default paths, for the first time program is run: + + // Get "x:\wherever\etc\binaries\" + wxFileName cwd = wxFileName::GetCwd()+wxT("\\"); + cwd.RemoveDir((int)cwd.GetDirCount()-1); + + if (!ConfigGet(wxT("FSF path"))) + { + wxFileName dir = cwd; dir.AppendDir(wxT("data\\tools\\fontbuilder\\settings")); + ConfigSet(wxT("FSF path"), dir.GetPath(wxPATH_GET_VOLUME)); + } + + if (!ConfigGet(wxT("FNT path"))) + { + wxFileName dir = cwd; dir.AppendDir(wxT("data\\mods\\official\\fonts")); + ConfigSet(wxT("FNT path"), dir.GetPath(wxPATH_GET_VOLUME)); + } + + if (!ConfigGet(wxT("Charset path"))) + { + wxFileName dir = cwd; dir.AppendDir(wxT("data\\tools\\fontbuilder\\charsets")); + ConfigSet(wxT("Charset path"), dir.GetPath(wxPATH_GET_VOLUME)); + } +} + +wxString ConfigGet(wxString key) +{ + wxConfig* cfg = (wxConfig*) wxConfig::Get(); + wxString ret; + cfg->Read(key, &ret); + return ret; +} + +void ConfigSet(wxString key, wxString value) +{ + wxConfig* cfg = (wxConfig*) wxConfig::Get(); + cfg->Write(key, value); +} + +void ConfigDestroy() +{ + delete wxConfig::Get(); +} + diff --git a/source/tools/fontbuilder/wxconfig.h b/source/tools/fontbuilder/wxconfig.h new file mode 100755 index 0000000000..9fb7d7a17a --- /dev/null +++ b/source/tools/fontbuilder/wxconfig.h @@ -0,0 +1,6 @@ +// $Id: wxconfig.h,v 1.1 2004/06/17 19:32:04 philip Exp $ + +void ConfigInit(); +wxString ConfigGet(wxString key); +void ConfigSet(wxString key, wxString value); +void ConfigDestroy(); diff --git a/source/tools/fontbuilder/wxframe.cpp b/source/tools/fontbuilder/wxframe.cpp new file mode 100755 index 0000000000..e44c4614ba --- /dev/null +++ b/source/tools/fontbuilder/wxframe.cpp @@ -0,0 +1,575 @@ +// $Id: wxframe.cpp,v 1.1 2004/06/17 19:32:04 philip Exp $ + +#include "stdafx.h" + +#include "wxframe.h" +#include "wxconfig.h" +#include "wxspinner.h" + +#include "platform/fontselect.h" + +#include "imagemanip.h" +#include "font.h" +#include "packer.h" +#include "filemanip.h" + +bool Changes = false; + +enum +{ + ID_Quit = wxID_HIGHEST+1, + ID_About, + ID_New, + ID_Save, + ID_Open, + ID_Recent, + + ID_GeneratePreview, + ID_GenerateTexture, + + ID_FontSelect0, + ID_FontSelect1, + ID_CharSelect, + + // For accessing values: + ID_PreviewText, + ID_Style_Size, + ID_Style_Boldness, + ID_Style_Italicness, + ID_Style_Tracking, + ID_Style_Leading +}; + +BEGIN_EVENT_TABLE(MainFrame, wxFrame) + EVT_MENU(ID_Quit, MainFrame::OnQuit) + EVT_MENU(ID_About, MainFrame::OnAbout) + EVT_MENU(ID_Save, MainFrame::OnSave) + EVT_MENU(ID_Open, MainFrame::OnOpen) + EVT_MENU_RANGE(wxID_FILE1, wxID_FILE9, MainFrame::OnMRUFile) + + EVT_BUTTON(ID_FontSelect0, MainFrame::OnFontSelect0) + EVT_BUTTON(ID_FontSelect1, MainFrame::OnFontSelect1) + EVT_BUTTON(ID_CharSelect, MainFrame::OnCharSelect) + + EVT_BUTTON(ID_GeneratePreview, MainFrame::OnGeneratePreview) + EVT_BUTTON(ID_GenerateTexture, MainFrame::OnGenerateTexture) + + EVT_CLOSE(MainFrame::OnClose) +END_EVENT_TABLE() + + + +BEGIN_EVENT_TABLE(BitmapPanel, wxPanel) + EVT_PAINT(BitmapPanel::OnPaint) +END_EVENT_TABLE() + + +MainFrame::MainFrame(const wxString& title, const wxPoint& pos, const wxSize& size) +: wxFrame((wxFrame *)NULL, -1, title, pos, size) +{ + RecentFiles = new wxFileHistory(5); + + wxMenu* menuFile = new wxMenu; + + //menuFile->Append(ID_New, wxT("&New")); + menuFile->Append(ID_Open, wxT("&Open...")); + menuFile->Append(ID_Save, wxT("&Save...")); + menuFile->AppendSeparator(); + wxMenu* menuRecent = new wxMenu; + menuFile->Append(ID_Recent, wxT("&Recent files"), menuRecent); + menuFile->AppendSeparator(); + menuFile->Append(ID_About, wxT("&About")); + menuFile->AppendSeparator(); + menuFile->Append(ID_Quit, wxT("E&xit")); + + RecentFiles->UseMenu(menuRecent); + + wxConfig* config = (wxConfig*)wxConfig::Get(); + RecentFiles->Load(*config); + + wxMenuBar* menuBar = new wxMenuBar; + menuBar->Append(menuFile, wxT("&File")); + + SetMenuBar(menuBar); + + CreateStatusBar(); + + // For textboxes that need to display Unicode text: + wxFont ArialUnicodeFont (12, wxDEFAULT, wxNORMAL, wxNORMAL, false, wxT("Arial Unicode MS"), wxFONTENCODING_SYSTEM); + + // Main panel that fills the whole window + wxPanel* Panel = new wxPanel(this); + + // Split the window into three main rows - controls, preview text, graphic + wxBoxSizer* OutlineSizer = new wxBoxSizer(wxVERTICAL); + + // Split the controls part into three main columns: font, styles, actions + wxBoxSizer* ControlSizer = new wxBoxSizer(wxHORIZONTAL); + + // Get some default values + FontSelectorDialog::DefaultFonts(FontName0, FontFilename0, FontName1, FontFilename1); + CharFilename = wxT(""); + CharName = wxT("Basic ASCII"); + + wxFlexGridSizer* FontSizer = new wxFlexGridSizer(2); + FontSizer->AddGrowableCol(0); + + FontSizer->Add(new wxStaticText(Panel, -1, wxT("Primary font:")), 0, wxALIGN_RIGHT | wxALL, 2); + FontSizer->Add(new wxButton(Panel, ID_FontSelect0, FontName0), 0, wxGROW | wxALL, 2); + + FontSizer->Add(new wxStaticText(Panel, -1, wxT("Secondary font:")), 0, wxALIGN_RIGHT | wxALL, 2); + FontSizer->Add(new wxButton(Panel, ID_FontSelect1, FontName1), 0, wxGROW | wxALL, 2); + + FontSizer->Add(new wxStaticText(Panel, -1, wxT("Character list:")), 0, wxALIGN_RIGHT | wxALL, 2); + FontSizer->Add(new wxButton(Panel, ID_CharSelect, CharName), 0, wxGROW | wxALL, 2); + + ControlSizer->Add(FontSizer, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_HORIZONTAL | wxALIGN_TOP, 8); + + wxFlexGridSizer* StyleSizer = new wxFlexGridSizer(2); + StyleSizer->AddGrowableCol(0); + + StyleSizer->Add(new wxStaticText(Panel, -1, wxT("Size:")), 0, wxALIGN_RIGHT | wxLEFT | wxRIGHT, 2); + StyleSizer->Add(new StyleSpinCtrl(Panel, ID_Style_Size, 1, 1024, 18), 0, wxGROW | wxLEFT | wxRIGHT, 2); + + StyleSizer->Add(new wxStaticText(Panel, -1, wxT("Boldness:")), 0, wxALIGN_RIGHT | wxLEFT | wxRIGHT, 2); + StyleSizer->Add(new StyleSpinCtrl(Panel, ID_Style_Boldness, 0, 16, 0), 0, wxGROW | wxLEFT | wxRIGHT, 2); + + StyleSizer->Add(new wxStaticText(Panel, -1, wxT("Italicness:")), 0, wxALIGN_RIGHT | wxLEFT | wxRIGHT, 2); + StyleSizer->Add(new StyleSpinCtrl(Panel, ID_Style_Italicness, -16, 16, 0), 0, wxGROW | wxLEFT | wxRIGHT, 2); + + StyleSizer->Add(new wxStaticText(Panel, -1, wxT("Tracking:")), 0, wxALIGN_RIGHT | wxLEFT | wxRIGHT, 2); + StyleSizer->Add(new StyleSpinCtrl(Panel, ID_Style_Tracking, -256, 256, 0), 0, wxGROW | wxLEFT | wxRIGHT, 2); + + StyleSizer->Add(new wxStaticText(Panel, -1, wxT("Leading:")), 0, wxALIGN_RIGHT | wxLEFT | wxRIGHT, 2); + StyleSizer->Add(new StyleSpinCtrl(Panel, ID_Style_Leading, -256, 256, 0), 0, wxGROW | wxLEFT | wxRIGHT, 2); + + ControlSizer->Add(StyleSizer, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER, 8); + + wxBoxSizer* GenerateSizer = new wxBoxSizer(wxVERTICAL); + GenerateSizer->Add(new wxButton(Panel, ID_GeneratePreview, wxT("Preview font")), 1, wxGROW | wxALL | wxALIGN_CENTER, 4); + GenerateSizer->Add(new wxButton(Panel, ID_GenerateTexture, wxT("Generate texture")), 1, wxGROW | wxALL | wxALIGN_CENTER, 4); + ControlSizer->Add(GenerateSizer, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_HORIZONTAL | wxALIGN_TOP, 8); + + OutlineSizer->Add(ControlSizer); + + OutlineSizer->Add(new wxStaticText(Panel, -1, wxT("Text to display in preview:")), 0, wxALL, 2); + + // Create a textbox containing "abcABCfunnysymbols" + wxString Font_string = wxT("abcABC"); Font_string += wchar_t(225); Font_string += wchar_t(223); Font_string += wchar_t(231); Font_string += wchar_t(779);Font_string += wchar_t(9812); + wxTextCtrl* PreviewTextBox = new wxTextCtrl(Panel, ID_PreviewText, Font_string, wxDefaultPosition, wxSize(300, 80), wxTE_MULTILINE); + PreviewTextBox->SetFont(ArialUnicodeFont); + + OutlineSizer->Add(PreviewTextBox, 0, wxGROW | wxLEFT | wxRIGHT, 4); + + PreviewPanel = new BitmapPanel(Panel); + OutlineSizer->Add(PreviewPanel, 1, wxGROW); + + Panel->SetSizer(OutlineSizer); + + PreviewImageData = NULL; + PreviewImage = NULL; + PreviewWidth = 512; + PreviewHeight = 256; +} + + +void MainFrame::OnQuit(wxCommandEvent& WXUNUSED(event)) +{ + Close(false); +} + +void MainFrame::OnClose(wxCloseEvent& event) +{ + if (Changes) + { + wxMessageDialog dlg (this, wxT("The font settings have been altered.\n\nDo you want to save the changes?"), wxT("Font Builder"), wxYES_NO|wxCANCEL | wxICON_QUESTION); + int ret = dlg.ShowModal(); + if (ret == wxID_CANCEL) + { + event.Veto(); + return; + } + else if (ret == wxID_YES) + { + if (! SaveDialog()) + { + event.Veto(); + return; + } + } + } + + wxConfig* config = (wxConfig*)wxConfig::Get(); + RecentFiles->Save(*config); + delete RecentFiles; + + delete PreviewImage; + delete[] PreviewImageData; + + event.Skip(); +} + + +void MainFrame::LoadSettings(wxString& filename) +{ + wxExprDatabase db; + + db.Read(filename); + + wxTextCtrl* PreviewTextCtrl = (wxTextCtrl*)wxWindow::FindWindowById(ID_PreviewText); + StyleSpinCtrl* BoldnessCtrl = (StyleSpinCtrl*)wxWindow::FindWindowById(ID_Style_Boldness); + StyleSpinCtrl* ItalicnessCtrl = (StyleSpinCtrl*)wxWindow::FindWindowById(ID_Style_Italicness); + StyleSpinCtrl* SizeCtrl = (StyleSpinCtrl*)wxWindow::FindWindowById(ID_Style_Size); + StyleSpinCtrl* TrackingCtrl = (StyleSpinCtrl*)wxWindow::FindWindowById(ID_Style_Tracking); + StyleSpinCtrl* LeadingCtrl = (StyleSpinCtrl*)wxWindow::FindWindowById(ID_Style_Leading); + wxButton* FontSelect0 = (wxButton*)wxWindow::FindWindowById(ID_FontSelect0); + wxButton* FontSelect1 = (wxButton*)wxWindow::FindWindowById(ID_FontSelect1); + wxButton* CharSelect = (wxButton*)wxWindow::FindWindowById(ID_CharSelect); + + db.BeginFind(); + + wxExpr *Settings = db.FindClauseByFunctor(wxT("Settings")); + Settings->GetAttributeValue(wxT("FontName0"), FontName0); + Settings->GetAttributeValue(wxT("FontName1"), FontName1); + Settings->GetAttributeValue(wxT("FontFilename0"), FontFilename0); + Settings->GetAttributeValue(wxT("FontFilename1"), FontFilename1); + FontSelect0->SetLabel(FontName0); + FontSelect1->SetLabel(FontName1); + + Settings->GetAttributeValue(wxT("CharName"), CharName); + Settings->GetAttributeValue(wxT("CharFilename"), CharFilename); + CharSelect->SetLabel(CharName); + + int t; + Settings->GetAttributeValue(wxT("Boldness"), t); BoldnessCtrl->SetValue(t); + Settings->GetAttributeValue(wxT("Italicness"), t); ItalicnessCtrl->SetValue(t); + Settings->GetAttributeValue(wxT("Size"), t); SizeCtrl->SetValue(t); + Settings->GetAttributeValue(wxT("Tracking"), t); TrackingCtrl->SetValue(t); + Settings->GetAttributeValue(wxT("Leading"), t); LeadingCtrl->SetValue(t); + + wxString PreviewTextUTF; + Settings->GetAttributeValue(wxT("PreviewText"), PreviewTextUTF); + + // Unpleasant conversion from UTF8 (pretending to be UTF16) into UTF16: + char* buffer = new char[PreviewTextUTF.length()+1]; + for (size_t i=0; iSetValue(PreviewText); + + Changes = false; +} + +void MainFrame::SaveSettings(wxString& filename) +{ + wxExprDatabase db; + + wxTextCtrl* PreviewTextCtrl = (wxTextCtrl*)wxWindow::FindWindowById(ID_PreviewText); + StyleSpinCtrl* BoldnessCtrl = (StyleSpinCtrl*)wxWindow::FindWindowById(ID_Style_Boldness); + StyleSpinCtrl* ItalicnessCtrl = (StyleSpinCtrl*)wxWindow::FindWindowById(ID_Style_Italicness); + StyleSpinCtrl* SizeCtrl = (StyleSpinCtrl*)wxWindow::FindWindowById(ID_Style_Size); + StyleSpinCtrl* TrackingCtrl = (StyleSpinCtrl*)wxWindow::FindWindowById(ID_Style_Tracking); + StyleSpinCtrl* LeadingCtrl = (StyleSpinCtrl*)wxWindow::FindWindowById(ID_Style_Leading); + + wxExpr *Settings = new wxExpr(wxT("Settings")); + Settings->AddAttributeValueString(wxT("FontName0"), FontName0); + Settings->AddAttributeValueString(wxT("FontName1"), FontName1); + Settings->AddAttributeValueString(wxT("FontFilename0"), FontFilename0); + Settings->AddAttributeValueString(wxT("FontFilename1"), FontFilename1); + Settings->AddAttributeValueString(wxT("CharName"), CharName); + Settings->AddAttributeValueString(wxT("CharFilename"), CharFilename); + Settings->AddAttributeValue(wxT("Boldness"), (long)BoldnessCtrl->GetValidValue()); + Settings->AddAttributeValue(wxT("Italicness"), (long)ItalicnessCtrl->GetValidValue()); + Settings->AddAttributeValue(wxT("Size"), (long)SizeCtrl->GetValidValue()); + Settings->AddAttributeValue(wxT("Tracking"), (long)TrackingCtrl->GetValidValue()); + Settings->AddAttributeValue(wxT("Leading"), (long)LeadingCtrl->GetValidValue()); + + // Convert into UTF8 and pretend it's ASCII, because wxExprDatabase doesn't like Unicode + wxCharBuffer PreviewTextUTF = PreviewTextCtrl->GetValue().mb_str(wxConvUTF8); + Settings->AddAttributeValueString(wxT("PreviewText"), wxString::FromAscii(PreviewTextUTF)); + + db.Append(Settings); + + db.Write(filename); + + Changes = false; +} + + +void MainFrame::OnOpen(wxCommandEvent& WXUNUSED(event)) +{ + wxFileDialog Dlg (this, wxT("Open font settings"), ConfigGet(wxT("FSF path")), wxEmptyString, wxT("Font settings (*.fst)|*.fst|All files (*.*)|*.*"), wxOPEN | wxHIDE_READONLY); + if (Dlg.ShowModal() == wxID_OK) + { + RecentFiles->AddFileToHistory(Dlg.GetPath()); + wxString path = Dlg.GetPath(); + LoadSettings(path); + ConfigSet(wxT("FSF path"), Dlg.GetDirectory()); + } +} + +bool MainFrame::SaveDialog() +{ + wxFileDialog Dlg (this, wxT("Save current settings"), ConfigGet(wxT("FSF path")), wxEmptyString, wxT("Font settings (*.fst)|*.fst|All files (*.*)|*.*"), wxSAVE | wxOVERWRITE_PROMPT); + if (Dlg.ShowModal() == wxID_OK) + { + RecentFiles->AddFileToHistory(Dlg.GetPath()); + wxString path = Dlg.GetPath(); + SaveSettings(path); + ConfigSet(wxT("FSF path"), Dlg.GetDirectory()); + return true; + } + return false; +} + + +void MainFrame::OnSave(wxCommandEvent& WXUNUSED(event)) +{ + SaveDialog(); +} + + +void MainFrame::OnMRUFile(wxCommandEvent& event) +{ + wxString filename = RecentFiles->GetHistoryFile(event.GetId() - wxID_FILE1); + LoadSettings(filename); + RecentFiles->AddFileToHistory(filename); +} + + + +void MainFrame::OnAbout(wxCommandEvent& WXUNUSED(event)) +{ + wxMessageBox(wxString::Format(wxT("Unicode Font Builder %s - created by Philip Taylor for WildFire Games"), version), wxT("About"), wxOK | wxICON_INFORMATION ); +} + + +void MainFrame::OnGeneratePreview(wxCommandEvent& WXUNUSED(event)) +{ + GeneratePreview(); +} + +void MainFrame::GeneratePreview() +{ + if (FontFilename0.IsEmpty() || FontFilename1.IsEmpty()) + return; + + PreviewPanel->GetSize(&PreviewWidth, &PreviewHeight); + + delete[] PreviewImageData; + PreviewImageData = GenerateImage(PreviewWidth, PreviewHeight); + + // Find all the relevant controls (slightly nicer than storing lots of pointers in the class) + wxTextCtrl* PreviewTextCtrl = (wxTextCtrl*)wxWindow::FindWindowById(ID_PreviewText); + StyleSpinCtrl* BoldnessCtrl = (StyleSpinCtrl*)wxWindow::FindWindowById(ID_Style_Boldness); + StyleSpinCtrl* ItalicnessCtrl = (StyleSpinCtrl*)wxWindow::FindWindowById(ID_Style_Italicness); + StyleSpinCtrl* SizeCtrl = (StyleSpinCtrl*)wxWindow::FindWindowById(ID_Style_Size); + StyleSpinCtrl* TrackingCtrl = (StyleSpinCtrl*)wxWindow::FindWindowById(ID_Style_Tracking); + StyleSpinCtrl* LeadingCtrl = (StyleSpinCtrl*)wxWindow::FindWindowById(ID_Style_Leading); + + try + { + + FontRenderer Font( + FontFilename0.ToAscii(), + FontFilename1.ToAscii(), + SizeCtrl->GetValidValue() ); + + Font.Boldness = BoldnessCtrl->GetValidValue(); + Font.Italicness = 5 * ItalicnessCtrl->GetValidValue(); + Font.Tracking = TrackingCtrl->GetValidValue(); + Font.Leading = LeadingCtrl->GetValidValue(); + Font.Outline = false; + + int x = 16, y = Font.GetLineSpacing(); + + wxString PreviewText = PreviewTextCtrl->GetValue(); + for (size_t i = 0; i < PreviewText.Length(); ++i) + { + if (PreviewText[i] == wxT('\n')) + { + x = 16; + y += Font.GetLineSpacing(); + } + else + { + Font.LoadGlyph(PreviewText[i]); + Font.RenderGlyph(PreviewImageData, x, y, PreviewWidth, PreviewHeight, PreviewWidth*3, false); + } + } + } + catch (const char* m) { + wxLogError(wxString::Format(wxT("Failed to generate preview: %s"), wxString::FromAscii(m))); + return; + } + + delete PreviewImage; + PreviewImage = new wxImage(PreviewWidth, PreviewHeight, PreviewImageData, true); + PreviewPanel->SetBitmap(new wxBitmap(PreviewImage)); + + PreviewPanel->Refresh(); + + Changes = true; +} + + +void MainFrame::OnGenerateTexture(wxCommandEvent& WXUNUSED(event)) +{ + wxFileDialog Dlg (this, wxT("Save font definition and texture"), ConfigGet(wxT("FNT path")), wxEmptyString, wxT("Font definition files (*.fnt)|*.fnt|All files (*.*)|*.*"), wxSAVE | wxOVERWRITE_PROMPT); + if (Dlg.ShowModal() == wxID_OK) + { + wxFileName FontDefnFilename (Dlg.GetPath()); + wxFileName TextureFilename = FontDefnFilename; + TextureFilename.SetExt(wxT("tga")); + GenerateTexture(TextureFilename.GetFullPath(), FontDefnFilename.GetFullPath()); + ConfigSet(wxT("FNT path"), TextureFilename.GetPath()); + } +} + + +bool ProgressDialogCallback(float Progress, wxString Msg, void* data) { + return ! ((wxProgressDialog *)data)->Update((int)(Progress*1024.0), Msg); +} + +void MainFrame::GenerateTexture(wxString TextureFilename, wxString FontDefnFilename) +{ + if (FontFilename0.IsEmpty() || FontFilename1.IsEmpty()) + return; + + // Find all the relevant controls (slightly nicer than storing lots of pointers in the class) + StyleSpinCtrl* BoldnessCtrl = (StyleSpinCtrl*)wxWindow::FindWindowById(ID_Style_Boldness); + StyleSpinCtrl* ItalicnessCtrl = (StyleSpinCtrl*)wxWindow::FindWindowById(ID_Style_Italicness); + StyleSpinCtrl* SizeCtrl = (StyleSpinCtrl*)wxWindow::FindWindowById(ID_Style_Size); + StyleSpinCtrl* TrackingCtrl = (StyleSpinCtrl*)wxWindow::FindWindowById(ID_Style_Tracking); + StyleSpinCtrl* LeadingCtrl = (StyleSpinCtrl*)wxWindow::FindWindowById(ID_Style_Leading); + + + // Work out what characters need to be included in the texture + + std::set Chars; + if (CharFilename == wxT("")) + { + for (wchar_t c = 0x20; c < 0x7f; ++c) + Chars.insert(c); + } + else + { + try + { + Chars = AnalyseChars(CharFilename); + } + catch (const char* m) { + wxLogError(wxString::Format(wxT("Failed to analyse character file: %s"), wxString::FromAscii(m))); + return; + } + } + // Add the 'missing' symbol (if it's not missing, it'll use a ? instead) + Chars.insert(0xFFFD); + + // Generate the texture + try + { + FontRenderer Font( + FontFilename0.ToAscii(), + FontFilename1.ToAscii(), + SizeCtrl->GetValidValue() ); + + Font.Boldness = BoldnessCtrl->GetValidValue(); + Font.Italicness = 5 * ItalicnessCtrl->GetValidValue(); + Font.Tracking = TrackingCtrl->GetValidValue(); + Font.Leading = LeadingCtrl->GetValidValue(); + Font.Outline = false; + + PackedFont Packed (&Font, Chars); + + wxProgressDialog ProgressDialog (wxT("Progress"), wxT("Thinking..."), 1024, this, wxPD_APP_MODAL | wxPD_CAN_ABORT | wxPD_ELAPSED_TIME); + + try + { + Packed.Generate(&ProgressDialogCallback, &ProgressDialog); + } + catch (const char* m) { + wxLogError(wxString::Format(wxT("Failed to generate texture: %s"), wxString::FromAscii(m))); + return; + } + + ProgressDialog.Destroy(); + + PreviewWidth = Packed.TextureWidth; + PreviewHeight = Packed.TextureHeight; + delete[] PreviewImageData; + PreviewImageData = new unsigned char[PreviewWidth*PreviewHeight*3]; + memcpy(PreviewImageData, Packed.TextureData, PreviewWidth*PreviewHeight*3); + + delete PreviewImage; + PreviewImage = new wxImage(PreviewWidth, PreviewHeight, PreviewImageData, true); + PreviewPanel->SetBitmap(new wxBitmap(PreviewImage)); + + PreviewPanel->Refresh(); + + wxFFile TGAFile(TextureFilename, "wb"); + if (! TGAFile.IsOpened()) + throw "Error opening texture file for output"; + RGB_OutputGreyscaleTGA(PreviewImageData, PreviewWidth, PreviewHeight, PreviewWidth*3, TGAFile); + TGAFile.Close(); + + wxFFile FntFile(FontDefnFilename, "w"); + if (! FntFile.IsOpened()) + throw "Error opening font definition file for output"; + FntFile.Write(Packed.FontDefinition); + FntFile.Close(); + } + catch (const char* m) { + wxLogError(wxString::Format(wxT("Failed to generate texture: %s"), wxString::FromAscii(m))); + return; + } + + Changes = true; +} + + +void MainFrame::OnFontSelect0(wxCommandEvent& event) +{ + FontSelectorDialog Dlg(this); + if (Dlg.ShowModal() == wxID_OK) + { + FontFilename0 = Dlg.FontFilename; + FontName0 = Dlg.FontName; + ((wxButton*)event.GetEventObject()) -> SetLabel(FontName0); + } +} + +void MainFrame::OnFontSelect1(wxCommandEvent& event) +{ + FontSelectorDialog Dlg(this); + if (Dlg.ShowModal() == wxID_OK) + { + FontFilename1 = Dlg.FontFilename; + FontName1 = Dlg.FontName; + ((wxButton*)event.GetEventObject()) -> SetLabel(FontName1); + } +} + +void MainFrame::OnCharSelect(wxCommandEvent& event) +{ + wxFileDialog Dlg(this, wxT("UTF16 text file containing desired characters"), ConfigGet(wxT("Charset path")), wxEmptyString, wxT("Text files (*.txt)|*.txt|All files (*.*)|*.*"), wxOPEN | wxHIDE_READONLY); + if (Dlg.ShowModal() == wxID_OK) + { + CharFilename = Dlg.GetPath(); + CharName = Dlg.GetFilename(); + ((wxButton*)event.GetEventObject()) -> SetLabel(CharName); + ConfigSet(wxT("Charset path"), Dlg.GetDirectory()); + } +} + + + +void BitmapPanel::OnPaint(wxPaintEvent& WXUNUSED(event)) +{ + wxPaintDC dc(this); + if (Bitmap) + dc.DrawBitmap(*Bitmap, 0, 0, false); +} diff --git a/source/tools/fontbuilder/wxframe.h b/source/tools/fontbuilder/wxframe.h new file mode 100755 index 0000000000..5361dd6894 --- /dev/null +++ b/source/tools/fontbuilder/wxframe.h @@ -0,0 +1,78 @@ +// $Id: wxframe.h,v 1.1 2004/06/17 19:32:04 philip Exp $ + +class BitmapPanel : public wxPanel +{ +public: + BitmapPanel(wxWindow* parent) + : wxPanel(parent) + { + Bitmap = NULL; + } + + ~BitmapPanel() + { + delete Bitmap; + } + + void OnPaint(wxPaintEvent& event); + void SetBitmap(wxBitmap* bmp) + { + delete Bitmap; + Bitmap = bmp; + } + +private: + wxBitmap* Bitmap; + + DECLARE_EVENT_TABLE() +}; + + + +class MainFrame : public wxFrame +{ +public: + MainFrame(const wxString& title, const wxPoint& pos, const wxSize& size); + + void OnQuit(wxCommandEvent& event); + void OnClose(wxCloseEvent& event); + void OnAbout(wxCommandEvent& event); + + bool SaveDialog(); + void OnSave(wxCommandEvent& event); + void OnOpen(wxCommandEvent& event); + + void OnMRUFile(wxCommandEvent& event); + + void OnFontSelect0(wxCommandEvent& event); + void OnFontSelect1(wxCommandEvent& event); + void OnCharSelect(wxCommandEvent& event); + + void OnGeneratePreview(wxCommandEvent& event); + void OnGenerateTexture(wxCommandEvent& event); + +private: + BitmapPanel* PreviewPanel; + unsigned char* PreviewImageData; + wxImage* PreviewImage; + + void GeneratePreview(); + int PreviewWidth, PreviewHeight; + + void GenerateTexture(wxString TextureFilename, wxString FontDefnFilename); + + void SaveSettings(wxString& filename); + void LoadSettings(wxString& filename); + + wxString FontName0; + wxString FontName1; + wxString FontFilename0; + wxString FontFilename1; + + wxString CharName; + wxString CharFilename; + + wxFileHistory* RecentFiles; + + DECLARE_EVENT_TABLE() +}; diff --git a/source/tools/fontbuilder/wxspinner.h b/source/tools/fontbuilder/wxspinner.h new file mode 100755 index 0000000000..d92fcc0519 --- /dev/null +++ b/source/tools/fontbuilder/wxspinner.h @@ -0,0 +1,22 @@ +// $Id: wxspinner.h,v 1.1 2004/06/17 19:32:04 philip Exp $ + +class StyleSpinCtrl : public wxSpinCtrl +{ +public: + + StyleSpinCtrl(wxWindow* parent, wxWindowID win_id, int min_val, int max_val, int initial_val) + : wxSpinCtrl(parent, win_id, wxEmptyString, wxDefaultPosition, wxSize(50, 20), wxSP_ARROW_KEYS, min_val, max_val, initial_val) + { + SetValue(initial_val); + } + + // Like GetValue, but guaranteed to be inside the range (Min..Max) + // no matter what people type in + int GetValidValue() + { + int Value = GetValue(); + int Min = GetMin(); + int Max = GetMax(); + return Value <= Min ? Min : Value >= Max ? Max : Value; + } +};