Java2D drawing colored bitmap fonts

I couldn’t come up with a more elegant way of drawing any color font from a single bitmap (png) font loaded in memory.

The only way I could get it to work was to render everything into a buffered image, which is then modified at pixel level by the font drawing.

The only other way I can think of how to do this would be to create the buffered image, then steal its array, and have to do my own pixel copying / color operations from there on instead of using drawImage calls… But I’d really like to avoid going that far if I could!


public final class Font {

	private static int trans = 16777215;

	public static final int w = 8;
	public static final int h = 8;
	
	public static void printString(BufferedImage bimg, String string, int x, int y, int width, int color, int bgcolor) {
		int px = 0;
		int py = 0;
		char c;
		int sx;
		int sy;
		int pixel_x = 0;
		int pixel_y = 0;
		int ix = 0;
		int iy = 0;
		int i = 0;
		int src_color = 0;
		
		for(i = 0; i < string.length(); i++) {
			c = string.charAt(i);
			if(c == 32) {
				px += w * 2;
				if(px / (w * 2) > width) {
					px = 0;
					py += h * 2;
				}
				continue;
			}
			sx = c;
			sy = 0;
			while(sx > 15) {
				sx -= 16;
				sy++;
			}
			sx *= w;
			sy *= h;
			for(ix = 0; ix < w; ix++) {
				for(iy = 0; iy < h; iy++) {
					src_color = Art.font.getRGB(sx + ix, sy + iy);
					if(src_color != trans) {
						pixel_x = x + px + (ix * 2);
						pixel_y = y + py + (iy * 2);
						if(pixel_x >= 0 && pixel_x + 2 < Game.WIDTH && pixel_y >= 0 && pixel_y + 2 < Game.HEIGHT) {
							bimg.setRGB(pixel_x + 1, pixel_y + 1, bgcolor);
							bimg.setRGB(pixel_x + 2, pixel_y + 2, bgcolor);
							bimg.setRGB(pixel_x + 2, pixel_y + 1, bgcolor);
							bimg.setRGB(pixel_x + 1, pixel_y + 2, bgcolor);
						}
						if(pixel_x >= 0 && pixel_x + 1 < Game.WIDTH && pixel_y >= 0 && pixel_y + 1 < Game.HEIGHT) {
							bimg.setRGB(pixel_x + 0, pixel_y + 0, color);
							bimg.setRGB(pixel_x + 1, pixel_y + 1, color);
							bimg.setRGB(pixel_x + 1, pixel_y + 0, color);
							bimg.setRGB(pixel_x + 0, pixel_y + 1, color);
						}
					}
				}
			}
			px += w * 2;
			if(px / (w * 2) > width) {
				px = 0;
				py += h * 2;
			}
		}
	}
}

I’d be tempted to take a look at Notch’s code for Prelude of the Chambered, pretty sure he did it in there… in particular I’d look at the Art and Bitmap classes… think that’s where it’s handled.

Hmm. I’ll have a look. I am pretty sure he’s just modifying the int’s in the array, which sucks because I’d have to write a lot of new rendering code.

I guess I don’t have too much to worry about. If I fill the screen with as many characters as it can possibly show, it goes from 60 fps to 47 fps. Any other normal text display doesn’t phase it.

Looking forward to any other contributions anyone else might have to this topic. Thanks.

Wait, you want to copy a portion of a BufferedImage into another BufferedImage right?


BufferedImage destination = ....
Graphics g = destination.getGraphics();

//(dx1,dy1) = destination top left, (dx2,dy2) destination bottom right, (sx1,sy1) = source top left, (sx2,sy2) = source bottom right
g.drawImage(fontImage,dx1,dy1,dx2,dy2,sx1,sy1,sx2,sy2,null);

To draw it a certain color, you use AlphaComposite to blend a color with the font image.

i have an image of a font my artist drew. its black and transparent

wherever there is a black pixel, i want to put a color of my choice in place of where that black pixel would go

Make the font white, draw it, then use AlphaComposite.SRC_IN to draw a colored rectangle above it.


	public static void test(Graphics2D g, String string, int x, int y, int cwidth, int color, int bgcolor) {
		int px = 0;
		int py = 0;
		char c;
		int sx;
		int sy;
		int pixel_x = 0;
		int pixel_y = 0;
		int ix = 0;
		int iy = 0;
		int i = 0;
		int src_color = 0;
		
		for(i = 0; i < string.length(); i++) {
			c = string.charAt(i);
			if(c == 32) {
				px += w * 2;
				if(px / (w * 2) > cwidth) {
					px = 0;
					py += h * 2;
				}
				continue;
			}
			sx = c;
			sy = 0;
			while(sx > 15) {
				sx -= 16;
				sy++;
			}
			sx *= w;
			sy *= h;
			
			pixel_x = x + px;
			pixel_y = y + py;
			
			g.drawImage(Art.font_white, pixel_x, pixel_y, pixel_x + w * 2, pixel_y + h * 2, sx, sy, sx + w, sy + h, null);
			
			
			px += w * 2;
			if(px / (w * 2) > cwidth) {
				px = 0;
				py += h * 2;
			}
		}
		
		Composite comp = g.getComposite();
		
		g.setComposite(AlphaComposite.SrcIn);
		g.setColor(Color.red);
		g.fillRect(x, y, cwidth * 16, 32);
		g.setComposite(comp);
	}

What am I doing wrong?

Here’s what happens if I comment out the composite / fillrect section

You’re supposed to set the Composite before you draw the rect :stuck_out_tongue:

Also, make sure you reset the AlphaComposite to what it was previously after drawing the rect.

did you read my code? because i did that.

Oh wait, you’re assuming that I’m rendering text to a image which gets drawn to another image, I think… which would explain why none of this is working because I’m just drawing the text (bitmapped font) directly onto the canvas.

So I’m guessing this would work if I was using a BufferedImage just for text rendering, clearing it to black every frame (???) and then using alpha composite to draw that to the canvas.

:open_mouth: I must be going blind, I swear I saw you setting the AlphaComposite after the fillRect XD

Anyway, according to the description of SRC_IN, only the overlapping source pixels are left.

And I just realized that you are drawing the text directly onto the screen right? There are already pixels there :stuck_out_tongue:

You need to draw onto a blank and transparent BufferedImage, then use the composite :slight_smile:

EDIT: haha, you just posted the same thing ::slight_smile:

EDIT2: no, you simply need to clear the BufferedImage, draw the text, then draw the colored rect using the composite. After that, simply draw that image onto the screen.

How do I clear the BufferedImage back to transparent each frame, to prevent dirty-ness :smiley:

g.setComposite(AlphaComposite.CLEAR);
g.setColor(new Color(0,0,0,0));
g.fillRect(0,0,width,height);

oh my god it works

:evil laugh:

BLAH! its not any faster than setRGB

Try to reuse objects. This should be faster since you’re not un-managing the BufferedImage.

I read that setRGB doesn’t unmanage the BufferedImage

O_o last time I checked, it should.

I get 47 FPS @ rendering 1872 characters (the most that will fit on the screen, at once) with both methods (setRGB, and the AlphaComposite)

I’m not recreating BufferedImages every frame or anything like that, so when you say re-use objects I’m not sure what you mean. I am already re-using objects.

Hmm, well I would stick with the AlphaComposite way, but it really should be a bit faster. Oh well :slight_smile: