Skip to content

Commit

Permalink
[#591] Make me.Font API consistent with Renderer and add support for …
Browse files Browse the repository at this point in the history
…WebGL

- Changes me.Font API to accept a Renderer reference (was Context2D reference)
- renderer.drawFont() is now private
- renderer.measureText() is gone! Use me.Font.measureText()
- TODO: Create a second texture cache for font textures, so we aren't creating thousands of new textures each frame in the font_test example
- XXX: Adds a new renderer.fontContext2D reference that we probably don't want to keep Replace this with the secondary texture cache
- XXX: find a better way to integrate renderer.drawFont() and me.Font.draw() using the renderer.fontContext2D hack SUCKS!
  • Loading branch information
parasyte committed Feb 4, 2015
1 parent 14cbe75 commit cfcea2e
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 143 deletions.
99 changes: 52 additions & 47 deletions examples/font_test/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,23 @@ var game = {
* Initialize the application
*/
onload: function() {

// Initialize the video.
if (!me.video.init(640, 480, {wrapper : "screen", scale : 'auto'})) {
alert("Your browser does not support HTML5 canvas.");
return;
}

// set all ressources to be loaded
me.loader.onload = this.loaded.bind(this);

// set all ressources to be loaded
me.loader.preload([{name: "atascii", type:"image", src: "atascii_8px.png"}]);

// load everything & display a loading screen
me.state.change(me.state.LOADING);
},

/**
* callback when everything is loaded
*/
Expand All @@ -41,7 +41,7 @@ var game = {
// switch to PLAY state
me.state.change(me.state.PLAY);
}

}; // game


Expand All @@ -58,63 +58,65 @@ var FontTest = me.Renderable.extend ({
// define a tween to cycle the font color
this.tween = new me.Tween(this.color)
.to({
g : 0,
g : 0,
b : 0
}, 2000)
.repeat( Infinity )
.yoyo(true)
.start();

// arial font
// arial font
this.font = new me.Font('Arial', 8, this.color);
// bitmap font
this.bFont = new me.BitmapFont("atascii", {x:8});
},

// draw function
draw : function(renderer) {
draw : function(renderer) {
var y_pos = 0;

// font size test
this.font.textAlign = "left";
this.font.lineWidth = "2";
this.font.setOpacity (0.5);
var context = renderer.getContext();
for (var i = 8; i < 48; i += 8) {
this.font.setFont('Arial', i, this.color);
this.font.draw(context, "Arial Text " + i + "px !" , 5 , y_pos );
y_pos+=this.font.measureText(context, "DUMMY").height;
this.font.draw(renderer, "Arial Text " + i + "px !" , 5 , y_pos );
y_pos += this.font.measureText(renderer, "DUMMY").height;
}
// one more with drawStroke this time
this.font.setFont('Arial', 48, this.color);
this.font.strokeStyle.parseCSS("red");
this.font.lineWidth = 3;
this.font.drawStroke(context, "Arial Text " + i + "px !" , 5 , y_pos );
this.font.drawStroke(renderer, "Arial Text " + i + "px !" , 5 , y_pos );

// bFont size test
// bFont size test
y_pos = 0;
this.bFont.textAlign = "right";
for (var i = 1;i<5;i++) {
this.bFont.setOpacity (0.2 * i);
this.bFont.resize(i);
this.bFont.draw(renderer, "BITMAP TEST" , me.video.renderer.getWidth() , y_pos );
y_pos+=this.bFont.measureText(context, "DUMMY").height;

y_pos += this.bFont.measureText(renderer, "DUMMY").height;
}

this.font.setOpacity(1);
this.bFont.setOpacity(1);

// font baseline test
this.font.setFont('Arial', 16, this.color);
var baseline = 200;

// Draw the baseline
context.beginPath();
context.moveTo(0, baseline + 0.5);
context.lineTo(me.video.renderer.getWidth(), baseline + 0.5);
context.strokeStyle = "red";
context.stroke();
if (renderer instanceof me.CanvasRenderer) {
// FIXME: Not WebGL compatible
var context = renderer.getContext();
context.beginPath();
context.moveTo(0, baseline + 0.5);
context.lineTo(me.video.renderer.getWidth(), baseline + 0.5);
context.strokeStyle = "red";
context.stroke();
}

var baselines = [
"bottom", "ideographic", "alphabetic", "middle", "hanging", "top"
Expand All @@ -126,69 +128,72 @@ var FontTest = me.Renderable.extend ({
for (var i = 0;i<baselines.length;i++) {
var text = baselines[i];
this.font.textBaseline = baselines[i];
this.font.draw(context, text, x_pos, baseline);
x_pos+=this.font.measureText(context, text + "@@@").width;
this.font.draw(renderer, text, x_pos, baseline);
x_pos += this.font.measureText(renderer, text + "@@@").width;
}

// restore default baseline
this.font.textBaseline = "top";

// ---- multiline testing -----

// font text
var text = "this is a multiline font\n test with melonjs and it\nworks even with a\n specific lineHeight value!";
this.font.textAlign = "center";
this.font.lineHeight = 1.1;
this.font.draw(context, text, 90, 210);
this.font.draw(renderer, text, 90, 210);
this.font.lineHeight = 1.1;

var text = "this is another font test \nwith right alignment\nand it still works!";
this.font.textAlign = "right";
this.font.draw(context, text, 200, 300);
// bFont test
this.font.textAlign = "right";
this.font.draw(renderer, text, 200, 300);

// bFont test
this.bFont.textAlign = "center";
var text = "THIS IS A MULTILINE\n BITMAP FONT WITH MELONJS\nAND IT WORKS";
this.bFont.resize(2);
this.bFont.draw(renderer, text + "\n" + text, 400, 230);
// bFont test

// bFont test
this.bFont.textAlign = "right";
var text = "ANOTHER FANCY MULTILINE\n BITMAP FONT WITH MELONJS\nAND IT STILL WORKS";
this.bFont.lineHeight = 1.2;
this.bFont.resize(3);
this.bFont.draw(renderer, text, 640, 400);
this.bFont.lineHeight = 1.0;

// baseline test with bitmap font
var x_pos = 0;
this.bFont.textAlign = "left";
this.bFont.resize(1);
var baseline = 375;

// Draw the baseline
context.beginPath();
context.moveTo(0, baseline + 0.5);
context.lineTo(me.video.renderer.getWidth(), baseline + 0.5);
context.strokeStyle = "red";
context.stroke();

if (renderer instanceof me.CanvasRenderer) {
// FIXME: Not WebGL compatible
context.beginPath();
context.moveTo(0, baseline + 0.5);
context.lineTo(me.video.renderer.getWidth(), baseline + 0.5);
context.strokeStyle = "red";
context.stroke();
}

// font baseline test
for (var i = 0; i < baselines.length; i++) {
var text = baselines[i].toUpperCase();
this.bFont.textBaseline = baselines[i];
this.bFont.draw(renderer, text, x_pos, baseline);
x_pos+=this.bFont.measureText(context, text + "@@@").width + 8;
x_pos += this.bFont.measureText(renderer, text + "@@@").width + 8;
}

// restore default alignement/baseline
this.font.textAlign = "left";
this.font.textBaseline = "top";
this.bFont.textAlign = "left";
this.bFont.textBaseline = "top";
this.bFont.textBaseline = "top";
},

onDeactivateEvent: function() {
me.pool.push(this.color);
}
});
});
87 changes: 52 additions & 35 deletions src/font/font.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
* @name me.Font#fillStyle
*/
this.fillStyle = new me.Color().copy(fillStyle);

/**
* defines the color used to draw the font stroke.<br>
* Default value : "#000000"
Expand Down Expand Up @@ -161,11 +161,13 @@
* @name measureText
* @memberOf me.Font
* @function
* @param {Context} context 2D Context
* @param {me.CanvasRenderer|me.WebGLRenderer} renderer Reference to the destination renderer instance
* @param {String} text
* @return {Object} returns an object, with two attributes: width (the width of the text) and height (the height of the text).
*/
measureText : function (context, text) {
measureText : function (renderer, text) {
var context = renderer.fontContext2D;

// draw the text
context.font = this.font;
context.fillStyle = this.fillStyle.toRGBA();
Expand All @@ -190,38 +192,27 @@
* @name draw
* @memberOf me.Font
* @function
* @param {Context} context 2D Context
* @param {me.CanvasRenderer|me.WebGLRenderer} renderer Reference to the destination renderer instance
* @param {String} text
* @param {Number} x
* @param {Number} y
*/

draw : function (context, text, x, y) {
draw : function (renderer, text, x, y) {
x = ~~x;
y = ~~y;

// save the previous global alpha value
var _alpha = context.globalAlpha;
context.globalAlpha *= this.getOpacity();
var _alpha = renderer.globalAlpha();
renderer.setGlobalAlpha(_alpha * this.getOpacity());

// update initial position
this.pos.set(x, y);
// draw the text
context.font = this.font;
context.fillStyle = this.fillStyle.toRGBA();
context.textAlign = this.textAlign;
context.textBaseline = this.textBaseline;

var strings = ("" + text).split("\n");
for (var i = 0; i < strings.length; i++) {
// draw the string
context.fillText(strings[i].trimRight(), x, y);
// add leading space
y += this.fontSize.y * this.lineHeight;
}
renderer.drawFont(this._drawFont(renderer.fontContext2D, text, x, y, false));

// restore the previous global alpha value
context.globalAlpha = _alpha;
renderer.setGlobalAlpha(_alpha);
},

/**
Expand All @@ -231,42 +222,68 @@
* @name drawStroke
* @memberOf me.Font
* @function
* @param {Context} context 2D Context
* @param {me.CanvasRenderer|me.WebGLRenderer} renderer Reference to the destination renderer instance
* @param {String} text
* @param {Number} x
* @param {Number} y
*/
drawStroke : function (context, text, x, y) {
drawStroke : function (renderer, text, x, y) {
x = ~~x;
y = ~~y;

// save the previous global alpha value
var _alpha = context.globalAlpha;
context.globalAlpha *= this.getOpacity();
var _alpha = renderer.globalAlpha();
renderer.setGlobalAlpha(_alpha * this.getOpacity());

// update initial position
this.pos.set(x, y);
// draw the text
renderer.drawFont(this._drawFont(renderer.fontContext2D, text, x, y, true));

// restore the previous global alpha value
renderer.setGlobalAlpha(_alpha);
},

/**
* @private
*/
_drawFont : function (context, text, x, y, stroke) {
context.font = this.font;
context.fillStyle = this.fillStyle.toRGBA();
context.strokeStyle = this.strokeStyle.toRGBA();
context.lineWidth = this.lineWidth;
if (stroke) {
context.strokeStyle = this.strokeStyle.toRGBA();
}
context.textAlign = this.textAlign;
context.textBaseline = this.textBaseline;

var strings = ("" + text).split("\n");
var strings = ("" + text).split("\n"), string = "";
var dw = 0;
var dy = y;
var lineHeight = this.fontSize.y * this.lineHeight;
for (var i = 0; i < strings.length; i++) {
var _string = strings[i].trimRight();
// draw the border
context.strokeText(_string, x, y);
string = strings[i].trimRight();
// measure the string
dw = Math.max(dw, context.measureText(string).width);
// draw the string
context.fillText(_string, x, y);
context.fillText(string, x, y);
// add leading space
y += this.fontSize.y * this.lineHeight;
y += lineHeight;
}

// restore the previous global alpha value
context.globalAlpha = _alpha;
// compute bounds
var dx = (this.textAlign === "right" ? x - dw : (
this.textAlign === "center" ? x - ~~(dw / 2) : x
));
dy = [ "top", "hanging" ].indexOf(this.textBaseline) >= 0 ? dy : (
this.textBaseline === "middle" ? dy - ~~(lineHeight / 2) : dy - lineHeight
);

return {
x: ~~dx,
y: ~~dy,
w: ~~(dw + 0.5),
h: ~~(strings.length * lineHeight + 0.5)
};
}
});
})();
10 changes: 5 additions & 5 deletions src/loader/loadingscreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,14 @@

draw : function (renderer) {
// measure the logo size
var logo1_width = renderer.measureText(this.logo1, "melon").width;
var xpos = (this.width - logo1_width - renderer.measureText(this.logo2, "JS").width) / 2;
var ypos = (this.height / 2) + (renderer.measureText(this.logo2, "melon").height);
var logo1_width = this.logo1.measureText(renderer, "melon").width;
var xpos = (this.width - logo1_width - this.logo2.measureText(renderer, "JS").width) / 2;
var ypos = (this.height / 2) + (this.logo2.measureText(renderer, "melon").height);

// draw the melonJS string
renderer.drawFont(this.logo1, "melon", xpos, ypos);
this.logo1.draw(renderer, "melon", xpos, ypos);
xpos += logo1_width;
renderer.drawFont(this.logo2, "JS", xpos, ypos);
this.logo2.draw(renderer, "JS", xpos, ypos);
}

});
Expand Down
Loading

0 comments on commit cfcea2e

Please sign in to comment.