Vex Flow Mohit Muthanna mohit@muthanna.com
Copyright Mohit Muthanna 2010
Requires a glyph font to be loaded and Vex.Flow.Font to be set.
/**
* A quick and dirty static glyph renderer. Renders glyphs from the default
* font defined in Vex.Flow.Font.
*
* @param {!Object} ctx The canvas context.
* @param {number} x_pos X coordinate.
* @param {number} y_pos Y coordinate.
* @param {number} point The point size to use.
* @param {string} val The glyph code in Vex.Flow.Font.
* @param {boolean} nocache If set, disables caching of font outline.
*/
Vex.Flow.renderGlyph = function(ctx, x_pos, y_pos, point, val, nocache) {
var scale = point * 72.0 / (Vex.Flow.Font.resolution * 100.0);
var metrics = Vex.Flow.Glyph.loadMetrics(Vex.Flow.Font, val, !nocache);
Vex.Flow.Glyph.renderOutline(ctx, metrics.outline, scale, x_pos, y_pos);
};
/**
* @constructor
*/
Vex.Flow.Glyph = (function() {
function Glyph(code, point, options) {
this.code = code;
this.point = point;
this.context = null;
this.options = {
cache: true,
font: Vex.Flow.Font
};
this.width = null;
this.metrics = null;
this.x_shift = 0;
this.y_shift = 0;
if (options) this.setOptions(options); else this.reset();
}
Glyph.prototype = {
setOptions: function(options) {
Vex.Merge(this.options, options);
this.reset();
},
setPoint: function(point) { this.point = point; return this; },
setStave: function(stave) { this.stave = stave; return this; },
setXShift: function(x_shift) { this.x_shift = x_shift; return this; },
setYShift: function(y_shift) { this.y_shift = y_shift; return this; },
setContext: function(context) { this.context = context; return this; },
getContext: function() { return this.context; },
reset: function() {
this.metrics = Vex.Flow.Glyph.loadMetrics(this.options.font, this.code,
this.options.cache);
this.scale = this.point * 72 / (this.options.font.resolution * 100);
},
setWidth: function(width) {
this.width = width;
return this;
},
getMetrics: function() {
if (!this.metrics) throw new Vex.RuntimeError("BadGlyph", "Glyph " +
this.code + " is not initialized.");
return {
x_min: this.metrics.x_min * this.scale,
x_max: this.metrics.x_max * this.scale,
width: this.width || (this.metrics.x_max - this.metrics.x_min) * this.scale,
height: this.metrics.ha * this.scale
};
},
render: function(ctx, x_pos, y_pos) {
if (!this.metrics) throw new Vex.RuntimeError("BadGlyph", "Glyph " +
this.code + " is not initialized.");
var outline = this.metrics.outline;
var scale = this.scale;
Glyph.renderOutline(ctx, outline, scale, x_pos, y_pos);
},
renderToStave: function(x) {
if (!this.metrics) throw new Vex.RuntimeError("BadGlyph", "Glyph " +
this.code + " is not initialized.");
if (!this.stave) throw new Vex.RuntimeError("GlyphError", "No valid stave");
if (!this.context) throw new Vex.RERR("GlyphError", "No valid context");
var outline = this.metrics.outline;
var scale = this.scale;
Glyph.renderOutline(this.context, outline, scale,
x + this.x_shift, this.stave.getYForGlyphs() + this.y_shift);
}
};
/* Static methods used to implement loading / unloading of glyphs */
Glyph.loadMetrics = function(font, code, cache) {
var glyph = font.glyphs[code];
if (!glyph) throw new Vex.RuntimeError("BadGlyph", "Glyph " + code +
" does not exist in font.");
var x_min = glyph.x_min;
var x_max = glyph.x_max;
var ha = glyph.ha;
var outline;
if (glyph.o) {
if (cache) {
if (glyph.cached_outline) {
outline = glyph.cached_outline;
} else {
outline = glyph.o.split(' ');
glyph.cached_outline = outline;
}
} else {
if (glyph.cached_outline) delete glyph.cached_outline;
outline = glyph.o.split(' ');
}
return {
x_min: x_min,
x_max: x_max,
ha: ha,
outline: outline
};
} else {
throw new Vex.RuntimeError("BadGlyph", "Glyph " + this.code +
" has no outline defined.");
}
};
Glyph.renderOutline = function(ctx, outline, scale, x_pos, y_pos) {
var outlineLength = outline.length;
ctx.beginPath();
ctx.moveTo(x_pos, y_pos);
for (var i = 0; i < outlineLength; ) {
var action = outline[i++];
switch(action) {
case 'm':
ctx.moveTo(x_pos + outline[i++] * scale,
y_pos + outline[i++] * -scale);
break;
case 'l':
ctx.lineTo(x_pos + outline[i++] * scale,
y_pos + outline[i++] * -scale);
break;
case 'q':
var cpx = x_pos + outline[i++] * scale;
var cpy = y_pos + outline[i++] * -scale;
ctx.quadraticCurveTo(
x_pos + outline[i++] * scale,
y_pos + outline[i++] * -scale, cpx, cpy);
break;
case 'b':
var x = x_pos + outline[i++] * scale;
var y = y_pos + outline[i++] * -scale;
ctx.bezierCurveTo(
x_pos + outline[i++] * scale, y_pos + outline[i++] * -scale,
x_pos + outline[i++] * scale, y_pos + outline[i++] * -scale,
x, y);
break;
}
}
ctx.fill();
};
return Glyph;
}());