// example2.cpp // 不用opengl直接写道tga显示 // 成功,用法在命令行下 // 要渲染的字体在程序中,参数是当前目录的 字体名称和 输出的tga图片的名称 // 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 #include FT_FREETYPE_H #include FT_STROKER_H #include #include #include #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 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(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 \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'????', face, 100, Pixel32(255, 90, 30), // color Pixel32(255, 255, 255), // outline color 3.0f); // outline width // 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: