Support for Angelcode BMFont format?

Has anyone written an importer/renderer for Angelcode’s BMFont bitmap font scheme? It seems to be the defacto standard for bitmap fonts (you can use Glyph on OSX or Hiero on OSX/Linux to generate them, not just the Windows BMFont tool)

Would certainly save me some time.

i’m using it in some project and it works ok, but why not just use ofTrueTypeFont?

[quote=“arturo, post:2, topic:6598”]why not just use ofTrueTypeFont?
[/quote]

I’m using full color text, not just the raw output of BMFont. I change the font texture in an image editor.

I just wrote some code to handle the BMFont format, thought I’d post it here since this post was the only OF BMFont reference I could find.

I used this as a starting point:
http://www.gamedev.net/topic/330742-quick-tutorial-variable-width-bitmap-fonts/

This version works with BMFont’s XML file format. I exported using “white text with alpha”, 32-bit. With white glyphs you can change the text color by calling ofSetColor(r, g, b).

BMFont.h:

  
#ifndef __BMFont_H_  
#define __BMFont_H_  
  
#include "ofMain.h"  
using namespace std;  
  
  
struct CharDescriptor {  
	unsigned short width, height;  
	short xOffset, yOffset;  
	short xAdvance;  
  
	ofImage *charImage;  
  
	CharDescriptor() : width(0), height(0), xOffset(0), yOffset(0), xAdvance(0) {}  
};  
  
class BMFont {  
	unsigned short lineHeight;  
	unsigned short base;  
	unsigned short width, height;  
  
	map<int, CharDescriptor> cCharMap;  
  
public:  
  
	bool  
	loadFontFiles(string FNTfileName);  
  
	void  
	paintChar(char inChar);  
  
	void  
	paintString(string inStr, int x, int y);  
  
	int  
	stringWidth(string inStr);  
  
};  
  
#endif //__BMFont_H_  
  

BMFont.cpp:

  
#include "BMFont.h"  
#include "ofxXmlSettings.h"  
  
bool  
BMFont::loadFontFiles(string FNTfileName) {  
  
	ofxXmlSettings XML;  
	bool loadedOK = XML.loadFile(FNTfileName);  
  
	if (!loadedOK) {  
		printf("BMFont::loadFontFiles: XML.loadFile() failed to load font file: %s\n", FNTfileName.c_str());  
		return false;  
	}  
  
	XML.pushTag("font", 0);     // push into the font tag  
  
	// load the common info for all chars  
	//   <common lineHeight="35" base="28" scaleW="256" scaleH="512" pages="1" packed="0" alphaChnl="0" redChnl="3" greenChnl="3" blueChnl="3"/>  
	lineHeight = XML.getAttribute("common", "lineHeight", 0);  
	base = XML.getAttribute("common", "base", 0);  
	width = XML.getAttribute("common", "scaleW", 0);  
	height = XML.getAttribute("common", "scaleH", 0);  
  
  
	// load in the image files  
	//<pages>  
	//    <page id="0" file="test_xml_0.png" />  
	//</pages>  
	XML.pushTag("pages", 0);  
	int pageCount = XML.getNumTags("page");  
	ofImage *pageAr[pageCount];  
	for (int onPageIdx = 0; onPageIdx < pageCount; onPageIdx++) {  
		string imageFileName = XML.getAttribute("page", "file", "", onPageIdx);  
  
		ofImage *pageImage = new ofImage();  
		loadedOK = pageImage->loadImage(imageFileName);  
		if (!loadedOK) {  
			printf("BMFont::loadFontFiles: loadImage failed to load image file: %s\n", imageFileName.c_str());  
			return false;  
		}  
  
		pageAr[onPageIdx] = pageImage;  
	}  
	XML.popTag();  
  
  
	// read in the info for each char  
	// <chars count="165">  
	int charCount = XML.getAttribute("chars", "count", 0);  
	XML.pushTag("chars", 0);  
  
	for (int onCharIdx = 0; onCharIdx < charCount; onCharIdx++) {  
		// <char id="32" x="250" y="26" width="3" height="1" xoffset="-1" yoffset="34" xadvance="8" page="0" chnl="15" />  
  
		int pageNum = XML.getAttribute("char", "page", -1, onCharIdx);  
		if ((pageNum < 0) || (pageNum >= pageCount)) {  
			printf("BMFont::loadFontFiles: bad page number on line %d: %d\n", onCharIdx, pageNum);  
			return false;  
		}  
  
		CharDescriptor charDesc;  
		charDesc.width = XML.getAttribute("char", "width", 0, onCharIdx);  
		charDesc.height = XML.getAttribute("char", "height", 0, onCharIdx);  
		charDesc.xOffset = XML.getAttribute("char", "xoffset", 0, onCharIdx);  
		charDesc.yOffset = XML.getAttribute("char", "yoffset", 0, onCharIdx);  
		charDesc.xAdvance = XML.getAttribute("char", "xadvance", 0, onCharIdx);  
		int id = XML.getAttribute("char", "id", 0, onCharIdx);  
		int x = XML.getAttribute("char", "x", 0, onCharIdx);  
		int y = XML.getAttribute("char", "y", 0, onCharIdx);  
  
		charDesc.charImage = new ofImage();  
		charDesc.charImage->cropFrom(*pageAr[pageNum], x, y, charDesc.width, charDesc.height);  
  
		cCharMap[id] = charDesc;            // id is the key we'll use to look it up with later on  
	}  
  
	// delete the images we created earlier  
	for (int onPageIdx = 0; onPageIdx < pageCount; onPageIdx++) {  
		delete pageAr[onPageIdx];  
	}  
  
	return true;  
}  
  
void  
BMFont::paintString(string inStr, int x, int y) {  
	// y should be the desired baseline  
  
	int length = inStr.length();  
	int origX = x;  
  
	for (int onCharIdx = 0; onCharIdx < length; onCharIdx++) {  
		int onChar = inStr.at(onCharIdx);  
  
		// sniff for multi-byte UTF-8  
		if (onChar < 0) {  
			onChar += 256;  
		}  
		// 192: 11000000, 223: 11011111  
		if ((onChar >= 192) && (onChar <= 223)) {  
			// multi-byte!  
			onCharIdx++;  
			int firstByte = onChar;  
			int secondByte = inStr.at(onCharIdx);  
			if (secondByte < 0) {  
				secondByte += 256;  
			}  
  
			onChar = ((firstByte - 192) * 64) + (secondByte - 128);  
		}  
  
		CharDescriptor charDesc = cCharMap[onChar];  
  
		if (charDesc.charImage == NULL) {  
			if (onChar == '\n') {  
				// if it's a line break, head to the next line  
				x = origX;  
				y += lineHeight;  
			}  
  
		} else {  
			charDesc.charImage->draw(x + charDesc.xOffset, y - base + charDesc.yOffset);  
		}  
  
		x += charDesc.xAdvance;  
	}  
  
}  
  
int  
BMFont::stringWidth(string inStr) {  
	// just calculate the width of the string  
  
	int length = inStr.length();  
	int width = 0;  
  
	for (int onCharIdx = 0; onCharIdx < length; onCharIdx++) {  
		int onChar = inStr[onCharIdx];  
  
		// sniff for multi-byte UTF-8  
		if (onChar < 0) {  
			onChar += 256;  
		}  
		// 192: 11000000, 223: 11011111  
		if ((onChar >= 192) && (onChar <= 223)) {  
			// multi-byte!  
			onCharIdx++;  
			int firstByte = onChar;  
			int secondByte = inStr.at(onCharIdx);  
			if (secondByte < 0) {  
				secondByte += 256;  
			}  
  
			onChar = ((firstByte - 192) * 64) + (secondByte - 128);  
		}  
  
		CharDescriptor charDesc = cCharMap[onChar];  
  
		width += charDesc.xAdvance;  
	}  
  
	return width;  
}  

To initialize the font info:

  
BMFont *testFont = new BMFont();  
bool loadedOK = testFont->loadFontFiles("test_white.fnt");  

At draw time:

  
testFont->paintString("My string!", 100, 100);  

I hope this helps someone!

[edit: updated to handle UTF-8 strings, added stringWidth()]