mirror of
https://github.com/cnwhy/QRCode-decode.git
synced 2026-08-12 20:31:40 +08:00
add build
This commit is contained in:
+60
@@ -0,0 +1,60 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Typescript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
|
||||||
|
.cache
|
||||||
@@ -1,44 +1,50 @@
|
|||||||
识别 QR码 图片
|
# qrcode-decode
|
||||||
> 整理自 [jsqrcode](https://github.com/LazarSoft/jsqrcode)
|
时要的二维码识别代码整理自 [jsqrcode](https://github.com/LazarSoft/jsqrcode);
|
||||||
|
|
||||||
**修改点:**
|
**修改点:**
|
||||||
1. 模块化
|
1. 模块化
|
||||||
2. 剥离识别函数,改成适合服务端使用
|
2. 剥离识别函数,改成适合服务端使用,但入参数还是保留为实现[ImageData](https://developer.mozilla.org/zh-CN/docs/Web/API/ImageData)接口的对像
|
||||||
3. 需要用其它方法把图片转为 `ImageData` 提供给解码函数
|
|
||||||
|
|
||||||
**demo web**
|
**关于 `ImageData`**
|
||||||
```html
|
> `Canvas` 中可以用 `ctx.getImageData` 方法得到;
|
||||||
<input id="file" type="file">
|
|
||||||
```
|
|
||||||
|
|
||||||
js:
|
如果你不想亲自把图片转为 `ImageData`, 根据你你的项目, 请使用这两个JS:
|
||||||
|
- `browser.js` 浏览器项目 有两个API `decodeByUrl`, `decodeByDom`
|
||||||
|
- `server.js` 服务端项目 提供两个API `decodeByPath`, `decodeByBuffer`
|
||||||
|
> 服务端当前支持 `bmp` , `jpg` , `png` , `gif` 格式;
|
||||||
|
|
||||||
|
## demo
|
||||||
|
### web
|
||||||
|
> web端最终是利用`Canvas`获取`ImageData`, 注意兼容及跨域问题
|
||||||
```js
|
```js
|
||||||
var qrcodeDecode = require('qrcode-decode');
|
var qrcodeDecode = require('qrcode-decode/browser');
|
||||||
|
// 传入二维码图片URL/dataURL
|
||||||
document.getElementById('file').onchange = function (event) {
|
qrcodeDecode.decodeByUrl(src, function (err, txt) {
|
||||||
var file = event.target.files[0];
|
if (err) { return console.log(err);}
|
||||||
new Promise(function (ok, no) {
|
alert(txt);
|
||||||
var reader = new FileReader();
|
})
|
||||||
reader.onload = evt => {
|
|
||||||
ok(evt.target.result);
|
// 传入DOM可以画到canvas的dom都可以 `img` `canvas` 'video' 等
|
||||||
};
|
var img = document.getElementById('img1');
|
||||||
reader.readAsDataURL(file);
|
qrcodeDecode.decodeByDom(img, function (err, txt) {
|
||||||
}).then((src)=>{
|
if (err) { return console.log(err);}
|
||||||
return new Promise(function(ok){
|
alert(txt);
|
||||||
var img = new Image();
|
|
||||||
img.src = src;
|
|
||||||
img.onload = function(){
|
|
||||||
var canvas = document.createElement("canvas");
|
|
||||||
var ctx = canvas.getContext('2d');
|
|
||||||
canvas.width = img.width;
|
|
||||||
canvas.height = img.height;
|
|
||||||
ctx.drawImage(img,0,0,canvas.width,canvas.height)
|
|
||||||
var imageData = ctx.getImageData(0,0,canvas.width,canvas.height)
|
|
||||||
ok(imageData);
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}).then(data => {
|
```
|
||||||
return qrcodeDecode(data);
|
|
||||||
}).then(alert);
|
### nodejs
|
||||||
}
|
> 注意: 服务器端API是以 `promise` 返回结果,你注意你的`node`版本;
|
||||||
|
```js
|
||||||
|
//解析文件
|
||||||
|
var qrcodeDecode = require('qrcode-decode/server');
|
||||||
|
qrcodeDecode.decodeByPath('xx/code.jpg').then(function(val){
|
||||||
|
console.log(val);
|
||||||
|
},console.error.bind(console))
|
||||||
|
|
||||||
|
//解析Buffer
|
||||||
|
fs.readFile(path, function (err, buffer) {
|
||||||
|
if (err) { return rej(err) }
|
||||||
|
resqrcodeDecode.decodeByBuffer(buffer);
|
||||||
|
})
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
+15
-9
@@ -1,25 +1,31 @@
|
|||||||
var qrDecode = require('./')
|
|
||||||
|
|
||||||
var decode =
|
var decode = require('./src/QRCodeDecode');
|
||||||
exports.decode = function (bom) {
|
var decodeByDom = function (dom) {
|
||||||
var canvas = document.createElement("canvas")
|
var canvas = document.createElement("canvas")
|
||||||
var ctx = canvas.getContext('2d')
|
var ctx = canvas.getContext('2d')
|
||||||
canvas.width = bom.width;
|
canvas.width = dom.width;
|
||||||
canvas.height = bom.height;
|
canvas.height = dom.height;
|
||||||
ctx.drawImage(bom, 0, 0, canvas.width, canvas.height);
|
ctx.drawImage(dom, 0, 0, canvas.width, canvas.height);
|
||||||
var data = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
var data = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
return qrDecode(data)
|
return decode(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.decodeByUrl = function (src, cb) {
|
var decodeByUrl = (src, cb) => {
|
||||||
var img = new Image();
|
var img = new Image();
|
||||||
|
img.crossOrigin="anonymous";
|
||||||
img.src = src;
|
img.src = src;
|
||||||
img.onload = function () {
|
img.onload = function () {
|
||||||
try {
|
try {
|
||||||
cb(null,decode(img));
|
cb(null, decodeByDom(img));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
cb(e);
|
cb(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
img.onerror = cb;
|
img.onerror = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
decodeByImageDate: decode,
|
||||||
|
decodeByDom: decodeByDom,
|
||||||
|
decodeByUrl: decodeByUrl
|
||||||
|
}
|
||||||
+4
-9
@@ -1,23 +1,20 @@
|
|||||||
var qrcodeDecode = require('../browser')
|
var qrcodeDecode = require('../browser')
|
||||||
console.log(1111)
|
|
||||||
document.getElementById('file').onchange = function (event) {
|
document.getElementById('file').onchange = function (event) {
|
||||||
var el = event.target;
|
var el = event.target;
|
||||||
if (!el.files.length) return;
|
if (!el.files.length) return;
|
||||||
// console.log(el.files[0]);
|
|
||||||
var file = el.files[0];
|
var file = el.files[0];
|
||||||
new Promise(function (ok, no) {
|
new Promise(function (ok, no) {
|
||||||
/* if(window.URL && window.URL.createObjectURL){ //缓存预览
|
if (window.URL && window.URL.createObjectURL) {
|
||||||
ok(window.URL.createObjectURL(file));
|
ok(window.URL.createObjectURL(file));
|
||||||
}else */
|
} else if (typeof FileReader) {
|
||||||
if (typeof FileReader) {
|
|
||||||
//urldata预览
|
|
||||||
var reader = new FileReader();
|
var reader = new FileReader();
|
||||||
reader.onload = evt => {
|
reader.onload = evt => {
|
||||||
ok(evt.target.result);
|
ok(evt.target.result);
|
||||||
};
|
};
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
} else {
|
} else {
|
||||||
ok();
|
no('浏览器不支持');
|
||||||
}
|
}
|
||||||
}).then((src) => {
|
}).then((src) => {
|
||||||
qrcodeDecode.decodeByUrl(src, function (err, txt) {
|
qrcodeDecode.decodeByUrl(src, function (err, txt) {
|
||||||
@@ -32,6 +29,4 @@ document.getElementById('file').onchange = function (event) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//el.outerHTML = el.outerHTML;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+3048
File diff suppressed because one or more lines are too long
Vendored
+7
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
+12
-6
@@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "qrcode-decode",
|
"name": "qrcode-decode",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"description": "",
|
"description": "QRCode parser/decode",
|
||||||
"main": "index.js",
|
"main": "src/QRCodeDecode.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "npm run demo",
|
"test": "node test/node.js",
|
||||||
"demo": "parcel ./demo/index.html"
|
"demo": "parcel ./demo/index.html",
|
||||||
|
"build": "bili browser.js --format umd,umd-min --banner --exports named"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -13,15 +14,20 @@
|
|||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"qrcode",
|
"qrcode",
|
||||||
"decode"
|
"decode",
|
||||||
|
"parser"
|
||||||
],
|
],
|
||||||
"author": "cnwhy",
|
"author": {
|
||||||
|
"name": "cnwhy",
|
||||||
|
"email": "w.why@163.com"
|
||||||
|
},
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/cnwhy/QRCode-decode/issues"
|
"url": "https://github.com/cnwhy/QRCode-decode/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/cnwhy/QRCode-decode#readme",
|
"homepage": "https://github.com/cnwhy/QRCode-decode#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"bili": "^3.1.2",
|
||||||
"parcel": "^1.9.7"
|
"parcel": "^1.9.7"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
var fs = require('fs')
|
var fs = require('fs')
|
||||||
var imgDecode = require('./src/imageDecode')
|
|
||||||
var imgType = require('image-type')
|
var imgType = require('image-type')
|
||||||
var qrDecode = require('./')
|
var imgDecode = require('./src/imageDecode')
|
||||||
// var jpg = require('./src/imageDecode/jpg');
|
var qrDecode = require('./src/QRCodeDecode')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过Buffer识别二维码
|
* 通过Buffer识别二维码
|
||||||
* @param {buufer} buffer 文件的Buffer
|
* @param {buufer} buffer 文件的Buffer
|
||||||
*/
|
*/
|
||||||
function decodeByBuffer(buffer,debug) {
|
function decodeByBuffer(buffer, options) {
|
||||||
|
options || (options = {});
|
||||||
|
var debug = options.debug;
|
||||||
var type;
|
var type;
|
||||||
return new Promise(function (res, rej) {
|
return new Promise(function (res, rej) {
|
||||||
type = (imgType(buffer) || {}).ext;
|
type = (imgType(buffer) || {}).ext;
|
||||||
@@ -34,6 +35,7 @@ function decodeByBuffer(buffer,debug) {
|
|||||||
throw 'not image!'
|
throw 'not image!'
|
||||||
}
|
}
|
||||||
}).then(function (imageData) {
|
}).then(function (imageData) {
|
||||||
|
//可能有多帧图片处理
|
||||||
if (type == 'gif' || type == 'png') {
|
if (type == 'gif' || type == 'png') {
|
||||||
return new Promise(function (res, rej) {
|
return new Promise(function (res, rej) {
|
||||||
var errList = [];
|
var errList = [];
|
||||||
@@ -41,26 +43,21 @@ function decodeByBuffer(buffer,debug) {
|
|||||||
var onerr = function (e) {
|
var onerr = function (e) {
|
||||||
errList.push(e);
|
errList.push(e);
|
||||||
if (errList.length < images.length) return;
|
if (errList.length < images.length) return;
|
||||||
rej('解码失败!')
|
rej(errList);
|
||||||
}
|
}
|
||||||
debug && console.log('length',imageData.length)
|
debug && console.log('gif_length: ', imageData.length)
|
||||||
if (imageData.length <= 0) {
|
if (imageData.length <= 0) {
|
||||||
rej('解码失败!')
|
rej('解码失败!')
|
||||||
} else if (imageData.length > 3) {
|
} else if (imageData.length > 5 && !options.allFrames) {
|
||||||
|
var l = 1, i = 0, sp;
|
||||||
images = [];
|
images = [];
|
||||||
var l = 1;
|
while (Math.pow(2, ++l) < imageData.length) {s}
|
||||||
var i = 0;
|
sp = (imageData.length - 1) / l;
|
||||||
while ( Math.pow(2,++l) < imageData.length){
|
|
||||||
}
|
|
||||||
// console.log('l:',l)
|
|
||||||
var sp = (imageData.length-1)/l;
|
|
||||||
// console.log('sp:',sp)
|
|
||||||
do {
|
do {
|
||||||
// console.log(Math.floor(i*sp));
|
|
||||||
images.push(imageData[Math.floor(i * sp)])
|
images.push(imageData[Math.floor(i * sp)])
|
||||||
} while (i++ < l)
|
} while (i++ < l)
|
||||||
}
|
}
|
||||||
debug && console.log(images.length)
|
debug && console.log('gif_decode_length: ',images.length)
|
||||||
images.forEach(function (v) {
|
images.forEach(function (v) {
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
try {
|
try {
|
||||||
@@ -81,14 +78,17 @@ function decodeByBuffer(buffer,debug) {
|
|||||||
* 识别二维码图片文件
|
* 识别二维码图片文件
|
||||||
* @param {String} path 文件路径
|
* @param {String} path 文件路径
|
||||||
*/
|
*/
|
||||||
exports.decodeByPath = function (path) {
|
function decodeByPath(path,options) {
|
||||||
return new Promise(function (res, rej) {
|
return new Promise(function (res, rej) {
|
||||||
fs.readFile(path, function (err, buffer) {
|
fs.readFile(path, function (err, buffer) {
|
||||||
if (err) { return rej(err) }
|
if (err) { return rej(err) }
|
||||||
res(decodeByBuffer(buffer));
|
res(decodeByBuffer(buffer,options));
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.decodeByBuffer = decodeByBuffer;
|
module.exports = {
|
||||||
exports.decodeByImageData = qrDecode;
|
decodeByBuffer: decodeByBuffer,
|
||||||
|
decodeByPath: decodeByPath,
|
||||||
|
decodeByImageData: qrDecode
|
||||||
|
}
|
||||||
@@ -1,525 +0,0 @@
|
|||||||
//https://github.com/shachaf/jsgif
|
|
||||||
|
|
||||||
// Generic functions
|
|
||||||
var bitsToNum = function (ba) {
|
|
||||||
return ba.reduce(function (s, n) { return s * 2 + n; }, 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
var byteToBitArr = function (bite) {
|
|
||||||
var a = [];
|
|
||||||
for (var i = 7; i >= 0; i--) {
|
|
||||||
a.push(!!(bite & (1 << i)));
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Stream
|
|
||||||
/**
|
|
||||||
* @constructor
|
|
||||||
*/ // Make compiler happy.
|
|
||||||
var Stream = function (data) {
|
|
||||||
|
|
||||||
this.data = data;
|
|
||||||
var len = this.data.length;
|
|
||||||
var pos = 0;
|
|
||||||
|
|
||||||
this.readByte = function () {
|
|
||||||
if (pos >= len) {
|
|
||||||
throw new Error('Attempted to read past end of stream.');
|
|
||||||
}
|
|
||||||
// return data.charCodeAt(pos++) & 0xFF;
|
|
||||||
// var bit = data[pos++];
|
|
||||||
// return String.fromCharCode(bit).charCodeAt(0) & 0xff;
|
|
||||||
return data[pos++];
|
|
||||||
};
|
|
||||||
|
|
||||||
this.readBytes = function (n) {
|
|
||||||
var bytes = [];
|
|
||||||
for (var i = 0; i < n; i++) {
|
|
||||||
bytes.push(this.readByte());
|
|
||||||
}
|
|
||||||
return bytes;
|
|
||||||
// var start = pos
|
|
||||||
// pos += n;
|
|
||||||
// if(start>= len || pos > len){
|
|
||||||
// throw new Error('Attempted to read past end of stream.');
|
|
||||||
// }
|
|
||||||
// return [].slice.call(data,start,pos)
|
|
||||||
};
|
|
||||||
|
|
||||||
this.read = function (n) {
|
|
||||||
var s = '';
|
|
||||||
for (var i = 0; i < n; i++) {
|
|
||||||
s += String.fromCharCode(this.readByte());
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.readUnsigned = function () { // Little-endian.
|
|
||||||
var a = this.readBytes(2);
|
|
||||||
return (a[1] << 8) + a[0];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var lzwDecode = function (minCodeSize, data, gct,GCE) {
|
|
||||||
// TODO: Now that the GIF parser is a bit different, maybe this should get an array of bytes instead of a String?
|
|
||||||
var pos = 0; // Maybe this streaming thing should be merged with the Stream?
|
|
||||||
|
|
||||||
var readCode = function (size) {
|
|
||||||
var code = 0;
|
|
||||||
for (var i = 0; i < size; i++) {
|
|
||||||
if (data[pos >> 3] & (1 << (pos & 7))) {
|
|
||||||
code |= 1 << i;
|
|
||||||
}
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
return code;
|
|
||||||
};
|
|
||||||
|
|
||||||
var output = [];
|
|
||||||
var imageData = [];
|
|
||||||
|
|
||||||
var clearCode = 1 << minCodeSize;
|
|
||||||
var eoiCode = clearCode + 1;
|
|
||||||
|
|
||||||
var codeSize = minCodeSize + 1;
|
|
||||||
|
|
||||||
var dict = [];
|
|
||||||
|
|
||||||
var clear = function () {
|
|
||||||
dict = [];
|
|
||||||
codeSize = minCodeSize + 1;
|
|
||||||
for (var i = 0; i < clearCode; i++) {
|
|
||||||
dict[i] = [i];
|
|
||||||
}
|
|
||||||
dict[clearCode] = [];
|
|
||||||
dict[eoiCode] = null;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
var code;
|
|
||||||
var last;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
last = code;
|
|
||||||
code = readCode(codeSize);
|
|
||||||
|
|
||||||
if (code === clearCode) {
|
|
||||||
clear();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (code === eoiCode) break;
|
|
||||||
|
|
||||||
if (code < dict.length) {
|
|
||||||
if (last !== clearCode) {
|
|
||||||
dict.push(dict[last].concat(dict[code][0]));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (code !== dict.length) throw new Error('Invalid LZW code.');
|
|
||||||
// try{
|
|
||||||
dict.push(dict[last].concat(dict[last][0]));
|
|
||||||
|
|
||||||
// }catch(e){
|
|
||||||
// console.log(minCodeSize,data);
|
|
||||||
// console.log(dict,last);
|
|
||||||
// throw e
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
var inItem = dict[code];
|
|
||||||
// output.push.apply(output, inItem);
|
|
||||||
|
|
||||||
inItem.forEach(function(v){
|
|
||||||
// output.push(v);
|
|
||||||
var rgb = gct[v]
|
|
||||||
imageData.push(rgb[0],rgb[1],rgb[2],GCE.transparencyIndex == v ? 0 : 255)
|
|
||||||
})
|
|
||||||
|
|
||||||
// imageData.push.apply(imageData, inItem.map(function(v){
|
|
||||||
// var c = gct[v];
|
|
||||||
// var rgba = [c[0],c[1],c[2],GCE.transparencyIndex == v ? 0 : 255]
|
|
||||||
// //rgba.push(GCE.transparencyIndex == v ? 0 : 255)
|
|
||||||
// // return color;
|
|
||||||
// return rgba;
|
|
||||||
// // return color.slice(0).push(GCE.transparencyIndex == v ? 0 : 255)
|
|
||||||
// }))
|
|
||||||
|
|
||||||
if (dict.length === (1 << codeSize) && codeSize < 12) {
|
|
||||||
// If we're at the last code and codeSize is 12, the next code will be a clearCode, and it'll be 12 bits long.
|
|
||||||
codeSize++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// I don't know if this is technically an error, but some GIFs do it.
|
|
||||||
//if (Math.ceil(pos / 8) !== data.length) throw new Error('Extraneous LZW bytes.');
|
|
||||||
return [output,imageData];
|
|
||||||
};
|
|
||||||
|
|
||||||
// The actual parsing; returns an object with properties.
|
|
||||||
var parseGIF = function (st, handler) {
|
|
||||||
handler || (handler = {});
|
|
||||||
var GCE,gct;
|
|
||||||
// LZW (GIF-specific)
|
|
||||||
var parseCT = function (entries) { // Each entry is 3 bytes, for RGB.
|
|
||||||
var ct = [];
|
|
||||||
for (var i = 0; i < entries; i++) {
|
|
||||||
ct.push(st.readBytes(3));
|
|
||||||
}
|
|
||||||
return ct;
|
|
||||||
};
|
|
||||||
|
|
||||||
var readSubBlocks = function () {
|
|
||||||
var size, data;
|
|
||||||
data = '';
|
|
||||||
do {
|
|
||||||
size = st.readByte();
|
|
||||||
data += st.read(size);
|
|
||||||
} while (size !== 0);
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
var readByteBlocks = function(){
|
|
||||||
var size, data;
|
|
||||||
data = [];
|
|
||||||
do {
|
|
||||||
size = st.readByte();
|
|
||||||
data.push.apply(data,st.readBytes(size))
|
|
||||||
// data = data.concat(st.readBytes(size));
|
|
||||||
} while (size !== 0);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
var parseHeader = function () {
|
|
||||||
var hdr = {};
|
|
||||||
hdr.sig = st.read(3);
|
|
||||||
hdr.ver = st.read(3);
|
|
||||||
if (hdr.sig !== 'GIF') throw new Error('Not a GIF file.'); // XXX: This should probably be handled more nicely.
|
|
||||||
|
|
||||||
hdr.width = st.readUnsigned();
|
|
||||||
hdr.height = st.readUnsigned();
|
|
||||||
|
|
||||||
var bits = byteToBitArr(st.readByte());
|
|
||||||
hdr.gctFlag = bits.shift();
|
|
||||||
hdr.colorRes = bitsToNum(bits.splice(0, 3));
|
|
||||||
hdr.sorted = bits.shift();
|
|
||||||
hdr.gctSize = bitsToNum(bits.splice(0, 3));
|
|
||||||
|
|
||||||
hdr.bgColor = st.readByte();
|
|
||||||
hdr.pixelAspectRatio = st.readByte(); // if not 0, aspectRatio = (pixelAspectRatio + 15) / 64
|
|
||||||
|
|
||||||
if (hdr.gctFlag) {
|
|
||||||
gct = hdr.gct = parseCT(1 << (hdr.gctSize + 1));
|
|
||||||
}
|
|
||||||
handler.hdr && handler.hdr(hdr);
|
|
||||||
};
|
|
||||||
|
|
||||||
var parseExt = function (block) {
|
|
||||||
var parseGCExt = function (block) {
|
|
||||||
var blockSize = st.readByte(); // Always 4
|
|
||||||
|
|
||||||
var bits = byteToBitArr(st.readByte());
|
|
||||||
block.reserved = bits.splice(0, 3); // Reserved; should be 000.
|
|
||||||
block.disposalMethod = bitsToNum(bits.splice(0, 3));
|
|
||||||
block.userInput = bits.shift();
|
|
||||||
block.transparencyGiven = bits.shift();
|
|
||||||
|
|
||||||
block.delayTime = st.readUnsigned();
|
|
||||||
|
|
||||||
block.transparencyIndex = st.readByte();
|
|
||||||
|
|
||||||
block.terminator = st.readByte();
|
|
||||||
GCE = block;
|
|
||||||
handler.gce && handler.gce(block);
|
|
||||||
};
|
|
||||||
|
|
||||||
var parseComExt = function (block) {
|
|
||||||
block.comment = readSubBlocks();
|
|
||||||
handler.com && handler.com(block);
|
|
||||||
};
|
|
||||||
|
|
||||||
var parsePTExt = function (block) {
|
|
||||||
// No one *ever* uses this. If you use it, deal with parsing it yourself.
|
|
||||||
var blockSize = st.readByte(); // Always 12
|
|
||||||
block.ptHeader = st.readBytes(12);
|
|
||||||
block.ptData = readSubBlocks();
|
|
||||||
handler.pte && handler.pte(block);
|
|
||||||
};
|
|
||||||
|
|
||||||
var parseAppExt = function (block) {
|
|
||||||
var parseNetscapeExt = function (block) {
|
|
||||||
var blockSize = st.readByte(); // Always 3
|
|
||||||
block.unknown = st.readByte(); // ??? Always 1? What is this?
|
|
||||||
block.iterations = st.readUnsigned();
|
|
||||||
block.terminator = st.readByte();
|
|
||||||
handler.app && handler.app.NETSCAPE && handler.app.NETSCAPE(block);
|
|
||||||
};
|
|
||||||
|
|
||||||
var parseUnknownAppExt = function (block) {
|
|
||||||
block.appData = readSubBlocks();
|
|
||||||
// FIXME: This won't work if a handler wants to match on any identifier.
|
|
||||||
handler.app && handler.app[block.identifier] && handler.app[block.identifier](block);
|
|
||||||
};
|
|
||||||
|
|
||||||
var blockSize = st.readByte(); // Always 11
|
|
||||||
block.identifier = st.read(8);
|
|
||||||
block.authCode = st.read(3);
|
|
||||||
switch (block.identifier) {
|
|
||||||
case 'NETSCAPE':
|
|
||||||
parseNetscapeExt(block);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
parseUnknownAppExt(block);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var parseUnknownExt = function (block) {
|
|
||||||
block.data = readSubBlocks();
|
|
||||||
handler.unknown && handler.unknown(block);
|
|
||||||
};
|
|
||||||
|
|
||||||
block.label = st.readByte();
|
|
||||||
switch (block.label) {
|
|
||||||
case 0xF9:
|
|
||||||
block.extType = 'gce';
|
|
||||||
parseGCExt(block);
|
|
||||||
break;
|
|
||||||
case 0xFE:
|
|
||||||
block.extType = 'com';
|
|
||||||
parseComExt(block);
|
|
||||||
break;
|
|
||||||
case 0x01:
|
|
||||||
block.extType = 'pte';
|
|
||||||
parsePTExt(block);
|
|
||||||
break;
|
|
||||||
case 0xFF:
|
|
||||||
block.extType = 'app';
|
|
||||||
parseAppExt(block);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
block.extType = 'unknown';
|
|
||||||
parseUnknownExt(block);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var parseImg = function (img) {
|
|
||||||
var deinterlace = function (pixels, width) {
|
|
||||||
// Of course this defeats the purpose of interlacing. And it's *probably*
|
|
||||||
// the least efficient way it's ever been implemented. But nevertheless...
|
|
||||||
|
|
||||||
var newPixels = new Array(pixels.length);
|
|
||||||
var rows = pixels.length / width;
|
|
||||||
var cpRow = function (toRow, fromRow) {
|
|
||||||
var fromPixels = pixels.slice(fromRow * width, (fromRow + 1) * width);
|
|
||||||
newPixels.splice.apply(newPixels, [toRow * width, width].concat(fromPixels));
|
|
||||||
};
|
|
||||||
|
|
||||||
// See appendix E.
|
|
||||||
var offsets = [0, 4, 2, 1];
|
|
||||||
var steps = [8, 8, 4, 2];
|
|
||||||
|
|
||||||
var fromRow = 0;
|
|
||||||
for (var pass = 0; pass < 4; pass++) {
|
|
||||||
for (var toRow = offsets[pass]; toRow < rows; toRow += steps[pass]) {
|
|
||||||
cpRow(toRow, fromRow)
|
|
||||||
fromRow++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return newPixels;
|
|
||||||
};
|
|
||||||
|
|
||||||
img.leftPos = st.readUnsigned();
|
|
||||||
img.topPos = st.readUnsigned();
|
|
||||||
img.width = st.readUnsigned();
|
|
||||||
img.height = st.readUnsigned();
|
|
||||||
|
|
||||||
var bits = byteToBitArr(st.readByte());
|
|
||||||
img.lctFlag = bits.shift();
|
|
||||||
img.interlaced = bits.shift();
|
|
||||||
img.sorted = bits.shift();
|
|
||||||
img.reserved = bits.splice(0, 2);
|
|
||||||
img.lctSize = bitsToNum(bits.splice(0, 3));
|
|
||||||
|
|
||||||
if (img.lctFlag) {
|
|
||||||
img.lct = parseCT(1 << (img.lctSize + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
img.lzwMinCodeSize = st.readByte();
|
|
||||||
|
|
||||||
// var lzwData = readSubBlocks();
|
|
||||||
var lzwData = readByteBlocks();
|
|
||||||
var lzwd = lzwDecode(img.lzwMinCodeSize, lzwData, gct,GCE);
|
|
||||||
img.pixels = lzwd[0];
|
|
||||||
img.data = lzwd[1];
|
|
||||||
// console.log(img.imageData)
|
|
||||||
if (img.interlaced) { // Move
|
|
||||||
console.log(img.pixels)
|
|
||||||
img.pixels = deinterlace(img.pixels, img.width);
|
|
||||||
console.log(img.pixels)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
handler.img && handler.img(img);
|
|
||||||
};
|
|
||||||
|
|
||||||
var parseBlock = function () {
|
|
||||||
var block = {};
|
|
||||||
block.sentinel = st.readByte();
|
|
||||||
|
|
||||||
switch (String.fromCharCode(block.sentinel)) { // For ease of matching
|
|
||||||
case '!':
|
|
||||||
block.type = 'ext';
|
|
||||||
parseExt(block);
|
|
||||||
break;
|
|
||||||
case ',':
|
|
||||||
block.type = 'img';
|
|
||||||
parseImg(block);
|
|
||||||
break;
|
|
||||||
case ';':
|
|
||||||
block.type = 'eof';
|
|
||||||
handler.eof && handler.eof(block);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error('Unknown block: 0x' + block.sentinel.toString(16)); // TODO: Pad this with a 0.
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (block.type !== 'eof') setTimeout(parseBlock,0);
|
|
||||||
if (block.type !== 'eof') parseBlock();
|
|
||||||
};
|
|
||||||
|
|
||||||
var parse = function () {
|
|
||||||
parseHeader();
|
|
||||||
parseBlock();
|
|
||||||
// setTimeout(parseBlock, 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
parse();
|
|
||||||
};
|
|
||||||
|
|
||||||
// BEGIN_NON_BOOKMARKLET_CODE
|
|
||||||
exports.Stream = Stream;
|
|
||||||
exports.parseGIF = parseGIF;
|
|
||||||
exports.decode = function (buffer) {
|
|
||||||
var gifData = { images: [] };
|
|
||||||
var gct;
|
|
||||||
var GCE;
|
|
||||||
var log = console.log;
|
|
||||||
|
|
||||||
// var showBool = function (b) {
|
|
||||||
// return b ? 'yes' : 'no';
|
|
||||||
// };
|
|
||||||
|
|
||||||
// var showColor = function (rgb) {
|
|
||||||
// // FIXME When I have an Internet connection.
|
|
||||||
// var showHex = function (n) { // Two-digit code.
|
|
||||||
// var hexChars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
|
|
||||||
// return hexChars[(n >>> 4) & 0xF] + hexChars[n & 0xF];
|
|
||||||
// };
|
|
||||||
// return '#' + showHex(rgb[0]) + showHex(rgb[1]) + showHex(rgb[2]);
|
|
||||||
// };
|
|
||||||
|
|
||||||
// var showDisposalMethod = function (dm) {
|
|
||||||
// var map = {
|
|
||||||
// 0: 'None',
|
|
||||||
// 1: 'Do not dispose',
|
|
||||||
// 2: 'Restore to background',
|
|
||||||
// 3: 'Restore to previous'
|
|
||||||
// };
|
|
||||||
// return map[dm] || 'Unknown';
|
|
||||||
// }
|
|
||||||
|
|
||||||
//尺寸 背景色 调色板 等
|
|
||||||
var doHdr = function (hdr) {
|
|
||||||
// log('Header:');
|
|
||||||
// log(' Version: %s', hdr.ver);
|
|
||||||
// log(' Size: %dx%d', hdr.width, hdr.height);
|
|
||||||
// log(' GCT? %s%s', showBool(hdr.gctFlag), hdr.gctFlag ? ' (' + hdr.gct.length + ' entries)' : '');
|
|
||||||
// log(' Color resolution: %d', hdr.colorRes);
|
|
||||||
// log(' Sorted? %s', showBool(hdr.sorted));
|
|
||||||
// log(' Background color: %s (%d)', hdr.gctFlag ? showColor(hdr.bgColor) : 'no GCT', hdr.bgColor);
|
|
||||||
// log(' Pixel aspect ratio: %d FIXME', hdr.pixelAspectRatio);
|
|
||||||
gct = hdr.gct;
|
|
||||||
gifData.width = hdr.width;
|
|
||||||
gifData.height = hdr.height;
|
|
||||||
};
|
|
||||||
//当前帧的非颜色信息 (透明色,延时 等))
|
|
||||||
var doGCE = function (gce) {
|
|
||||||
// log('GCE:');
|
|
||||||
// log(' Disposal method: %d (%s)', gce.disposalMethod, showDisposalMethod(gce.disposalMethod));
|
|
||||||
// log(' User input expected? %s', showBool(gce.userInput));
|
|
||||||
// log(' Transparency given? %s%s', showBool(gce.transparencyGiven),
|
|
||||||
// gce.transparencyGiven ? ' (index: ' + gce.transparencyIndex + ')' : '');
|
|
||||||
// log(' Delay time: %d', gce.delayTime);
|
|
||||||
GCE = gce;
|
|
||||||
};
|
|
||||||
//当前帧的图片图像信息
|
|
||||||
// var doimgTime = 0;
|
|
||||||
var doImg = function (img) {
|
|
||||||
// log('Image descriptor:');
|
|
||||||
// log(Object.assign({},img,{'pixels':Math.max.apply(Math,img.pixels)}));
|
|
||||||
// log(' Geometry: %dx%d+%d+%d', img.width, img.height, img.leftPos, img.topPos);
|
|
||||||
// log(' LCT? %s%s', showBool(img.lctFlag), img.lctFlag ? ' (' + img.lct.length + ' entries)' : '');
|
|
||||||
// log(' Interlaced? %s', showBool(img.interlaced));
|
|
||||||
// log(' %d pixels', img.pixels.length);
|
|
||||||
// var t1 = Date.now();
|
|
||||||
// var data = [];
|
|
||||||
// console.log(data.length , img.width * img.height)
|
|
||||||
var imageData = {
|
|
||||||
left: img.leftPos,
|
|
||||||
top: img.topPos,
|
|
||||||
width: img.width,
|
|
||||||
height: img.height,
|
|
||||||
// data: data,
|
|
||||||
data: img.data,
|
|
||||||
}
|
|
||||||
// img.pixels.forEach(function (v) {
|
|
||||||
// var color = gct[v];
|
|
||||||
// data.push.apply(data, color.concat(GCE.transparencyIndex == v ? 0 : 255));
|
|
||||||
// })
|
|
||||||
// console.log(data.length)
|
|
||||||
// console.log(img.imageData.length)
|
|
||||||
gifData.images.push(imageData);
|
|
||||||
// doimgTime += Date.now() - t1;
|
|
||||||
};
|
|
||||||
|
|
||||||
var doNetscape = function (block) {
|
|
||||||
// log('Netscape application extension:');
|
|
||||||
// log(' Iterations: %d%s', block.iterations, block.iterations === 0 ? ' (infinite)' : '');
|
|
||||||
};
|
|
||||||
|
|
||||||
var doCom = function (com) {
|
|
||||||
// log('Comment extension:');
|
|
||||||
// log(' Comment: [31m%s[0m', com.comment);
|
|
||||||
};
|
|
||||||
|
|
||||||
var doEOF = function (eof) {
|
|
||||||
// log('EOF:');
|
|
||||||
// log(eof);
|
|
||||||
};
|
|
||||||
|
|
||||||
var doUnknownApp = function (block) {
|
|
||||||
// log(block)
|
|
||||||
};
|
|
||||||
|
|
||||||
var doUnknownExt = function (block) {
|
|
||||||
// log(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
var handler = {
|
|
||||||
hdr: doHdr,
|
|
||||||
img: doImg,
|
|
||||||
gce: doGCE,
|
|
||||||
com: doCom,
|
|
||||||
app: {
|
|
||||||
NETSCAPE: doNetscape,
|
|
||||||
unknown: doUnknownApp
|
|
||||||
},
|
|
||||||
eof: doEOF
|
|
||||||
};
|
|
||||||
|
|
||||||
var st = new Stream(buffer);
|
|
||||||
parseGIF(st, handler);
|
|
||||||
// console.log('doimgTime:',doimgTime)
|
|
||||||
return gifData;
|
|
||||||
}
|
|
||||||
+80
-22
@@ -18,15 +18,19 @@ var byteToBitArr = function (bite) {
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/ // Make compiler happy.
|
*/ // Make compiler happy.
|
||||||
var Stream = function (data) {
|
var Stream = function (data) {
|
||||||
|
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.len = this.data.length;
|
var len = this.data.length;
|
||||||
this.pos = 0;
|
var pos = 0;
|
||||||
|
|
||||||
this.readByte = function () {
|
this.readByte = function () {
|
||||||
if (this.pos >= this.data.length) {
|
if (pos >= len) {
|
||||||
throw new Error('Attempted to read past end of stream.');
|
throw new Error('Attempted to read past end of stream.');
|
||||||
}
|
}
|
||||||
return data.charCodeAt(this.pos++) & 0xFF;
|
// return data.charCodeAt(pos++) & 0xFF;
|
||||||
|
// var bit = data[pos++];
|
||||||
|
// return String.fromCharCode(bit).charCodeAt(0) & 0xff;
|
||||||
|
return data[pos++];
|
||||||
};
|
};
|
||||||
|
|
||||||
this.readBytes = function (n) {
|
this.readBytes = function (n) {
|
||||||
@@ -35,6 +39,12 @@ var Stream = function (data) {
|
|||||||
bytes.push(this.readByte());
|
bytes.push(this.readByte());
|
||||||
}
|
}
|
||||||
return bytes;
|
return bytes;
|
||||||
|
// var start = pos
|
||||||
|
// pos += n;
|
||||||
|
// if(start>= len || pos > len){
|
||||||
|
// throw new Error('Attempted to read past end of stream.');
|
||||||
|
// }
|
||||||
|
// return [].slice.call(data,start,pos)
|
||||||
};
|
};
|
||||||
|
|
||||||
this.read = function (n) {
|
this.read = function (n) {
|
||||||
@@ -51,14 +61,14 @@ var Stream = function (data) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
var lzwDecode = function (minCodeSize, data) {
|
var lzwDecode = function (minCodeSize, data, gct,GCE) {
|
||||||
// TODO: Now that the GIF parser is a bit different, maybe this should get an array of bytes instead of a String?
|
// TODO: Now that the GIF parser is a bit different, maybe this should get an array of bytes instead of a String?
|
||||||
var pos = 0; // Maybe this streaming thing should be merged with the Stream?
|
var pos = 0; // Maybe this streaming thing should be merged with the Stream?
|
||||||
|
|
||||||
var readCode = function (size) {
|
var readCode = function (size) {
|
||||||
var code = 0;
|
var code = 0;
|
||||||
for (var i = 0; i < size; i++) {
|
for (var i = 0; i < size; i++) {
|
||||||
if (data.charCodeAt(pos >> 3) & (1 << (pos & 7))) {
|
if (data[pos >> 3] & (1 << (pos & 7))) {
|
||||||
code |= 1 << i;
|
code |= 1 << i;
|
||||||
}
|
}
|
||||||
pos++;
|
pos++;
|
||||||
@@ -67,6 +77,7 @@ var lzwDecode = function (minCodeSize, data) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var output = [];
|
var output = [];
|
||||||
|
var imageData = [];
|
||||||
|
|
||||||
var clearCode = 1 << minCodeSize;
|
var clearCode = 1 << minCodeSize;
|
||||||
var eoiCode = clearCode + 1;
|
var eoiCode = clearCode + 1;
|
||||||
@@ -105,9 +116,32 @@ var lzwDecode = function (minCodeSize, data) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (code !== dict.length) throw new Error('Invalid LZW code.');
|
if (code !== dict.length) throw new Error('Invalid LZW code.');
|
||||||
|
// try{
|
||||||
dict.push(dict[last].concat(dict[last][0]));
|
dict.push(dict[last].concat(dict[last][0]));
|
||||||
|
|
||||||
|
// }catch(e){
|
||||||
|
// console.log(minCodeSize,data);
|
||||||
|
// console.log(dict,last);
|
||||||
|
// throw e
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
output.push.apply(output, dict[code]);
|
var inItem = dict[code];
|
||||||
|
// output.push.apply(output, inItem);
|
||||||
|
|
||||||
|
inItem.forEach(function(v){
|
||||||
|
// output.push(v);
|
||||||
|
var rgb = gct[v]
|
||||||
|
imageData.push(rgb[0],rgb[1],rgb[2],GCE.transparencyIndex == v ? 0 : 255)
|
||||||
|
})
|
||||||
|
|
||||||
|
// imageData.push.apply(imageData, inItem.map(function(v){
|
||||||
|
// var c = gct[v];
|
||||||
|
// var rgba = [c[0],c[1],c[2],GCE.transparencyIndex == v ? 0 : 255]
|
||||||
|
// //rgba.push(GCE.transparencyIndex == v ? 0 : 255)
|
||||||
|
// // return color;
|
||||||
|
// return rgba;
|
||||||
|
// // return color.slice(0).push(GCE.transparencyIndex == v ? 0 : 255)
|
||||||
|
// }))
|
||||||
|
|
||||||
if (dict.length === (1 << codeSize) && codeSize < 12) {
|
if (dict.length === (1 << codeSize) && codeSize < 12) {
|
||||||
// If we're at the last code and codeSize is 12, the next code will be a clearCode, and it'll be 12 bits long.
|
// If we're at the last code and codeSize is 12, the next code will be a clearCode, and it'll be 12 bits long.
|
||||||
@@ -117,13 +151,13 @@ var lzwDecode = function (minCodeSize, data) {
|
|||||||
|
|
||||||
// I don't know if this is technically an error, but some GIFs do it.
|
// I don't know if this is technically an error, but some GIFs do it.
|
||||||
//if (Math.ceil(pos / 8) !== data.length) throw new Error('Extraneous LZW bytes.');
|
//if (Math.ceil(pos / 8) !== data.length) throw new Error('Extraneous LZW bytes.');
|
||||||
return output;
|
return [output,imageData];
|
||||||
};
|
};
|
||||||
|
|
||||||
// The actual parsing; returns an object with properties.
|
// The actual parsing; returns an object with properties.
|
||||||
var parseGIF = function (st, handler) {
|
var parseGIF = function (st, handler) {
|
||||||
handler || (handler = {});
|
handler || (handler = {});
|
||||||
|
var GCE,gct;
|
||||||
// LZW (GIF-specific)
|
// LZW (GIF-specific)
|
||||||
var parseCT = function (entries) { // Each entry is 3 bytes, for RGB.
|
var parseCT = function (entries) { // Each entry is 3 bytes, for RGB.
|
||||||
var ct = [];
|
var ct = [];
|
||||||
@@ -143,6 +177,17 @@ var parseGIF = function (st, handler) {
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var readByteBlocks = function(){
|
||||||
|
var size, data;
|
||||||
|
data = [];
|
||||||
|
do {
|
||||||
|
size = st.readByte();
|
||||||
|
data.push.apply(data,st.readBytes(size))
|
||||||
|
// data = data.concat(st.readBytes(size));
|
||||||
|
} while (size !== 0);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
var parseHeader = function () {
|
var parseHeader = function () {
|
||||||
var hdr = {};
|
var hdr = {};
|
||||||
hdr.sig = st.read(3);
|
hdr.sig = st.read(3);
|
||||||
@@ -162,7 +207,7 @@ var parseGIF = function (st, handler) {
|
|||||||
hdr.pixelAspectRatio = st.readByte(); // if not 0, aspectRatio = (pixelAspectRatio + 15) / 64
|
hdr.pixelAspectRatio = st.readByte(); // if not 0, aspectRatio = (pixelAspectRatio + 15) / 64
|
||||||
|
|
||||||
if (hdr.gctFlag) {
|
if (hdr.gctFlag) {
|
||||||
hdr.gct = parseCT(1 << (hdr.gctSize + 1));
|
gct = hdr.gct = parseCT(1 << (hdr.gctSize + 1));
|
||||||
}
|
}
|
||||||
handler.hdr && handler.hdr(hdr);
|
handler.hdr && handler.hdr(hdr);
|
||||||
};
|
};
|
||||||
@@ -182,7 +227,7 @@ var parseGIF = function (st, handler) {
|
|||||||
block.transparencyIndex = st.readByte();
|
block.transparencyIndex = st.readByte();
|
||||||
|
|
||||||
block.terminator = st.readByte();
|
block.terminator = st.readByte();
|
||||||
|
GCE = block;
|
||||||
handler.gce && handler.gce(block);
|
handler.gce && handler.gce(block);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -302,12 +347,17 @@ var parseGIF = function (st, handler) {
|
|||||||
|
|
||||||
img.lzwMinCodeSize = st.readByte();
|
img.lzwMinCodeSize = st.readByte();
|
||||||
|
|
||||||
var lzwData = readSubBlocks();
|
// var lzwData = readSubBlocks();
|
||||||
|
var lzwData = readByteBlocks();
|
||||||
img.pixels = lzwDecode(img.lzwMinCodeSize, lzwData);
|
var lzwd = lzwDecode(img.lzwMinCodeSize, lzwData, gct,GCE);
|
||||||
|
img.pixels = lzwd[0];
|
||||||
|
img.data = lzwd[1];
|
||||||
|
// console.log(img.imageData)
|
||||||
if (img.interlaced) { // Move
|
if (img.interlaced) { // Move
|
||||||
|
console.log(img.pixels)
|
||||||
img.pixels = deinterlace(img.pixels, img.width);
|
img.pixels = deinterlace(img.pixels, img.width);
|
||||||
|
console.log(img.pixels)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.img && handler.img(img);
|
handler.img && handler.img(img);
|
||||||
@@ -404,6 +454,7 @@ exports.decode = function (buffer) {
|
|||||||
GCE = gce;
|
GCE = gce;
|
||||||
};
|
};
|
||||||
//当前帧的图片图像信息
|
//当前帧的图片图像信息
|
||||||
|
// var doimgTime = 0;
|
||||||
var doImg = function (img) {
|
var doImg = function (img) {
|
||||||
// log('Image descriptor:');
|
// log('Image descriptor:');
|
||||||
// log(Object.assign({},img,{'pixels':Math.max.apply(Math,img.pixels)}));
|
// log(Object.assign({},img,{'pixels':Math.max.apply(Math,img.pixels)}));
|
||||||
@@ -411,19 +462,25 @@ exports.decode = function (buffer) {
|
|||||||
// log(' LCT? %s%s', showBool(img.lctFlag), img.lctFlag ? ' (' + img.lct.length + ' entries)' : '');
|
// log(' LCT? %s%s', showBool(img.lctFlag), img.lctFlag ? ' (' + img.lct.length + ' entries)' : '');
|
||||||
// log(' Interlaced? %s', showBool(img.interlaced));
|
// log(' Interlaced? %s', showBool(img.interlaced));
|
||||||
// log(' %d pixels', img.pixels.length);
|
// log(' %d pixels', img.pixels.length);
|
||||||
var data = [];
|
// var t1 = Date.now();
|
||||||
|
// var data = [];
|
||||||
|
// console.log(data.length , img.width * img.height)
|
||||||
var imageData = {
|
var imageData = {
|
||||||
left: img.leftPos,
|
left: img.leftPos,
|
||||||
top: img.topPos,
|
top: img.topPos,
|
||||||
width: img.width,
|
width: img.width,
|
||||||
height: img.height,
|
height: img.height,
|
||||||
data: data
|
// data: data,
|
||||||
|
data: img.data,
|
||||||
}
|
}
|
||||||
img.pixels.forEach(function (v) {
|
// img.pixels.forEach(function (v) {
|
||||||
var color = gct[v];
|
// var color = gct[v];
|
||||||
data.push.apply(data, color.concat(GCE.transparencyIndex == v ? 0 : 255));
|
// data.push.apply(data, color.concat(GCE.transparencyIndex == v ? 0 : 255));
|
||||||
})
|
// })
|
||||||
|
// console.log(data.length)
|
||||||
|
// console.log(img.imageData.length)
|
||||||
gifData.images.push(imageData);
|
gifData.images.push(imageData);
|
||||||
|
// doimgTime += Date.now() - t1;
|
||||||
};
|
};
|
||||||
|
|
||||||
var doNetscape = function (block) {
|
var doNetscape = function (block) {
|
||||||
@@ -461,7 +518,8 @@ exports.decode = function (buffer) {
|
|||||||
eof: doEOF
|
eof: doEOF
|
||||||
};
|
};
|
||||||
|
|
||||||
var st = new Stream(buffer.toString('binary'));
|
var st = new Stream(buffer);
|
||||||
parseGIF(st, handler);
|
parseGIF(st, handler);
|
||||||
|
// console.log('doimgTime:',doimgTime)
|
||||||
return gifData;
|
return gifData;
|
||||||
}
|
}
|
||||||
@@ -3,28 +3,18 @@ var jpgDecode = new jpg.JpegDecoder();
|
|||||||
var upng = require('./png');
|
var upng = require('./png');
|
||||||
var bmpDecode = require('./bmp');
|
var bmpDecode = require('./bmp');
|
||||||
var gifDecode = require('./gif').decode;
|
var gifDecode = require('./gif').decode;
|
||||||
var gif1Decode = require('./gif.1').decode;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
exports.bmp = function (buffer) {
|
exports.bmp = function (buffer) {
|
||||||
// return new Promise(function (res, rej) {
|
|
||||||
// res(bmpDecode(buffer));
|
|
||||||
// })
|
|
||||||
return bmpDecode(buffer);
|
return bmpDecode(buffer);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.jpg = function (buffer) {
|
exports.jpg = function (buffer) {
|
||||||
// return new Promise(function (res, rej) {
|
|
||||||
// jpgDecode.parse(buffer);
|
|
||||||
// res(jpgDecode.getImageData());
|
|
||||||
// })
|
|
||||||
jpgDecode.parse(buffer);
|
jpgDecode.parse(buffer);
|
||||||
return jpgDecode.getImageData();
|
return jpgDecode.getImageData();
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.png = function (buffer) {
|
exports.png = function (buffer) {
|
||||||
// console.log('png');
|
|
||||||
var png = upng.decode(buffer);
|
var png = upng.decode(buffer);
|
||||||
var datas = upng.toRGBA8(png);
|
var datas = upng.toRGBA8(png);
|
||||||
var images = [];
|
var images = [];
|
||||||
@@ -39,21 +29,7 @@ exports.png = function (buffer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
exports.gif = function (data) {
|
exports.gif = function (data) {
|
||||||
// return new Promise(function (res, rej) {
|
|
||||||
// var gif = gifDecode(data);
|
|
||||||
// res(gif.images);
|
|
||||||
// })
|
|
||||||
var gif = gifDecode(data);
|
var gif = gifDecode(data);
|
||||||
return gif.images;
|
return gif.images;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.gif1 = function (data) {
|
|
||||||
// return new Promise(function (res, rej) {
|
|
||||||
// var gif = gifDecode(data);
|
|
||||||
// res(gif.images);
|
|
||||||
// })
|
|
||||||
var gif = gif1Decode(data);
|
|
||||||
return gif.images;
|
|
||||||
|
|
||||||
}
|
|
||||||
+22
-3
@@ -6,12 +6,31 @@ var P = function (p) {
|
|||||||
return path.join(__dirname, p);
|
return path.join(__dirname, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
var test = function (path, mark) {
|
var _test = function (path, mark) {
|
||||||
return imgDecode.decodeByPath(P(path))
|
return imgDecode.decodeByPath(P(path))
|
||||||
.then(console.log.bind(console, mark + ': '), console.error.bind(console, mark + ':'))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var max = 100
|
||||||
|
async function test_time(path, mark) {
|
||||||
|
var t = Date.now();
|
||||||
|
for (var i = 0; i < max; i++) {
|
||||||
|
await _test(path, mark);
|
||||||
|
}
|
||||||
|
console.log(mark + ' 100次 : ', Date.now() - t,' ms');
|
||||||
|
}
|
||||||
|
console.table({a:1,b:2})
|
||||||
|
// var test = test_time;
|
||||||
|
|
||||||
|
var test = function (path, mark) {
|
||||||
|
return _test(path, mark)
|
||||||
|
.then(console.log.bind(console, mark + ': '))
|
||||||
|
.then(function () {
|
||||||
|
return test_time(path, mark);
|
||||||
|
},console.error.bind(console, mark + ':'))
|
||||||
|
};
|
||||||
|
|
||||||
Promise.resolve()
|
Promise.resolve()
|
||||||
.then(function () { test('./img/16.bmp', 'bmp16 ') })
|
.then(function () { return test('./img/16.bmp', 'bmp16 ') })
|
||||||
.then(function () { return test('./img/24.bmp', 'bmp24 ') })
|
.then(function () { return test('./img/24.bmp', 'bmp24 ') })
|
||||||
.then(function () { return test('./img/32.bmp', 'bmp32 ') })
|
.then(function () { return test('./img/32.bmp', 'bmp32 ') })
|
||||||
.then(function () { return test('/img/lx.jpg', 'jpg_lx') }) //jpg 连续
|
.then(function () { return test('/img/lx.jpg', 'jpg_lx') }) //jpg 连续
|
||||||
|
|||||||
Reference in New Issue
Block a user