NOGDUS $1670.00 has been donated to NOGDUS!
September 20, 2017, 08:19:23 PM *
Welcome, Guest. Please login or register.

Login with username, password and session length
 
   Home   Help Search Login Register  
Pages: [1]   Go Down
  Print  
Author Topic: BitmapFont class  (Read 1550 times)
0 Members and 1 Guest are viewing this topic.
Richard Marks
Administrator
Offline Offline

Respect: 3425
« on: January 15, 2012, 02:26:22 PM »

Here I present to you a useful little gem of a class, the BitmapFont.

This class lets us use images for printing awesome text that would otherwise be difficult to do with standard fonts.

First off, the class limitations:

* Only ASCII English text is supported. No UNICODE multiple languages are supported.
* No alignment can be done automatically.
* Cannot change text color without using another image.

And the class features:

* Can use text of any size and color
* Can print text on any BitmapData surface

The class requires a source image that has the character glyphs laid out like this:


That is, 16 columns of 6 rows of equal-sized cells in which you may draw anything you want to display for that character.
Please note that the letters are organized in ASCII order, starting with the space (ASCII character #32) and ending with the tilde (ASCII character #126)

And now the class itself:

Code:
package {
import flash.display.BitmapData;
import flash.geom.Rectangle;
import flash.geom.Point;

class BitmapFont
{
private var mySource:BitmapData;
private var myRects:Vector.<Rectangle> = new Vector.<Rectangle>(94);
private var myCellWidth:int;
private var myCellHeight:int;
private var myNewLine:int;
private var mySpace:int;

private var myLetterPt:Point = new Point;

public function BitmapFont(source:BitmapData = null)
{
mySource = null;
myNewLine = 0;
mySpace = 0;

if (source != null)
{
buildFont(source);
}
}

public function buildFont(source:BitmapData):void
{
if (source == null)
{
return;
}

mySource = source;
myCellWidth = mySource.width / 16;
myCellHeight = mySource.height / 6;

var top:int = myCellHeight;
var baseA:int = myCellHeight;

var currentChar:int = 0;
var lx:int, ly:int, rx:int, ry:int, tx:int, ty:int, bx:int, by:int, px:int, py:int;

var keyColor:uint = mySource.getPixel32(0, 0);
var aCode:int = "A".charCodeAt() - 32;

for (var row:int = 0; row < 6; row++)
{
for (var col:int = 0; col < 16; col++)
{
myRects[currentChar] = new Rectangle;
myRects[currentChar].x = col * myCellWidth;
myRects[currentChar].y = row * myCellHeight;
myRects[currentChar].width = myCellWidth;
myRects[currentChar].height = myCellHeight;

// find left side of character
for (lx = 0; lx < myCellWidth; lx++)
{
for (ly = 0; ly < myCellHeight; ly++)
{
px = (col * myCellWidth) + lx;
py = (row * myCellHeight) + ly;

if (mySource.getPixel32(px, py) != keyColor)
{
myRects[currentChar].x = px;
lx = myCellWidth;
ly = myCellHeight;
}
}
}

// find right side of character
for (rx = myCellWidth - 1; rx >= 0; rx--)
{
for (ry = 0; ry < myCellHeight; ry++)
{
px = (col * myCellWidth) + rx;
py = (row * myCellHeight) + ry;

if (mySource.getPixel32(px, py) != keyColor)
{
myRects[currentChar].width = (px - myRects[currentChar].x) + 1;
rx = -1;
ry = myCellHeight;
}
}
}

// find top of character
for (ty = 0; ty < myCellHeight; ty++)
{
for (tx = 0; tx < myCellWidth; tx++)
{
px = (col * myCellWidth) + tx;
py = (row * myCellHeight) + ty;

if (mySource.getPixel32(px, py) != keyColor)
{
if (ty < top)
{
top = ty;
}
tx = myCellWidth;
ty = myCellHeight;
}
}
}

// find 'A' baseline
if (currentChar == aCode)
{
for (by = myCellHeight - 1; by >= 0; by--)
{
for (bx = 0; bx < myCellWidth; bx++)
{
px = (col * myCellWidth) + bx;
py = (row * myCellHeight) + by;

if (mySource.getPixel32(px, py) != keyColor)
{
baseA = by;
bx = myCellWidth;
by = -1;
}
}
}
}
currentChar++;
}
}

mySpace = uint(myCellWidth * 0.5);
myNewLine = baseA - top;

for (currentChar = 0; currentChar < 94; currentChar++)
{
myRects[currentChar].y += top;
myRects[currentChar].height -= top;
}

// make all keycolor pixels have alpha of 0 for nice text printing
for (px = 0; px < mySource.width; px++)
{
for (py = 0; py < mySource.height; py++)
{
if (mySource.getPixel32(px, py) == keyColor)
{
mySource.setPixel32(px, py, 0x00000000);
}
}
}
}

public function write(target:BitmapData, x:Number, y:Number, ...text):void
{
if (mySource == null || target == null) { return; }

var ox:int = x, oy:int = y;

// put all the passed params into a single string for us to use
var textString:String = "";
for each(var o:* in text) { textString += String(o); }

var textLength:int = textString.length;
var asc:int;
var currentChar:String;
for (var i:int = 0; i < textLength; i++)
{
currentChar = textString.charAt(i);
if (currentChar == " ")
{
ox += mySpace;
}
else if (currentChar == "\n")
{
oy += myNewLine;
ox = x;
}
else
{
asc = currentChar.charCodeAt() - 32;
myLetterPt.x = ox;
myLetterPt.y = oy;
target.copyPixels(mySource, myRects[asc], myLetterPt, null, null, true);
ox += myRects[asc].width + 1;
}
}
}
}
}



Here is a small sample that shows the usage of the class:

Assume that FONT_PNG is an embedded asset in the swf, and screenBuffer is a BitmapData that is to be drawn on.

Code:
var fontSource:BitmapData = (new FONT_PNG).bitmapData;
var bmpFont:BitmapFont = new BitmapFont(fontSource);

bmpFont.write(screenBuffer, 32, 64, "Hello, World!", "\n", "This is a literal number:", 100, "\n\n");

And that is all!
Enjoy.


Thanks to LazyFoo's bitmap font tutorial for C++ SDL as I used it pretty much verbatim - only porting to ActionScript 3.0 and a few minor changes.

The class is far from optimized, however it gets the job done, and  I'l leave optimizing if and when necessary to the reader. Cool
Logged

Tags:
Pages: [1]   Go Up
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.21 | SMF © 2015, Simple Machines
.: Theme by Richard Marks :.
Valid XHTML 1.0! Valid CSS!