// example2.cpp
// This file demonstrates how to render a coloured glyph with a differently
// coloured outline.
//
// Written Feb. 2009 by Erik Möller,
// with slight modifications by Werner Lemberg
//
// Public domain.
//
// Eric uses similar code in real applications; see
//
// http://www.timetrap.se
// http://www.emberwind.se
//
// for more.
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_STROKER_H
#include <vector>
#include <fstream>
#include <iostream>
#ifdef _MSC_VER
#define MIN __min
#define MAX __max
#else
#define MIN std::min
#define MAX std::max
#endif
// Define some fixed size types.
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;
// Try to figure out what endian this machine is using. Note that the test
// below might fail for cross compilation; additionally, multi-byte
// characters are implementation-defined in C preprocessors.
#if (('1234' >> 24) == '1')
#elif (('4321' >> 24) == '1')
#define BIG_ENDIAN
#else
#error "Couldn't determine the endianness!"
#endif
// A simple 32-bit pixel.
union Pixel32
{
Pixel32()
: integer(0) { }
Pixel32(uint8 bi, uint8 gi, uint8 ri, uint8 ai = 255)
{
b = bi;
g = gi;
r = ri;
a = ai;
}
uint32 integer;
struct
{
#ifdef BIG_ENDIAN
uint8 a, r, g, b;
#else // BIG_ENDIAN
uint8 b, g, r, a;
#endif // BIG_ENDIAN
};
};
struct Vec2
{
Vec2() { }
Vec2(float a, float b)
: x(a), y(b) { }
float x, y;
};
struct Rect
{
Rect() { }
Rect(float left, float top, float right, float bottom)
: xmin(left), xmax(right), ymin(top), ymax(bottom) { }
void Include(const Vec2 &r)
{
xmin = MIN(xmin, r.x);
ymin = MIN(ymin, r.y);
xmax = MAX(xmax, r.x);
ymax = MAX(ymax, r.y);
}
float Width() const { return xmax - xmin + 1; }
float Height() const { return ymax - ymin + 1; }
float xmin, xmax, ymin, ymax;
};
// TGA Header struct to make it simple to dump a TGA to disc.
#if defined(_MSC_VER) || defined(__GNUC__)
#pragma pack(push, 1)
#pragma pack(1) // Dont pad the following struct.
#endif
struct TGAHeader
{
uint8 idLength, // Length of optional identification sequence.
paletteType, // Is a palette present? (1=yes)
imageType; // Image data type (0=none, 1=indexed, 2=rgb,
// 3=grey, +8=rle packed).
uint16 firstPaletteEntry, // First palette index, if present.
numPaletteEntries; // Number of palette entries, if present.
uint8 paletteBits; // Number of bits per palette entry.
uint16 x, // Horiz. pixel coord. of lower left of image.
y, // Vert. pixel coord. of lower left of image.
width, // Image width in pixels.
height; // Image height in pixels.
uint8 depth, // Image color depth (bits per pixel).
descriptor; // Image attribute flags.
};
#if defined(_MSC_VER) || defined(__GNUC__)
#pragma pack(pop)
#endif
bool
WriteTGA(const std::string &filename,
const Pixel32 *pxl,
uint16 width,
uint16 height)
{
std::ofstream file(filename.c_str(), std::ios::binary);
if (file)
{
TGAHeader header;
memset(&header, 0, sizeof(TGAHeader));
header.imageType = 2;
header.width = width;
header.height = height;
header.depth = 32;
header.descriptor = 0x20;
file.write((const char *)&header, sizeof(TGAHeader));
file.write((const char *)pxl, sizeof(Pixel32) * width * height);
return true;
}
return false;
}
// A horizontal pixel span generated by the FreeType renderer.
struct Span
{
Span() { }
Span(int _x, int _y, int _width, int _coverage)
: x(_x), y(_y), width(_width), coverage(_coverage) { }
int x, y, width, coverage;
};
typedef std::vector<Span> Spans;
// Each time the renderer calls us back we just push another span entry on
// our list.
void
RasterCallback(const int y,
const int count,
const FT_Span * const spans,
void * const user)
{
Spans *sptr = (Spans *)user;
for (int i = 0; i < count; ++i)
sptr->push_back(Span(spans[i].x, y, spans[i].len, spans[i].coverage));
}
// Set up the raster parameters and render the outline.
void
RenderSpans(FT_Library &library,
FT_Outline * const outline,
Spans *spans)
{
FT_Raster_Params params;
memset(¶ms, 0, sizeof(params));
params.flags = FT_RASTER_FLAG_AA | FT_RASTER_FLAG_DIRECT;
params.gray_spans = RasterCallback;
params.user = spans;
FT_Outline_Render(library, outline, ¶ms);
}
// Render the specified character as a colored glyph with a colored outline
// and dump it to a TGA.
void
WriteGlyphAsTGA(FT_Library &library,
const std::string &fileName,
wchar_t ch,
FT_Face &face,
int size,
const Pixel32 &fontCol,
const Pixel32 outlineCol,
float outlineWidth)
{
// Set the size to use.
if (FT_Set_Char_Size(face, size << 6, size << 6, 90, 90) == 0)
{
// Load the glyph we are looking for.
FT_UInt gindex = FT_Get_Char_Index(face, ch);
if (FT_Load_Glyph(face, gindex, FT_LOAD_NO_BITMAP) == 0)
{
// Need an outline for this to work.
if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)
{
// Render the basic glyph to a span list.
Spans spans;
RenderSpans(library, &face->glyph->outline, &spans);
// Next we need the spans for the outline.
Spans outlineSpans;
// Set up a stroker.
FT_Stroker stroker;
FT_Stroker_New(library, &stroker);
FT_Stroker_Set(stroker,
(int)(outlineWidth * 64),
FT_STROKER_LINECAP_ROUND,
FT_STROKER_LINEJOIN_ROUND,
0);
FT_Glyph glyph;
if (FT_Get_Glyph(face->glyph, &glyph) == 0)
{
FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1);
// Again, this needs to be an outline to work.
if (glyph->format == FT_GLYPH_FORMAT_OUTLINE)
{
// Render the outline spans to the span list
FT_Outline *o =
&reinterpret_cast<FT_OutlineGlyph>(glyph)->outline;
RenderSpans(library, o, &outlineSpans);
}
// Clean up afterwards.
FT_Stroker_Done(stroker);
FT_Done_Glyph(glyph);
// Now we need to put it all together.
if (!spans.empty())
{
// Figure out what the bounding rect is for both the span lists.
Rect rect(spans.front().x,
spans.front().y,
spans.front().x,
spans.front().y);
for (Spans::iterator s = spans.begin();
s != spans.end(); ++s)
{
rect.Include(Vec2(s->x, s->y));
rect.Include(Vec2(s->x + s->width - 1, s->y));
}
for (Spans::iterator s = outlineSpans.begin();
s != outlineSpans.end(); ++s)
{
rect.Include(Vec2(s->x, s->y));
rect.Include(Vec2(s->x + s->width - 1, s->y));
}
#if 0
// This is unused in this test but you would need this to draw
// more than one glyph.
float bearingX = face->glyph->metrics.horiBearingX >> 6;
float bearingY = face->glyph->metrics.horiBearingY >> 6;
float advance = face->glyph->advance.x >> 6;
#endif
// Get some metrics of our image.
int imgWidth = rect.Width(),
imgHeight = rect.Height(),
imgSize = imgWidth * imgHeight;
// Allocate data for our image and clear it out to transparent.
Pixel32 *pxl = new Pixel32[imgSize];
memset(pxl, 0, sizeof(Pixel32) * imgSize);
// Loop over the outline spans and just draw them into the
// image.
for (Spans::iterator s = outlineSpans.begin();
s != outlineSpans.end(); ++s)
for (int w = 0; w < s->width; ++w)
pxl[(int)((imgHeight - 1 - (s->y - rect.ymin)) * imgWidth
+ s->x - rect.xmin + w)] =
Pixel32(outlineCol.r, outlineCol.g, outlineCol.b,
s->coverage);
// Then loop over the regular glyph spans and blend them into
// the image.
for (Spans::iterator s = spans.begin();
s != spans.end(); ++s)
for (int w = 0; w < s->width; ++w)
{
Pixel32 &dst =
pxl[(int)((imgHeight - 1 - (s->y - rect.ymin)) * imgWidth
+ s->x - rect.xmin + w)];
Pixel32 src = Pixel32(fontCol.r, fontCol.g, fontCol.b,
s->coverage);
dst.r = (int)(dst.r + ((src.r - dst.r) * src.a) / 255.0f);
dst.g = (int)(dst.g + ((src.g - dst.g) * src.a) / 255.0f);
dst.b = (int)(dst.b + ((src.b - dst.b) * src.a) / 255.0f);
dst.a = MIN(255, dst.a + src.a);
}
// Dump the image to disk.
WriteTGA(fileName, pxl, imgWidth, imgHeight);
delete [] pxl;
}
}
}
}
}
}
int
main(int argc,
char **argv)
{
if (argc != 3)
{
std::cerr << "Render letter `B' of given font as a TGA image.\n";
std::cerr << "\n";
std::cerr << "usage: example2 <font> <TGA-file>\n";
return 1;
}
// Initialize FreeType.
FT_Library library;
FT_Init_FreeType(&library);
// Open up a font file.
std::ifstream fontFile(argv[1], std::ios::binary);
if (fontFile)
{
// Read the entire file to a memory buffer.
fontFile.seekg(0, std::ios::end);
std::fstream::pos_type fontFileSize = fontFile.tellg();
fontFile.seekg(0);
unsigned char *fontBuffer = new unsigned char[fontFileSize];
fontFile.read((char *)fontBuffer, fontFileSize);
// Create a face from a memory buffer. Be sure not to delete the memory
// buffer until you are done using that font as FreeType will reference
// it directly.
FT_Face face;
FT_New_Memory_Face(library, fontBuffer, fontFileSize, 0, &face);
// Dump out a single glyph to a tga.
WriteGlyphAsTGA(library,
argv[2],
L'B',
face,
100,
Pixel32(255, 90, 30),
Pixel32(255, 255, 255),
3.0f);
// Now that we are done it is safe to delete the memory.
delete [] fontBuffer;
}
// Clean up the library
FT_Done_FreeType(library);
return 1;
}
// Local Variables:
// coding: utf-8
// End: