move image encoding functions to image module
This commit is contained in:
parent
816993e1b9
commit
842d536dc8
4 changed files with 416 additions and 147 deletions
|
@ -5,23 +5,60 @@ Ox.load('Image', function() {
|
||||||
Ox.Image('png/Lenna.png', function(image) {
|
Ox.Image('png/Lenna.png', function(image) {
|
||||||
var body = Ox.element('body'),
|
var body = Ox.element('body'),
|
||||||
select = Ox.element('<select>').appendTo(body);
|
select = Ox.element('<select>').appendTo(body);
|
||||||
['Filter...', 'blur', 'channel', 'contour', 'invert', 'edges', 'emboss', 'motionBlur', 'posterize', 'sharpen', 'solarize'].forEach(function(filter) {
|
[
|
||||||
|
'Method...', 'blur(1)', 'blur(2)', 'blur(3)',
|
||||||
|
'channel("r")', 'channel("g")', 'channel("b")',
|
||||||
|
'channel("h")', 'channel("s")', 'channel("l")',
|
||||||
|
'contour()', 'edges()', 'emboss()',
|
||||||
|
'encode("some secret stuff")', 'decode()',
|
||||||
|
'encode("some secret stuff", true)', 'decode(true)',
|
||||||
|
'encode("some secret stuff", 1)', 'decode(1)',
|
||||||
|
'encode("some secret stuff", 127)', 'decode(127)',
|
||||||
|
'hue(0)', 'hue(120)', 'hue(240)',
|
||||||
|
'invert()',
|
||||||
|
'lightness(-0.5)', 'lightness(0.5)',
|
||||||
|
'motionBlur()', 'posterize()',
|
||||||
|
'saturation(-0.5)', 'saturation(0.5)',
|
||||||
|
'sharpen()', 'solarize()', 'src("png/Lenna.png")'
|
||||||
|
].forEach(function(filter) {
|
||||||
Ox.element('<option>').html(filter).appendTo(select);
|
Ox.element('<option>').html(filter).appendTo(select);
|
||||||
});
|
});
|
||||||
select[0].onchange = function() {
|
select[0].onchange = function() {
|
||||||
if (select[0].value[0] == select[0].value[0].toLowerCase()) {
|
if (select[0].value[0] == select[0].value[0].toLowerCase()) {
|
||||||
Ox.element('#filter').attr({
|
var split = select[0].value.split('('),
|
||||||
src: image[select[0].value]().url()
|
fn = split[0],
|
||||||
|
args = split[1].length == 1 ? []
|
||||||
|
: split[1].split(')')[0].split(', ').map(function(v) {
|
||||||
|
return v == 'true' ? true
|
||||||
|
: v == 'false'? false
|
||||||
|
: v[0] == '"' ? v.split('"')[1]
|
||||||
|
: parseFloat(v);
|
||||||
});
|
});
|
||||||
|
if (fn == 'encode' || fn == 'src') {
|
||||||
|
image[fn].apply(null, Ox.merge(args, function(image) {
|
||||||
|
Ox.element('#filter').attr({
|
||||||
|
src: image.src()
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
} else if (fn == 'decode') {
|
||||||
|
image[fn].apply(null, Ox.merge(args, function(str) {
|
||||||
|
alert(str);
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
Ox.element('#filter').attr({
|
||||||
|
src: image[fn].apply(null, args).src()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
select[0].selectedIndex = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ox.element('<br>').appendTo(body);
|
Ox.element('<br>').appendTo(body);
|
||||||
Ox.element('<img>').attr({
|
Ox.element('<img>').attr({
|
||||||
src: image.url()
|
src: image.src()
|
||||||
}).appendTo(body);
|
}).appendTo(body);
|
||||||
Ox.element('<img>').attr({
|
Ox.element('<img>').attr({
|
||||||
id: 'filter',
|
id: 'filter',
|
||||||
src: image.url()
|
src: image.src()
|
||||||
}).appendTo(body);
|
}).appendTo(body);
|
||||||
//Ox.element('<img>').attr({src: image.saturation(0.5).blur().url()}).appendTo(Ox.element('body'));
|
//Ox.element('<img>').attr({src: image.saturation(0.5).blur().url()}).appendTo(Ox.element('body'));
|
||||||
});
|
});
|
||||||
|
|
BIN
demos/image/png/Lenna.png
Normal file
BIN
demos/image/png/Lenna.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 464 KiB |
|
@ -5,17 +5,43 @@ Ox.load.Image = function(options, callback) {
|
||||||
var self = {},
|
var self = {},
|
||||||
that = {};
|
that = {};
|
||||||
|
|
||||||
|
self.callback = arguments[arguments.length - 1];
|
||||||
if (arguments.length == 2) {
|
if (arguments.length == 2) {
|
||||||
self.src = arguments[0];
|
self.src = arguments[0];
|
||||||
self.callback = arguments[1];
|
self.image = new Image();
|
||||||
|
self.image.onload = init;
|
||||||
|
self.image.src = self.src;
|
||||||
} else {
|
} else {
|
||||||
self.width = arguments[0];
|
self.width = arguments[0];
|
||||||
self.height = arguments[1];
|
self.height = arguments[1];
|
||||||
self.callback = arguments[2];
|
self.background = arguments.length == 4 ? arguments[2] : [0, 0, 0, 0];
|
||||||
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getIndex(x, y) {
|
function error(mode) {
|
||||||
return (Ox.mod(x, self.width) + Ox.mod(y * self.width, self.width * self.height)) * 4;
|
throw new RangeError('PNG codec can\'t ' + mode + ' ' + (
|
||||||
|
mode == 'encode' ? 'data' : 'image'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCapacity(bpb) {
|
||||||
|
var capacity = 0;
|
||||||
|
that.forEach(function(rgba) {
|
||||||
|
capacity += rgba[3] == 255 ? bpb / 8 : 0;
|
||||||
|
});
|
||||||
|
return capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIndex() {
|
||||||
|
if (arguments.length == 1) {
|
||||||
|
xy = arguments[0];
|
||||||
|
} else {
|
||||||
|
xy = [arguments[0], arguments[1]];
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
Ox.mod(xy[0], self.width)
|
||||||
|
+ Ox.mod(xy[1] * self.width, self.width * self.height)
|
||||||
|
) * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getXY(index) {
|
function getXY(index) {
|
||||||
|
@ -23,67 +49,39 @@ Ox.load.Image = function(options, callback) {
|
||||||
return [index % self.width, Math.floor(index / self.width)];
|
return [index % self.width, Math.floor(index / self.width)];
|
||||||
}
|
}
|
||||||
|
|
||||||
self.filter = function(filter, bias) {
|
function init() {
|
||||||
bias = bias || 0;
|
if (self.image) {
|
||||||
var filterSize = Math.sqrt(filter.length),
|
self.width = self.image.width;
|
||||||
d = (filterSize - 1) / 2,
|
self.height = self.image.height;
|
||||||
imageData = self.context.getImageData(0, 0, self.width, self.height),
|
|
||||||
imageDataNew = self.context.createImageData(self.width, self.height),
|
|
||||||
data = [],
|
|
||||||
n = imageData.data.length,
|
|
||||||
xy, filterIndex, pixelIndex;
|
|
||||||
for (var i = 0; i < n; i += 4) {
|
|
||||||
for (var c = 0; c < 3; c++) {
|
|
||||||
data[i + c] = 0;
|
|
||||||
}
|
}
|
||||||
filterIndex = 0;
|
self.canvas = Ox.element('<canvas>').attr({
|
||||||
xy = getXY(i);
|
width: self.width,
|
||||||
for (var x = -d; x <= d; x++) {
|
height: self.height
|
||||||
for (var y = -d; y <= d; y++) {
|
});
|
||||||
pixelIndex = getIndex(xy[0] + x, xy[1] + y);
|
self.context = self.canvas[0].getContext('2d');
|
||||||
for (var c = 0; c < 3; c++) {
|
if (self.image) {
|
||||||
data[i + c] += imageData.data[pixelIndex + c] * filter[filterIndex];
|
self.context.drawImage(self.image, 0, 0);
|
||||||
|
} else if (Ox.sum(self.background)) {
|
||||||
|
that.fillStyle(self.background);
|
||||||
|
that.fillRect(0, 0, self.width, self.height);
|
||||||
}
|
}
|
||||||
filterIndex++;
|
self.imageData = self.context.getImageData(0, 0, self.width, self.height);
|
||||||
|
self.data = self.imageData.data;
|
||||||
|
self.callback(that);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
for (var i = 0; i < n; i += 4) {
|
|
||||||
for (var c = 0; c < 3; c++) {
|
|
||||||
imageDataNew.data[i + c] = Ox.limit(Math.round(data[i + c] + bias), 0, 255)
|
|
||||||
}
|
|
||||||
imageDataNew.data[i + 3] = 255;
|
|
||||||
}
|
|
||||||
self.context.putImageData(imageDataNew, 0, 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.map = function(fn) {
|
function setSL(sl, d) {
|
||||||
var imageData = self.context.getImageData(0, 0, self.width, self.height),
|
var c = sl == 's' ? 1 : 2;
|
||||||
i, n = imageData.data.length, c;
|
return that.map(function(rgba) {
|
||||||
for (i = 0; i < n; i += 4) {
|
|
||||||
var data = fn([
|
|
||||||
imageData.data[i], imageData.data[i + 1],
|
|
||||||
imageData.data[i + 2], imageData.data[i + 3]
|
|
||||||
], getXY(i));
|
|
||||||
for (c = 0; c < 4; c++) {
|
|
||||||
imageData.data[i + c] = data[c];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
self.context.putImageData(imageData, 0, 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.setSL = function(sl, d) {
|
|
||||||
var c = sl == 'saturation' ? 1 : 2;
|
|
||||||
self.map(function(rgba) {
|
|
||||||
var hsl = Ox.hsl([rgba[0], rgba[1], rgba[2]]), rgb;
|
var hsl = Ox.hsl([rgba[0], rgba[1], rgba[2]]), rgb;
|
||||||
hsl[c] = d < 0 ? hsl[c] * (d + 1) : hsl[c] + (1 - hsl[c]) * d;
|
hsl[c] = d < 0 ? hsl[c] * (d + 1) : hsl[c] + (1 - hsl[c]) * d;
|
||||||
rgb = Ox.rgb(hsl);
|
rgb = Ox.rgb(hsl);
|
||||||
return [rgb[0], rgb[1], rgb[2], rgba[3]];
|
return Ox.merge(rgb, rgba[3]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
that.blur = function(val) {
|
that.blur = function(val) {
|
||||||
self.filter(val == 1 ? [
|
return that.filter(val == 1 ? [
|
||||||
0.0, 0.2, 0.0,
|
0.0, 0.2, 0.0,
|
||||||
0.2, 0.2, 0.2,
|
0.2, 0.2, 0.2,
|
||||||
0.0, 0.2, 0.0
|
0.0, 0.2, 0.0
|
||||||
|
@ -102,83 +100,320 @@ Ox.load.Image = function(options, callback) {
|
||||||
0.00, 1/37, 1/37, 1/37, 1/37, 1/37, 0.00,
|
0.00, 1/37, 1/37, 1/37, 1/37, 1/37, 0.00,
|
||||||
0.00, 0.00, 1/37, 1/37, 1/37, 0.00, 0.00
|
0.00, 0.00, 1/37, 1/37, 1/37, 0.00, 0.00
|
||||||
]);
|
]);
|
||||||
return that;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
that.brightness = function(val) {
|
that.channel = function(c) {
|
||||||
self.setSL('brightness', val);
|
c = c.toLowerCase();
|
||||||
return that;
|
return that.map(function(rgba) {
|
||||||
};
|
var i = ['r', 'g', 'b'].indexOf(c), rgb, val;
|
||||||
|
if (i > -1) {
|
||||||
that.channel = function(channel) {
|
return Ox.map(rgba, function(v, c) {
|
||||||
self.map(function(rgba) {
|
return c == i || c == 3 ? v : 0;
|
||||||
var hue = Ox.hsl([rgba[0], rgba[1], rgba[2]])[0],
|
});
|
||||||
rgb = Ox.rgb([hue, 1, 0.5]);
|
} else {
|
||||||
return [rgb[0], rgb[1], rgb[2], rgba[3]];
|
i = ['h', 's', 'l'].indexOf(c);
|
||||||
|
val = Ox.hsl([rgba[0], rgba[1], rgba[2]])[i];
|
||||||
|
rgb = i == 0
|
||||||
|
? Ox.rgb([val, 1, 0.5])
|
||||||
|
: Ox.map(Ox.range(3), function(v) {
|
||||||
|
return parseInt(val * 255);
|
||||||
|
});
|
||||||
|
return Ox.merge(rgb, rgba[3]);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return that;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
that.contour = function(val) {
|
that.contour = function(val) {
|
||||||
self.filter([
|
return that.filter([
|
||||||
+1, +1, +1,
|
+1, +1, +1,
|
||||||
+1, -7, +1,
|
+1, -7, +1,
|
||||||
+1, +1, +1
|
+1, +1, +1
|
||||||
]);
|
]);
|
||||||
return that;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
that.edges = function(val) {
|
that.edges = function(val) {
|
||||||
self.filter([
|
return that.filter([
|
||||||
-1, -1, -1,
|
-1, -1, -1,
|
||||||
-1, +8, -1,
|
-1, +8, -1,
|
||||||
-1, -1, -1
|
-1, -1, -1
|
||||||
]);
|
]).saturation(-1);
|
||||||
that.saturation(-1);
|
|
||||||
return that;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
that.emboss = function(val) {
|
that.emboss = function(val) {
|
||||||
self.filter([
|
return that.filter([
|
||||||
-1, -1, +0,
|
-1, -1, +0,
|
||||||
-1, +0, +1,
|
-1, +0, +1,
|
||||||
+0, +1, +1
|
+0, +1, +1
|
||||||
], 128);
|
], 128).saturation(-1);
|
||||||
that.saturation(-1);
|
};
|
||||||
|
|
||||||
|
that.decode = function() {
|
||||||
|
var callback = arguments[arguments.length - 1],
|
||||||
|
deflate = Ox.isBoolean(arguments[0]) ? arguments[0]
|
||||||
|
: Ox.isBoolean(arguments[1]) ? arguments[1] : false,
|
||||||
|
mode = Ox.isNumber(arguments[0]) ? arguments[0]
|
||||||
|
: Ox.isNumber(arguments[1]) ? arguments[1] : 0,
|
||||||
|
bin = '',
|
||||||
|
bits = mode < 1 ? [-mode] : Ox.map(Ox.range(8), function(b) {
|
||||||
|
return mode & 1 << b ? b : null;
|
||||||
|
}),
|
||||||
|
done = 0; len = 4, str = '';
|
||||||
|
that.forEach(function(rgba, xy) {
|
||||||
|
if (rgba[3] == 255) {
|
||||||
|
var index = getIndex(xy);
|
||||||
|
Ox.loop(3, function(c) {
|
||||||
|
var i = index + c;
|
||||||
|
Ox.forEach(bits, function(bit) {
|
||||||
|
if (mode < 1) {
|
||||||
|
bin += Ox.sum(Ox.range(8).map(function(b) {
|
||||||
|
return self.data[i] & 1 << b;
|
||||||
|
})) % 2;
|
||||||
|
} else {
|
||||||
|
bin += +!!(self.data[i] & i << bit);
|
||||||
|
}
|
||||||
|
/*mode > 0 &&*/ Ox.print(bin)
|
||||||
|
if (bin.length == 8) {
|
||||||
|
str += Ox.char(parseInt(bin, 2));
|
||||||
|
bin = '';
|
||||||
|
if (str.length == len) {
|
||||||
|
if (++done == 1) {
|
||||||
|
len = Ox.decodeBase256(str);
|
||||||
|
Ox.print("LEN", len)
|
||||||
|
if (len + 4 > getCapacity(1)) {
|
||||||
|
error('decode');
|
||||||
|
}
|
||||||
|
str = '';
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (done == 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
Ox.print("STR", str, str.length, str.charCodeAt(0));
|
||||||
|
if (deflate) {
|
||||||
|
Ox.decodeDeflate(str, callback);
|
||||||
|
} else {
|
||||||
|
callback(Ox.decodeUTF8(str));
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
error('decode');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
that.encode = function(str) {
|
||||||
|
var callback = arguments[arguments.length - 1],
|
||||||
|
deflate = Ox.isBoolean(arguments[1]) ? arguments[1]
|
||||||
|
: Ox.isBoolean(arguments[2]) ? arguments[2] : false,
|
||||||
|
mode = Ox.isNumber(arguments[1]) ? arguments[1]
|
||||||
|
: Ox.isNumber(arguments[2]) ? arguments[2] : 0,
|
||||||
|
b = 0, bin,
|
||||||
|
bits = mode < 1 ? [-mode] : Ox.map(Ox.range(8), function(bit) {
|
||||||
|
return mode & 1 << bit ? bit : null;
|
||||||
|
}),
|
||||||
|
cap = getCapacity(bits.length), len;
|
||||||
|
Ox.print("CAPACITY", cap);
|
||||||
|
// Compress the string
|
||||||
|
str = Ox[deflate ? 'encodeDeflate' : 'encodeUTF8'](str);
|
||||||
|
Ox.print("STR", str, str.length, str.charCodeAt(0));
|
||||||
|
len = str.length;
|
||||||
|
// Prefix the string with its length, as a four-byte value
|
||||||
|
str = Ox.pad(Ox.encodeBase256(len), 4, Ox.char(0)) + str;
|
||||||
|
str.length > cap && error('encode');
|
||||||
|
while (str.length < cap) {
|
||||||
|
str += str.substr(4, len);
|
||||||
|
}
|
||||||
|
str = str.substr(0, Math.ceil(cap));
|
||||||
|
// Create an array of bit values
|
||||||
|
bin = Ox.flatten(Ox.map(str, function(chr) {
|
||||||
|
return Ox.map(Ox.range(8), function(i) {
|
||||||
|
return chr.charCodeAt(0) >> 7 - i & 1;
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
b = 0;
|
||||||
|
that.forEach(function(rgba, xy) {
|
||||||
|
if (rgba[3] == 255) {
|
||||||
|
var index = getIndex(xy);
|
||||||
|
Ox.loop(3, function(c) {
|
||||||
|
var i = index + c;
|
||||||
|
Ox.forEach(bits, function(bit) {
|
||||||
|
if ((
|
||||||
|
mode < 1
|
||||||
|
? Ox.sum(Ox.range(8).map(function(bit) {
|
||||||
|
return self.data[i] & 1 << bit;
|
||||||
|
})) % 2
|
||||||
|
: self.data[i] & 1 << bit
|
||||||
|
) != bin[b++]) {
|
||||||
|
self.data[i] ^= 1 << bit;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
/*
|
||||||
|
if (mode < 1) {
|
||||||
|
if (Ox.sum(Ox.range(8).map(function(bit) {
|
||||||
|
return self.data[i] & 1 << bit;
|
||||||
|
})) % 2 != bin[b++]) {
|
||||||
|
// If the number of bits set to one, modulo 2,
|
||||||
|
// is not equal to the data bit, flip one bit
|
||||||
|
self.data[i] ^= 1 << bits[0];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ox.forEach(bits, function(bit) {
|
||||||
|
if (self.data[i] & 1 << bit != bin[b++]) {
|
||||||
|
// If the bit is not equal to the data bit,
|
||||||
|
// flip it
|
||||||
|
self.data[i] ^= 1 << bit;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.context.putImageData(self.imageData, 0, 0);
|
||||||
|
callback(that);
|
||||||
|
};
|
||||||
|
|
||||||
|
that.fillRect = function(x, y, w, h) {
|
||||||
|
self.context.fillRect(x, y, w, h);
|
||||||
|
return that;
|
||||||
|
};
|
||||||
|
|
||||||
|
that.fillStyle = function() {
|
||||||
|
if (arguments.length == 0) {
|
||||||
|
return self.context.fillStyle;
|
||||||
|
} else {
|
||||||
|
self.context.fillStyle = arguments[0];
|
||||||
|
return that;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
that.filter = function(filter, bias) {
|
||||||
|
bias = bias || 0;
|
||||||
|
var filterSize = Math.sqrt(filter.length),
|
||||||
|
d = (filterSize - 1) / 2,
|
||||||
|
imageData = self.context.createImageData(self.width, self.height),
|
||||||
|
data = [];
|
||||||
|
self.imageData = self.context.getImageData(0, 0, self.width, self.height);
|
||||||
|
self.data = self.imageData.data;
|
||||||
|
Ox.loop(0, self.data.length, 4, function(i) {
|
||||||
|
var filterIndex = 0,
|
||||||
|
xy = getXY(i);
|
||||||
|
Ox.loop(3, function(c) {
|
||||||
|
data[i + c] = 0;
|
||||||
|
});
|
||||||
|
Ox.loop(-d, d + 1, function(x) {
|
||||||
|
Ox.loop(-d, d + 1, function(y) {
|
||||||
|
var pixelIndex = getIndex(xy[0] + x, xy[1] + y);
|
||||||
|
Ox.loop(3, function(c) {
|
||||||
|
data[i + c] += self.data[pixelIndex + c] * filter[filterIndex];
|
||||||
|
});
|
||||||
|
filterIndex++;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Ox.loop(0, self.data.length, 4, function(i) {
|
||||||
|
Ox.loop(4, function(c) {
|
||||||
|
imageData.data[i + c] = c < 3
|
||||||
|
? Ox.limit(Math.round(data[i + c] + bias), 0, 255)
|
||||||
|
: self.data[i + c];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
self.context.putImageData(imageData, 0, 0);
|
||||||
|
self.imageData = imageData;
|
||||||
|
self.data = data;
|
||||||
|
return that;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*@
|
||||||
|
forEach <f> Pixel-wise forEach function
|
||||||
|
(fn) -> <o> The image object
|
||||||
|
fn <f> Iterator function
|
||||||
|
rgba <[n]> RGBA values
|
||||||
|
xy <[n]> XY coordinates
|
||||||
|
@*/
|
||||||
|
that.forEach = function(fn) {
|
||||||
|
Ox.loop(0, self.data.length, 4, function(i) {
|
||||||
|
return fn([
|
||||||
|
self.data[i], self.data[i + 1],
|
||||||
|
self.data[i + 2], self.data[i + 3]
|
||||||
|
], getXY(i));
|
||||||
|
})
|
||||||
return that;
|
return that;
|
||||||
};
|
};
|
||||||
|
|
||||||
that.hue = function(val) {
|
that.hue = function(val) {
|
||||||
self.map(function(rgba) {
|
return that.map(function(rgba) {
|
||||||
var hsl = Ox.hsl([rgba[0], rgba[1], rgba[2]]), rgb;
|
var hsl = Ox.hsl([rgba[0], rgba[1], rgba[2]]), rgb;
|
||||||
hsl[0] = (hsl[0] + val) % 360;
|
hsl[0] = (hsl[0] + val) % 360;
|
||||||
rgb = Ox.rgb(hsl);
|
rgb = Ox.rgb(hsl);
|
||||||
return [rgb[0], rgb[1], rgb[2], rgba[3]];
|
return Ox.merge(rgb, rgba[3]);
|
||||||
});
|
});
|
||||||
return that;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
that.invert = function() {
|
that.invert = function() {
|
||||||
self.map(function(rgba) {
|
return that.map(function(rgba) {
|
||||||
return [255 - rgba[0], 255 - rgba[1], 255 - rgba[2], rgba[3]];
|
return [255 - rgba[0], 255 - rgba[1], 255 - rgba[2], rgba[3]];
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
that.lightness = function(val) {
|
||||||
|
return setSL('l', val);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*@
|
||||||
|
map <f> Pixel-wise map function
|
||||||
|
(fn) -> <o> The image object
|
||||||
|
fn <f> Iterator function
|
||||||
|
rgba <[n]> RGBA values
|
||||||
|
xy <[n]> XY coordinates
|
||||||
|
@*/
|
||||||
|
that.map = function(fn) {
|
||||||
|
self.imageData = self.context.getImageData(0, 0, self.width, self.height);
|
||||||
|
self.data = self.imageData.data;
|
||||||
|
Ox.loop(0, self.data.length, 4, function(i) {
|
||||||
|
fn([
|
||||||
|
self.data[i], self.data[i + 1],
|
||||||
|
self.data[i + 2], self.data[i + 3]
|
||||||
|
], getXY(i)).forEach(function(val, c) {
|
||||||
|
self.data[i + c] = val;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
self.context.putImageData(self.imageData, 0, 0);
|
||||||
return that;
|
return that;
|
||||||
};
|
};
|
||||||
|
|
||||||
that.motionBlur = function() {
|
that.motionBlur = function() {
|
||||||
self.filter([
|
return that.filter([
|
||||||
0.2, 0.0, 0.0, 0.0, 0.0,
|
0.2, 0.0, 0.0, 0.0, 0.0,
|
||||||
0.0, 0.2, 0.0, 0.0, 0.0,
|
0.0, 0.2, 0.0, 0.0, 0.0,
|
||||||
0.0, 0.0, 0.2, 0.0, 0.0,
|
0.0, 0.0, 0.2, 0.0, 0.0,
|
||||||
0.0, 0.0, 0.0, 0.2, 0.0,
|
0.0, 0.0, 0.0, 0.2, 0.0,
|
||||||
0.0, 0.0, 0.0, 0.0, 0.2
|
0.0, 0.0, 0.0, 0.0, 0.2
|
||||||
]);
|
]);
|
||||||
return that;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
that.pixel = function(x, y, val) {
|
||||||
|
var index = getIndex(x, y);
|
||||||
|
if (!val) {
|
||||||
|
return Ox.range(4).map(function(c) {
|
||||||
|
return self.data[i + c];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
val.forEach(function(v, c) {
|
||||||
|
self.data[i + c] = v;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
that.posterize = function() {
|
that.posterize = function() {
|
||||||
that.blur(3);
|
return that.blur(3).map(function(rgba) {
|
||||||
self.map(function(rgba) {
|
|
||||||
return [
|
return [
|
||||||
Math.floor(rgba[0] / 64) * 64,
|
Math.floor(rgba[0] / 64) * 64,
|
||||||
Math.floor(rgba[1] / 64) * 64,
|
Math.floor(rgba[1] / 64) * 64,
|
||||||
|
@ -186,35 +421,8 @@ Ox.load.Image = function(options, callback) {
|
||||||
rgba[3]
|
rgba[3]
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
return that;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
that.saturation = function(val) {
|
|
||||||
self.setSL('saturation', val);
|
|
||||||
return that;
|
|
||||||
};
|
|
||||||
|
|
||||||
that.sharpen = function(val) {
|
|
||||||
self.filter([
|
|
||||||
-1, -1, -1,
|
|
||||||
-1, +9, -1,
|
|
||||||
-1, -1, -1
|
|
||||||
]);
|
|
||||||
return that;
|
|
||||||
};
|
|
||||||
|
|
||||||
that.solarize = function() {
|
|
||||||
self.map(function(rgba) {
|
|
||||||
return [
|
|
||||||
rgba[0] < 128 ? rgba[0] : 255 - rgba[0],
|
|
||||||
rgba[1] < 128 ? rgba[1] : 255 - rgba[1],
|
|
||||||
rgba[2] < 128 ? rgba[2] : 255 - rgba[2],
|
|
||||||
rgba[3]
|
|
||||||
];
|
|
||||||
});
|
|
||||||
return that;
|
|
||||||
}
|
|
||||||
|
|
||||||
that.resize = function(width, height) {
|
that.resize = function(width, height) {
|
||||||
self.canvas.attr({
|
self.canvas.attr({
|
||||||
width: width,
|
width: width,
|
||||||
|
@ -223,34 +431,54 @@ Ox.load.Image = function(options, callback) {
|
||||||
return that;
|
return that;
|
||||||
}
|
}
|
||||||
|
|
||||||
that.url = function() {
|
that.saturation = function(val) {
|
||||||
return self.canvas[0].toDataURL();
|
return setSL('s', val);
|
||||||
return that;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (self.src) {
|
that.sharpen = function(val) {
|
||||||
self.image = new Image();
|
return that.filter([
|
||||||
self.image.onload = init;
|
-1, -1, -1,
|
||||||
self.image.src = self.src;
|
-1, +9, -1,
|
||||||
} else {
|
-1, -1, -1
|
||||||
init();
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
that.solarize = function() {
|
||||||
|
return that.map(function(rgba) {
|
||||||
|
return [
|
||||||
|
rgba[0] < 128 ? rgba[0] : 255 - rgba[0],
|
||||||
|
rgba[1] < 128 ? rgba[1] : 255 - rgba[1],
|
||||||
|
rgba[2] < 128 ? rgba[2] : 255 - rgba[2],
|
||||||
|
rgba[3]
|
||||||
|
];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
that.src = function() {
|
||||||
if (self.image) {
|
if (arguments.length == 0) {
|
||||||
|
return self.canvas[0].toDataURL();
|
||||||
|
} else {
|
||||||
|
var callback = arguments[1];
|
||||||
|
self.src = arguments[0];
|
||||||
|
self.image = new Image();
|
||||||
|
self.image.onload = function() {
|
||||||
self.width = self.image.width;
|
self.width = self.image.width;
|
||||||
self.height = self.image.height;
|
self.height = self.image.height;
|
||||||
}
|
self.canvas.attr({
|
||||||
self.canvas = Ox.element('<canvas>').attr({
|
|
||||||
width: self.width,
|
width: self.width,
|
||||||
height: self.height
|
height: self.height
|
||||||
});
|
});
|
||||||
self.context = self.canvas[0].getContext('2d');
|
|
||||||
if (self.image) {
|
|
||||||
self.context.drawImage(self.image, 0, 0);
|
self.context.drawImage(self.image, 0, 0);
|
||||||
|
self.imageData = self.context.getImageData(0, 0, self.width, self.height);
|
||||||
|
self.data = self.imageData.data;
|
||||||
|
callback && callback(that);
|
||||||
}
|
}
|
||||||
self.callback(that);
|
self.image.src = self.src;
|
||||||
|
return that;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
that.toDataURL = that.src;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
18
source/Ox.js
18
source/Ox.js
|
@ -309,12 +309,15 @@ Ox.flatten = function(arr) {
|
||||||
Ox.merge <f> Merges an array with one or more other arrays
|
Ox.merge <f> Merges an array with one or more other arrays
|
||||||
> Ox.merge([1], [2, 3, 2], [1])
|
> Ox.merge([1], [2, 3, 2], [1])
|
||||||
[1, 2, 3, 2, 1]
|
[1, 2, 3, 2, 1]
|
||||||
|
> Ox.merge(1, [2, 3, 2], 1)
|
||||||
|
[1, 2, 3, 2, 1]
|
||||||
@*/
|
@*/
|
||||||
Ox.merge = function(arr) {
|
Ox.merge = function(arr) {
|
||||||
|
arr = Ox.isArray(arr) ? arr : [arr];
|
||||||
Ox.forEach(Array.prototype.slice.call(arguments, 1), function(arg) {
|
Ox.forEach(Array.prototype.slice.call(arguments, 1), function(arg) {
|
||||||
Ox.forEach(arg, function(val) {
|
Ox.isArray(arg) ? Ox.forEach(arg, function(val) {
|
||||||
arr.push(val);
|
arr.push(val);
|
||||||
});
|
}) : arr.push(arg);
|
||||||
});
|
});
|
||||||
return arr;
|
return arr;
|
||||||
};
|
};
|
||||||
|
@ -2082,14 +2085,15 @@ Ox.element = function(str) {
|
||||||
head, tail and chunk names are removed.
|
head, tail and chunk names are removed.
|
||||||
(str) -> <s> The encoded string
|
(str) -> <s> The encoded string
|
||||||
str <s> The string to be encoded
|
str <s> The string to be encoded
|
||||||
|
# Test with: Ox.decodeDeflate(Ox.encodeDeflate('foo'), alert)
|
||||||
@*/
|
@*/
|
||||||
|
|
||||||
Ox.encodeDeflate = function(str) {
|
Ox.encodeDeflate = function(str, callback) {
|
||||||
// Make sure we can encode the full unicode range of characters.
|
// Make sure we can encode the full unicode range of characters.
|
||||||
str = Ox.encodeUTF8(str);
|
str = Ox.encodeUTF8(str);
|
||||||
// We can only safely write to RGB, so we need 1 pixel for 3 bytes.
|
// We can only safely write to RGB, so we need 1 pixel for 3 bytes.
|
||||||
var len = str.length, c = Ox.canvas(Math.ceil((4 + len) / 3), 1),
|
var len = str.length, c = Ox.canvas(Math.ceil((4 + len) / 3), 1),
|
||||||
data = '', idat, ihdr;
|
data = '', idat;
|
||||||
// Prefix the string with its length, left-padded with 0-bytes to
|
// Prefix the string with its length, left-padded with 0-bytes to
|
||||||
// length of 4 bytes, and right-pad the result with non-0-bytes to a
|
// length of 4 bytes, and right-pad the result with non-0-bytes to a
|
||||||
// length that is a multiple of 3.
|
// length that is a multiple of 3.
|
||||||
|
@ -2105,7 +2109,7 @@ Ox.element = function(str) {
|
||||||
// The first 16 and the last 12 bytes of a PNG are always the same and
|
// The first 16 and the last 12 bytes of a PNG are always the same and
|
||||||
// can be discarded, the first 17 remaining bytes are part of the IHDR
|
// can be discarded, the first 17 remaining bytes are part of the IHDR
|
||||||
// chunk, and the rest are IDAT chunks.
|
// chunk, and the rest are IDAT chunks.
|
||||||
ihdr = Ox.sub(str, 16, 33); idat = Ox.sub(str, 33, -12);
|
data = Ox.sub(str, 16, 33); idat = Ox.sub(str, 33, -12);
|
||||||
while (idat) {
|
while (idat) {
|
||||||
// Each IDAT chunk consists of 4 bytes length, 4 bytes "IDAT" and
|
// Each IDAT chunk consists of 4 bytes length, 4 bytes "IDAT" and
|
||||||
// length bytes data, so we can optimize by removing each "IDAT".
|
// length bytes data, so we can optimize by removing each "IDAT".
|
||||||
|
@ -2113,7 +2117,8 @@ Ox.element = function(str) {
|
||||||
data += len + idat.substr(8, 12 + (len = Ox.decodeBase256(len)));
|
data += len + idat.substr(8, 12 + (len = Ox.decodeBase256(len)));
|
||||||
idat = idat.substr(12 + len);
|
idat = idat.substr(12 + len);
|
||||||
}
|
}
|
||||||
return ihdr + data;
|
callback && callback(data);
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
|
@ -2127,7 +2132,6 @@ Ox.element = function(str) {
|
||||||
str <s> The string to be decoded
|
str <s> The string to be decoded
|
||||||
callback <f> Callback function
|
callback <f> Callback function
|
||||||
str <s> The decoded string
|
str <s> The decoded string
|
||||||
# Test with: Ox.decodeDeflate(Ox.encodeDeflate('foo'), alert)
|
|
||||||
@*/
|
@*/
|
||||||
|
|
||||||
Ox.decodeDeflate = function(str, callback) {
|
Ox.decodeDeflate = function(str, callback) {
|
||||||
|
|
Loading…
Reference in a new issue