blob: ffa23c40ce93ffa00b09c345a350563e903bbba3 [file] [log] [blame]
/**
* Copyright 2014 Google Inc. All rights reserved.
*
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*
* @fileoverview Description of this file.
*
* A polyfill for HTML Canvas features, including
* Path2D support.
*/
if (CanvasRenderingContext2D.prototype.ellipse === undefined) {
CanvasRenderingContext2D.prototype.ellipse = function(x, y, radiusX, radiusY, rotation, startAngle, endAngle, antiClockwise) {
this.save();
this.translate(x, y);
this.rotate(rotation);
this.scale(radiusX, radiusY);
this.arc(0, 0, 1, startAngle, endAngle, antiClockwise);
this.restore();
};
}
if (typeof Path2D !== 'function' || typeof Path2D.prototype.addPath !== 'function' || typeof Path2D.prototype.ellipse !== 'function') {
(function() {
var canvasPrototype = CanvasRenderingContext2D.prototype;
function Path_(arg) {
this.ops_ = [];
if (arg === undefined) {
return;
}
if (typeof arg == 'string') {
try {
this.ops_ = parser.parse(arg);
} catch(e) {
// Treat an invalid SVG path as an empty path.
}
} else if (arg.hasOwnProperty('ops_')) {
this.ops_ = arg.ops_.slice(0);
} else {
throw 'Error: ' + typeof arg + 'is not a valid argument to Path';
}
}
// TODO(jcgregorio) test for arcTo and implement via something.
// Path methods that map simply to the CanvasRenderingContext2D.
var simple_mapping = [
'closePath',
'moveTo',
'lineTo',
'quadraticCurveTo',
'bezierCurveTo',
'rect',
'arc',
'arcTo',
'ellipse'
];
function createFunction(name) {
return function() {
var i, len = arguments.length;
var args = [];
for(i=0;i<len;i+=1){
args.push(arguments[i]);
}
this.ops_.push({type: name, args: args});
};
}
// Add simple_mapping methods to Path2D.
for (var i=0; i<simple_mapping.length; i++) {
var name = simple_mapping[i];
Path_.prototype[name] = createFunction(name);
}
Path_.prototype.addPath = function(path, tr) {
var hasTx = false;
if (tr && (tr.a != 1 || tr.b != 0 || tr.c != 0 || tr.d != 1 || tr.e != 0 || tr.f != 0)) {
hasTx = true;
this.ops_.push({type: 'save', args: []});
this.ops_.push({type: 'transform', args: [tr.a, tr.b, tr.c, tr.d, tr.e, tr.f]});
}
this.ops_ = this.ops_.concat(path.ops_);
if (hasTx) {
this.ops_.push({type: 'restore', args: []});
}
};
var original_fill = canvasPrototype.fill;
var original_stroke = canvasPrototype.stroke;
var original_clip = canvasPrototype.clip;
// Replace methods on CanvasRenderingContext2D with ones that understand Path2D.
canvasPrototype.fill = function(arg) {
if (arg instanceof Path_) {
this.beginPath();
for (var i = 0, len = arg.ops_.length; i < len; i++) {
var op = arg.ops_[i];
canvasPrototype[op.type].apply(this, op.args);
}
len = arguments.length;
var args = [];
for(i=1;i<len;i+=1){
args.push(arguments[i]);
}
original_fill.apply(this, args);
} else {
original_fill.apply(this, arguments);
}
};
canvasPrototype.stroke = function(arg) {
if (arg instanceof Path_) {
this.beginPath();
for (var i = 0, len = arg.ops_.length; i < len; i++) {
var op = arg.ops_[i];
canvasPrototype[op.type].apply(this, op.args);
}
original_stroke.call(this);
} else {
original_stroke.call(this);
}
};
canvasPrototype.clip = function(arg) {
if (arg instanceof Path_) {
this.beginPath();
for (var i = 0, len = arg.ops_.length; i < len; i++) {
var op = arg.ops_[i];
canvasPrototype[op.type].apply(this, op.args);
}
len = arguments.length;
var args = [];
for(i=1;i<len;i+=1){
args.push(arguments[i]);
}
original_clip.apply(this, args);
} else {
original_clip.apply(this, arguments);
}
};
// Set up externs.
Path2D = Path_;
})();
}