Impact

This forum is read only and just serves as an archive. If you have any questions, please post them on github.com/phoboslab/impact

1 decade ago by mkreitler

Hi all,

While porting one of my Impact games to Ejecta, I ran into a problem: one of my bitmap fonts is wider than the max texture size. I realize there are several ways to fix this, but most of them seem painful.

Can anyone recommend an easy workaround?

Wish list: a "max width" setting in the font tool that causes the characters to wrap to lower lines if necessary.

1 decade ago by mkreitler

So, I worked around this by extending the Font class (see below for code). It functions as it used to, but you can call extendFont() and pass an array of Images and it will rebuild the font, assuming it's been chopped up into the original file followed by the files in the list.

For example:

newFont:    new ig.Font('pathToFonts\font.png'),
fontExImg:  [ new ig.Image('pathToFonts\second_font_file.png'),
                     new ig.Image('pathToFonts\third_font_file.png')],

...

init: function() {
    this.newFont.extendFont(this.fontExImg);
}


In this case, I've chopped the original font file into 3 sub-images: font.png, second_font_file.png, third_font_file.png.

Yeah, this is hacky. A better way would be to subclass Font to MultiFileFont, but I'm on a tight schedule and didn't want to ramp up on adding new native classes.

Here's the new version of Font.js:

ig.module(
	'impact.font'
)
.requires(
	'impact.image'
)
.defines(function(){


ig.Font = ig.Image.extend({
	widthMap: [],
	indices: [],
	extImages: [],
	extBreakpoints: [],
	firstChar: 32,
	height: 0,
	
	extendFont: function( extPath ) {
		while (extPath && extPath.length) {
			this.extImages.push(extPath.shift());
		}

		this._loadMetrics( this.data );
	},

	
	onload: function( ev ) {
		this._loadMetrics( this.data );
		this.parent( ev );
	},
	
	
	widthForString: function( s ) {
		var width = 0;
		for( var i = 0; i < s.length; i++ ) {
			width += this.widthMap[s.charCodeAt(i) - this.firstChar] + 1;
		}
		return width;
	},
	
	
	draw: function( text, x, y, align ) {
		var curImage = this;

		if( typeof(text) != 'string' ) {
			text = text.toString();
		}
		
		if( align == ig.Font.ALIGN.RIGHT || align == ig.Font.ALIGN.CENTER ) {
			var width = 0;
			for( var i = 0; i < text.length; i++ ) {
				var c = text.charCodeAt(i);
				width += this.widthMap[c - this.firstChar] + 1;
			}
			x -= align == ig.Font.ALIGN.CENTER ? width/2 : width;
		}
		
		for( var i = 0; i < text.length; i++ ) {
			var c = text.charCodeAt(i);
			x += this._drawChar( c - this.firstChar, x, y );
		}
		ig.Image.drawCount += text.length;
	},
	
	
	_drawChar: function( c, targetX, targetY ) {
		if( !this.loaded || c < 0 || c >= this.indices.length ) { return 0; }
		
		var scale = ig.system.scale;

		var realIndex = c;

		// Figure out which image to use.
		curImage = this;
		for (var i=0; i<this.extImages.length; ++i) {
			if (c >= this.extBreakpoints[i]) {
				curImage = this.extImages[i];
			}
		}

		var charX = this.indices[c] * scale;
		var charY = 0;
		var charWidth = this.widthMap[c] * scale;
		var charHeight = (this.height-2) * scale;		
		
		ig.system.context.drawImage( 
			curImage.data,
			charX, charY,
			charWidth, charHeight,
			ig.system.getDrawPos(targetX), ig.system.getDrawPos(targetY),
			charWidth, charHeight
		);
		
		return this.widthMap[c] + 1;
	},
	
	
	_loadMetrics: function( image ) {
		// Draw the bottommost line of this font image into an offscreen canvas
		// and analyze it pixel by pixel.
		// A run of non-transparent pixels represents a character and its width
		
		this.height = image.height-1;
		this.widthMap = [];
		this.indices = [];

		var canvas = document.createElement('canvas');
		canvas.width = image.width;
		canvas.height = image.height;
		var ctx = canvas.getContext('2d');
		ctx.drawImage( image, 0, 0 );
		var px = ctx.getImageData(0, image.height-1, image.width, 1);
		
		var currentImage = 0;
		var currentChar = 0;
		var currentWidth = 0;
		for (var i=0; i<1 + this.extImages.length; ++i) {
			if (i > 0) {
				image = this.extImages[i - 1].data;
				this.extBreakpoints.push(currentChar);
				currentWidth = 0;

				if (image) {
						canvas = document.createElement('canvas');
						canvas.width = image.width;
						canvas.height = image.height;
						ctx = canvas.getContext('2d');
						ctx.drawImage( image, 0, 0 );
						px = ctx.getImageData(0, image.height-1, image.width, 1);
				}
				else {
					break;
				}
			}

			for( var x = 0; x < image.width; x++ ) {
				var index = x * 4 + 3; // alpha component of this pixel
				if( px.data[index] > ig.Font.ALPHA_THRESHOLD ) {
					currentWidth++;
				}
				else if( px.data[index] <= ig.Font.ALPHA_THRESHOLD && currentWidth ) {
					this.widthMap.push( currentWidth );
					this.indices.push( x-currentWidth );
					currentChar++;
					currentWidth = 0;
				}
			}
		}

		this.widthMap.push( currentWidth );
		this.indices.push( x-currentWidth );
	}
});

ig.Font.ALPHA_THRESHOLD = 2;
ig.Font.ALIGN = {
	LEFT: 0,
	RIGHT: 1,
	CENTER: 2
};

});
Page 1 of 1
« first « previous next › last »