I know jules don’t like the oldschool pixel font but that can be very useful in many circumstances.
AGG included a multi-platform process to do that with a little proprietary format (not documented) and an example for win32 platform. So, i’ve investigate the question for my own application, and this is a little project that i’m happy to offer to you.
This is some little static function decomposed in two parts:
RasterFont
Just a little createFont method that is platform specific (win32) and generate two source code files in the exe directory (one cpp and one header).
//==============================================================================
#include "RasterFont.h"
//==============================================================================
typedef agg::rendering_buffer buffer;
typedef agg::pixfmt_bgr24 pixel_format;
typedef agg::renderer_base<pixel_format> render_base;
typedef agg::renderer_scanline_bin_solid<render_base> renderer_bin;
typedef agg::glyph_raster_bin<agg::rgba8> glyph_generator;
typedef agg::font_engine_win32_tt_int32 font_engine_type;
typedef agg::font_cache_manager<font_engine_type> font_manager_type;
//==============================================================================
static String getBinaryFormattedString (agg::pixfmt_bgr24 pixel_format,
const agg::glyph_cache *glyph,
const int charnumber,
const int x, const int y,
const int rows, const int baseline,
const int num_bytes)
{
String bytes = String::empty;
uint16 width = (uint16)glyph->advance_x;
int row = 0;
for (; row < rows; ++row)
{
//String line;
int lastmax = 0;
int byte = 0;
for (; byte < num_bytes; ++byte)
{
uint8 bits = 0; // binary accumulator
uint8 bitcount = 7; // 8 bits counter (7..0)
int col = x + (8 * byte);
int max;
int currentmax = (int)(8 * (byte+1));
if (num_bytes > 1)
{
max = col + jlimit (0, (int)width, (currentmax - lastmax));
lastmax = currentmax;
}
else
max = col + 8;
for (; col < max; ++col)
{
agg::rgba8 color = pixel_format.pixel(col, row);
if (color.r == 255 &&
color.g == 255 &&
color.b == 255)
{
bits &= ~(1 << bitcount);
}
else
if (color.r == 0 &&
color.g == 0 &&
color.b == 0)
{
bits |= (1 << bitcount);
}
--bitcount;
}
bytes += String::formatted(T("0x%02x,"), bits);
}
}
return bytes;
}
//==============================================================================
void RasterFont::createFont (const char* fontname,
const double size,
const int style,
const bool italic)
{
const int total = 256; // agg proprietary format is ugly limited
// to 255 characters (byte size)
int frame_width = roundDoubleToInt(size*size);
int frame_height = frame_width;
unsigned char* pixel_buffer = new unsigned char[frame_width * frame_height * 3];
memset(pixel_buffer, 255, frame_width * frame_height * 3);
buffer rbuf (pixel_buffer, frame_width, frame_height, frame_width * 3);
rbuf.clear (0);
pixel_format pf(rbuf);
render_base ren_base(pf);
ren_base.clear(agg::rgba(1,1,1,0));
renderer_bin ren_bin(ren_base);
HDC dc = ::GetDC(0);
font_engine_type engine (dc);
font_manager_type manager (engine);
StringArray exportFont;
Array<uint16> exportTable;
uint16 temp = 0;
String exportname = String (fontname).toLowerCase().removeCharacters(T(" ")) +
String((int)size);
if (style == FW_BOLD)
exportname += T("_bold");
if (italic)
exportname += T("_italic");
int ascent, descent, baseline;
engine.resolution (96); // 96 DPI is the standard for Windows OS.
bool result = engine.create_font (fontname,
agg::glyph_ren_native_mono,
size, 0.0, style, italic,
ANSI_CHARSET, FF_DONTCARE);
if (result)
{
TEXTMETRIC metric;
GetTextMetrics (dc, &metric);
ascent = metric.tmAscent;
descent = metric.tmDescent;
baseline = ascent - (int)size;
manager.precache (' ', total);
int i=32; int max=total;
for (; i<max; ++i)
{
const agg::glyph_cache *glyph = manager.glyph (i);
if (glyph)
{
manager.init_embedded_adaptors (glyph, 0, descent);
ren_bin.color (agg::rgba8(0,0,0));
agg::render_scanlines (manager.mono_adaptor (),
manager.mono_scanline (),
ren_bin);
int bytes = 0;
double ax = glyph->advance_x;
if (ax > 8)
bytes = roundDoubleToInt(ceil(ax / 8));
else
bytes = 1;
exportTable.add (temp);
temp += (ascent * bytes) + 1;
const juce_wchar* c = (const juce_wchar*)&i;
String character = String(c, 1);
exportFont.add (T(" ") + String(ax) + String::formatted(T(", // 0x%02x '"), i) + character + T("'"));
exportFont.add (T(" ") + getBinaryFormattedString(pf, glyph, i, 0, baseline, ascent, descent, bytes));
exportFont.add (T(" "));
ren_base.clear(agg::rgba(1,1,1,0));
}
}
}
delete [] pixel_buffer;
::ReleaseDC(0, dc);
/**
=======================================================
Auto-generation of ressource source code (header & cpp)
=======================================================
*/
String exportH = String::empty;
exportH += T("#ifndef __PIXELFONT_") + exportname.toUpperCase() + T("_H__\r\n");
exportH += T("#define __PIXELFONT_") + exportname.toUpperCase() + T("_H__\r\n");
exportH += T("\r\n");
exportH += T("#include \"agg_basics.h\"\r\n");
exportH += T("\r\n");
exportH += T("namespace pixelfonts\r\n");
exportH += T("{\r\n");
exportH += T(" extern const agg::int8u ") + exportname + T("[];\r\n");
exportH += T("}\r\n");
exportH += T("\r\n");
exportH += T("#endif // __PIXELFONT_") + exportname.toUpperCase() + T("_H__");
File fileH (File::getSpecialLocation(File::currentApplicationFile).getParentDirectory().getFullPathName() +
T("\\") + exportname + T(".h"));
fileH.replaceWithText (exportH);
String exportCPP = String::empty;
exportCPP += T("#include \"") + exportname + T(".h\"\r\n");
exportCPP += T("\r\n");
exportCPP += T("namespace pixelfonts\r\n");
exportCPP += T("{\r\n");
exportCPP += T(" const agg::int8u ") + exportname + T("[] =\r\n");
exportCPP += T(" {\r\n");
exportCPP += T(" ") + String (ascent) + T(", ")
+ String (baseline) + T(", ")
+ String (32) + T(", ")
+ String (total) + T("-32,\r\n");
exportCPP += T("\r\n ");
int ecnt = 0;
int ecpp = 0; int emax = exportTable.size();
for (; ecpp < emax; ++ecpp)
{
uint8 byte1 = exportTable[ecpp] & 0x00FF;
uint8 byte2 = exportTable[ecpp] >> 8;
if (ecnt > 8)
{
exportCPP += T("\r\n ");
ecnt = 0;
}
exportCPP += String::formatted(T("0x%02x,"), byte1);
exportCPP += String::formatted(T("0x%02x,"), byte2);
++ecnt;
}
exportCPP += T("\r\n\r\n");
ecpp = 0; emax = exportFont.size();
for (; ecpp < emax; ++ecpp)
exportCPP += exportFont[ecpp] + T("\r\n");
exportCPP += T(" 0\r\n");
exportCPP += T(" };\r\n");
exportCPP += T("}");
File fileCPP (File::getSpecialLocation(File::currentApplicationFile).getParentDirectory().getFullPathName() +
T("\\") + exportname + T(".cpp"));
fileCPP.replaceWithText (exportCPP);
}
//==============================================================================
I’m not sure that work with all font, but i’ve include in the project dozen of font converted by.
RasterText
This is the crossplatform part composed of 3 statics methods :
getTextWidth () - return the width of the text in pixel for the specified pixel font.
getTextHeight () - return the height in pixel for the specified pixel font.
drawText () - draw the text with the specified pixel font in the provided Graphics.
//==============================================================================
#include "RasterText.h"
//==============================================================================
typedef agg::pixfmt_alpha_blend_rgba <agg::blender_rgba32,
agg::rendering_buffer,
agg::pixel32_type> pixelformat;
typedef agg::renderer_base <pixelformat> renderer;
typedef agg::glyph_raster_bin <agg::rgba8> generator;
typedef agg::renderer_scanline_aa_solid <renderer> renderer_solid;
typedef agg::span_allocator <agg::rgba8> span_alloc_type;
typedef agg::span_solid <agg::rgba8> span_generator_type;
typedef agg::renderer_scanline_aa <renderer, span_alloc_type, span_generator_type> renderer_type;
//==============================================================================
double RasterText::getTextWidth (String text, const agg::int8u* font)
{
generator glyph (font);
return glyph.width ((const juce_wchar *)text);
}
//==============================================================================
double RasterText::getTextHeight (const agg::int8u* font)
{
generator glyph (font);
return glyph.height ();
}
//==============================================================================
void RasterText::drawText (Graphics &g,
Rectangle<int> &bounds,
const agg::int8u* font,
String text,
Colour textColour)
{
int x = bounds.getX();
int y = bounds.getY();
int w = bounds.getWidth();
int h = bounds.getHeight();
Image dest(Image::ARGB, w, h, true);
Image::BitmapData datas (dest, 0, 0, w, h, true);
agg::rendering_buffer buffer (datas.getPixelPointer(0,0), w, h, w * 4);
buffer.clear (0);
generator glyph (font);
pixelformat pixel (buffer);
renderer baserenderer (pixel);
renderer_solid ren (baserenderer);
baserenderer.clear (agg::rgba(0,0,0,0));
span_alloc_type sa;
span_generator_type sg;
renderer_type rb (baserenderer, sa, sg);
sg.color (agg::rgba(textColour.getFloatBlue(),
textColour.getFloatGreen(),
textColour.getFloatRed(),
textColour.getFloatAlpha()));
agg::renderer_raster_htext<renderer_type, generator> textrenderer(rb, glyph);
textrenderer.render_text (0, glyph.base_line(), (const juce_wchar *)text, false);
g.drawImageAt (&dest, x, y);
}
//==============================================================================
I include some great looking font that directly work on the 3 major OS and included in a specific directory.
[list]
- Tahoma (the Windows XP font)
- Tahoma bold (the same in bold condition)
- Pixel Arial (a close that original Arial font but in small size)
- and a lot of free font (pixel/bitmap) found on http://www.dafont.com website
[/list]
I hope someone will find interest with that.
DOWNLOAD
The VS2008++ project, including AGG2.4 and all fonts : HERE
The executable that provide the image showed on top of this page : HERE
.oO* Max *Oo.
[attachment=0]AGGRasterText_src.zip[/attachment]