image module update
This commit is contained in:
parent
1b4591dcbe
commit
1e38ff6b9e
4 changed files with 188 additions and 152 deletions
|
@ -8,6 +8,7 @@ Ox.load('UI', {
|
||||||
files: Ox.merge([
|
files: Ox.merge([
|
||||||
'Ox.js',
|
'Ox.js',
|
||||||
'Ox.Geo/Ox.Geo.js',
|
'Ox.Geo/Ox.Geo.js',
|
||||||
|
'Ox.Image/Ox.Image.js',
|
||||||
], files.filter(function(file) {
|
], files.filter(function(file) {
|
||||||
return Ox.endsWith(file, '.js');
|
return Ox.endsWith(file, '.js');
|
||||||
})),
|
})),
|
||||||
|
|
|
@ -6,14 +6,15 @@ Ox.load('Image', function() {
|
||||||
var body = Ox.element('body'),
|
var body = Ox.element('body'),
|
||||||
select = Ox.element('<select>').appendTo(body);
|
select = Ox.element('<select>').appendTo(body);
|
||||||
[
|
[
|
||||||
'Method...', 'blur(1)', 'blur(2)', 'blur(3)',
|
'Method...', 'blur(1)', 'blur(5)',
|
||||||
'channel("r")', 'channel("g")', 'channel("b")', 'channel("a")',
|
'channel("r")', 'channel("g")', 'channel("b")', 'channel("a")',
|
||||||
'channel("h")', 'channel("s")', 'channel("l")',
|
'channel("h")', 'channel("s")', 'channel("l")',
|
||||||
'contour()', 'edges()', 'emboss()',
|
'contour()', 'depth(1)', 'depth(4)', 'edges()', 'emboss()',
|
||||||
'encode("some secret stuff")', 'decode()',
|
'encode("secret")', 'decode()',
|
||||||
'encode("some secret stuff", true)', 'decode(true)',
|
'encode("secret", false)', 'decode(false)',
|
||||||
'encode("some secret stuff", 1)', 'decode(1)',
|
'encode("secret", 1)', 'decode(1)',
|
||||||
'encode("some secret stuff.", 15)', 'decode(15)',
|
'encode("secret", false, 15)', 'decode(false, 15)',
|
||||||
|
'encode("secret", 127)', 'decode(127)',
|
||||||
'hue(-60)', 'hue(60)',
|
'hue(-60)', 'hue(60)',
|
||||||
'invert()',
|
'invert()',
|
||||||
'lightness(-0.5)', 'lightness(0.5)',
|
'lightness(-0.5)', 'lightness(0.5)',
|
||||||
|
|
|
@ -1,5 +1,17 @@
|
||||||
Ox.load.Image = function(options, callback) {
|
Ox.load.Image = function(options, callback) {
|
||||||
|
|
||||||
|
//@ Image ------------------------------------------------------------------
|
||||||
|
|
||||||
|
/*@
|
||||||
|
Ox.Image <f> Generic image object
|
||||||
|
(src, callback) -> <u> undefined
|
||||||
|
(width, height, callback) -> <u> undefined
|
||||||
|
(width, height, background, callback) -> <u> undefined
|
||||||
|
src <s> Image source (local, remote or data URL)
|
||||||
|
width <n> Width in px
|
||||||
|
image <n> Height in px
|
||||||
|
background <[n]> Background color (RGB or RGBA)
|
||||||
|
@*/
|
||||||
Ox.Image = function() {
|
Ox.Image = function() {
|
||||||
|
|
||||||
var self = {},
|
var self = {},
|
||||||
|
@ -81,25 +93,22 @@ Ox.load.Image = function(options, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
that.blur = function(val) {
|
that.blur = function(val) {
|
||||||
return that.filter(val == 1 ? [
|
var filter = [],
|
||||||
0.0, 0.2, 0.0,
|
size = val * 2 + 1;
|
||||||
0.2, 0.2, 0.2,
|
sum = 0
|
||||||
0.0, 0.2, 0.0
|
Ox.loop(size, function(x) {
|
||||||
] : val == 2 ? [
|
Ox.loop(size, function(y) {
|
||||||
0.00, 1/21, 1/21, 1/21, 0.00,
|
var isInCircle = +(Math.sqrt(
|
||||||
1/21, 1/21, 1/21, 1/21, 1/21,
|
Math.pow(x - val, 2) + Math.pow(y - val, 2)
|
||||||
1/21, 1/21, 1/21, 1/21, 1/21,
|
) <= val);
|
||||||
1/21, 1/21, 1/21, 1/21, 1/21,
|
sum += isInCircle;
|
||||||
0.00, 1/21, 1/21, 1/21, 0.00
|
filter.push(isInCircle)
|
||||||
] : [
|
});
|
||||||
0.00, 0.00, 1/37, 1/37, 1/37, 0.00, 0.00,
|
});
|
||||||
0.00, 1/37, 1/37, 1/37, 1/37, 1/37, 0.00,
|
filter = filter.map(function(val) {
|
||||||
1/37, 1/37, 1/37, 1/37, 1/37, 1/37, 1/37,
|
return val / sum;
|
||||||
1/37, 1/37, 1/37, 1/37, 1/37, 1/37, 1/37,
|
})
|
||||||
1/37, 1/37, 1/37, 1/37, 1/37, 1/37, 1/37,
|
return that.filter(filter);
|
||||||
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
|
|
||||||
]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
that.channel = function(str) {
|
that.channel = function(str) {
|
||||||
|
@ -133,6 +142,15 @@ Ox.load.Image = function(options, callback) {
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
that.depth = function(val) {
|
||||||
|
var pow = Math.pow(2, 8 - val);
|
||||||
|
return that.map(function(rgba) {
|
||||||
|
return rgba.map(function(v, i) {
|
||||||
|
return i < 3 ? Math.floor(v / pow) * pow : v
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
that.edges = function(val) {
|
that.edges = function(val) {
|
||||||
return that.filter([
|
return that.filter([
|
||||||
-1, -1, -1,
|
-1, -1, -1,
|
||||||
|
@ -149,79 +167,35 @@ Ox.load.Image = function(options, callback) {
|
||||||
], 128).saturation(-1);
|
], 128).saturation(-1);
|
||||||
};
|
};
|
||||||
|
|
||||||
that.decode = function() {
|
/*@
|
||||||
var callback = arguments[arguments.length - 1],
|
encode <f> Encodes a string into the image
|
||||||
deflate = Ox.isBoolean(arguments[0]) ? arguments[0]
|
For most purposes, deflate and mode should be omitted, since the
|
||||||
: Ox.isBoolean(arguments[1]) ? arguments[1] : false,
|
defaults make the existence of the message harder to detect. A valid
|
||||||
mode = Ox.isNumber(arguments[0]) ? arguments[0]
|
use case for deflate and mode would be to encode, additionally to
|
||||||
: Ox.isNumber(arguments[1]) ? arguments[1] : 0,
|
the secret string, a more easily detected decoy string: <code>
|
||||||
bin = '',
|
image.encode(decoy, false, 1, function(image) { image.encode(secret,
|
||||||
bits = mode < 1 ? [-mode] : Ox.map(Ox.range(8), function(b) {
|
-1, callback); })</code>.
|
||||||
return mode & 1 << b ? b : null;
|
(str, callback) -> <o> The image object (unmodified)
|
||||||
}),
|
(str, deflate, callback) -> <o> The image object (unmodified)
|
||||||
done = 0; len = 4, str = '';
|
(str, mode, callback) -> <o> The image object (unmodified)
|
||||||
that.forEach(function(rgba, xy) {
|
(str, deflate, mode, callback) -> <o> The image object (unmodified)
|
||||||
if (rgba[3] == 255) {
|
(str, mode, deflate, callback) -> <o> The image object (unmodified)
|
||||||
var index = getIndex(xy);
|
str <s> The string to be encoded
|
||||||
Ox.loop(3, function(c) {
|
callback <f> Callback function
|
||||||
var i = index + c;
|
image <o> The image object (modified)
|
||||||
Ox.forEach(bits, function(bit) {
|
deflate <b|true> If true, compress string with deflate
|
||||||
if (mode < 1) {
|
mode <n|0> Encoding mode
|
||||||
bin += Ox.sum(Ox.range(8).map(function(bit) {
|
If mode is between -7 and 0, the string will be encoded one bit
|
||||||
return +!!(self.data[i] & 1 << bit);
|
per byte, as the number of bits within that byte set to 1,
|
||||||
})) % 2;
|
modulo 2, by flipping, if necessary, the most (mode -7) to least
|
||||||
} else {
|
(mode 0) significant bit. If mode is between 1 and 255, the
|
||||||
bin += +!!(self.data[i] & 1 << bit);
|
string will be encoded bitwise into all bits per byte that, in
|
||||||
}
|
mode, are set to 1.
|
||||||
// /*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(Ox.map(str, function(chr) {
|
|
||||||
return Ox.pad(chr.charCodeAt(0).toString(2), 8);
|
|
||||||
}).join(' '))
|
|
||||||
Ox.print("LEN", len, getCapacity(bits.length), bits, xy, c)
|
|
||||||
Ox.print(Ox.range(index).map(function(i) {
|
|
||||||
return that.pixel(i, 0).map(function(px, j) {
|
|
||||||
return j < 4 ? Ox.pad(px.toString(2), 8) : null;
|
|
||||||
}).join(',')
|
|
||||||
}).join('\n'))
|
|
||||||
if (len + 4 > getCapacity(bits.length)) {
|
|
||||||
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) {
|
that.encode = function(str) {
|
||||||
var callback = arguments[arguments.length - 1],
|
var callback = arguments[arguments.length - 1],
|
||||||
deflate = Ox.isBoolean(arguments[1]) ? arguments[1]
|
deflate = Ox.isBoolean(arguments[1]) ? arguments[1]
|
||||||
: Ox.isBoolean(arguments[2]) ? arguments[2] : false,
|
: Ox.isBoolean(arguments[2]) ? arguments[2] : true,
|
||||||
mode = Ox.isNumber(arguments[1]) ? arguments[1]
|
mode = Ox.isNumber(arguments[1]) ? arguments[1]
|
||||||
: Ox.isNumber(arguments[2]) ? arguments[2] : 0,
|
: Ox.isNumber(arguments[2]) ? arguments[2] : 0,
|
||||||
b = 0, bin,
|
b = 0, bin,
|
||||||
|
@ -229,13 +203,12 @@ Ox.load.Image = function(options, callback) {
|
||||||
return mode & 1 << bit ? bit : null;
|
return mode & 1 << bit ? bit : null;
|
||||||
}),
|
}),
|
||||||
cap = getCapacity(bits.length), len;
|
cap = getCapacity(bits.length), len;
|
||||||
Ox.print("CAPACITY", cap);
|
Ox.print("CAPACITY", cap)
|
||||||
// Compress the string
|
// Compress the string
|
||||||
str = Ox[deflate ? 'encodeDeflate' : 'encodeUTF8'](str);
|
str = Ox[deflate ? 'encodeDeflate' : 'encodeUTF8'](str);
|
||||||
Ox.print("STR", str, str.length, str.charCodeAt(0));
|
|
||||||
len = str.length;
|
len = str.length;
|
||||||
// Prefix the string with its length, as a four-byte value
|
// Prefix the string with its length, as a four-byte value
|
||||||
str = Ox.pad(Ox.encodeBase256(len), 4, Ox.char(0)) + str;
|
str = Ox.pad(Ox.encodeBase256(len), 4, '\u0000') + str;
|
||||||
str.length > cap && error('encode');
|
str.length > cap && error('encode');
|
||||||
while (str.length < cap) {
|
while (str.length < cap) {
|
||||||
str += str.substr(4, len);
|
str += str.substr(4, len);
|
||||||
|
@ -249,6 +222,7 @@ Ox.load.Image = function(options, callback) {
|
||||||
}));
|
}));
|
||||||
b = 0;
|
b = 0;
|
||||||
that.forEach(function(rgba, xy) {
|
that.forEach(function(rgba, xy) {
|
||||||
|
// If alpha is not 255, the RGB values may not be preserved
|
||||||
if (rgba[3] == 255) {
|
if (rgba[3] == 255) {
|
||||||
var index = getIndex(xy);
|
var index = getIndex(xy);
|
||||||
Ox.loop(3, function(c) {
|
Ox.loop(3, function(c) {
|
||||||
|
@ -268,32 +242,70 @@ Ox.load.Image = function(options, callback) {
|
||||||
self.data[i] ^= 1 << bit;
|
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);
|
self.context.putImageData(self.imageData, 0, 0);
|
||||||
callback(that);
|
callback(that);
|
||||||
|
return that;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
that.decode = function() {
|
||||||
|
var callback = arguments[arguments.length - 1],
|
||||||
|
deflate = Ox.isBoolean(arguments[0]) ? arguments[0]
|
||||||
|
: Ox.isBoolean(arguments[1]) ? arguments[1] : true,
|
||||||
|
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) {
|
||||||
|
bin += mode < 1
|
||||||
|
? Ox.sum(Ox.range(8).map(function(bit) {
|
||||||
|
return +!!(self.data[i] & 1 << bit);
|
||||||
|
})) % 2
|
||||||
|
: +!!(self.data[i] & 1 << bit);
|
||||||
|
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)
|
||||||
|
len + 4 > getCapacity(bits.length)
|
||||||
|
&& error('decode');
|
||||||
|
str = '';
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (done == 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
if (deflate) {
|
||||||
|
Ox.print('DEFLATE')
|
||||||
|
Ox.decodeDeflate(str, callback);
|
||||||
|
} else {
|
||||||
|
callback(Ox.decodeUTF8(str));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
error('decode');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
that.fillRect = function(x, y, w, h) {
|
that.fillRect = function(x, y, w, h) {
|
||||||
self.context.fillRect(x, y, w, h);
|
self.context.fillRect(x, y, w, h);
|
||||||
return that;
|
return that;
|
||||||
|
|
92
source/Ox.js
92
source/Ox.js
|
@ -1857,6 +1857,17 @@ Ox.element = function(str) {
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
/*@
|
/*@
|
||||||
|
click <f> Binds a function to the click event
|
||||||
|
(callback) -> <o> This element
|
||||||
|
callback <f> Callback function
|
||||||
|
event <o> The DOM event
|
||||||
|
@*/
|
||||||
|
// fixme: why not a generic bind?
|
||||||
|
click: function(callback) {
|
||||||
|
this[0].onclick = callback;
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
/*@
|
||||||
css <f> Gets or sets a CSS attribute
|
css <f> Gets or sets a CSS attribute
|
||||||
(key) -> <s> Value
|
(key) -> <s> Value
|
||||||
(key, value) -> <o> This element
|
(key, value) -> <o> This element
|
||||||
|
@ -1912,6 +1923,15 @@ Ox.element = function(str) {
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
/*@
|
/*@
|
||||||
|
removeAttr <f> Removes an attribute
|
||||||
|
(key) -> <o> This element
|
||||||
|
key <s> The attribute
|
||||||
|
@*/
|
||||||
|
removeAttr: function(key) {
|
||||||
|
this[0].removeAttribute(key);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
/*@
|
||||||
removeClass <f> Removes a class name
|
removeClass <f> Removes a class name
|
||||||
(className) -> <o> This element
|
(className) -> <o> This element
|
||||||
className <s> Class name
|
className <s> Class name
|
||||||
|
@ -2092,13 +2112,12 @@ Ox.element = function(str) {
|
||||||
// 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),
|
// The string length may not be a multiple of 3, so we need to encode
|
||||||
data = '', idat;
|
// the number of padding bytes (1 byte), the string, and non-0-bytes
|
||||||
// Prefix the string with its length, left-padded with 0-bytes to
|
// as padding, so that the combined length becomes a multiple of 3.
|
||||||
// length of 4 bytes, and right-pad the result with non-0-bytes to a
|
var len = 1 + str.length, c = Ox.canvas(Math.ceil(len / 3), 1),
|
||||||
// length that is a multiple of 3.
|
data, idat, pad = (3 - len % 3) % 3;
|
||||||
str = Ox.pad(Ox.pad(Ox.encodeBase256(len), 4, '\u0000') + str,
|
str = Ox.char(pad) + str + Ox.repeat('\u00FF', pad);
|
||||||
-Math.ceil((4 + len) / 3) * 3, '\u00FF');
|
|
||||||
Ox.loop(c.data.length, function(i) {
|
Ox.loop(c.data.length, function(i) {
|
||||||
// Write character codes into RGB, and 255 into ALPHA
|
// Write character codes into RGB, and 255 into ALPHA
|
||||||
c.data[i] = i % 4 < 3 ? str.charCodeAt(i - parseInt(i / 4)) : 255;
|
c.data[i] = i % 4 < 3 ? str.charCodeAt(i - parseInt(i / 4)) : 255;
|
||||||
|
@ -2106,15 +2125,17 @@ Ox.element = function(str) {
|
||||||
c.context.putImageData(c.imageData, 0, 0);
|
c.context.putImageData(c.imageData, 0, 0);
|
||||||
// Get the PNG data from the data URL and decode it from base64.
|
// Get the PNG data from the data URL and decode it from base64.
|
||||||
str = atob(c.canvas.toDataURL().split(',')[1]);
|
str = atob(c.canvas.toDataURL().split(',')[1]);
|
||||||
// The first 16 and the last 12 bytes of a PNG are always the same and
|
// Discard bytes 0 to 15 (8 bytes PNG signature, 4 bytes IHDR length, 4
|
||||||
// can be discarded, the first 17 remaining bytes are part of the IHDR
|
// bytes IHDR name), keep bytes 16 to 19 (width), discard bytes 20 to 29
|
||||||
// chunk, and the rest are IDAT chunks.
|
// (4 bytes height, 5 bytes flags), keep bytes 29 to 32 (IHDR checksum),
|
||||||
data = Ox.sub(str, 16, 33); idat = Ox.sub(str, 33, -12);
|
// keep the rest (IDAT chunks), discard the last 12 bytes (IEND chunk).
|
||||||
|
data = str.substr(16, 4) + str.substr(29, 4);
|
||||||
|
idat = str.substr(33, str.length - 45);
|
||||||
while (idat) {
|
while (idat) {
|
||||||
// Each IDAT chunk consists of 4 bytes length, 4 bytes "IDAT" and
|
// Each IDAT chunk is 4 bytes length, 4 bytes name, length bytes
|
||||||
// length bytes data, so we can optimize by removing each "IDAT".
|
// data and 4 bytes checksum. We can discard the name parts.
|
||||||
len = idat.substr(0, 4);
|
len = idat.substr(0, 4);
|
||||||
data += len + idat.substr(8, 12 + (len = Ox.decodeBase256(len)));
|
data += len + idat.substr(8, 4 + (len = Ox.decodeBase256(len)));
|
||||||
idat = idat.substr(12 + len);
|
idat = idat.substr(12 + len);
|
||||||
}
|
}
|
||||||
callback && callback(data);
|
callback && callback(data);
|
||||||
|
@ -2135,24 +2156,24 @@ Ox.element = function(str) {
|
||||||
@*/
|
@*/
|
||||||
|
|
||||||
Ox.decodeDeflate = function(str, callback) {
|
Ox.decodeDeflate = function(str, callback) {
|
||||||
function decodeHex(str) {
|
|
||||||
return str.split(' ').map(function(v) {
|
|
||||||
return Ox.char(parseInt(v, 16));
|
|
||||||
}).join('');
|
|
||||||
}
|
|
||||||
var image = new Image(),
|
var image = new Image(),
|
||||||
// The first 16 and the last 12 bytes of a PNG are always the same,
|
// PNG file signature and IHDR chunk
|
||||||
// the first 17 remaining bytes are part of the IHDR chunk, and the
|
data = '\u0089PNG\r\n\u001A\n\u0000\u0000\u0000\u000DIHDR'
|
||||||
// rest are IDAT chunks.
|
+ str.substr(0, 4) + '\u0000\u0000\u0000\u0001'
|
||||||
head = decodeHex('89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52'),
|
+ '\u0008\u0006\u0000\u0000\u0000' + str.substr(4, 4),
|
||||||
tail = decodeHex('00 00 00 00 49 45 4E 44 AE 42 60 82'),
|
// IDAT chunks
|
||||||
data = '', ihdr = str.substr(0, 17), idat = str.substr(17), len;
|
idat = str.substr(8), len;
|
||||||
|
function error() {
|
||||||
|
throw new RangeError('Deflate codec can\'t decode data.');
|
||||||
|
}
|
||||||
while (idat) {
|
while (idat) {
|
||||||
// Reinsert the IDAT chunk names
|
// Reinsert the IDAT chunk names
|
||||||
len = idat.substr(0, 4);
|
len = idat.substr(0, 4);
|
||||||
data += len + 'IDAT' + idat.substr(4, 8 + (len = Ox.decodeBase256(len)));
|
data += len + 'IDAT' + idat.substr(4, 4 + (len = Ox.decodeBase256(len)));
|
||||||
idat = idat.substr(8 + len);
|
idat = idat.substr(8 + len);
|
||||||
}
|
}
|
||||||
|
// IEND chunk
|
||||||
|
data += '\u0000\u0000\u0000\u0000IEND\u00AE\u0042\u0060\u0082';
|
||||||
// Unfortunately, we can't synchronously set the source of an image,
|
// Unfortunately, we can't synchronously set the source of an image,
|
||||||
// draw it onto a canvas, and read its data.
|
// draw it onto a canvas, and read its data.
|
||||||
image.onload = function() {
|
image.onload = function() {
|
||||||
|
@ -2160,16 +2181,17 @@ Ox.element = function(str) {
|
||||||
// Read one character per RGB byte, ignore ALPHA.
|
// Read one character per RGB byte, ignore ALPHA.
|
||||||
return i % 4 < 3 ? Ox.char(v) : '';
|
return i % 4 < 3 ? Ox.char(v) : '';
|
||||||
}).join('');
|
}).join('');
|
||||||
callback(
|
try {
|
||||||
// Parse the first 4 bytes as length and the next length bytes
|
// Parse the first byte as number of bytes to chop at the end,
|
||||||
// as an UTF8-encoded string.
|
// and the rest, without these bytes, as an UTF8-encoded string.
|
||||||
Ox.decodeUTF8(str.substr(4, Ox.decodeBase256(str.substr(0, 4))))
|
str = Ox.decodeUTF8(str.substr(1, str.length - 1 - str.charCodeAt(0)))
|
||||||
);
|
} catch (e) {
|
||||||
|
error();
|
||||||
|
}
|
||||||
|
callback(str);
|
||||||
}
|
}
|
||||||
image.onerror = function() {
|
image.onerror = error;
|
||||||
throw new RangeError('Deflate codec can\'t decode data.')
|
image.src = 'data:image/png;base64,' + btoa(data);
|
||||||
}
|
|
||||||
image.src = 'data:image/png;base64,' + btoa(head + ihdr + data + tail);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
|
|
Loading…
Reference in a new issue