cleanup; rename vars; fix deflate
This commit is contained in:
parent
a393591741
commit
ad3b50cb82
1 changed files with 354 additions and 387 deletions
|
@ -1,46 +1,5 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
function cap(width, height) {
|
|
||||||
// returns maximum encoding capacity of an image
|
|
||||||
return parseInt(width * height * 3/8) - 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
function seek(data, px) {
|
|
||||||
// returns this, or the next, opaque pixel
|
|
||||||
while (data[px * 4 + 3] < 255) {
|
|
||||||
if (++px * 4 == data.length) {
|
|
||||||
throwPNGError('de');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return px;
|
|
||||||
}
|
|
||||||
|
|
||||||
function xor(byte) {
|
|
||||||
// returns "1"-bits-in-byte % 2
|
|
||||||
// use: num.toString(2).replace(/0/g, '').length % 2
|
|
||||||
var xor = 0;
|
|
||||||
Ox.range(8).forEach(function(i) {
|
|
||||||
xor ^= byte >> i & 1;
|
|
||||||
});
|
|
||||||
return xor;
|
|
||||||
}
|
|
||||||
|
|
||||||
function throwPNGError(str) {
|
|
||||||
throw new RangeError(
|
|
||||||
'PNG codec can\'t ' +
|
|
||||||
(str == 'en' ? 'encode data' : 'decode image')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function throwUTF8Error(byte, pos) {
|
|
||||||
throw new RangeError(
|
|
||||||
'UTF-8 codec can\'t decode byte 0x' +
|
|
||||||
byte.toString(16).toUpperCase() + ' at position ' + pos
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
Ox.encodeBase26 <b> Encode a number as bijective base26
|
Ox.encodeBase26 <b> Encode a number as bijective base26
|
||||||
See <a href="http://en.wikipedia.org/wiki/Bijective_numeration">
|
See <a href="http://en.wikipedia.org/wiki/Bijective_numeration">
|
||||||
|
@ -48,13 +7,13 @@
|
||||||
> Ox.encodeBase26(4461)
|
> Ox.encodeBase26(4461)
|
||||||
'FOO'
|
'FOO'
|
||||||
@*/
|
@*/
|
||||||
Ox.encodeBase26 = function(num) {
|
Ox.encodeBase26 = function(number) {
|
||||||
var ret = '';
|
var string = '';
|
||||||
while (num) {
|
while (number) {
|
||||||
ret = String.fromCharCode(64 + num % 26) + ret;
|
string = String.fromCharCode(64 + number % 26) + string;
|
||||||
num = parseInt(num / 26);
|
number = parseInt(number / 26);
|
||||||
}
|
}
|
||||||
return ret;
|
return string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
|
@ -64,9 +23,9 @@
|
||||||
> Ox.decodeBase26('foo')
|
> Ox.decodeBase26('foo')
|
||||||
4461
|
4461
|
||||||
@*/
|
@*/
|
||||||
Ox.decodeBase26 = function(str) {
|
Ox.decodeBase26 = function(string) {
|
||||||
return str.toUpperCase().split('').reverse().reduce(function(p, v, i) {
|
return string.toUpperCase().split('').reverse().reduce(function(p, c, i) {
|
||||||
return p + (v.charCodeAt(0) - 64) * Math.pow(26, i);
|
return p + (c.charCodeAt(0) - 64) * Math.pow(26, i);
|
||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -78,9 +37,9 @@
|
||||||
> Ox.encodeBase32(33819)
|
> Ox.encodeBase32(33819)
|
||||||
'110V'
|
'110V'
|
||||||
@*/
|
@*/
|
||||||
Ox.encodeBase32 = function(num) {
|
Ox.encodeBase32 = function(number) {
|
||||||
return Ox.map(num.toString(32), function(char) {
|
return Ox.map(number.toString(32), function(character) {
|
||||||
return Ox.BASE_32_DIGITS[parseInt(char, 32)];
|
return Ox.BASE_32_DIGITS[parseInt(character, 32)];
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -94,10 +53,10 @@
|
||||||
> Ox.decodeBase32('?').toString()
|
> Ox.decodeBase32('?').toString()
|
||||||
'NaN'
|
'NaN'
|
||||||
@*/
|
@*/
|
||||||
Ox.decodeBase32 = function(str) {
|
Ox.decodeBase32 = function(string) {
|
||||||
return parseInt(Ox.map(str.toUpperCase(), function(char) {
|
return parseInt(Ox.map(string.toUpperCase(), function(character) {
|
||||||
var index = Ox.BASE_32_DIGITS.indexOf(
|
var index = Ox.BASE_32_DIGITS.indexOf(
|
||||||
Ox.BASE_32_ALIASES[char] || char
|
Ox.BASE_32_ALIASES[character] || character
|
||||||
);
|
);
|
||||||
return (index == -1 ? ' ' : index).toString(32);
|
return (index == -1 ? ' ' : index).toString(32);
|
||||||
}), 32);
|
}), 32);
|
||||||
|
@ -108,8 +67,8 @@
|
||||||
> Ox.encodeBase64(32394)
|
> Ox.encodeBase64(32394)
|
||||||
'foo'
|
'foo'
|
||||||
@*/
|
@*/
|
||||||
Ox.encodeBase64 = function(num) {
|
Ox.encodeBase64 = function(number) {
|
||||||
return btoa(Ox.encodeBase256(num)).replace(/=/g, '');
|
return btoa(Ox.encodeBase256(number)).replace(/=/g, '');
|
||||||
};
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
|
@ -117,8 +76,8 @@
|
||||||
> Ox.decodeBase64('foo')
|
> Ox.decodeBase64('foo')
|
||||||
32394
|
32394
|
||||||
@*/
|
@*/
|
||||||
Ox.decodeBase64 = function(str) {
|
Ox.decodeBase64 = function(string) {
|
||||||
return Ox.decodeBase256(atob(str));
|
return Ox.decodeBase256(atob(string));
|
||||||
};
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
|
@ -126,13 +85,13 @@
|
||||||
> Ox.encodeBase128(1685487)
|
> Ox.encodeBase128(1685487)
|
||||||
'foo'
|
'foo'
|
||||||
@*/
|
@*/
|
||||||
Ox.encodeBase128 = function(num) {
|
Ox.encodeBase128 = function(number) {
|
||||||
var str = '';
|
var string = '';
|
||||||
while (num) {
|
while (number) {
|
||||||
str = Ox.char(num & 127) + str;
|
string = Ox.char(number & 127) + string;
|
||||||
num >>= 7;
|
number >>= 7;
|
||||||
}
|
}
|
||||||
return str;
|
return string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
|
@ -140,9 +99,9 @@
|
||||||
> Ox.decodeBase128('foo')
|
> Ox.decodeBase128('foo')
|
||||||
1685487
|
1685487
|
||||||
@*/
|
@*/
|
||||||
Ox.decodeBase128 = function(str) {
|
Ox.decodeBase128 = function(string) {
|
||||||
return str.split('').reverse().reduce(function(p, v, i) {
|
return string.split('').reverse().reduce(function(p, c, i) {
|
||||||
return p + (v.charCodeAt(0) << i * 7);
|
return p + (c.charCodeAt(0) << i * 7);
|
||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -151,13 +110,13 @@
|
||||||
> Ox.encodeBase256(6713199)
|
> Ox.encodeBase256(6713199)
|
||||||
'foo'
|
'foo'
|
||||||
@*/
|
@*/
|
||||||
Ox.encodeBase256 = function(num) {
|
Ox.encodeBase256 = function(number) {
|
||||||
var str = '';
|
var string = '';
|
||||||
while (num) {
|
while (number) {
|
||||||
str = Ox.char(num & 255) + str;
|
string = Ox.char(number & 255) + string;
|
||||||
num >>= 8;
|
number >>= 8;
|
||||||
}
|
}
|
||||||
return str;
|
return string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
|
@ -165,9 +124,9 @@
|
||||||
> Ox.decodeBase256('foo')
|
> Ox.decodeBase256('foo')
|
||||||
6713199
|
6713199
|
||||||
@*/
|
@*/
|
||||||
Ox.decodeBase256 = function(str) {
|
Ox.decodeBase256 = function(string) {
|
||||||
return str.split('').reverse().reduce(function(p, v, i) {
|
return string.split('').reverse().reduce(function(p, c, i) {
|
||||||
return p + (v.charCodeAt(0) << i * 8);
|
return p + (c.charCodeAt(0) << i * 8);
|
||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -183,36 +142,39 @@
|
||||||
# Test with: Ox.decodeDeflate(Ox.encodeDeflate('foo'), alert)
|
# Test with: Ox.decodeDeflate(Ox.encodeDeflate('foo'), alert)
|
||||||
@*/
|
@*/
|
||||||
|
|
||||||
Ox.encodeDeflate = function(str, callback) {
|
Ox.encodeDeflate = function(string, 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);
|
string = Ox.encodeUTF8(string);
|
||||||
// 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.
|
||||||
// The string length may not be a multiple of 3, so we need to encode
|
// The string length may not be a multiple of 3, so we need to encode
|
||||||
// the number of padding bytes (1 byte), the string, and non-0-bytes
|
// the number of padding bytes (1 byte), the string, and non-0-bytes
|
||||||
// as padding, so that the combined length becomes a multiple of 3.
|
// as padding, so that the combined length becomes a multiple of 3.
|
||||||
var len = 1 + str.length, c = Ox.canvas(Math.ceil(len / 3), 1),
|
var length = 1 + string.length, c = Ox.canvas(Math.ceil(length / 3), 1),
|
||||||
data, idat, pad = (3 - len % 3) % 3;
|
data, idat, pad = (3 - length % 3) % 3;
|
||||||
str = Ox.char(pad) + str + Ox.repeat('\u00FF', pad);
|
string = Ox.char(pad) + string + Ox.repeat('\u00FF', pad);
|
||||||
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 ? string.charCodeAt(i - parseInt(i / 4)) : 255;
|
||||||
});
|
});
|
||||||
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]);
|
string = atob(c.canvas.toDataURL().split(',')[1]);
|
||||||
// Discard bytes 0 to 15 (8 bytes PNG signature, 4 bytes IHDR length, 4
|
// Discard bytes 0 to 15 (8 bytes PNG signature, 4 bytes IHDR length, 4
|
||||||
// bytes IHDR name), keep bytes 16 to 19 (width), discard bytes 20 to 29
|
// bytes IHDR name), keep bytes 16 to 19 (width), discard bytes 20 to 29
|
||||||
// (4 bytes height, 5 bytes flags), keep bytes 29 to 32 (IHDR checksum),
|
// (4 bytes height, 5 bytes flags), keep bytes 29 to 32 (IHDR checksum),
|
||||||
// keep the rest (IDAT chunks), discard the last 12 bytes (IEND chunk).
|
// keep the rest (IDAT chunks), discard the last 12 bytes (IEND chunk).
|
||||||
data = str.slice(16, 20) + str.slice(29, 33);
|
data = string.slice(16, 20) + string.slice(29, 33);
|
||||||
idat = str.slice(33, -45);
|
idat = string.slice(33, -12);
|
||||||
while (idat) {
|
while (idat) {
|
||||||
// Each IDAT chunk is 4 bytes length, 4 bytes name, length bytes
|
// Each IDAT chunk is 4 bytes length, 4 bytes name, length bytes
|
||||||
// data and 4 bytes checksum. We can discard the name parts.
|
// data and 4 bytes checksum. We can discard the name parts.
|
||||||
len = idat.slice(0, 4);
|
length = idat.slice(0, 4);
|
||||||
data += len + idat.slice(8, 12 + (len = Ox.decodeBase256(len)));
|
data += length + idat.slice(8, 12 + (
|
||||||
idat = idat.slice(12 + len);
|
length = Ox.decodeBase256(length)
|
||||||
|
));
|
||||||
|
idat = idat.slice(12 + length);
|
||||||
}
|
}
|
||||||
|
// Allow for async use, symmetrical to Ox.decodeDeflate
|
||||||
callback && callback(data);
|
callback && callback(data);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
@ -230,40 +192,42 @@
|
||||||
str <s> The decoded string
|
str <s> The decoded string
|
||||||
@*/
|
@*/
|
||||||
|
|
||||||
Ox.decodeDeflate = function(str, callback) {
|
Ox.decodeDeflate = function(string, callback) {
|
||||||
var image = new Image(),
|
var image = new Image(),
|
||||||
// PNG file signature and IHDR chunk
|
// PNG file signature and IHDR chunk
|
||||||
data = '\u0089PNG\r\n\u001A\n\u0000\u0000\u0000\u000DIHDR'
|
data = '\u0089PNG\r\n\u001A\n\u0000\u0000\u0000\u000DIHDR'
|
||||||
+ str.slice(0, 4) + '\u0000\u0000\u0000\u0001'
|
+ string.slice(0, 4) + '\u0000\u0000\u0000\u0001'
|
||||||
+ '\u0008\u0006\u0000\u0000\u0000' + str.slice(4, 8),
|
+ '\u0008\u0006\u0000\u0000\u0000' + string.slice(4, 8),
|
||||||
// IDAT chunks
|
// IDAT chunks
|
||||||
idat = str.slice(8), len;
|
idat = string.slice(8), length;
|
||||||
function error() {
|
function error() {
|
||||||
throw new RangeError('Deflate codec can\'t decode data.');
|
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.slice(0, 4);
|
length = idat.slice(0, 4);
|
||||||
data += len + 'IDAT' + idat.slice(4, 8 + (len = Ox.decodeBase256(len)));
|
data += length + 'IDAT' + idat.slice(4, 8 + (
|
||||||
idat = idat.slice(8 + len);
|
length = Ox.decodeBase256(length)
|
||||||
|
));
|
||||||
|
idat = idat.slice(8 + length);
|
||||||
}
|
}
|
||||||
// IEND chunk
|
// IEND chunk
|
||||||
data += '\u0000\u0000\u0000\u0000IEND\u00AE\u0042\u0060\u0082';
|
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() {
|
||||||
str = Ox.toArray(Ox.canvas(image).data).map(function(v, i) {
|
string = Ox.toArray(Ox.canvas(image).data).map(function(value, index) {
|
||||||
// Read one character per RGB byte, ignore ALPHA.
|
// Read one character per RGB byte, ignore ALPHA.
|
||||||
return i % 4 < 3 ? Ox.char(v) : '';
|
return index % 4 < 3 ? Ox.char(value) : '';
|
||||||
}).join('');
|
}).join('');
|
||||||
try {
|
try {
|
||||||
// Parse the first byte as number of bytes to chop at the end,
|
// Parse the first byte as number of bytes to chop at the end,
|
||||||
// and the rest, without these bytes, as an UTF8-encoded string.
|
// and the rest, without these bytes, as an UTF8-encoded string.
|
||||||
str = Ox.decodeUTF8(str.slice(1, -str.CharCodeAt(0)));
|
string = Ox.decodeUTF8(string.slice(1, -string.charCodeAt(0)));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error();
|
error();
|
||||||
}
|
}
|
||||||
callback(str);
|
callback(string);
|
||||||
}
|
}
|
||||||
image.onerror = error;
|
image.onerror = error;
|
||||||
image.src = 'data:image/png;base64,' + btoa(data);
|
image.src = 'data:image/png;base64,' + btoa(data);
|
||||||
|
@ -360,21 +324,26 @@
|
||||||
> Ox.decodeUTF8('\u00C2\u00A5\u00E2\u0082\u00AC\u0024')
|
> Ox.decodeUTF8('\u00C2\u00A5\u00E2\u0082\u00AC\u0024')
|
||||||
'¥€$'
|
'¥€$'
|
||||||
@*/
|
@*/
|
||||||
Ox.decodeUTF8 = function(str) {
|
Ox.decodeUTF8 = function(string) {
|
||||||
var code,
|
var code, i = 0, length = string.length, ret = '';
|
||||||
i = 0,
|
function error(byte, position) {
|
||||||
len = str.length,
|
throw new RangeError(
|
||||||
ret = '';
|
'UTF-8 codec can\'t decode byte 0x' +
|
||||||
while (i < len) {
|
byte.toString(16).toUpperCase() + ' at position ' + position
|
||||||
code = Ox.range(3).map(function(o) {
|
);
|
||||||
return str.charCodeAt(i + o);
|
}
|
||||||
});
|
while (i < length) {
|
||||||
|
code = [
|
||||||
|
string.charCodeAt(i),
|
||||||
|
string.charCodeAt(i + 1),
|
||||||
|
string.charCodeAt(i + 2)
|
||||||
|
];
|
||||||
if (code[0] <= 128) {
|
if (code[0] <= 128) {
|
||||||
ret += str[i];
|
ret += string[i];
|
||||||
i++;
|
i++;
|
||||||
} else if (
|
} else if (
|
||||||
code[0] >= 192 && code[0] < 240
|
code[0] >= 192 && code[0] < 240
|
||||||
&& i < len - (code[0] < 224 ? 1 : 2)
|
&& i < length - (code[0] < 224 ? 1 : 2)
|
||||||
) {
|
) {
|
||||||
if (code[1] >= 128 && code[1] < 192) {
|
if (code[1] >= 128 && code[1] < 192) {
|
||||||
if (code[0] < 224) {
|
if (code[0] < 224) {
|
||||||
|
@ -389,16 +358,14 @@
|
||||||
);
|
);
|
||||||
i += 3;
|
i += 3;
|
||||||
} else {
|
} else {
|
||||||
throwUTF8Error(code[2], i + 2);
|
error(code[2], i + 2);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throwUTF8Error(code[1], i + 1);
|
error(code[1], i + 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throwUTF8Error(code[0], i);
|
error(code[0], i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
Loading…
Reference in a new issue