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; } }
void pl3DPipeline::Render(plDrawable* d, const hsTArray<int16_t>& visList) { // Reset here, since we can push/pop renderTargets after BeginRender() but // before this function, which necessitates this being called if (fView.fXformResetFlags != 0) ITransformsToDevice(); plDrawableSpans *ds = plDrawableSpans::ConvertNoRef(d); if (ds) { RenderSpans(ds, visList); } }
Glyph::Glyph(FT_Library &library, FontFace &fontFace, int c, int outlineWidth, bool hinting) : mFont(fontFace) { mChar = c; FT_Face &face = fontFace.GetFTFace(); int flags = FT_LOAD_DEFAULT; if (!hinting) { flags = FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING; } FT_Error error = FT_Load_Char(face, c, flags); if (error) { return; } mGlyphIndex = FT_Get_Char_Index(face, c); // Load The Glyph For Our Character. if(FT_Load_Glyph( face, mGlyphIndex, flags )) throw std::runtime_error("FT_Load_Glyph failed"); // Move The Face's Glyph Into A Glyph Object. FT_Glyph glyph; if(FT_Get_Glyph( face->glyph, &glyph )) throw std::runtime_error("FT_Get_Glyph failed"); FT_Stroker stroker; FT_Stroker_New(library, &stroker); FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); FT_Glyph_StrokeBorder(&glyph, stroker, false, true); FT_OutlineGlyph olglyph = reinterpret_cast<FT_OutlineGlyph>(glyph); FT_Outline outline = olglyph->outline; RenderSpans(library, &outline); FT_Stroker_Done(stroker); // Get metrics FT_Glyph_Metrics metrics = face->glyph->metrics; mAdvance.x = metrics.horiAdvance * kOneOver64; mAdvance.y = metrics.vertAdvance * kOneOver64; mBearing.x = metrics.horiBearingX * kOneOver64; mBearing.y = metrics.horiBearingY * kOneOver64; mSize.x = glm::round(metrics.width * kOneOver64); mSize.y = glm::round(metrics.height * kOneOver64); // Adjust for outline? mAdvance.x += outlineWidth; // Draw spans if(mSpans.size() > 0) { GlyphSpan front = mSpans.front(); Rect bounds(front.x, front.y, front.x, front.y); for(int i = 0; i < mSpans.size(); i++) { bounds.Include(mSpans[i].x, mSpans[i].y + 1); bounds.Include(mSpans[i].x + mSpans[i].width, mSpans[i].y); } int width = bounds.GetWidth(); int height = bounds.GetHeight(); mDataSize.x = width; mDataSize.y = height; int size = width * height; mBuffer = new unsigned char[size]; memset(mBuffer, 0, size); for(int i = 0; i < mSpans.size(); i++) { GlyphSpan &span = mSpans[i]; for (int w = 0; w < span.width; ++w) { mBuffer[(int)((height - 1 - (span.y - bounds.top)) * width + span.x - bounds.left + w)] = span.coverage; } } } FT_Done_Glyph(glyph); }