You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
350 lines
6.8 KiB
350 lines
6.8 KiB
'use strict';
|
|
|
|
/*!
|
|
* Canvas - Context2d
|
|
* Copyright (c) 2010 LearnBoost <tj@learnboost.com>
|
|
* MIT Licensed
|
|
*/
|
|
|
|
/**
|
|
* Module dependencies.
|
|
*/
|
|
|
|
var canvas = require('./bindings')
|
|
, Context2d = canvas.CanvasRenderingContext2d
|
|
, CanvasGradient = canvas.CanvasGradient
|
|
, CanvasPattern = canvas.CanvasPattern
|
|
, ImageData = canvas.ImageData;
|
|
|
|
/**
|
|
* Export `Context2d` as the module.
|
|
*/
|
|
|
|
var Context2d = exports = module.exports = Context2d;
|
|
|
|
/**
|
|
* Cache color string RGBA values.
|
|
*/
|
|
|
|
var cache = {};
|
|
|
|
/**
|
|
* Text baselines.
|
|
*/
|
|
|
|
var baselines = ['alphabetic', 'top', 'bottom', 'middle', 'ideographic', 'hanging'];
|
|
|
|
/**
|
|
* Font RegExp helpers.
|
|
*/
|
|
|
|
var weights = 'normal|bold|bolder|lighter|[1-9]00'
|
|
, styles = 'normal|italic|oblique'
|
|
, units = 'px|pt|pc|in|cm|mm|%'
|
|
, string = '\'([^\']+)\'|"([^"]+)"|[\\w-]+';
|
|
|
|
/**
|
|
* Font parser RegExp;
|
|
*/
|
|
|
|
var fontre = new RegExp('^ *'
|
|
+ '(?:(' + weights + ') *)?'
|
|
+ '(?:(' + styles + ') *)?'
|
|
+ '([\\d\\.]+)(' + units + ') *'
|
|
+ '((?:' + string + ')( *, *(?:' + string + '))*)'
|
|
);
|
|
|
|
/**
|
|
* Parse font `str`.
|
|
*
|
|
* @param {String} str
|
|
* @return {Object}
|
|
* @api private
|
|
*/
|
|
|
|
var parseFont = exports.parseFont = function(str){
|
|
var font = {}
|
|
, captures = fontre.exec(str);
|
|
|
|
// Invalid
|
|
if (!captures) return;
|
|
|
|
// Cached
|
|
if (cache[str]) return cache[str];
|
|
|
|
// Populate font object
|
|
font.weight = captures[1] || 'normal';
|
|
font.style = captures[2] || 'normal';
|
|
font.size = parseFloat(captures[3]);
|
|
font.unit = captures[4];
|
|
font.family = captures[5].replace(/["']/g, '').split(',').map(function (family) {
|
|
return family.trim();
|
|
}).join(',');
|
|
|
|
// TODO: dpi
|
|
// TODO: remaining unit conversion
|
|
switch (font.unit) {
|
|
case 'pt':
|
|
font.size /= .75;
|
|
break;
|
|
case 'in':
|
|
font.size *= 96;
|
|
break;
|
|
case 'mm':
|
|
font.size *= 96.0 / 25.4;
|
|
break;
|
|
case 'cm':
|
|
font.size *= 96.0 / 2.54;
|
|
break;
|
|
}
|
|
|
|
return cache[str] = font;
|
|
};
|
|
|
|
/**
|
|
* Enable or disable image smoothing.
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
Context2d.prototype.__defineSetter__('imageSmoothingEnabled', function(val){
|
|
this._imageSmoothing = !! val;
|
|
this.patternQuality = val ? 'best' : 'fast';
|
|
});
|
|
|
|
/**
|
|
* Get image smoothing value.
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
Context2d.prototype.__defineGetter__('imageSmoothingEnabled', function(val){
|
|
return !! this._imageSmoothing;
|
|
});
|
|
|
|
/**
|
|
* Create a pattern from `Image` or `Canvas`.
|
|
*
|
|
* @param {Image|Canvas} image
|
|
* @param {String} repetition
|
|
* @return {CanvasPattern}
|
|
* @api public
|
|
*/
|
|
|
|
Context2d.prototype.createPattern = function(image, repetition){
|
|
// TODO Use repetition (currently always 'repeat')
|
|
return new CanvasPattern(image);
|
|
};
|
|
|
|
/**
|
|
* Create a linear gradient at the given point `(x0, y0)` and `(x1, y1)`.
|
|
*
|
|
* @param {Number} x0
|
|
* @param {Number} y0
|
|
* @param {Number} x1
|
|
* @param {Number} y1
|
|
* @return {CanvasGradient}
|
|
* @api public
|
|
*/
|
|
|
|
Context2d.prototype.createLinearGradient = function(x0, y0, x1, y1){
|
|
return new CanvasGradient(x0, y0, x1, y1);
|
|
};
|
|
|
|
/**
|
|
* Create a radial gradient at the given point `(x0, y0)` and `(x1, y1)`
|
|
* and radius `r0` and `r1`.
|
|
*
|
|
* @param {Number} x0
|
|
* @param {Number} y0
|
|
* @param {Number} r0
|
|
* @param {Number} x1
|
|
* @param {Number} y1
|
|
* @param {Number} r1
|
|
* @return {CanvasGradient}
|
|
* @api public
|
|
*/
|
|
|
|
Context2d.prototype.createRadialGradient = function(x0, y0, r0, x1, y1, r1){
|
|
return new CanvasGradient(x0, y0, r0, x1, y1, r1);
|
|
};
|
|
|
|
/**
|
|
* Reset transform matrix to identity, then apply the given args.
|
|
*
|
|
* @param {...}
|
|
* @api public
|
|
*/
|
|
|
|
Context2d.prototype.setTransform = function(){
|
|
this.resetTransform();
|
|
this.transform.apply(this, arguments);
|
|
};
|
|
|
|
/**
|
|
* Set the fill style with the given css color string.
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
Context2d.prototype.__defineSetter__('fillStyle', function(val){
|
|
if (!val) return;
|
|
if ('CanvasGradient' == val.constructor.name
|
|
|| 'CanvasPattern' == val.constructor.name) {
|
|
this.lastFillStyle = val;
|
|
this._setFillPattern(val);
|
|
} else if ('string' == typeof val) {
|
|
this._setFillColor(val);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Get previous fill style.
|
|
*
|
|
* @return {CanvasGradient|String}
|
|
* @api public
|
|
*/
|
|
|
|
Context2d.prototype.__defineGetter__('fillStyle', function(){
|
|
return this.lastFillStyle || this.fillColor;
|
|
});
|
|
|
|
/**
|
|
* Set the stroke style with the given css color string.
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
Context2d.prototype.__defineSetter__('strokeStyle', function(val){
|
|
if (!val) return;
|
|
if ('CanvasGradient' == val.constructor.name
|
|
|| 'CanvasPattern' == val.constructor.name) {
|
|
this.lastStrokeStyle = val;
|
|
this._setStrokePattern(val);
|
|
} else if ('string' == typeof val) {
|
|
this._setStrokeColor(val);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Get previous stroke style.
|
|
*
|
|
* @return {CanvasGradient|String}
|
|
* @api public
|
|
*/
|
|
|
|
Context2d.prototype.__defineGetter__('strokeStyle', function(){
|
|
return this.lastStrokeStyle || this.strokeColor;
|
|
});
|
|
|
|
/**
|
|
* Set font.
|
|
*
|
|
* @see exports.parseFont()
|
|
* @api public
|
|
*/
|
|
|
|
Context2d.prototype.__defineSetter__('font', function(val){
|
|
if (!val) return;
|
|
if ('string' == typeof val) {
|
|
var font;
|
|
if (font = parseFont(val)) {
|
|
this.lastFontString = val;
|
|
this._setFont(
|
|
font.weight
|
|
, font.style
|
|
, font.size
|
|
, font.unit
|
|
, font.family);
|
|
}
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Get the current font.
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
Context2d.prototype.__defineGetter__('font', function(){
|
|
return this.lastFontString || '10px sans-serif';
|
|
});
|
|
|
|
/**
|
|
* Set text baseline.
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
Context2d.prototype.__defineSetter__('textBaseline', function(val){
|
|
if (!val) return;
|
|
var n = baselines.indexOf(val);
|
|
if (~n) {
|
|
this.lastBaseline = val;
|
|
this._setTextBaseline(n);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Get the current baseline setting.
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
Context2d.prototype.__defineGetter__('textBaseline', function(){
|
|
return this.lastBaseline || 'alphabetic';
|
|
});
|
|
|
|
/**
|
|
* Set text alignment.
|
|
*
|
|
* @api public
|
|
*/
|
|
|
|
Context2d.prototype.__defineSetter__('textAlign', function(val){
|
|
switch (val) {
|
|
case 'center':
|
|
this._setTextAlignment(0);
|
|
this.lastTextAlignment = val;
|
|
break;
|
|
case 'left':
|
|
case 'start':
|
|
this._setTextAlignment(-1);
|
|
this.lastTextAlignment = val;
|
|
break;
|
|
case 'right':
|
|
case 'end':
|
|
this._setTextAlignment(1);
|
|
this.lastTextAlignment = val;
|
|
break;
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Get the current font.
|
|
*
|
|
* @see exports.parseFont()
|
|
* @api public
|
|
*/
|
|
|
|
Context2d.prototype.__defineGetter__('textAlign', function(){
|
|
return this.lastTextAlignment || 'start';
|
|
});
|
|
|
|
/**
|
|
* Create `ImageData` with the given dimensions or
|
|
* `ImageData` instance for dimensions.
|
|
*
|
|
* @param {Number|ImageData} width
|
|
* @param {Number} height
|
|
* @return {ImageData}
|
|
* @api public
|
|
*/
|
|
|
|
Context2d.prototype.createImageData = function(width, height){
|
|
if ('ImageData' == width.constructor.name) {
|
|
height = width.height;
|
|
width = width.width;
|
|
}
|
|
return new ImageData(new Uint8ClampedArray(width * height * 4), width, height);
|
|
};
|
|
|