/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.albite.font;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.microedition.lcdui.Graphics;
import org.albite.image.AlbiteImageException;
import org.albite.image.AlbiteImageMono;
/**
*
* @author Albus Dumbledore
*/
public class AlbiteBitmapFont extends AlbiteFont {
protected final String fontname;
public final int lineHeight;
public final int lineSpacing;
public final int maximumWidth;
private final Glyph[] glyphs;
private final AlbiteImageMono glyphsCanvas;
private final byte[] glyphsCanvasData;
private final int glyphsCanvasWidth;
private final int glyphsCanvasHeight;
/*
* shared by all requests
*/
protected final int[] imageBuffer;
public final int spaceWidth;
public final int dashWidth;
public final int questionWidth;
public AlbiteBitmapFont(final String fontname)
throws IOException, AlbiteFontException {
this.fontname = fontname;
final String fontFileName = "/res/font/" + fontname;
/* reading character descriptions */
InputStream in;
DataInputStream din;
in = this.getClass().getResourceAsStream(fontFileName + FILE_EXTENSION);
if (in == null) {
throw new IOException("Font " + fontname + "was not found!");
}
din = new DataInputStream(in);
if (din.readInt() != MAGIC_NUMBER) {
throw new AlbiteFontException(INVALID_FILE_ERROR);
}
/*
* 1 byte for linespacing
*/
lineSpacing = din.readByte();
/*
* 1 byte for lineheight: Characters wouldn't be likely to be
* more than 127 pixels high, would they?
*/
lineHeight = din.readByte();
/*
* 4 bytes for character range
*/
glyphs = new Glyph[din.readInt() + 1];
/*
* 2 bytes for maximum width
*/
maximumWidth = din.readShort();
/*
* 4 bytes for character count
*/
int charsCount = din.readInt();
int currentChar = 0;
short x, y, w, h, xo, yo, xa;
for (int i = 0; i < charsCount; i++) {
currentChar = din.readInt();
x = din.readShort();
y = din.readShort();
w = din.readShort();
h = din.readShort();
xo = din.readShort();
yo = din.readShort();
xa = din.readShort();
glyphs[currentChar] = new Glyph(x, y, w, h, xo, yo, xa);
}
din.close();
/* read glyphs image */
in = this.getClass().getResourceAsStream(
fontFileName + AlbiteImageMono.FILE_EXTENSION);
if (in == null) {
throw new AlbiteFontException("Missing graphics for font "
+ fontname);
}
try {
glyphsCanvas = new AlbiteImageMono(in);
} catch (AlbiteImageException aie) {
throw new AlbiteFontException("Could not load graphics for font "
+ fontname);
}
glyphsCanvasData = glyphsCanvas.getData();
glyphsCanvasWidth = glyphsCanvas.getWidth();
glyphsCanvasHeight = glyphsCanvas.getHeight();
imageBuffer = new int[lineHeight * maximumWidth];
if (' ' < glyphs.length && glyphs[' '] != null) {
spaceWidth = glyphs[' '].xadvance;
} else {
spaceWidth = 0;
}
if ('-' < glyphs.length && glyphs['-'] != null) {
dashWidth = glyphs['-'].xadvance;
} else {
dashWidth = 0;
}
if ('?' < glyphs.length && glyphs['?'] != null) {
questionWidth = glyphs['?'].xadvance;
} else {
questionWidth = 0;
}
/*
* Null char must be regarded as a space
* Generally, there must not be any null chars for rendering
* as the parsers should have ommitted them, but this
* is a safe measure.
*/
glyphs[0] = glyphs[' '];
}
public final int charWidth(char c) {
if (c < glyphs.length) {
final Glyph g = glyphs[c];
if (g == null) {
//non-supported chars are replaced by `?`
return questionWidth;
} else {
return g.xadvance;
}
} else {
//non-supported chars are replaced by `?`
return questionWidth;
}
}
public final void drawChars(
final Graphics g,
final int color,
final char[] buffer,
int x, final int y,
final int offset,
final int length) {
int end = offset+length;
int c;
int glyphLen = glyphs.length;
Glyph glyph;
for (int i = offset; i < end; i++) {
c = buffer[i];
if (c < glyphLen) {
glyph = glyphs[c];
} else {
//non-supported chars are replaced by `?`
glyph = glyphs['?'];
}
if (glyph == null) {
glyph = glyphs['?'];
}
drawCharFromGlyph(g, color, glyph, x, y);
x += glyph.xadvance;
}
}
private void drawCharFromGlyph(
final Graphics g,
final int color,
final Glyph glyph,
final int x, final int y) {
/* copy from source to buffer and meanwhile transform color */
final int widthIn = glyphsCanvasWidth;
final int glyphWidth = glyph.width;
final int glyphHeight = glyph.height;
final int glyphX = glyph.x;
final int glyphY = glyph.y;
int yy = 0;
int xxend = 0;
int skip = widthIn - glyphWidth;
int xx = (glyphY * widthIn) + glyphX;
int i = 0;
while (yy < glyphHeight) {
xxend = xx + glyphWidth;
while(xx < xxend) {
/* mask + add color */
imageBuffer[i] = (glyphsCanvasData[xx] << 24) + color;
i++;
xx++;
}
xx += skip;
yy++;
}
g.drawRGB(imageBuffer, 0, glyphWidth,
x + glyph.xoffset, y + glyph.yoffset,
glyphWidth, glyphHeight, true);
}
public final void drawChar(
final Graphics g,
final int color,
final char c,
final int x, final int y) {
/* non-supported chars are not rendered */
if (c < glyphs.length) {
Glyph glyph = glyphs[c];
if (glyph != null) {
drawCharFromGlyph(g, color, glyph, x, y);
}
}
}
public int getLineHeight() {
return lineHeight;
}
}