This repository has been archived on 2024-01-19. You can view files and clone it, but cannot push or open issues or pull requests.
epubreader/public/pixastic/pixastic.worker.control.js
2023-06-18 01:47:01 +02:00

1581 lines
55 KiB
JavaScript

//importScripts("pixastic.js");
// ... but Chromium refuses to import pixastic.js when CSP2 nonce is active, so inline it instead...
/*!
* Pixastic - JavaScript Image Processing
* http://pixastic.com/
* Copyright 2012, Jacob Seidelin
*
* Dual licensed under the MPL 1.1 or GPLv3 licenses.
* http://pixastic.com/license-mpl.txt
* http://pixastic.com/license-gpl-3.0.txt
*
*/
var Pixastic = (function() {
var worker;
function createImageData(ctx, width, height) {
if (ctx.createImageData) {
return ctx.createImageData(width, height);
} else {
return ctx.getImageData(0, 0, width, height);
}
}
function Pixastic(ctx, workerControlPath) {
var P = {},
width = ctx.canvas.width,
height = ctx.canvas.height,
queue = [],
workerControlPath = workerControlPath || "";
if (!worker) {
if (typeof window.Worker != "undefined") {
try {
worker = new window.Worker(workerControlPath + "pixastic.worker.control.js");
} catch(e) {
if (location.protocol == "file:") {
Pixastic.log("Could not create native worker, running from file://")
} else {
Pixastic.log("Could not create native worker.")
}
}
}
if (!worker) {
worker = new Pixastic.Worker();
}
}
for (var e in Pixastic.Effects) {
if (Pixastic.Effects.hasOwnProperty(e)) {
(function(e) {
P[e] = function(options) {
queue.push({
effect : e,
options : options
});
return P;
}
P.done = function(callback, progress) {
var inData, outData;
try {
inData = ctx.getImageData(0, 0, width, height);
} catch(e) {
if (location.protocol == "file:") {
throw new Error("Could not access image data, running from file://");
} else {
throw new Error("Could not access image data, is canvas tainted by cross-origin data?");
}
}
outData = createImageData(ctx, width, height);
worker.postMessage({
queue : queue,
inData : inData,
outData : outData,
width : width,
height : height
});
worker.onmessage = function(message) {
var d = message.data;
switch (d.event) {
case "done" :
ctx.putImageData(d.data, 0, 0);
if (callback) {
callback();
}
if (progress) {
progress(1);
}
break;
case "progress" :
if (progress) {
progress(d.data);
}
break;
case "error" :
break;
}
}
if (progress) {
progress(0);
}
}
})(e);
}
}
return P;
}
Pixastic.Worker = function() {
var me = this;
function processMessage(data) {
var queue = data.queue,
inData = data.inData,
outData = data.outData,
width = data.width,
height = data.height,
tmpData;
for (var i=0;i<queue.length;i++) {
var e = queue[i].effect,
options = queue[i].options,
progressCallback;
if (i > 0) {
tmpData = inData;
inData = outData;
outData = tmpData;
}
if (typeof importScripts == "function") {
progressCallback = function(p) {
me.onmessage({
data : {
event : "progress",
data : (i + p) / queue.length
}
});
return p;
}
}
Pixastic.Effects[e](inData.data, outData.data, width, height, options, progressCallback);
me.onmessage({
data : {
event : "progress",
data : (i+1) / queue.length
}
});
}
me.onmessage({
data : {
event : "done",
data : outData
}
});
}
this.postMessage = function(data) {
setTimeout(function() {
processMessage(data)
}, 0);
};
this.onmessage = function() {};
}
Pixastic.log = function(str) {
if (typeof console != "undefined" && console.log) {
console.log("Pixastic: " + str);
}
};
function toCanvas(o) {
var canvas;
if (typeof o == "object") {
if (typeof o.tagName == "string") {
if (o.tagName.toLowerCase() == "canvas" || o.tagName.toLowerCase() == "img") {
canvas = document.createElement("canvas");
canvas.width = o.width;
canvas.height = o.height;
canvas.getContext("2d").drawImage(o, 0,0);
}
} else if ((window.ImageData && o instanceof window.ImageData)
|| (typeof o.width == "number" && typeof o.height == "number" && typeof o.data == "object")) {
canvas = document.createElement("canvas");
canvas.width = o.width;
canvas.height = o.height;
canvas.getContext("2d").putImageData(o, 0, 0);
}
}
return canvas;
};
function toImage(o) {
var canvas = toCanvas(o),
image = new Image();
image.width = canvas.width;
image.height = canvas.height;
image.src = canvas.toDataURL();
return image;
};
function toImageData(o) {
var canvas = toCanvas(o),
ctx = canvas.getContext("2d");
return ctx.getImageData(0, 0, canvas.width, canvas.height);
};
function histogram(imageData) {
var values = [],
i, p,
data = imageData.data,
round = Math.round,
maxValue,
n = imageData.width * imageData.height;
for (i=0;i<256;i++) {
values[i] = 0;
}
for (i=0;i<n;i++) {
p = i * 4;
values[ round((data[p]+data[p+1]+data[p+2])/3) ]++;
}
maxValue = 0;
for (i=0;i<256;i++) {
if (values[i] > maxValue) {
maxValue = values[i];
}
}
return {
maxValue : maxValue,
values : values
};
}
Pixastic.toCanvas = toCanvas;
Pixastic.toImage = toImage;
Pixastic.toImageData = toImageData;
Pixastic.histogram = histogram;
Pixastic.Color = {
rgb2hsl : function(r, g, b) {
if (r < 0) r = 0;
if (g < 0) g = 0;
if (b < 0) b = 0;
if (r > 255) r = 0;
if (g > 255) g = 0;
if (b > 255) b = 0;
},
rgb2hsv : function(r, g, b) {
},
rgb2hex : function(r, g, b) {
},
hsl2rgb : function(h, s, l) {
},
hsv2rgb : function(h, s, v) {
}
}
return Pixastic;
})();
// END pixastic.js
//importScripts("pixastic.effects.js");
// ... but Chromium refuses to import pixastic.effects.js when CSP2 nonce is active, so inline it instead...
Pixastic.Effects = (function() {
function defaultOptions(options, defaults) {
var O = {};
for (var opt in defaults) {
if (typeof options[opt] == "undefined") {
O[opt] = defaults[opt];
} else {
O[opt] = options[opt];
}
}
return O;
}
function clamp(val, min, max) {
return Math.min(max, Math.max(min, val));
}
function convolve3x3(inData, outData, width, height, kernel, progress, alpha, invert, mono) {
var idx, r, g, b, a,
pyc, pyp, pyn,
pxc, pxp, pxn,
x, y,
prog, lastProg = 0,
n = width * height * 4,
k00 = kernel[0][0], k01 = kernel[0][1], k02 = kernel[0][2],
k10 = kernel[1][0], k11 = kernel[1][1], k12 = kernel[1][2],
k20 = kernel[2][0], k21 = kernel[2][1], k22 = kernel[2][2],
p00, p01, p02,
p10, p11, p12,
p20, p21, p22;
for (y=0;y<height;++y) {
pyc = y * width * 4;
pyp = pyc - width * 4;
pyn = pyc + width * 4;
if (y < 1) pyp = pyc;
if (y >= width-1) pyn = pyc;
for (x=0;x<width;++x) {
idx = (y * width + x) * 4;
pxc = x * 4;
pxp = pxc - 4;
pxn = pxc + 4;
if (x < 1) pxp = pxc;
if (x >= width-1) pxn = pxc;
p00 = pyp + pxp; p01 = pyp + pxc; p02 = pyp + pxn;
p10 = pyc + pxp; p11 = pyc + pxc; p12 = pyc + pxn;
p20 = pyn + pxp; p21 = pyn + pxc; p22 = pyn + pxn;
r = inData[p00] * k00 + inData[p01] * k01 + inData[p02] * k02
+ inData[p10] * k10 + inData[p11] * k11 + inData[p12] * k12
+ inData[p20] * k20 + inData[p21] * k21 + inData[p22] * k22;
g = inData[p00 + 1] * k00 + inData[p01 + 1] * k01 + inData[p02 + 1] * k02
+ inData[p10 + 1] * k10 + inData[p11 + 1] * k11 + inData[p12 + 1] * k12
+ inData[p20 + 1] * k20 + inData[p21 + 1] * k21 + inData[p22 + 1] * k22;
b = inData[p00 + 2] * k00 + inData[p01 + 2] * k01 + inData[p02 + 2] * k02
+ inData[p10 + 2] * k10 + inData[p11 + 2] * k11 + inData[p12 + 2] * k12
+ inData[p20 + 2] * k20 + inData[p21 + 2] * k21 + inData[p22 + 2] * k22;
if (alpha) {
a = inData[p00 + 3] * k00 + inData[p01 + 3] * k01 + inData[p02 + 3] * k02
+ inData[p10 + 3] * k10 + inData[p11 + 3] * k11 + inData[p12 + 3] * k12
+ inData[p20 + 3] * k20 + inData[p21 + 3] * k21 + inData[p22 + 3] * k22;
} else {
a = inData[idx+3];
}
if (mono) {
r = g = b = (r + g + b) / 3;
}
if (invert) {
r = 255 - r;
g = 255 - g;
b = 255 - b;
}
outData[idx] = r;
outData[idx+1] = g;
outData[idx+2] = b;
outData[idx+3] = a;
if (progress) {
prog = (idx/n*100 >> 0) / 100;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
}
}
function convolve5x5(inData, outData, width, height, kernel, progress, alpha, invert, mono) {
var idx, r, g, b, a,
pyc, pyp, pyn, pypp, pynn,
pxc, pxp, pxn, pxpp, pxnn,
x, y,
prog, lastProg = 0,
n = width * height * 4,
k00 = kernel[0][0], k01 = kernel[0][1], k02 = kernel[0][2], k03 = kernel[0][3], k04 = kernel[0][4],
k10 = kernel[1][0], k11 = kernel[1][1], k12 = kernel[1][2], k13 = kernel[1][3], k14 = kernel[1][4],
k20 = kernel[2][0], k21 = kernel[2][1], k22 = kernel[2][2], k23 = kernel[2][3], k24 = kernel[2][4],
k30 = kernel[3][0], k31 = kernel[3][1], k32 = kernel[3][2], k33 = kernel[3][3], k34 = kernel[3][4],
k40 = kernel[4][0], k41 = kernel[4][1], k42 = kernel[4][2], k43 = kernel[4][3], k44 = kernel[4][4],
p00, p01, p02, p03, p04,
p10, p11, p12, p13, p14,
p20, p21, p22, p23, p24,
p30, p31, p32, p33, p34,
p40, p41, p42, p43, p44;
for (y=0;y<height;++y) {
pyc = y * width * 4;
pyp = pyc - width * 4;
pypp = pyc - width * 4 * 2;
pyn = pyc + width * 4;
pynn = pyc + width * 4 * 2;
if (y < 1) pyp = pyc;
if (y >= width-1) pyn = pyc;
if (y < 2) pypp = pyp;
if (y >= width-2) pynn = pyn;
for (x=0;x<width;++x) {
idx = (y * width + x) * 4;
pxc = x * 4;
pxp = pxc - 4;
pxn = pxc + 4;
pxpp = pxc - 8;
pxnn = pxc + 8;
if (x < 1) pxp = pxc;
if (x >= width-1) pxn = pxc;
if (x < 2) pxpp = pxp;
if (x >= width-2) pxnn = pxn;
p00 = pypp + pxpp; p01 = pypp + pxp; p02 = pypp + pxc; p03 = pypp + pxn; p04 = pypp + pxnn;
p10 = pyp + pxpp; p11 = pyp + pxp; p12 = pyp + pxc; p13 = pyp + pxn; p14 = pyp + pxnn;
p20 = pyc + pxpp; p21 = pyc + pxp; p22 = pyc + pxc; p23 = pyc + pxn; p24 = pyc + pxnn;
p30 = pyn + pxpp; p31 = pyn + pxp; p32 = pyn + pxc; p33 = pyn + pxn; p34 = pyn + pxnn;
p40 = pynn + pxpp; p41 = pynn + pxp; p42 = pynn + pxc; p43 = pynn + pxn; p44 = pynn + pxnn;
r = inData[p00] * k00 + inData[p01] * k01 + inData[p02] * k02 + inData[p03] * k04 + inData[p02] * k04
+ inData[p10] * k10 + inData[p11] * k11 + inData[p12] * k12 + inData[p13] * k14 + inData[p12] * k14
+ inData[p20] * k20 + inData[p21] * k21 + inData[p22] * k22 + inData[p23] * k24 + inData[p22] * k24
+ inData[p30] * k30 + inData[p31] * k31 + inData[p32] * k32 + inData[p33] * k34 + inData[p32] * k34
+ inData[p40] * k40 + inData[p41] * k41 + inData[p42] * k42 + inData[p43] * k44 + inData[p42] * k44;
g = inData[p00+1] * k00 + inData[p01+1] * k01 + inData[p02+1] * k02 + inData[p03+1] * k04 + inData[p02+1] * k04
+ inData[p10+1] * k10 + inData[p11+1] * k11 + inData[p12+1] * k12 + inData[p13+1] * k14 + inData[p12+1] * k14
+ inData[p20+1] * k20 + inData[p21+1] * k21 + inData[p22+1] * k22 + inData[p23+1] * k24 + inData[p22+1] * k24
+ inData[p30+1] * k30 + inData[p31+1] * k31 + inData[p32+1] * k32 + inData[p33+1] * k34 + inData[p32+1] * k34
+ inData[p40+1] * k40 + inData[p41+1] * k41 + inData[p42+1] * k42 + inData[p43+1] * k44 + inData[p42+1] * k44;
b = inData[p00+2] * k00 + inData[p01+2] * k01 + inData[p02+2] * k02 + inData[p03+2] * k04 + inData[p02+2] * k04
+ inData[p10+2] * k10 + inData[p11+2] * k11 + inData[p12+2] * k12 + inData[p13+2] * k14 + inData[p12+2] * k14
+ inData[p20+2] * k20 + inData[p21+2] * k21 + inData[p22+2] * k22 + inData[p23+2] * k24 + inData[p22+2] * k24
+ inData[p30+2] * k30 + inData[p31+2] * k31 + inData[p32+2] * k32 + inData[p33+2] * k34 + inData[p32+2] * k34
+ inData[p40+2] * k40 + inData[p41+2] * k41 + inData[p42+2] * k42 + inData[p43+2] * k44 + inData[p42+2] * k44;
if (alpha) {
a = inData[p00+3] * k00 + inData[p01+3] * k01 + inData[p02+3] * k02 + inData[p03+3] * k04 + inData[p02+3] * k04
+ inData[p10+3] * k10 + inData[p11+3] * k11 + inData[p12+3] * k12 + inData[p13+3] * k14 + inData[p12+3] * k14
+ inData[p20+3] * k20 + inData[p21+3] * k21 + inData[p22+3] * k22 + inData[p23+3] * k24 + inData[p22+3] * k24
+ inData[p30+3] * k30 + inData[p31+3] * k31 + inData[p32+3] * k32 + inData[p33+3] * k34 + inData[p32+3] * k34
+ inData[p40+3] * k40 + inData[p41+3] * k41 + inData[p42+3] * k42 + inData[p43+3] * k44 + inData[p42+3] * k44;
} else {
a = inData[idx+3];
}
if (mono) {
r = g = b = (r + g + b) / 3;
}
if (invert) {
r = 255 - r;
g = 255 - g;
b = 255 - b;
}
outData[idx] = r;
outData[idx+1] = g;
outData[idx+2] = b;
outData[idx+3] = a;
if (progress) {
prog = (idx/n*100 >> 0) / 100;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
}
}
function gaussian(inData, outData, width, height, kernelSize, progress) {
var r, g, b, a, idx,
n = width * height * 4,
x, y, i, j,
inx, iny, w,
tmpData = [],
maxKernelSize = 13,
kernelSize = clamp(kernelSize, 3, maxKernelSize),
k1 = -kernelSize / 2 + (kernelSize % 2 ? 0.5 : 0),
k2 = kernelSize + k1,
weights,
kernels = [[1]],
prog, lastProg = 0;
for (i=1;i<maxKernelSize;++i) {
kernels[0][i] = 0;
}
for (i=1;i<maxKernelSize;++i) {
kernels[i] = [1];
for (j=1;j<maxKernelSize;++j) {
kernels[i][j] = kernels[i-1][j] + kernels[i-1][j-1];
}
}
weights = kernels[kernelSize - 1]
for (i=0,w=0;i<kernelSize;++i) {
w += weights[i];
}
for (i=0;i<kernelSize;++i) {
weights[i] /= w;
}
// pass 1
for (y=0;y<height;++y) {
for (x=0;x<width;++x) {
r = g = b = a = 0;
for (i=k1;i<k2;++i) {
inx = x + i;
iny = y;
w = weights[i - k1];
if (inx < 0) {
inx = 0;
}
if (inx >= width) {
inx = width - 1;
}
idx = (iny * width + inx) * 4;
r += inData[idx] * w;
g += inData[idx + 1] * w;
b += inData[idx + 2] * w;
a += inData[idx + 3] * w;
}
idx = (y * width + x) * 4;
tmpData[idx] = r;
tmpData[idx+1] = g;
tmpData[idx+2] = b;
tmpData[idx+3] = a;
if (progress) {
prog = (idx/n*50 >> 0) / 100;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
}
lastProg = 0;
// pass 2
for (y=0;y<height;++y) {
for (x=0;x<width;++x) {
r = g = b = a = 0;
for (i=k1;i<k2;++i) {
inx = x;
iny = y + i;
w = weights[i - k1];
if (iny < 0) {
iny = 0;
}
if (iny >= height) {
iny = height - 1;
}
idx = (iny * width + inx) * 4;
r += tmpData[idx] * w;
g += tmpData[idx + 1] * w;
b += tmpData[idx + 2] * w;
a += tmpData[idx + 3] * w;
}
idx = (y * width + x) * 4;
outData[idx] = r;
outData[idx+1] = g;
outData[idx+2] = b;
outData[idx+3] = a;
if (progress) {
prog = 0.5 + (idx/n*50 >> 0) / 100;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
}
}
return {
invert : function(inData, outData, width, height, options, progress) {
var n = width * height * 4,
prog, lastProg = 0;
for (i=0;i<n;i+=4) {
outData[i] = 255 - inData[i];
outData[i+1] = 255 - inData[i+1];
outData[i+2] = 255 - inData[i+2];
outData[i+3] = inData[i+3];
if (progress) {
prog = (i/n*100 >> 0) / 100;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
},
sepia : function(inData, outData, width, height, options, progress) {
var n = width * height * 4,
prog, lastProg = 0,
r, g, b;
for (var i=0;i<n;i+=4) {
r = inData[i];
g = inData[i+1];
b = inData[i+2];
outData[i] = (r * 0.393 + g * 0.769 + b * 0.189);
outData[i+1] = (r * 0.349 + g * 0.686 + b * 0.168);
outData[i+2] = (r * 0.272 + g * 0.534 + b * 0.131);
outData[i+3] = inData[i+3];
if (progress) {
prog = (i/n*100 >> 0) / 100;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
},
solarize : function(inData, outData, width, height, options, progress) {
var n = width * height * 4,
prog, lastProg = 0,
r, g, b;
for (i=0;i<n;i+=4) {
r = inData[i];
g = inData[i+1];
b = inData[i+2];
outData[i] = r > 127 ? 255 - r : r;
outData[i+1] = g > 127 ? 255 - g : g;
outData[i+2] = b > 127 ? 255 - b : b;
outData[i+3] = inData[i+3];
if (progress) {
prog = (i/n*100 >> 0) / 100;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
},
brightness : function(inData, outData, width, height, options, progress) {
options = defaultOptions(options, {
brightness : 0,
contrast : 0
});
var contrast = clamp(options.contrast, -1, 1) / 2,
brightness = 1 + clamp(options.brightness, -1, 1),
prog, lastProg = 0,
r, g, b,
n = width * height * 4;
var brightMul = brightness < 0 ? - brightness : brightness;
var brightAdd = brightness < 0 ? 0 : brightness;
contrast = 0.5 * Math.tan((contrast + 1) * Math.PI/4);
contrastAdd = - (contrast - 0.5) * 255;
for (var i=0;i<n;i+=4) {
r = inData[i];
g = inData[i+1];
b = inData[i+2];
r = (r + r * brightMul + brightAdd) * contrast + contrastAdd;
g = (g + g * brightMul + brightAdd) * contrast + contrastAdd;
b = (b + b * brightMul + brightAdd) * contrast + contrastAdd;
outData[i] = r;
outData[i+1] = g;
outData[i+2] = b;
outData[i+3] = inData[i+3];
if (progress) {
prog = (i/n*100 >> 0) / 100;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
},
desaturate : function(inData, outData, width, height, options, progress) {
var n = width * height * 4,
prog, lastProg = 0,
level;
for (var i=0;i<n;i+=4) {
level = inData[i] * 0.3 + inData[i+1] * 0.59 + inData[i+2] * 0.11;
outData[i] = level;
outData[i+1] = level;
outData[i+2] = level;
outData[i+3] = inData[i+3];
if (progress) {
prog = (i/n*100 >> 0) / 100;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
},
lighten : function(inData, outData, width, height, options, progress) {
var n = width * height * 4,
prog, lastProg = 0,
mul = 1 + clamp(options.amount, 0, 1);
for (var i=0;i<n;i+=4) {
outData[i] = inData[i] * mul;
outData[i+1] = inData[i+1] * mul;
outData[i+2] = inData[i+2] * mul;
outData[i+3] = inData[i+3];
if (progress) {
prog = (i/n*100 >> 0) / 100;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
},
noise : function(inData, outData, width, height, options, progress) {
var n = width * height * 4,
prog, lastProg = 0,
amount = clamp(options.amount, 0, 1),
strength = clamp(options.strength, 0, 1),
mono = !!options.mono,
random = Math.random,
rnd, r, g, b;
for (var i=0;i<n;i+=4) {
r = inData[i];
g = inData[i+1];
b = inData[i+2];
rnd = random();
if (rnd < amount) {
if (mono) {
rnd = strength * ((rnd / amount) * 2 - 1) * 255;
r += rnd;
g += rnd;
b += rnd;
} else {
r += strength * random() * 255;
g += strength * random() * 255;
b += strength * random() * 255;
}
}
outData[i] = r;
outData[i+1] = g;
outData[i+2] = b;
outData[i+3] = inData[i+3];
if (progress) {
prog = (i/n*100 >> 0) / 100;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
},
flipv : function(inData, outData, width, height, options, progress) {
var inPix, outPix,
n = width * height * 4,
prog, lastProg = 0,
x, y;
for (y=0;y<height;++y) {
for (x=0;x<width;++x) {
inPix = (y * width + x) * 4;
outPix = (y * width + (width - x - 1)) * 4;
outData[outPix] = inData[inPix];
outData[outPix+1] = inData[inPix+1];
outData[outPix+2] = inData[inPix+2];
outData[outPix+3] = inData[inPix+3];
if (progress) {
prog = (inPix/n*100 >> 0) / 100;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
}
},
fliph : function(inData, outData, width, height, options, progress) {
var inPix, outPix,
n = width * height * 4,
prog, lastProg = 0,
x, y;
for (y=0;y<height;++y) {
for (x=0;x<width;++x) {
inPix = (y * width + x) * 4;
outPix = ((height - y - 1) * width + x) * 4;
outData[outPix] = inData[inPix];
outData[outPix+1] = inData[inPix+1];
outData[outPix+2] = inData[inPix+2];
outData[outPix+3] = inData[inPix+3];
if (progress) {
prog = (inPix/n*100 >> 0) / 100;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
}
},
blur : function(inData, outData, width, height, options, progress) {
gaussian(inData, outData, width, height, options.kernelSize, progress);
},
glow : function(inData, outData, width, height, options, progress) {
var n = width * height * 4,
i, r, g, b,
amount = options.amount,
tmpData = [],
gaussProgress,
prog, lastProg = 0;
if (progress) {
gaussProgress = function(p) {
progress(p * 0.8);
return p;
}
}
gaussian(inData, tmpData, width, height, options.kernelSize, gaussProgress);
for (i=0;i<n;i+=4) {
r = inData[i] + tmpData[i] * amount;
g = inData[i+1] + tmpData[i+1] * amount;
b = inData[i+2] + tmpData[i+2] * amount;
if (r > 255) r = 255;
if (g > 255) g = 255;
if (b > 255) b = 255;
outData[i] = r;
outData[i+1] = g;
outData[i+2] = b;
outData[i+3] = inData[i+3];
if (progress) {
prog = 0.8 + (i/n*100 >> 0) / 100 * 0.2;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
},
convolve3x3 : function(inData, outData, width, height, options, progress) {
convolve3x3(inData, outData, width, height, options.kernel, progress);
},
convolve5x5 : function(inData, outData, width, height, options, progress) {
convolve5x5(inData, outData, width, height, options.kernel, progress);
},
// A 3x3 high-pass filter
sharpen3x3 : function(inData, outData, width, height, options, progress) {
var a = - clamp(options.strength, 0, 1);
convolve3x3(
inData, outData, width, height,
[[a, a, a],
[a, 1-a*8, a],
[a, a, a]],
progress
);
},
// A 5x5 high-pass filter
sharpen5x5 : function(inData, outData, width, height, options, progress) {
var a = - clamp(options.strength, 0, 1);
convolve5x5(
inData, outData, width, height,
[[a, a, a, a, a],
[a, a, a, a, a],
[a, a, 1-a*24, a, a],
[a, a, a, a, a],
[a, a, a, a, a]],
progress
);
},
// A 3x3 low-pass mean filter
soften3x3 : function(inData, outData, width, height, options, progress) {
var c = 1/9;
convolve3x3(
inData, outData, width, height,
[[c, c, c],
[c, c, c],
[c, c, c]],
progress
);
},
// A 5x5 low-pass mean filter
soften5x5 : function(inData, outData, width, height, options, progress) {
var c = 1/25;
convolve5x5(
inData, outData, width, height,
[[c, c, c, c, c],
[c, c, c, c, c],
[c, c, c, c, c],
[c, c, c, c, c],
[c, c, c, c, c]],
progress
);
},
// A 3x3 Cross edge-detect
crossedges : function(inData, outData, width, height, options, progress) {
var a = clamp(options.strength, 0, 1) * 5
convolve3x3(
inData, outData, width, height,
[[ 0, -a, 0],
[-a, 0, a],
[ 0, a, 0]],
progress,
false, true
);
},
// 3x3 directional emboss
emboss : function(inData, outData, width, height, options, progress) {
var amount = options.amount,
angle = options.angle,
x = Math.cos(-angle) * amount,
y = Math.sin(-angle) * amount,
n = width * height * 4,
a00 = -x - y,
a10 = -x,
a20 = y - x,
a01 = -y,
a21 = y,
a02 = -y + x,
a12 = x,
a22 = y + x,
tmpData = [],
prog, lastProg = 0,
convProgress;
if (progress) {
convProgress = function(p) {
progress(p * 0.5)
return p;
};
}
convolve3x3(
inData, tmpData, width, height,
[[a00, a01, a02],
[a10, 0, a12],
[a20, a21, a22]]
);
for (var i=0;i<n;i+=4) {
outData[i] = 128 + tmpData[i];
outData[i+1] = 128 + tmpData[i+1];
outData[i+2] = 128 + tmpData[i+2];
outData[i+3] = inData[i+3];
if (progress) {
prog = 0.5 + (i/n*100 >> 0) / 100 * 0.5;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
},
// A 3x3 Sobel edge detect (similar to Photoshop's)
findedges : function(inData, outData, width, height, options, progress) {
var n = width * height * 4,
i,
data1 = [],
data2 = [],
gr1, gr2, gg1, gg2, gb1, gb2,
prog, lastProg = 0,
convProgress1, convProgress2;
if (progress) {
convProgress1 = function(p) {
progress(p * 0.4);
return p;
};
convProgress2 = function(p) {
progress(0.4 + p * 0.4);
return p;
};
}
convolve3x3(inData, data1, width, height,
[[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]]
);
convolve3x3(inData, data2, width, height,
[[-1, -2, -1],
[ 0, 0, 0],
[ 1, 2, 1]]
);
for (i=0;i<n;i+=4) {
gr1 = data1[i];
gr2 = data2[i];
gg1 = data1[i+1];
gg2 = data2[i+1];
gb1 = data1[i+2];
gb2 = data2[i+2];
if (gr1 < 0) gr1 = -gr1;
if (gr2 < 0) gr2 = -gr2;
if (gg1 < 0) gg1 = -gg1;
if (gg2 < 0) gg2 = -gg2;
if (gb1 < 0) gb1 = -gb1;
if (gb2 < 0) gb2 = -gb2;
outData[i] = 255 - (gr1 + gr2) * 0.8;
outData[i+1] = 255 - (gg1 + gg2) * 0.8;
outData[i+2] = 255 - (gb1 + gb2) * 0.8;
outData[i+3] = inData[i+3];
if (progress) {
prog = 0.8 + (i/n*100 >> 0) / 100 * 0.2;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
},
// A 3x3 edge enhance
edgeenhance3x3 : function(inData, outData, width, height, options, progress) {
convolve3x3(
inData, outData, width, height,
[[-1/9, -1/9, -1/9],
[-1/9, 17/9, -1/9],
[-1/9, -1/9, -1/9]],
progress
);
},
// A 5x5 edge enhance
edgeenhance5x5 : function(inData, outData, width, height, options, progress) {
convolve5x5(
inData, outData, width, height,
[[-1/25, -1/25, -1/25, -1/25, -1/25],
[-1/25, -1/25, -1/25, -1/25, -1/25],
[-1/25, -1/25, 49/25, -1/25, -1/25],
[-1/25, -1/25, -1/25, -1/25, -1/25],
[-1/25, -1/25, -1/25, -1/25, -1/25]],
progress
);
},
// A 3x3 Laplacian edge-detect
laplace3x3 : function(inData, outData, width, height, options, progress) {
convolve3x3(
inData, outData, width, height,
[[-1, -1, -1],
[-1, 8, -1],
[-1, -1, -1]],
progress,
false, true, true
);
},
// A 5x5 Laplacian edge-detect
laplace5x5 : function(inData, outData, width, height, options, progress) {
convolve5x5(
inData, outData, width, height,
[[-1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1],
[-1, -1, 24, -1, -1],
[-1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1]],
progress,
false, true, true
);
},
coloradjust : function(inData, outData, width, height, options, progress) {
var n = width * height * 4,
r, g, b,
prog, lastProg = 0,
ar = clamp(options.r, -1, 1) * 255,
ag = clamp(options.g, -1, 1) * 255,
ab = clamp(options.b, -1, 1) * 255;
for (var i=0;i<n;i+=4) {
r = inData[i] + ar;
g = inData[i+1] + ag;
b = inData[i+2] + ab;
if (r < 0) r = 0;
if (g < 0) g = 0;
if (b < 0) b = 0;
if (r > 255) r = 255;
if (g > 255) g = 255;
if (b > 255) b = 255;
outData[i] = r;
outData[i+1] = g;
outData[i+2] = b;
outData[i+3] = inData[i+3];
if (progress) {
prog = (i/n*100 >> 0) / 100;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
},
colorfilter : function(inData, outData, width, height, options, progress) {
var n = width * height * 4,
i, r, g, b,
luminosity = !!options.luminosity,
prog, lastProg = 0,
min, max, h, l, h1, chroma, tmp, m,
ar = clamp(options.r, 0, 1),
ag = clamp(options.g, 0, 1),
ab = clamp(options.b, 0, 1);
for (i=0;i<n;i+=4) {
r = inData[i] / 255;
g = inData[i+1] / 255;
b = inData[i+2] / 255;
l = r * 0.3 + g * 0.59 + b * 0.11;
r = (r + r * ar) / 2;
g = (g + g * ag) / 2;
b = (b + b * ab) / 2;
if (luminosity) {
min = max = r;
if (g > max) max = g;
if (b > max) max = b;
if (g < min) min = g;
if (b < min) min = b;
chroma = (max - min);
if (r == max) {
h = ((g - b) / chroma) % 6;
} else if (g == max) {
h = ((b - r) / chroma) + 2;
} else {
h = ((r - g) / chroma) + 4;
}
h1 = h >> 0;
tmp = chroma * (h - h1);
r = g = b = l - (r * 0.3 + g * 0.59 + b * 0.11);
if (h1 == 0) {
r += chroma;
g += tmp;
} else if (h1 == 1) {
r += chroma - tmp;
g += chroma;
} else if (h1 == 2) {
g += chroma;
b += tmp;
} else if (h1 == 3) {
g += chroma - tmp;
b += chroma;
} else if (h1 == 4) {
r += tmp;
b += chroma;
} else if (h1 == 5) {
r += chroma;
b += chroma - tmp;
}
}
outData[i] = r * 255;
outData[i+1] = g * 255;
outData[i+2] = b * 255;
outData[i+3] = inData[i+3];
if (progress) {
prog = (i/n*100 >> 0) / 100;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
},
hsl : function(inData, outData, width, height, options, progress) {
var n = width * height * 4,
hue = clamp(options.hue, -1, 1),
saturation = clamp(options.saturation, -1, 1),
lightness = clamp(options.lightness, -1, 1),
satMul = 1 + saturation * (saturation < 0 ? 1 : 2),
lightMul = lightness < 0 ? 1 + lightness : 1 - lightness,
lightAdd = lightness < 0 ? 0 : lightness * 255,
vs, ms, vm, h, s, l, v, m, vmh, sextant,
prog, lastProg = 0;
hue = (hue * 6) % 6;
for (var i=0;i<n;i+=4) {
r = inData[i];
g = inData[i+1];
b = inData[i+2];
if (hue != 0 || saturation != 0) {
// ok, here comes rgb to hsl + adjust + hsl to rgb, all in one jumbled mess.
// It's not so pretty, but it's been optimized to get somewhat decent performance.
// The transforms were originally adapted from the ones found in Graphics Gems, but have been heavily modified.
vs = r;
if (g > vs) vs = g;
if (b > vs) vs = b;
ms = r;
if (g < ms) ms = g;
if (b < ms) ms = b;
vm = (vs-ms);
l = (ms+vs)/510;
if (l > 0 && vm > 0) {
if (l <= 0.5) {
s = vm / (vs+ms) * satMul;
if (s > 1) s = 1;
v = (l * (1+s));
} else {
s = vm / (510-vs-ms) * satMul;
if (s > 1) s = 1;
v = (l+s - l*s);
}
if (r == vs) {
if (g == ms) {
h = 5 + ((vs-b)/vm) + hue;
} else {
h = 1 - ((vs-g)/vm) + hue;
}
} else if (g == vs) {
if (b == ms) {
h = 1 + ((vs-r)/vm) + hue;
} else {
h = 3 - ((vs-b)/vm) + hue;
}
} else {
if (r == ms) {
h = 3 + ((vs-g)/vm) + hue;
} else {
h = 5 - ((vs-r)/vm) + hue;
}
}
if (h < 0) h += 6;
if (h >= 6) h -= 6;
m = (l + l - v);
sextant = h >> 0;
vmh = (v - m) * (h - sextant);
if (sextant == 0) {
r = v;
g = m + vmh;
b = m;
} else if (sextant == 1) {
r = v - vmh;
g = v;
b = m;
} else if (sextant == 2) {
r = m;
g = v;
b = m + vmh;
} else if (sextant == 3) {
r = m;
g = v - vmh;
b = v;
} else if (sextant == 4) {
r = m + vmh;
g = m;
b = v;
} else if (sextant == 5) {
r = v;
g = m;
b = v - vmh;
}
r *= 255;
g *= 255;
b *= 255;
}
}
r = r * lightMul + lightAdd;
g = g * lightMul + lightAdd;
b = b * lightMul + lightAdd;
if (r < 0) r = 0;
if (g < 0) g = 0;
if (b < 0) b = 0;
if (r > 255) r = 255;
if (g > 255) g = 255;
if (b > 255) b = 255;
outData[i] = r;
outData[i+1] = g;
outData[i+2] = b;
outData[i+3] = inData[i+3];
if (progress) {
prog = (i/n*100 >> 0) / 100;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
},
posterize : function(inData, outData, width, height, options, progress) {
var numLevels = clamp(options.levels, 2, 256),
numAreas = 256 / numLevels,
numValues = 256 / (numLevels-1),
r, g, b,
n = width * height * 4,
prog, lastProg = 0;
for (i=0;i<n;i+=4) {
outData[i] = numValues * ((inData[i] / numAreas)>>0);
outData[i+1] = numValues * ((inData[i+1] / numAreas)>>0);
outData[i+2] = numValues * ((inData[i+2] / numAreas)>>0);
outData[i+3] = inData[i+3];
if (progress) {
prog = (i/n*100 >> 0) / 100;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
},
removenoise : function(inData, outData, width, height, options, progress) {
var r, g, b, c, y, x, idx,
pyc, pyp, pyn,
pxc, pxp, pxn,
minR, minG, minB, maxR, maxG, maxB,
n, prog, lastProg = 0;
n = width * height * 4;
for (y=0;y<height;++y) {
pyc = y * width * 4;
pyp = pyc - width * 4;
pyn = pyc + width * 4;
if (y < 1) pyp = pyc;
if (y >= width-1) pyn = pyc;
for (x=0;x<width;++x) {
idx = (y * width + x) * 4;
pxc = x * 4;
pxp = pxc - 4;
pxn = pxc + 4;
if (x < 1) pxp = pxc;
if (x >= width-1) pxn = pxc;
minR = maxR = inData[pyc + pxp];
c = inData[pyc + pxn];
if (c < minR) minR = c;
if (c > maxR) maxR = c;
c = inData[pyp + pxc];
if (c < minR) minR = c;
if (c > maxR) maxR = c;
c = inData[pyn + pxc];
if (c < minR) minR = c;
if (c > maxR) maxR = c;
minG = inData[pyc + pxp + 1];
c = inData[pyc + pxn + 1];
if (c < minG) minG = c;
c = inData[pyp + pxc + 1];
if (c < minG) minG = c;
c = inData[pyn + pxc + 1];
if (c < minG) minG = c;
minB = inData[pyc + pxp + 2];
c = inData[pyc + pxn + 2];
if (c < minB) minB = c;
c = inData[pyp + pxc + 2];
if (c < minB) minB = c;
c = inData[pyn + pxc + 2];
if (c < minB) minB = c;
r = inData[idx]
g = inData[idx + 1]
b = inData[idx + 2]
if (r < minR) r = minR;
if (r > maxR) r = maxR;
if (g < minG) g = minG;
if (g > maxG) g = maxG;
if (b < minB) b = minB;
if (b > maxB) b = maxB;
outData[idx] = r;
outData[idx+1] = g;
outData[idx+2] = b;
outData[idx+3] = inData[idx+3];
if (progress) {
prog = (idx/n*100 >> 0) / 100;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
}
},
mosaic : function(inData, outData, width, height, options, progress) {
var blockSize = clamp(options.blockSize, 1, Math.max(width, height)),
yBlocks = Math.ceil(height / blockSize),
xBlocks = Math.ceil(width / blockSize),
y0, y1, x0, x1, idx, pidx,
n = yBlocks * xBlocks,
prog, lastProg = 0;
for (i=0, y0=0, bidx=0;i<yBlocks;i++) {
y1 = clamp(y0 + blockSize, 0, height);
for(j=0, x0=0;j<xBlocks;j++,bidx++) {
x1 = clamp(x0 + blockSize, 0, width);
idx = (y0 * width + x0) << 2;
var r = inData[idx], g = inData[idx+1], b = inData[idx+2];
for(bi=y0;bi<y1;bi++) {
for(bj=x0;bj<x1;bj++) {
pidx = (bi*width+bj) << 2;
outData[pidx] = r, outData[pidx+1] = g, outData[pidx+2] = b;
outData[pidx+3] = inData[pidx+3];
}
}
x0 = x1;
if (progress) {
prog = (bidx/n*100 >> 0) / 100;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
y0 = y1;
}
},
equalize : function(inData, outData, width, height, options, progress) {
var n = width * height, p, i, level, ratio,
prog, lastProg;
var round = Math.round;
// build histogram
var pdf = new Array(256);
for (i=0;i<256;i++) {
pdf[i] = 0;
}
for (i=0;i<n;i++) {
p = i * 4;
level = clamp(round(inData[p] * 0.3 + inData[p+1] * 0.59 + inData[p+2] * 0.11), 0, 255);
outData[p+3] = level;
pdf[ level ]++;
}
// build cdf
var cdf = new Array(256);
cdf[0] = pdf[0];
for(i=1;i<256;i++) {
cdf[i] = cdf[i-1] + pdf[i];
}
// normalize cdf
for(i=0;i<256;i++) {
cdf[i] = cdf[i] / n * 255.0;
}
// map the pixel values
for (i=0;i<n;i++) {
p = i * 4;
level = outData[p+3];
ratio = cdf[level] / (level || 1);
outData[p] = clamp(round(inData[p] * ratio), 0, 255);
outData[p+1] = clamp(round(inData[p+1] * ratio), 0, 255);
outData[p+2] = clamp(round(inData[p+2] * ratio), 0, 255);
outData[p+3] = inData[p+3];
if (progress) {
prog = (i/n*100 >> 0) / 100;
if (prog > lastProg) {
lastProg = progress(prog);
}
}
}
}
};
})();
// END pixastic.effects.js
var worker = new Pixastic.Worker();
worker.onmessage = function(message) {
postMessage(message.data);
}
onmessage = function(message) {
worker.postMessage(message.data);
}