1635 lines
44 KiB
JavaScript
1635 lines
44 KiB
JavaScript
|
/*
|
||
|
Canvas Query 1.0.1
|
||
|
http://canvasquery.org
|
||
|
(c) 2012-2014 http://rezoner.net
|
||
|
Canvas Query may be freely distributed under the MIT license.
|
||
|
*/
|
||
|
|
||
|
(function() {
|
||
|
|
||
|
var COCOONJS = false;
|
||
|
var NODEJS = !(typeof exports === "undefined");
|
||
|
|
||
|
if (NODEJS) {
|
||
|
var Canvas = require('canvas');
|
||
|
var Image = Canvas.Image;
|
||
|
var fs = require("fs");
|
||
|
} else {
|
||
|
var Canvas = window.HTMLCanvasElement;
|
||
|
var Image = window.HTMLImageElement;
|
||
|
var COCOONJS = navigator.isCocoonJS;
|
||
|
}
|
||
|
|
||
|
var cq = function(selector) {
|
||
|
if (arguments.length === 0) {
|
||
|
var canvas = cq.createCanvas(window.innerWidth, window.innerHeight);
|
||
|
window.addEventListener("resize", function() {
|
||
|
// canvas.width = window.innerWidth;
|
||
|
// canvas.height = window.innerHeight;
|
||
|
});
|
||
|
} else if (typeof selector === "string") {
|
||
|
var canvas = document.querySelector(selector);
|
||
|
} else if (typeof selector === "number") {
|
||
|
var canvas = cq.createCanvas(arguments[0], arguments[1]);
|
||
|
} else if (selector instanceof Image) {
|
||
|
var canvas = cq.createCanvas(selector);
|
||
|
} else if (selector instanceof cq.Layer) {
|
||
|
return selector;
|
||
|
} else {
|
||
|
var canvas = selector;
|
||
|
}
|
||
|
|
||
|
return new cq.Layer(canvas);
|
||
|
};
|
||
|
|
||
|
cq.lineSpacing = 1.0;
|
||
|
|
||
|
cq.cocoon = function(selector) {
|
||
|
if (arguments.length === 0) {
|
||
|
var canvas = cq.createCocoonCanvas(window.innerWidth, window.innerHeight);
|
||
|
window.addEventListener("resize", function() {});
|
||
|
} else if (typeof selector === "string") {
|
||
|
var canvas = document.querySelector(selector);
|
||
|
} else if (typeof selector === "number") {
|
||
|
var canvas = cq.createCocoonCanvas(arguments[0], arguments[1]);
|
||
|
} else if (selector instanceof Image) {
|
||
|
var canvas = cq.createCocoonCanvas(selector);
|
||
|
} else if (selector instanceof cq.Layer) {
|
||
|
return selector;
|
||
|
} else {
|
||
|
var canvas = selector;
|
||
|
}
|
||
|
|
||
|
return new cq.Layer(canvas);
|
||
|
}
|
||
|
|
||
|
|
||
|
cq.extend = function() {
|
||
|
for (var i = 1; i < arguments.length; i++) {
|
||
|
for (var j in arguments[i]) {
|
||
|
arguments[0][j] = arguments[i][j];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return arguments[0];
|
||
|
};
|
||
|
|
||
|
cq.augment = function() {
|
||
|
for (var i = 1; i < arguments.length; i++) {
|
||
|
_.extend(arguments[0], arguments[i]);
|
||
|
arguments[i](arguments[0]);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
cq.distance = function(x1, y1, x2, y2) {
|
||
|
if (arguments.length > 2) {
|
||
|
var dx = x1 - x2;
|
||
|
var dy = y1 - y2;
|
||
|
|
||
|
return Math.sqrt(dx * dx + dy * dy);
|
||
|
} else {
|
||
|
return Math.abs(x1 - y1);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
cq.extend(cq, {
|
||
|
|
||
|
smoothing: true,
|
||
|
|
||
|
blend: function(below, above, mode, mix) {
|
||
|
|
||
|
if (typeof mix === "undefined") mix = 1;
|
||
|
|
||
|
var below = cq(below);
|
||
|
var mask = below.clone();
|
||
|
var above = cq(above);
|
||
|
|
||
|
below.save();
|
||
|
below.globalAlpha(mix);
|
||
|
below.globalCompositeOperation(mode);
|
||
|
below.drawImage(above.canvas, 0, 0);
|
||
|
below.restore();
|
||
|
|
||
|
mask.save();
|
||
|
mask.globalCompositeOperation("source-in");
|
||
|
mask.drawImage(below.canvas, 0, 0);
|
||
|
mask.restore();
|
||
|
|
||
|
return mask;
|
||
|
},
|
||
|
|
||
|
matchColor: function(color, palette) {
|
||
|
var rgbPalette = [];
|
||
|
|
||
|
for (var i = 0; i < palette.length; i++) {
|
||
|
rgbPalette.push(cq.color(palette[i]));
|
||
|
}
|
||
|
|
||
|
var imgData = cq.color(color);
|
||
|
|
||
|
var difList = [];
|
||
|
for (var j = 0; j < rgbPalette.length; j++) {
|
||
|
var rgbVal = rgbPalette[j];
|
||
|
var rDif = Math.abs(imgData[0] - rgbVal[0]),
|
||
|
gDif = Math.abs(imgData[1] - rgbVal[1]),
|
||
|
bDif = Math.abs(imgData[2] - rgbVal[2]);
|
||
|
difList.push(rDif + gDif + bDif);
|
||
|
}
|
||
|
|
||
|
var closestMatch = 0;
|
||
|
for (var j = 0; j < palette.length; j++) {
|
||
|
if (difList[j] < difList[closestMatch]) {
|
||
|
closestMatch = j;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return palette[closestMatch];
|
||
|
},
|
||
|
|
||
|
temp: function(width, height) {
|
||
|
if (!this.tempLayer) {
|
||
|
this.tempLayer = cq(1, 1);
|
||
|
}
|
||
|
|
||
|
if (width instanceof Image) {
|
||
|
this.tempLayer.width = width.width;
|
||
|
this.tempLayer.height = width.height;
|
||
|
this.tempLayer.context.drawImage(width, 0, 0);
|
||
|
} else if (width instanceof Canvas) {
|
||
|
this.tempLayer.width = width.width;
|
||
|
this.tempLayer.height = width.height;
|
||
|
this.tempLayer.context.drawImage(width, 0, 0);
|
||
|
} else if (width instanceof CanvasQuery.Layer) {
|
||
|
this.tempLayer.width = width.width;
|
||
|
this.tempLayer.height = width.height;
|
||
|
this.tempLayer.context.drawImage(width.canvas, 0, 0);
|
||
|
} else {
|
||
|
this.tempLayer.width = width;
|
||
|
this.tempLayer.height = height;
|
||
|
}
|
||
|
|
||
|
return this.tempLayer;
|
||
|
},
|
||
|
|
||
|
wrapValue: function(value, min, max) {
|
||
|
if (value < min) return max + (value % max);
|
||
|
if (value >= max) return value % max;
|
||
|
return value;
|
||
|
},
|
||
|
|
||
|
limitValue: function(value, min, max) {
|
||
|
return value < min ? min : value > max ? max : value;
|
||
|
},
|
||
|
|
||
|
mix: function(a, b, amount) {
|
||
|
return a + (b - a) * amount;
|
||
|
},
|
||
|
|
||
|
hexToRgb: function(hex) {
|
||
|
if (hex.length === 7) return ['0x' + hex[1] + hex[2] | 0, '0x' + hex[3] + hex[4] | 0, '0x' + hex[5] + hex[6] | 0];
|
||
|
else return ['0x' + hex[1] + hex[1] | 0, '0x' + hex[2] + hex[2] | 0, '0x' + hex[3] + hex[3] | 0];
|
||
|
},
|
||
|
|
||
|
rgbToHex: function(r, g, b) {
|
||
|
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1, 7);
|
||
|
},
|
||
|
|
||
|
/* author: http://mjijackson.com/ */
|
||
|
|
||
|
rgbToHsl: function(r, g, b) {
|
||
|
|
||
|
if (r instanceof Array) {
|
||
|
b = r[2];
|
||
|
g = r[1];
|
||
|
r = r[0];
|
||
|
}
|
||
|
|
||
|
r /= 255, g /= 255, b /= 255;
|
||
|
var max = Math.max(r, g, b),
|
||
|
min = Math.min(r, g, b);
|
||
|
var h, s, l = (max + min) / 2;
|
||
|
|
||
|
if (max == min) {
|
||
|
h = s = 0; // achromatic
|
||
|
} else {
|
||
|
var d = max - min;
|
||
|
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||
|
switch (max) {
|
||
|
case r:
|
||
|
h = (g - b) / d + (g < b ? 6 : 0);
|
||
|
break;
|
||
|
case g:
|
||
|
h = (b - r) / d + 2;
|
||
|
break;
|
||
|
case b:
|
||
|
h = (r - g) / d + 4;
|
||
|
break;
|
||
|
}
|
||
|
h /= 6;
|
||
|
}
|
||
|
|
||
|
return [h, s, l];
|
||
|
},
|
||
|
|
||
|
/* author: http://mjijackson.com/ */
|
||
|
|
||
|
hue2rgb: function(p, q, t) {
|
||
|
if (t < 0) t += 1;
|
||
|
if (t > 1) t -= 1;
|
||
|
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
||
|
if (t < 1 / 2) return q;
|
||
|
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
||
|
return p;
|
||
|
},
|
||
|
|
||
|
hslToRgb: function(h, s, l) {
|
||
|
var r, g, b;
|
||
|
|
||
|
if (s == 0) {
|
||
|
r = g = b = l; // achromatic
|
||
|
} else {
|
||
|
|
||
|
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||
|
var p = 2 * l - q;
|
||
|
r = this.hue2rgb(p, q, h + 1 / 3);
|
||
|
g = this.hue2rgb(p, q, h);
|
||
|
b = this.hue2rgb(p, q, h - 1 / 3);
|
||
|
}
|
||
|
|
||
|
return [r * 255 | 0, g * 255 | 0, b * 255 | 0];
|
||
|
},
|
||
|
|
||
|
rgbToHsv: function(r, g, b) {
|
||
|
if (r instanceof Array) {
|
||
|
b = r[2];
|
||
|
g = r[1];
|
||
|
r = r[0];
|
||
|
}
|
||
|
|
||
|
r = r / 255, g = g / 255, b = b / 255;
|
||
|
var max = Math.max(r, g, b),
|
||
|
min = Math.min(r, g, b);
|
||
|
var h, s, v = max;
|
||
|
|
||
|
var d = max - min;
|
||
|
s = max == 0 ? 0 : d / max;
|
||
|
|
||
|
if (max == min) {
|
||
|
h = 0; // achromatic
|
||
|
} else {
|
||
|
switch (max) {
|
||
|
case r:
|
||
|
h = (g - b) / d + (g < b ? 6 : 0);
|
||
|
break;
|
||
|
case g:
|
||
|
h = (b - r) / d + 2;
|
||
|
break;
|
||
|
case b:
|
||
|
h = (r - g) / d + 4;
|
||
|
break;
|
||
|
}
|
||
|
h /= 6;
|
||
|
}
|
||
|
|
||
|
return [h, s, v];
|
||
|
},
|
||
|
|
||
|
hsvToRgb: function(h, s, v) {
|
||
|
var r, g, b;
|
||
|
|
||
|
var i = Math.floor(h * 6);
|
||
|
var f = h * 6 - i;
|
||
|
var p = v * (1 - s);
|
||
|
var q = v * (1 - f * s);
|
||
|
var t = v * (1 - (1 - f) * s);
|
||
|
|
||
|
switch (i % 6) {
|
||
|
case 0:
|
||
|
r = v, g = t, b = p;
|
||
|
break;
|
||
|
case 1:
|
||
|
r = q, g = v, b = p;
|
||
|
break;
|
||
|
case 2:
|
||
|
r = p, g = v, b = t;
|
||
|
break;
|
||
|
case 3:
|
||
|
r = p, g = q, b = v;
|
||
|
break;
|
||
|
case 4:
|
||
|
r = t, g = p, b = v;
|
||
|
break;
|
||
|
case 5:
|
||
|
r = v, g = p, b = q;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return [r * 255, g * 255, b * 255];
|
||
|
},
|
||
|
|
||
|
color: function() {
|
||
|
var result = new cq.Color();
|
||
|
result.parse(arguments[0], arguments[1]);
|
||
|
return result;
|
||
|
},
|
||
|
|
||
|
poolArray: [],
|
||
|
|
||
|
pool: function() {
|
||
|
|
||
|
if (!this.poolArray.length) {
|
||
|
for (var i = 0; i < 100; i++) {
|
||
|
this.poolArray.push(this.createCanvas(1, 1));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return this.poolArray.pop();
|
||
|
|
||
|
},
|
||
|
|
||
|
createCanvas: function(width, height) {
|
||
|
if (NODEJS) {
|
||
|
var result = new Canvas;
|
||
|
} else {
|
||
|
var result = document.createElement("canvas");
|
||
|
}
|
||
|
|
||
|
if (arguments[0] instanceof Image || arguments[0] instanceof Canvas) {
|
||
|
var image = arguments[0];
|
||
|
result.width = image.width;
|
||
|
result.height = image.height;
|
||
|
result.getContext("2d").drawImage(image, 0, 0);
|
||
|
} else {
|
||
|
result.width = width;
|
||
|
result.height = height;
|
||
|
}
|
||
|
|
||
|
|
||
|
return result;
|
||
|
},
|
||
|
|
||
|
createCocoonCanvas: function(width, height) {
|
||
|
var result = document.createElement("screencanvas");
|
||
|
|
||
|
if (arguments[0] instanceof Image) {
|
||
|
var image = arguments[0];
|
||
|
result.width = image.width;
|
||
|
result.height = image.height;
|
||
|
result.getContext("2d").drawImage(image, 0, 0);
|
||
|
} else {
|
||
|
result.width = width;
|
||
|
result.height = height;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
},
|
||
|
|
||
|
createImageData: function(width, height) {
|
||
|
return cq.createCanvas(width, height).getContext("2d").createImageData(width, height);
|
||
|
}
|
||
|
|
||
|
});
|
||
|
|
||
|
cq.Layer = function(canvas) {
|
||
|
this.context = canvas.getContext("2d");
|
||
|
this.canvas = canvas;
|
||
|
this.update();
|
||
|
}
|
||
|
|
||
|
cq.Layer.prototype = {
|
||
|
|
||
|
update: function() {
|
||
|
this.context.webkitImageSmoothingEnabled = cq.smoothing;
|
||
|
this.context.mozImageSmoothingEnabled = cq.smoothing;
|
||
|
this.context.msImageSmoothingEnabled = cq.smoothing;
|
||
|
this.context.imageSmoothingEnabled = cq.smoothing;
|
||
|
|
||
|
if (COCOONJS) CocoonJS.App.setAntialias(cq.smoothing);
|
||
|
},
|
||
|
|
||
|
appendTo: function(selector) {
|
||
|
if (typeof selector === "object") {
|
||
|
var element = selector;
|
||
|
} else {
|
||
|
var element = document.querySelector(selector);
|
||
|
}
|
||
|
|
||
|
element.appendChild(this.canvas);
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
a: function(a) {
|
||
|
if (arguments.length) {
|
||
|
this.previousAlpha = this.globalAlpha();
|
||
|
return this.globalAlpha(a);
|
||
|
} else
|
||
|
return this.globalAlpha();
|
||
|
},
|
||
|
|
||
|
ra: function() {
|
||
|
return this.a(this.previousAlpha);
|
||
|
},
|
||
|
|
||
|
drawRegion: function(image, region, x, y, scale) {
|
||
|
scale = scale || 1;
|
||
|
|
||
|
return this.drawImage(
|
||
|
image, region[0], region[1], region[2], region[3],
|
||
|
x | 0, y | 0, region[2] * scale | 0, region[3] * scale | 0
|
||
|
);
|
||
|
},
|
||
|
|
||
|
cache: function() {
|
||
|
return this.clone().canvas;
|
||
|
|
||
|
/* FFS .... image.src is no longer synchronous when assigning dataURL */
|
||
|
|
||
|
var image = new Image;
|
||
|
image.src = this.canvas.toDataURL();
|
||
|
return image;
|
||
|
},
|
||
|
|
||
|
blendOn: function(what, mode, mix) {
|
||
|
cq.blend(what, this, mode, mix);
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
posterize: function(pc, inc) {
|
||
|
pc = pc || 32;
|
||
|
inc = inc || 4;
|
||
|
var imgdata = this.getImageData(0, 0, this.width, this.height);
|
||
|
var data = imgdata.data;
|
||
|
|
||
|
for (var i = 0; i < data.length; i += inc) {
|
||
|
data[i] -= data[i] % pc; // set value to nearest of 8 possibilities
|
||
|
data[i + 1] -= data[i + 1] % pc; // set value to nearest of 8 possibilities
|
||
|
data[i + 2] -= data[i + 2] % pc; // set value to nearest of 8 possibilities
|
||
|
}
|
||
|
|
||
|
this.putImageData(imgdata, 0, 0); // put image data to canvas
|
||
|
},
|
||
|
|
||
|
|
||
|
bw: function(pc) {
|
||
|
pc = 128;
|
||
|
var imgdata = this.getImageData(0, 0, this.width, this.height);
|
||
|
var data = imgdata.data;
|
||
|
// 8-bit: rrr ggg bb
|
||
|
for (var i = 0; i < data.length; i += 4) {
|
||
|
var v = ((data[i] + data[i + 1] + data[i + 2]) / 3);
|
||
|
|
||
|
v = (v / 128 | 0) * 128;
|
||
|
//data[i] = v; // set value to nearest of 8 possibilities
|
||
|
//data[i + 1] = v; // set value to nearest of 8 possibilities
|
||
|
data[i + 2] = (v / 255) * data[i]; // set value to nearest of 8 possibilities
|
||
|
|
||
|
}
|
||
|
|
||
|
this.putImageData(imgdata, 0, 0); // put image data to canvas
|
||
|
},
|
||
|
|
||
|
blend: function(what, mode, mix) {
|
||
|
if (typeof what === "string") {
|
||
|
var color = what;
|
||
|
what = cq(this.canvas.width, this.canvas.height);
|
||
|
what.fillStyle(color).fillRect(0, 0, this.canvas.width, this.canvas.height);
|
||
|
}
|
||
|
|
||
|
var result = cq.blend(this, what, mode, mix);
|
||
|
|
||
|
this.canvas = result.canvas;
|
||
|
this.context = result.context;
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
|
||
|
textWithBackground: function(text, x, y, background, padding) {
|
||
|
var w = this.measureText(text).width;
|
||
|
var h = this.fontHeight() * 0.8;
|
||
|
var f = this.fillStyle();
|
||
|
var padding = padding || 2;
|
||
|
|
||
|
this.fillStyle(background).fillRect(x - w / 2 - padding * 2, y - padding, w + padding * 4, h + padding * 2)
|
||
|
this.fillStyle(f).textAlign("center").textBaseline("top").fillText(text, x, y);
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
fillCircle: function(x, y, r) {
|
||
|
this.context.beginPath();
|
||
|
this.context.arc(x, y, r, 0, Math.PI * 2);
|
||
|
this.context.fill();
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
strokeCircle: function(x, y, r) {
|
||
|
this.context.beginPath();
|
||
|
this.context.arc(x, y, r, 0, Math.PI * 2);
|
||
|
this.context.stroke();
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
circle: function(x, y, r) {
|
||
|
this.context.arc(x, y, r, 0, Math.PI * 2);
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
crop: function(x, y, w, h) {
|
||
|
|
||
|
if (arguments.length === 1) {
|
||
|
|
||
|
var y = arguments[0][1];
|
||
|
var w = arguments[0][2];
|
||
|
var h = arguments[0][3];
|
||
|
var x = arguments[0][0];
|
||
|
}
|
||
|
|
||
|
var canvas = cq.createCanvas(w, h);
|
||
|
var context = canvas.getContext("2d");
|
||
|
|
||
|
context.drawImage(this.canvas, x, y, w, h, 0, 0, w, h);
|
||
|
this.canvas.width = w;
|
||
|
this.canvas.height = h;
|
||
|
this.clear();
|
||
|
this.context.drawImage(canvas, 0, 0);
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
set: function(properties) {
|
||
|
cq.extend(this.context, properties);
|
||
|
},
|
||
|
|
||
|
resize: function(width, height) {
|
||
|
var w = width,
|
||
|
h = height;
|
||
|
|
||
|
if (arguments.length === 1) {
|
||
|
w = arguments[0] * this.canvas.width | 0;
|
||
|
h = arguments[0] * this.canvas.height | 0;
|
||
|
} else {
|
||
|
|
||
|
if (height === false) {
|
||
|
if (this.canvas.width > width) {
|
||
|
h = this.canvas.height * (width / this.canvas.width) | 0;
|
||
|
w = width;
|
||
|
} else {
|
||
|
w = this.canvas.width;
|
||
|
h = this.canvas.height;
|
||
|
}
|
||
|
} else if (width === false) {
|
||
|
if (this.canvas.width > width) {
|
||
|
w = this.canvas.width * (height / this.canvas.height) | 0;
|
||
|
h = height;
|
||
|
} else {
|
||
|
w = this.canvas.width;
|
||
|
h = this.canvas.height;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var cqresized = cq(w, h).drawImage(this.canvas, 0, 0, this.canvas.width, this.canvas.height, 0, 0, w, h);
|
||
|
this.canvas = cqresized.canvas;
|
||
|
this.context = cqresized.context;
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
imageLine: function(image, region, x, y, ex, ey, scale) {
|
||
|
if (!region) region = [0, 0, image.width, image.height];
|
||
|
|
||
|
var distance = cq.distance(x, y, ex, ey);
|
||
|
var count = distance / region[3] + 0.5 | 0;
|
||
|
var angle = Math.atan2(ey - y, ex - x) + Math.PI / 2;
|
||
|
|
||
|
this.save();
|
||
|
|
||
|
this.translate(x, y);
|
||
|
this.rotate(angle);
|
||
|
|
||
|
if (scale) this.scale(scale, 1.0);
|
||
|
|
||
|
for (var i = 0; i <= count; i++) {
|
||
|
this.drawRegion(image, region, -region[2] / 2 | 0, -region[3] * (i + 1));
|
||
|
}
|
||
|
|
||
|
this.restore();
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
trim: function(color, changes) {
|
||
|
var transparent;
|
||
|
|
||
|
if (color) {
|
||
|
color = cq.color(color).toArray();
|
||
|
transparent = !color[3];
|
||
|
} else transparent = true;
|
||
|
|
||
|
var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
|
||
|
var sourcePixels = sourceData.data;
|
||
|
|
||
|
var bound = [this.canvas.width, this.canvas.height, 0, 0];
|
||
|
|
||
|
var width = this.canvas.width;
|
||
|
var height = this.canvas.height;
|
||
|
|
||
|
for (var i = 0, len = sourcePixels.length; i < len; i += 4) {
|
||
|
if (transparent) {
|
||
|
if (!sourcePixels[i + 3]) continue;
|
||
|
} else if (sourcePixels[i + 0] === color[0] && sourcePixels[i + 1] === color[1] && sourcePixels[i + 2] === color[2]) continue;
|
||
|
|
||
|
var x = (i / 4 | 0) % this.canvas.width | 0;
|
||
|
var y = (i / 4 | 0) / this.canvas.width | 0;
|
||
|
|
||
|
if (x < bound[0]) bound[0] = x;
|
||
|
if (x > bound[2]) bound[2] = x;
|
||
|
|
||
|
if (y < bound[1]) bound[1] = y;
|
||
|
if (y > bound[3]) bound[3] = y;
|
||
|
}
|
||
|
|
||
|
|
||
|
if (bound[2] === 0 && bound[3] === 0) {} else {
|
||
|
if (changes) {
|
||
|
changes.left = bound[0];
|
||
|
changes.top = bound[1];
|
||
|
|
||
|
changes.bottom = height - bound[3];
|
||
|
changes.right = width - bound[2] - bound[0];
|
||
|
|
||
|
changes.width = bound[2] - bound[0];
|
||
|
changes.height = bound[3] - bound[1];
|
||
|
}
|
||
|
|
||
|
this.crop(bound[0], bound[1], bound[2] - bound[0] + 1, bound[3] - bound[1] + 1);
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
matchPalette: function(palette) {
|
||
|
var imgData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
|
||
|
|
||
|
var rgbPalette = [];
|
||
|
|
||
|
for (var i = 0; i < palette.length; i++) {
|
||
|
rgbPalette.push(cq.color(palette[i]));
|
||
|
}
|
||
|
|
||
|
|
||
|
for (var i = 0; i < imgData.data.length; i += 4) {
|
||
|
var difList = [];
|
||
|
if (!imgData.data[i + 3]) continue;
|
||
|
|
||
|
for (var j = 0; j < rgbPalette.length; j++) {
|
||
|
var rgbVal = rgbPalette[j];
|
||
|
var rDif = Math.abs(imgData.data[i] - rgbVal[0]),
|
||
|
gDif = Math.abs(imgData.data[i + 1] - rgbVal[1]),
|
||
|
bDif = Math.abs(imgData.data[i + 2] - rgbVal[2]);
|
||
|
difList.push(rDif + gDif + bDif);
|
||
|
}
|
||
|
|
||
|
var closestMatch = 0;
|
||
|
|
||
|
for (var j = 0; j < palette.length; j++) {
|
||
|
if (difList[j] < difList[closestMatch]) {
|
||
|
closestMatch = j;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var paletteRgb = cq.hexToRgb(palette[closestMatch]);
|
||
|
imgData.data[i] = paletteRgb[0];
|
||
|
imgData.data[i + 1] = paletteRgb[1];
|
||
|
imgData.data[i + 2] = paletteRgb[2];
|
||
|
|
||
|
/* dithering */
|
||
|
//imgData.data[i + 3] = (255 * Math.random() < imgData.data[i + 3]) ? 255 : 0;
|
||
|
|
||
|
//imgData.data[i + 3] = imgData.data[i + 3] > 128 ? 255 : 0;
|
||
|
/*
|
||
|
if (i % 3 === 0) {
|
||
|
imgData.data[i] -= cq.limitValue(imgData.data[i] - 50, 0, 255);
|
||
|
imgData.data[i + 1] -= cq.limitValue(imgData.data[i + 1] - 50, 0, 255);
|
||
|
imgData.data[i + 2] -= cq.limitValue(imgData.data[i + 2] - 50, 0, 255);
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
}
|
||
|
|
||
|
this.context.putImageData(imgData, 0, 0);
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
getPalette: function() {
|
||
|
var palette = [];
|
||
|
var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
|
||
|
var sourcePixels = sourceData.data;
|
||
|
|
||
|
for (var i = 0, len = sourcePixels.length; i < len; i += 4) {
|
||
|
if (sourcePixels[i + 3]) {
|
||
|
var hex = cq.rgbToHex(sourcePixels[i + 0], sourcePixels[i + 1], sourcePixels[i + 2]);
|
||
|
if (palette.indexOf(hex) === -1) palette.push(hex);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return palette;
|
||
|
},
|
||
|
|
||
|
mapPalette: function() {
|
||
|
|
||
|
},
|
||
|
|
||
|
colorToMask: function(color, inverted) {
|
||
|
color = cq.color(color).toArray();
|
||
|
var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
|
||
|
var sourcePixels = sourceData.data;
|
||
|
|
||
|
var mask = [];
|
||
|
|
||
|
for (var i = 0, len = sourcePixels.length; i < len; i += 4) {
|
||
|
if (sourcePixels[i + 0] == color[0] && sourcePixels[i + 1] == color[1] && sourcePixels[i + 2] == color[2]) mask.push(inverted || false);
|
||
|
else mask.push(!inverted);
|
||
|
}
|
||
|
|
||
|
return mask;
|
||
|
},
|
||
|
|
||
|
grayscaleToMask: function() {
|
||
|
|
||
|
var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
|
||
|
var sourcePixels = sourceData.data;
|
||
|
|
||
|
var mask = [];
|
||
|
|
||
|
for (var i = 0, len = sourcePixels.length; i < len; i += 4) {
|
||
|
mask.push(((sourcePixels[i + 0] + sourcePixels[i + 1] + sourcePixels[i + 2]) / 3) / 255);
|
||
|
}
|
||
|
|
||
|
return mask;
|
||
|
},
|
||
|
|
||
|
applyMask: function(mask) {
|
||
|
var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
|
||
|
var sourcePixels = sourceData.data;
|
||
|
|
||
|
var mode = typeof mask[0] === "boolean" ? "bool" : "byte";
|
||
|
|
||
|
for (var i = 0, len = sourcePixels.length; i < len; i += 4) {
|
||
|
var value = mask[i / 4];
|
||
|
sourcePixels[i + 3] = value * 255 | 0;
|
||
|
}
|
||
|
|
||
|
this.context.putImageData(sourceData, 0, 0);
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
fillMask: function(mask) {
|
||
|
|
||
|
var sourceData = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
|
||
|
var sourcePixels = sourceData.data;
|
||
|
|
||
|
var maskType = typeof mask[0] === "boolean" ? "bool" : "byte";
|
||
|
var colorMode = arguments.length === 2 ? "normal" : "gradient";
|
||
|
|
||
|
var color = cq.color(arguments[1]);
|
||
|
if (colorMode === "gradient") colorB = cq.color(arguments[2]);
|
||
|
|
||
|
for (var i = 0, len = sourcePixels.length; i < len; i += 4) {
|
||
|
var value = mask[i / 4];
|
||
|
|
||
|
if (maskType === "byte") value /= 255;
|
||
|
|
||
|
if (colorMode === "normal") {
|
||
|
if (value) {
|
||
|
sourcePixels[i + 0] = color[0] | 0;
|
||
|
sourcePixels[i + 1] = color[1] | 0;
|
||
|
sourcePixels[i + 2] = color[2] | 0;
|
||
|
sourcePixels[i + 3] = value * 255 | 0;
|
||
|
}
|
||
|
} else {
|
||
|
sourcePixels[i + 0] = color[0] + (colorB[0] - color[0]) * value | 0;
|
||
|
sourcePixels[i + 1] = color[1] + (colorB[1] - color[1]) * value | 0;
|
||
|
sourcePixels[i + 2] = color[2] + (colorB[2] - color[2]) * value | 0;
|
||
|
sourcePixels[i + 3] = 255;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.context.putImageData(sourceData, 0, 0);
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
clear: function(color) {
|
||
|
if (color) {
|
||
|
this.context.fillStyle = color;
|
||
|
this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
||
|
} else {
|
||
|
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
clone: function() {
|
||
|
|
||
|
// var result = cq.createCanvas(this.canvas);
|
||
|
|
||
|
var result = cq.pool();
|
||
|
result.width = this.width;
|
||
|
result.height = this.height;
|
||
|
result.getContext("2d").drawImage(this.canvas, 0, 0);
|
||
|
|
||
|
return cq(result);
|
||
|
},
|
||
|
|
||
|
gradientText: function(text, x, y, maxWidth, gradient) {
|
||
|
|
||
|
var words = text.split(" ");
|
||
|
|
||
|
var h = this.fontHeight() * 2;
|
||
|
|
||
|
var ox = 0;
|
||
|
var oy = 0;
|
||
|
|
||
|
if (maxWidth) {
|
||
|
var line = 0;
|
||
|
var lines = [""];
|
||
|
|
||
|
for (var i = 0; i < words.length; i++) {
|
||
|
var word = words[i] + " ";
|
||
|
var wordWidth = this.context.measureText(word).width;
|
||
|
|
||
|
if (ox + wordWidth > maxWidth) {
|
||
|
lines[++line] = "";
|
||
|
ox = 0;
|
||
|
}
|
||
|
|
||
|
lines[line] += word;
|
||
|
|
||
|
ox += wordWidth;
|
||
|
}
|
||
|
} else var lines = [text];
|
||
|
|
||
|
for (var i = 0; i < lines.length; i++) {
|
||
|
var oy = y + i * h * 0.6 | 0;
|
||
|
var lingrad = this.context.createLinearGradient(0, oy, 0, oy + h * 0.6 | 0);
|
||
|
|
||
|
for (var j = 0; j < gradient.length; j += 2) {
|
||
|
lingrad.addColorStop(gradient[j], gradient[j + 1]);
|
||
|
}
|
||
|
|
||
|
var text = lines[i];
|
||
|
|
||
|
this.fillStyle(lingrad).fillText(text, x, oy);
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
outline: function() {
|
||
|
var data = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
|
||
|
var pixels = data.data;
|
||
|
|
||
|
var newData = this.createImageData(this.canvas.width, this.canvas.height);
|
||
|
var newPixels = newData.data;
|
||
|
|
||
|
var canvas = this.canvas;
|
||
|
|
||
|
function check(x, y) {
|
||
|
|
||
|
if (x < 0) return 0;
|
||
|
if (x >= canvas.width) return 0;
|
||
|
if (y < 0) return 0;
|
||
|
if (y >= canvas.height) return 0;
|
||
|
|
||
|
var i = (x + y * canvas.width) * 4;
|
||
|
|
||
|
return pixels[i + 3] > 0;
|
||
|
|
||
|
}
|
||
|
|
||
|
for (var x = 0; x < this.canvas.width; x++) {
|
||
|
for (var y = 0; y < this.canvas.height; y++) {
|
||
|
|
||
|
var full = 0;
|
||
|
var i = (y * canvas.width + x) * 4;
|
||
|
|
||
|
if (!pixels[i + 3]) continue;
|
||
|
|
||
|
full += check(x - 1, y);
|
||
|
full += check(x + 1, y);
|
||
|
full += check(x, y - 1);
|
||
|
full += check(x, y + 1);
|
||
|
|
||
|
if (full !== 4) {
|
||
|
|
||
|
newPixels[i] = 255;
|
||
|
newPixels[i + 1] = 255;
|
||
|
newPixels[i + 2] = 255;
|
||
|
newPixels[i + 3] = 255;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.context.putImageData(newData, 0, 0);
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
setHsl: function() {
|
||
|
|
||
|
if (arguments.length === 1) {
|
||
|
var args = arguments[0];
|
||
|
} else {
|
||
|
var args = arguments;
|
||
|
}
|
||
|
|
||
|
var data = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
|
||
|
var pixels = data.data;
|
||
|
var r, g, b, a, h, s, l, hsl = [],
|
||
|
newPixel = [];
|
||
|
|
||
|
for (var i = 0, len = pixels.length; i < len; i += 4) {
|
||
|
hsl = cq.rgbToHsl(pixels[i + 0], pixels[i + 1], pixels[i + 2]);
|
||
|
|
||
|
h = args[0] === false ? hsl[0] : cq.limitValue(args[0], 0, 1);
|
||
|
s = args[1] === false ? hsl[1] : cq.limitValue(args[1], 0, 1);
|
||
|
l = args[2] === false ? hsl[2] : cq.limitValue(args[2], 0, 1);
|
||
|
|
||
|
newPixel = cq.hslToRgb(h, s, l);
|
||
|
|
||
|
pixels[i + 0] = newPixel[0];
|
||
|
pixels[i + 1] = newPixel[1];
|
||
|
pixels[i + 2] = newPixel[2];
|
||
|
}
|
||
|
|
||
|
this.context.putImageData(data, 0, 0);
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
shiftHsl: function() {
|
||
|
|
||
|
if (arguments.length === 1) {
|
||
|
var args = arguments[0];
|
||
|
} else {
|
||
|
var args = arguments;
|
||
|
}
|
||
|
|
||
|
var data = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
|
||
|
var pixels = data.data;
|
||
|
var r, g, b, a, h, s, l, hsl = [],
|
||
|
newPixel = [];
|
||
|
|
||
|
for (var i = 0, len = pixels.length; i < len; i += 4) {
|
||
|
hsl = cq.rgbToHsl(pixels[i + 0], pixels[i + 1], pixels[i + 2]);
|
||
|
|
||
|
if (pixels[i + 0] !== pixels[i + 1] || pixels[i + 1] !== pixels[i + 2]) {
|
||
|
h = args[0] === false ? hsl[0] : cq.wrapValue(hsl[0] + args[0], 0, 1);
|
||
|
s = args[1] === false ? hsl[1] : cq.limitValue(hsl[1] + args[1], 0, 1);
|
||
|
} else {
|
||
|
h = hsl[0];
|
||
|
s = hsl[1];
|
||
|
}
|
||
|
|
||
|
l = args[2] === false ? hsl[2] : cq.limitValue(hsl[2] + args[2], 0, 1);
|
||
|
|
||
|
newPixel = cq.hslToRgb(h, s, l);
|
||
|
|
||
|
pixels[i + 0] = newPixel[0];
|
||
|
pixels[i + 1] = newPixel[1];
|
||
|
pixels[i + 2] = newPixel[2];
|
||
|
}
|
||
|
|
||
|
|
||
|
this.context.putImageData(data, 0, 0);
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
applyColor: function(color) {
|
||
|
|
||
|
if (COCOONJS) return this;
|
||
|
this.save();
|
||
|
|
||
|
this.globalCompositeOperation("source-in");
|
||
|
this.clear(color);
|
||
|
|
||
|
this.restore();
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
negative: function(src, dst) {
|
||
|
|
||
|
var data = this.context.getImageData(0, 0, this.canvas.width, this.canvas.height);
|
||
|
var pixels = data.data;
|
||
|
var r, g, b, a, h, s, l, hsl = [],
|
||
|
newPixel = [];
|
||
|
|
||
|
for (var i = 0, len = pixels.length; i < len; i += 4) {
|
||
|
pixels[i + 0] = 255 - pixels[i + 0];
|
||
|
pixels[i + 1] = 255 - pixels[i + 1];
|
||
|
pixels[i + 2] = 255 - pixels[i + 2];
|
||
|
}
|
||
|
|
||
|
this.context.putImageData(data, 0, 0);
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
roundRect: function(x, y, width, height, radius) {
|
||
|
|
||
|
this.beginPath();
|
||
|
this.moveTo(x + radius, y);
|
||
|
this.lineTo(x + width - radius, y);
|
||
|
this.quadraticCurveTo(x + width, y, x + width, y + radius);
|
||
|
this.lineTo(x + width, y + height - radius);
|
||
|
this.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
|
||
|
this.lineTo(x + radius, y + height);
|
||
|
this.quadraticCurveTo(x, y + height, x, y + height - radius);
|
||
|
this.lineTo(x, y + radius);
|
||
|
this.quadraticCurveTo(x, y, x + radius, y);
|
||
|
this.closePath();
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
markupText: function(text) {
|
||
|
|
||
|
|
||
|
},
|
||
|
|
||
|
wrappedText: function(text, x, y, maxWidth, newlineCallback) {
|
||
|
|
||
|
var words = text.split(" ");
|
||
|
|
||
|
var lineHeight = this.fontHeight();
|
||
|
|
||
|
var ox = 0;
|
||
|
var oy = 0;
|
||
|
|
||
|
if (maxWidth) {
|
||
|
var line = 0;
|
||
|
var lines = [""];
|
||
|
|
||
|
for (var i = 0; i < words.length; i++) {
|
||
|
var word = words[i] + " ";
|
||
|
var wordWidth = this.context.measureText(word).width;
|
||
|
|
||
|
if (ox + wordWidth > maxWidth || words[i] === "\n") {
|
||
|
lines[++line] = "";
|
||
|
ox = 0;
|
||
|
}
|
||
|
if (words[i] !== "\n") {
|
||
|
lines[line] += word;
|
||
|
|
||
|
ox += wordWidth;
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
} else {
|
||
|
var lines = [text];
|
||
|
}
|
||
|
|
||
|
for (var i = 0; i < lines.length; i++) {
|
||
|
var oy = y + i * lineHeight | 0;
|
||
|
|
||
|
var text = lines[i];
|
||
|
|
||
|
if (newlineCallback) newlineCallback.call(this, x, y + oy);
|
||
|
|
||
|
this.fillText(text, x, oy);
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
fontHeights: {},
|
||
|
|
||
|
fontHeight: function() {
|
||
|
var font = this.font();
|
||
|
|
||
|
if (!this.fontHeights[font]) {
|
||
|
var temp = cq(100, 100);
|
||
|
var height = 0;
|
||
|
var changes = {};
|
||
|
temp.font(font).fillStyle("#fff");
|
||
|
temp.textBaseline("bottom").fillText("gM", 25, 100);
|
||
|
temp.trim(false, changes);
|
||
|
height += changes.bottom;
|
||
|
|
||
|
var temp = cq(100, 100);
|
||
|
var changes = {};
|
||
|
temp.font(font).fillStyle("#fff");
|
||
|
temp.textBaseline("top").fillText("gM", 25, 0);
|
||
|
temp.trim(false, changes);
|
||
|
height += changes.top;
|
||
|
|
||
|
var temp = cq(100, 100);
|
||
|
var changes = {};
|
||
|
temp.font(font).fillStyle("#fff");
|
||
|
temp.textBaseline("alphabetic").fillText("gM", 50, 50);
|
||
|
temp.trim(false, changes);
|
||
|
height += temp.height;
|
||
|
|
||
|
this.fontHeights[font] = height;
|
||
|
}
|
||
|
|
||
|
return this.fontHeights[font];
|
||
|
},
|
||
|
|
||
|
textBoundaries: function(text, maxWidth) {
|
||
|
var words = text.split(" ");
|
||
|
|
||
|
var h = this.fontHeight();
|
||
|
|
||
|
var ox = 0;
|
||
|
var oy = 0;
|
||
|
|
||
|
if (maxWidth) {
|
||
|
var line = 0;
|
||
|
var lines = [""];
|
||
|
|
||
|
for (var i = 0; i < words.length; i++) {
|
||
|
var word = words[i] + " ";
|
||
|
var wordWidth = this.context.measureText(word).width;
|
||
|
|
||
|
if (ox + wordWidth > maxWidth || words[i] === "\n") {
|
||
|
lines[++line] = "";
|
||
|
ox = 0;
|
||
|
}
|
||
|
|
||
|
if (words[i] !== "\n") {
|
||
|
lines[line] += word;
|
||
|
ox += wordWidth;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
var lines = [text];
|
||
|
maxWidth = this.measureText(text).width;
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
height: lines.length * h,
|
||
|
width: maxWidth,
|
||
|
lines: lines.length,
|
||
|
lineHeight: h
|
||
|
}
|
||
|
},
|
||
|
|
||
|
repeatImageRegion: function(image, sx, sy, sw, sh, dx, dy, dw, dh) {
|
||
|
this.save();
|
||
|
this.rect(dx, dy, dw, dh);
|
||
|
this.clip();
|
||
|
|
||
|
for (var x = 0, len = Math.ceil(dw / sw); x < len; x++) {
|
||
|
for (var y = 0, leny = Math.ceil(dh / sh); y < leny; y++) {
|
||
|
this.drawImage(image, sx, sy, sw, sh, dx + x * sw, dy + y * sh, sw, sh);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.restore();
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
repeatImage: function(image, x, y, w, h) {
|
||
|
// if (!env.details) return this;
|
||
|
|
||
|
if (arguments.length < 9) {
|
||
|
this.repeatImageRegion(image, 0, 0, image.width, image.height, x, y, w, h);
|
||
|
} else {
|
||
|
this.repeatImageRegion.apply(this, arguments);
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
borderImage: function(image, x, y, w, h, t, r, b, l, fill) {
|
||
|
|
||
|
// if (!env.details) return this;
|
||
|
|
||
|
if (typeof t === "object") {
|
||
|
|
||
|
var bottomLeft = t.bottomLeft || [0, 0, 0, 0];
|
||
|
var bottomRight = t.bottomRight || [0, 0, 0, 0];
|
||
|
var topLeft = t.topLeft || [0, 0, 0, 0];
|
||
|
var topRight = t.topRight || [0, 0, 0, 0];
|
||
|
|
||
|
var clh = bottomLeft[3] + topLeft[3];
|
||
|
var crh = bottomRight[3] + topRight[3];
|
||
|
var ctw = topLeft[2] + topRight[2];
|
||
|
var cbw = bottomLeft[2] + bottomRight[2];
|
||
|
|
||
|
t.fillPadding = [0, 0, 0, 0];
|
||
|
|
||
|
if (t.left) t.fillPadding[0] = t.left[2];
|
||
|
if (t.top) t.fillPadding[1] = t.top[3];
|
||
|
if (t.right) t.fillPadding[2] = t.right[2];
|
||
|
if (t.bottom) t.fillPadding[3] = t.bottom[3];
|
||
|
|
||
|
// if (!t.fillPadding) t.fillPadding = [0, 0, 0, 0];
|
||
|
|
||
|
if (t.fill) {
|
||
|
this.drawImage(image, t.fill[0], t.fill[1], t.fill[2], t.fill[3], x + t.fillPadding[0], y + t.fillPadding[1], w - t.fillPadding[2] - t.fillPadding[0], h - t.fillPadding[3] - t.fillPadding[1]);
|
||
|
} else {
|
||
|
// this.fillRect(x + t.fillPadding[0], y + t.fillPadding[1], w - t.fillPadding[2] - t.fillPadding[0], h - t.fillPadding[3] - t.fillPadding[1]);
|
||
|
}
|
||
|
|
||
|
if (t.left) this[t.left[4] === "stretch" ? "drawImage" : "repeatImage"](image, t.left[0], t.left[1], t.left[2], t.left[3], x, y + topLeft[3], t.left[2], h - clh);
|
||
|
if (t.right) this[t.right[4] === "stretch" ? "drawImage" : "repeatImage"](image, t.right[0], t.right[1], t.right[2], t.right[3], x + w - t.right[2], y + topRight[3], t.right[2], h - crh);
|
||
|
if (t.top) this[t.top[4] === "stretch" ? "drawImage" : "repeatImage"](image, t.top[0], t.top[1], t.top[2], t.top[3], x + topLeft[2], y, w - ctw, t.top[3]);
|
||
|
if (t.bottom) this[t.bottom[4] === "stretch" ? "drawImage" : "repeatImage"](image, t.bottom[0], t.bottom[1], t.bottom[2], t.bottom[3], x + bottomLeft[2], y + h - t.bottom[3], w - cbw, t.bottom[3]);
|
||
|
|
||
|
if (t.bottomLeft) this.drawImage(image, t.bottomLeft[0], t.bottomLeft[1], t.bottomLeft[2], t.bottomLeft[3], x, y + h - t.bottomLeft[3], t.bottomLeft[2], t.bottomLeft[3]);
|
||
|
if (t.topLeft) this.drawImage(image, t.topLeft[0], t.topLeft[1], t.topLeft[2], t.topLeft[3], x, y, t.topLeft[2], t.topLeft[3]);
|
||
|
if (t.topRight) this.drawImage(image, t.topRight[0], t.topRight[1], t.topRight[2], t.topRight[3], x + w - t.topRight[2], y, t.topRight[2], t.topRight[3]);
|
||
|
if (t.bottomRight) this.drawImage(image, t.bottomRight[0], t.bottomRight[1], t.bottomRight[2], t.bottomRight[3], x + w - t.bottomRight[2], y + h - t.bottomRight[3], t.bottomRight[2], t.bottomRight[3]);
|
||
|
|
||
|
|
||
|
} else {
|
||
|
|
||
|
|
||
|
/* top */
|
||
|
if (t > 0 && w - l - r > 0) this.drawImage(image, l, 0, image.width - l - r, t, x + l, y, w - l - r, t);
|
||
|
|
||
|
/* bottom */
|
||
|
if (b > 0 && w - l - r > 0) this.drawImage(image, l, image.height - b, image.width - l - r, b, x + l, y + h - b, w - l - r, b);
|
||
|
// console.log(x, y, w, h, t, r, b, l);
|
||
|
// console.log(image, 0, t, l, image.height - b - t, x, y + t, l, h - b - t);
|
||
|
/* left */
|
||
|
if (l > 0 && h - b - t > 0) this.drawImage(image, 0, t, l, image.height - b - t, x, y + t, l, h - b - t);
|
||
|
|
||
|
|
||
|
/* right */
|
||
|
if (r > 0 && h - b - t > 0) this.drawImage(image, image.width - r, t, r, image.height - b - t, x + w - r, y + t, r, h - b - t);
|
||
|
|
||
|
/* top-left */
|
||
|
if (l > 0 && t > 0) this.drawImage(image, 0, 0, l, t, x, y, l, t);
|
||
|
|
||
|
/* top-right */
|
||
|
if (r > 0 && t > 0) this.drawImage(image, image.width - r, 0, r, t, x + w - r, y, r, t);
|
||
|
|
||
|
/* bottom-right */
|
||
|
if (r > 0 && b > 0) this.drawImage(image, image.width - r, image.height - b, r, b, x + w - r, y + h - b, r, b);
|
||
|
|
||
|
/* bottom-left */
|
||
|
if (l > 0 && b > 0) this.drawImage(image, 0, image.height - b, l, b, x, y + h - b, l, b);
|
||
|
|
||
|
if (fill) {
|
||
|
if (typeof fill === "string") {
|
||
|
this.fillStyle(fill).fillRect(x + l, y + t, w - l - r, h - t - b);
|
||
|
} else {
|
||
|
if (w - l - r > 0 && h - t - b > 0)
|
||
|
this.drawImage(image, l, t, image.width - r - l, image.height - b - t, x + l, y + t, w - l - r, h - t - b);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
setPixel: function(color, x, y) {
|
||
|
|
||
|
/* fillRect is slow! */
|
||
|
|
||
|
return this.fillStyle(color).fillRect(x, y, 1, 1);
|
||
|
|
||
|
/* this is how it should work - but it does not */
|
||
|
|
||
|
color = cq.color(color);
|
||
|
|
||
|
var pixel = this.createImageData(1, 1);
|
||
|
|
||
|
pixel.data[0] = color[0];
|
||
|
pixel.data[1] = color[1];
|
||
|
pixel.data[2] = color[2];
|
||
|
pixel.data[3] = 1.0;
|
||
|
|
||
|
this.putImageData(pixel, x, y);
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
getPixel: function(x, y) {
|
||
|
var pixel = this.context.getImageData(x, y, 1, 1).data;
|
||
|
return cq.color([pixel[0], pixel[1], pixel[2], pixel[3]]);
|
||
|
},
|
||
|
|
||
|
createImageData: function(width, height) {
|
||
|
if (false && this.context.createImageData) {
|
||
|
return this.context.createImageData.apply(this.context, arguments);
|
||
|
} else {
|
||
|
if (!this.emptyCanvas) {
|
||
|
this.emptyCanvas = cq.createCanvas(width, height);
|
||
|
this.emptyCanvasContext = this.emptyCanvas.getContext("2d");
|
||
|
}
|
||
|
|
||
|
this.emptyCanvas.width = width;
|
||
|
this.emptyCanvas.height = height;
|
||
|
return this.emptyCanvasContext.getImageData(0, 0, width, height);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
setLineDash: function(dash) {
|
||
|
if (this.context.setLineDash) {
|
||
|
this.context.setLineDash(dash);
|
||
|
return this;
|
||
|
} else return this;
|
||
|
},
|
||
|
|
||
|
measureText: function() {
|
||
|
return this.context.measureText.apply(this.context, arguments);
|
||
|
},
|
||
|
|
||
|
getLineDash: function() {
|
||
|
return this.context.getLineDash();
|
||
|
},
|
||
|
|
||
|
createRadialGradient: function() {
|
||
|
return this.context.createRadialGradient.apply(this.context, arguments);
|
||
|
},
|
||
|
|
||
|
createLinearGradient: function() {
|
||
|
return this.context.createLinearGradient.apply(this.context, arguments);
|
||
|
},
|
||
|
|
||
|
createPattern: function() {
|
||
|
return this.context.createPattern.apply(this.context, arguments);
|
||
|
},
|
||
|
|
||
|
getImageData: function() {
|
||
|
return this.context.getImageData.apply(this.context, arguments);
|
||
|
},
|
||
|
|
||
|
get width() {
|
||
|
return this.canvas.width;
|
||
|
},
|
||
|
|
||
|
get height() {
|
||
|
return this.canvas.height;
|
||
|
},
|
||
|
|
||
|
set width(w) {
|
||
|
this.canvas.width = w;
|
||
|
this.update();
|
||
|
return this.canvas.width;
|
||
|
},
|
||
|
|
||
|
set height(h) {
|
||
|
this.canvas.height = h;
|
||
|
this.update();
|
||
|
return this.canvas.height;
|
||
|
}
|
||
|
|
||
|
|
||
|
};
|
||
|
|
||
|
/* extend Layer with drawing context methods */
|
||
|
|
||
|
var methods = ["arc", "arcTo", "beginPath", "bezierCurveTo", "clearRect", "clip", "closePath", "createLinearGradient", "createRadialGradient", "createPattern", "drawFocusRing", "drawImage", "fill", "fillRect", "fillText", "getImageData", "isPointInPath", "lineTo", "measureText", "moveTo", "putImageData", "quadraticCurveTo", "rect", "restore", "rotate", "save", "scale", "setTransform", "stroke", "strokeRect", "strokeText", "transform", "translate", "setLineDash"];
|
||
|
|
||
|
for (var i = 0; i < methods.length; i++) {
|
||
|
var name = methods[i];
|
||
|
|
||
|
// this.debug = true;
|
||
|
|
||
|
if (cq.Layer.prototype[name]) continue;
|
||
|
|
||
|
if (!this.debug) {
|
||
|
// if (!cq.Layer.prototype[name]) cq.Layer.prototype[name] = Function("this.context." + name + ".apply(this.context, arguments); return this;");
|
||
|
|
||
|
var self = this;
|
||
|
|
||
|
(function(name) {
|
||
|
|
||
|
cq.Layer.prototype[name] = function() {
|
||
|
this.context[name].apply(this.context, arguments);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
})(name);
|
||
|
|
||
|
} else {
|
||
|
|
||
|
var self = this;
|
||
|
|
||
|
(function(name) {
|
||
|
|
||
|
cq.Layer.prototype[name] = function() {
|
||
|
try {
|
||
|
this.context[name].apply(this.context, arguments);
|
||
|
return this;
|
||
|
} catch (e) {
|
||
|
var err = new Error();
|
||
|
console.log(err.stack);
|
||
|
throw (e + err.stack);
|
||
|
|
||
|
console.log(e, name, arguments);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
})(name);
|
||
|
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
/* create setters and getters */
|
||
|
|
||
|
var properties = ["canvas", "fillStyle", "font", "globalAlpha", "globalCompositeOperation", "lineCap", "lineJoin", "lineWidth", "miterLimit", "shadowOffsetX", "shadowOffsetY", "shadowBlur", "shadowColor", "strokeStyle", "textAlign", "textBaseline", "lineDashOffset"];
|
||
|
|
||
|
for (var i = 0; i < properties.length; i++) {
|
||
|
var name = properties[i];
|
||
|
if (!cq.Layer.prototype[name]) cq.Layer.prototype[name] = Function("if(arguments.length) { this.context." + name + " = arguments[0]; return this; } else { return this.context." + name + "; }");
|
||
|
};
|
||
|
|
||
|
/* color */
|
||
|
|
||
|
cq.Color = function(data, type) {
|
||
|
|
||
|
if (arguments.length) this.parse(data, type);
|
||
|
}
|
||
|
|
||
|
cq.Color.prototype = {
|
||
|
|
||
|
toString: function() {
|
||
|
return this.toRgb();
|
||
|
},
|
||
|
|
||
|
parse: function(args, type) {
|
||
|
if (args[0] instanceof cq.Color) {
|
||
|
this[0] = args[0][0];
|
||
|
this[1] = args[0][1];
|
||
|
this[2] = args[0][2];
|
||
|
this[3] = args[0][3];
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (typeof args === "string") {
|
||
|
var match = null;
|
||
|
|
||
|
if (args[0] === "#") {
|
||
|
var rgb = cq.hexToRgb(args);
|
||
|
this[0] = rgb[0];
|
||
|
this[1] = rgb[1];
|
||
|
this[2] = rgb[2];
|
||
|
this[3] = 1.0;
|
||
|
} else if (match = args.match(/rgb\((.*),(.*),(.*)\)/)) {
|
||
|
this[0] = match[1] | 0;
|
||
|
this[1] = match[2] | 0;
|
||
|
this[2] = match[3] | 0;
|
||
|
this[3] = 1.0;
|
||
|
} else if (match = args.match(/rgba\((.*),(.*),(.*)\)/)) {
|
||
|
this[0] = match[1] | 0;
|
||
|
this[1] = match[2] | 0;
|
||
|
this[2] = match[3] | 0;
|
||
|
this[3] = match[4] | 0;
|
||
|
} else if (match = args.match(/hsl\((.*),(.*),(.*)\)/)) {
|
||
|
this.fromHsl(match[1], match[2], match[3]);
|
||
|
} else if (match = args.match(/hsv\((.*),(.*),(.*)\)/)) {
|
||
|
this.fromHsv(match[1], match[2], match[3]);
|
||
|
}
|
||
|
} else {
|
||
|
switch (type) {
|
||
|
case "hsl":
|
||
|
case "hsla":
|
||
|
|
||
|
this.fromHsl(args[0], args[1], args[2], args[3]);
|
||
|
break;
|
||
|
|
||
|
case "hsv":
|
||
|
case "hsva":
|
||
|
|
||
|
this.fromHsv(args[0], args[1], args[2], args[3]);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
this[0] = args[0];
|
||
|
this[1] = args[1];
|
||
|
this[2] = args[2];
|
||
|
this[3] = typeof args[3] === "undefined" ? 1.0 : args[3];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
a: function(a) {
|
||
|
return this.alpha(a);
|
||
|
},
|
||
|
|
||
|
alpha: function(a) {
|
||
|
this[3] = a;
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
fromHsl: function() {
|
||
|
var components = arguments[0] instanceof Array ? arguments[0] : arguments;
|
||
|
var color = cq.hslToRgb(components[0], components[1], components[2]);
|
||
|
|
||
|
this[0] = color[0];
|
||
|
this[1] = color[1];
|
||
|
this[2] = color[2];
|
||
|
this[3] = typeof arguments[3] === "undefined" ? 1.0 : arguments[3];
|
||
|
},
|
||
|
|
||
|
fromHsv: function() {
|
||
|
var components = arguments[0] instanceof Array ? arguments[0] : arguments;
|
||
|
var color = cq.hsvToRgb(components[0], components[1], components[2]);
|
||
|
|
||
|
this[0] = color[0];
|
||
|
this[1] = color[1];
|
||
|
this[2] = color[2];
|
||
|
this[3] = typeof arguments[3] === "undefined" ? 1.0 : arguments[3];
|
||
|
},
|
||
|
|
||
|
toArray: function() {
|
||
|
return [this[0], this[1], this[2], this[3]];
|
||
|
},
|
||
|
|
||
|
toRgb: function() {
|
||
|
return "rgb(" + this[0] + ", " + this[1] + ", " + this[2] + ")";
|
||
|
},
|
||
|
|
||
|
toRgba: function() {
|
||
|
return "rgba(" + this[0] + ", " + this[1] + ", " + this[2] + ", " + this[3] + ")";
|
||
|
},
|
||
|
|
||
|
toHex: function() {
|
||
|
return cq.rgbToHex(this[0], this[1], this[2]);
|
||
|
},
|
||
|
|
||
|
toHsl: function() {
|
||
|
var c = cq.rgbToHsl(this[0], this[1], this[2]);
|
||
|
c[3] = this[3];
|
||
|
return c;
|
||
|
},
|
||
|
|
||
|
toHsv: function() {
|
||
|
var c = cq.rgbToHsv(this[0], this[1], this[2]);
|
||
|
c[3] = this[3];
|
||
|
return c;
|
||
|
},
|
||
|
|
||
|
gradient: function(target, steps) {
|
||
|
var targetColor = cq.color(target);
|
||
|
},
|
||
|
|
||
|
shiftHsl: function() {
|
||
|
var hsl = this.toHsl();
|
||
|
|
||
|
if (this[0] !== this[1] || this[1] !== this[2]) {
|
||
|
var h = arguments[0] === false ? hsl[0] : cq.wrapValue(hsl[0] + arguments[0], 0, 1);
|
||
|
var s = arguments[1] === false ? hsl[1] : cq.limitValue(hsl[1] + arguments[1], 0, 1);
|
||
|
} else {
|
||
|
var h = hsl[0];
|
||
|
var s = hsl[1];
|
||
|
}
|
||
|
|
||
|
var l = arguments[2] === false ? hsl[2] : cq.limitValue(hsl[2] + arguments[2], 0, 1);
|
||
|
|
||
|
this.fromHsl(h, s, l);
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
setHsl: function() {
|
||
|
var hsl = this.toHsl();
|
||
|
|
||
|
var h = arguments[0] === false ? hsl[0] : cq.limitValue(arguments[0], 0, 1);
|
||
|
var s = arguments[1] === false ? hsl[1] : cq.limitValue(arguments[1], 0, 1);
|
||
|
var l = arguments[2] === false ? hsl[2] : cq.limitValue(arguments[2], 0, 1);
|
||
|
|
||
|
this.fromHsl(h, s, l);
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
if (NODEJS)
|
||
|
global["cq"] = global["CanvasQuery"] = cq;
|
||
|
else
|
||
|
window["cq"] = window["CanvasQuery"] = cq;
|
||
|
|
||
|
|
||
|
/* nodejs specific stuff */
|
||
|
|
||
|
cq.Layer.prototype.saveAsPNG = function(path) {
|
||
|
fs.writeFileSync(path, this.canvas.toBuffer());
|
||
|
}
|
||
|
|
||
|
cq.loadFromFile = function(path) {
|
||
|
var buffer = fs.readFileSync(path);
|
||
|
var img = new Image;
|
||
|
img.src = buffer;
|
||
|
return cq(img);
|
||
|
}
|
||
|
|
||
|
|
||
|
})();
|