+
+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
+
+
+- Choose a primary font, which will be used for as much as
+ it can be used for.
+
- 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.
+
- 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.
+
- 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).
+
- 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.
+
- 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;
+ }
+};