add build

master
cnwhy 7 years ago
parent 5a11eff012
commit 518ed4af94

60
.gitignore vendored

@ -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">
如果你不想亲自把图片转为 `ImageData`, 根据你你的项目, 请使用这两个JS:
- `browser.js` 浏览器项目 有两个API `decodeByUrl`, `decodeByDom`
- `server.js` 服务端项目 提供两个API `decodeByPath`, `decodeByBuffer`
> 服务端当前支持 `bmp` , `jpg` , `png` , `gif` 格式;
## demo
### web
> web端最终是利用`Canvas`获取`ImageData`, 注意兼容及跨域问题
```js
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);
})
``` ```
js: ### nodejs
> 注意: 服务器端API是以 `promise` 返回结果,你注意你的`node`版本;
```js ```js
var qrcodeDecode = require('qrcode-decode'); //解析文件
var qrcodeDecode = require('qrcode-decode/server');
document.getElementById('file').onchange = function (event) { qrcodeDecode.decodeByPath('xx/code.jpg').then(function(val){
var file = event.target.files[0]; console.log(val);
new Promise(function (ok, no) { },console.error.bind(console))
var reader = new FileReader();
reader.onload = evt => { //解析Buffer
ok(evt.target.result); fs.readFile(path, function (err, buffer) {
}; if (err) { return rej(err) }
reader.readAsDataURL(file); resqrcodeDecode.decodeByBuffer(buffer);
}).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);
}
``` ```

@ -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
}

@ -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
dist/qrcode-decode.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +0,0 @@
module.exports = require('./src/QRCodeDecode');

@ -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){ do {
images.push(imageData[Math.floor(i * sp)])
} while (i++ < l)
} }
// console.log('l:',l) debug && console.log('gif_decode_length: ',images.length)
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)
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: %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;
}

@ -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;
}

@ -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 连续

Loading…
Cancel
Save