add build

This commit is contained in:
cnwhy
2018-07-23 18:36:54 +08:00
parent 5a11eff012
commit 518ed4af94
14 changed files with 3325 additions and 669 deletions
+60
View File
@@ -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
+42 -36
View File
@@ -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
qrcodeDecode.decodeByUrl(src, function (err, txt) {
if (err) { return console.log(err);}
alert(txt);
})
// 传入DOM可以画到canvas的dom都可以 `img` `canvas` 'video' 等
var img = document.getElementById('img1');
qrcodeDecode.decodeByDom(img, function (err, txt) {
if (err) { return console.log(err);}
alert(txt);
})
```
### 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);
})
document.getElementById('file').onchange = function (event) {
var file = event.target.files[0];
new Promise(function (ok, no) {
var reader = new FileReader();
reader.onload = evt => {
ok(evt.target.result);
};
reader.readAsDataURL(file);
}).then((src)=>{
return new Promise(function(ok){
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);
}
``` ```
+19 -13
View File
@@ -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
}
+9 -14
View File
@@ -1,37 +1,32 @@
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) {
var msg = document.createElement("div") var msg = document.createElement("div")
if(err){ if (err) {
console.log(err); console.log(err);
msg.innerHTML = "err: <br>" + err; msg.innerHTML = "err: <br>" + err;
}else{ } else {
msg.innerHTML = txt; msg.innerHTML = txt;
} }
document.body.appendChild(msg); document.body.appendChild(msg);
}) })
}); });
//el.outerHTML = el.outerHTML;
} }
+3048
View File
File diff suppressed because one or more lines are too long
+7
View File
File diff suppressed because one or more lines are too long
+1
View File
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
module.exports = require('./src/QRCodeDecode');
+12 -6
View File
@@ -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": {
+23 -23
View File
@@ -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){ do {
} images.push(imageData[Math.floor(i * sp)])
// console.log('l:',l) } while (i++ < l)
var sp = (imageData.length-1)/l;
// console.log('sp:',sp)
do{
// console.log(Math.floor(i*sp));
images.push(imageData[Math.floor(i*sp)])
}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
}
-525
View File
@@ -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: %s', 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;
}
+81 -23
View File
@@ -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.');
dict.push(dict[last].concat(dict[last][0])); // try{
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;
} }
-24
View File
@@ -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
View File
@@ -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 连续