forked from 0ad/0ad
308 lines
7.2 KiB
C++
Executable File
308 lines
7.2 KiB
C++
Executable File
// $Id: font.cpp,v 1.2 2004/06/19 12:56:09 philip Exp $
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "wx/wx.h"
|
|
|
|
#include "font.h"
|
|
#include <math.h>
|
|
|
|
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;
|
|
}
|