gifencoder
Advanced tools
Comparing version 0.1.0 to 0.2.0
@@ -11,3 +11,3 @@ /* | ||
var Readable = require('stream').Readable; | ||
var stream = require('stream'); | ||
var NeuQuant = require('./TypedNeuQuant.js'); | ||
@@ -67,18 +67,55 @@ var LZWEncoder = require('./LZWEncoder.js'); | ||
this.readStream = null; | ||
this.started = false; // started encoding | ||
this.readStreams = []; | ||
this.out = new ByteArray(); | ||
} | ||
GIFEncoder.prototype.createReadStream = function () { | ||
if (this.readStream !== null) return this.readStream; | ||
this.readStream = new Readable(); | ||
this.readStream._read = function () {}; | ||
return this.readStream; | ||
GIFEncoder.prototype.createReadStream = function (rs) { | ||
if (!rs) { | ||
rs = new stream.Readable(); | ||
rs._read = function () {}; | ||
} | ||
this.readStreams.push(rs); | ||
return rs; | ||
}; | ||
GIFEncoder.prototype.createWriteStream = function (options) { | ||
var self = this; | ||
if (options) { | ||
Object.keys(options).forEach(function (option) { | ||
var fn = 'set' + option[0].toUpperCase() + option.substr(1); | ||
if (~['setDelay', 'setFrameRate', 'setDispose', 'setRepeat', | ||
'setTransparent', 'setQuality'].indexOf(fn)) { | ||
self[fn].call(self, options[option]); | ||
} | ||
}); | ||
} | ||
var ws = new stream.Duplex({ objectMode: true }); | ||
ws._read = function () {}; | ||
this.createReadStream(ws); | ||
var self = this; | ||
ws._write = function (data, enc, next) { | ||
if (!self.started) self.start(); | ||
self.addFrame(data); | ||
next(); | ||
}; | ||
var end = ws.end; | ||
ws.end = function () { | ||
end.apply(ws, [].slice.call(arguments)); | ||
self.finish(); | ||
}; | ||
return ws; | ||
}; | ||
GIFEncoder.prototype.emit = function() { | ||
if (this.readStream === null) return; | ||
var self = this; | ||
if (this.readStreams.length === 0) return; | ||
if (this.out.cursor < this.out.data.length) { | ||
this.readStream.push(new Buffer(this.out.data.slice(this.out.cursor, this.out.data.length))); | ||
this.readStreams.forEach(function (rs) { | ||
rs.push(new Buffer(self.out.data.slice(self.out.cursor, self.out.data.length))); | ||
}); | ||
} | ||
@@ -89,5 +126,8 @@ this.out.cursor = this.out.data.length; | ||
GIFEncoder.prototype.end = function() { | ||
if (this.readStream === null) return; | ||
if (this.readStreams.length === null) return; | ||
this.emit(); | ||
this.readStream.push(null); | ||
this.readStreams.forEach(function (rs) { | ||
rs.push(null); | ||
}); | ||
this.readStreams = []; | ||
}; | ||
@@ -206,2 +246,3 @@ | ||
this.out.writeUTFBytes("GIF89a"); | ||
this.started = true; | ||
this.emit(); | ||
@@ -208,0 +249,0 @@ }; |
{ | ||
"name": "gifencoder", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"description": "Streaming server-side animated (and non-animated) gif generation for node.js", | ||
@@ -42,4 +42,7 @@ "main": "index.js", | ||
"canvas": "~1.1.0", | ||
"concat-stream": "~1.0.1" | ||
"concat-stream": "~1.0.1", | ||
"png-js": "~0.1.1", | ||
"after": "~0.8.1", | ||
"range": "0.0.2" | ||
} | ||
} |
@@ -65,3 +65,3 @@ # gifencoder | ||
## Streaming API | ||
## Streaming API - Reads | ||
@@ -105,1 +105,17 @@ You can also use a streaming API to receive data: | ||
``` | ||
## Streaming API - Writes | ||
You can also stream writes of pixel data (or canvas contexts) to the encoder: | ||
``` js | ||
var GIFEncoder = require('gifencoder'); | ||
var encoder = new GIFEncoder(854, 480); | ||
var bitMapStream = makeMyBitMaps(); // read stream that creates RGBA 1-dimensional bitmaps | ||
bitMapStream | ||
.pipe(encoder.createWriteStream({ repeat: -1, delay: 500, quality: 10 })) | ||
.pipe(fs.createWriteStream('myanimated.gif')); | ||
``` | ||
NB: The chunks that get emitted by your read stream must either by a 1-dimensional bitmap of RGBA | ||
data (either an array or Buffer), or a canvas 2D `context`. |
@@ -6,7 +6,23 @@ var expect = require('expect.js'), | ||
concat = require('concat-stream'), | ||
stream = require('stream'), | ||
png = require('png-js'), | ||
after = require('after'), | ||
range = require('range'), | ||
GIFEncoder = require('..'); | ||
function getData(ctx, width, height) { | ||
return ctx.getImageData(0, 0, width || ctx.canvas.width, height || ctx.canvas.height).data; | ||
} | ||
function fixtures(file) { | ||
return path.join(__dirname, 'fixtures', file); | ||
} | ||
function root(file) { | ||
return path.join(__dirname, '..', file); | ||
} | ||
describe('GIFEncoder', function() { | ||
it('should be able to Generate a PNG', function(done) { | ||
var buf = fs.readFileSync(path.join(__dirname, 'fixtures', 'in.png')); | ||
var buf = fs.readFileSync(fixtures('in.png')); | ||
var img = new Canvas.Image(); | ||
@@ -37,3 +53,3 @@ img.src = buf; | ||
var out = encoder.stream().getData(); | ||
var expected = fs.readFileSync(path.join(__dirname, 'fixtures', 'out.gif')); | ||
var expected = fs.readFileSync(fixtures('out.gif')); | ||
@@ -45,4 +61,4 @@ expect(out).to.eql(expected); | ||
it('should expose a streaming interface', function(done) { | ||
var buf = fs.readFileSync(path.join(__dirname, 'fixtures', 'in.png')); | ||
it('should expose a read streaming interface', function(done) { | ||
var buf = fs.readFileSync(fixtures('in.png')); | ||
var img = new Canvas.Image(); | ||
@@ -56,3 +72,3 @@ img.src = buf; | ||
encoder.createReadStream().pipe(concat(function (data) { | ||
var expected = fs.readFileSync(path.join(__dirname, 'fixtures', 'out.gif')); | ||
var expected = fs.readFileSync(fixtures('out.gif')); | ||
expect(data).to.eql(expected); | ||
@@ -80,2 +96,204 @@ done(); | ||
}); | ||
it('should expose a write streaming interface', function(done) { | ||
var buf = fs.readFileSync(fixtures('in.png')); | ||
var img = new Canvas.Image(); | ||
img.src = buf; | ||
var canvas = new Canvas(img.width, img.height); | ||
var ctx = canvas.getContext('2d'); | ||
var encoder = new GIFEncoder(img.width, img.height); | ||
encoder.createReadStream().pipe(concat(function (data) { | ||
var expected = fs.readFileSync(fixtures('out.gif')); | ||
expect(data).to.eql(expected); | ||
done(); | ||
})); | ||
encoder.setRepeat(-1); | ||
encoder.setDelay(500); | ||
encoder.setQuality(10); | ||
var ws = encoder.createWriteStream(); | ||
ctx.drawImage(img, 0, 0); | ||
ws.write(ctx); | ||
ctx.fillStyle = '#ff0000'; | ||
ctx.fillRect(0, 0, img.width, img.height); | ||
ws.write(ctx); | ||
ctx.fillStyle = '#0000ff'; | ||
ctx.fillRect(0, 0, img.width, img.height); | ||
ws.write(ctx); | ||
ws.end(); | ||
}); | ||
it('should be able to pipe with bitmaps', function(done) { | ||
var rs = new stream.Readable({ objectMode: true }); | ||
var frames = []; | ||
rs._read = function () { | ||
if (frames.length) { | ||
rs.push(frames.shift()); | ||
} else { | ||
rs.push(null); | ||
} | ||
}; | ||
var buf = fs.readFileSync(fixtures('in.png')); | ||
var img = new Canvas.Image(); | ||
img.src = buf; | ||
var canvas = new Canvas(img.width, img.height); | ||
var ctx = canvas.getContext('2d'); | ||
var encoder = new GIFEncoder(img.width, img.height); | ||
encoder.createReadStream().pipe(concat(function (data) { | ||
var expected = fs.readFileSync(fixtures('out.gif')); | ||
expect(data).to.eql(expected); | ||
done(); | ||
})); | ||
encoder.setRepeat(-1); | ||
encoder.setDelay(500); | ||
encoder.setQuality(10); | ||
var ws = encoder.createWriteStream(); | ||
ctx.drawImage(img, 0, 0); | ||
frames.push(getData(ctx)); | ||
ctx.fillStyle = '#ff0000'; | ||
ctx.fillRect(0, 0, img.width, img.height); | ||
frames.push(getData(ctx)); | ||
ctx.fillStyle = '#0000ff'; | ||
ctx.fillRect(0, 0, img.width, img.height); | ||
frames.push(getData(ctx)); | ||
rs.pipe(ws); | ||
}); | ||
it('should pipe with png file bitmaps', function(done) { | ||
function createReadStream() { | ||
var rs = new stream.Readable({ objectMode: true }); | ||
rs._read = function () { }; | ||
var n = 3; | ||
var next = after(n, finish); | ||
var frames = []; | ||
range(0, n).forEach(function (i) { | ||
png.decode(fixtures('frame' + i + '.png'), function (pixels) { | ||
frames[i] = pixels; | ||
next(); | ||
}); | ||
}); | ||
function finish() { | ||
(function next() { | ||
if (frames.length) { | ||
rs.push(frames.shift()); | ||
setImmediate(next); | ||
} else { | ||
rs.push(null); | ||
} | ||
})(); | ||
} | ||
return rs; | ||
} | ||
var encoder = new GIFEncoder(854, 480); | ||
encoder.createReadStream().pipe(concat(function (data) { | ||
var expected = fs.readFileSync(fixtures('out.gif')); | ||
expect(data).to.eql(expected); | ||
done(); | ||
})); | ||
encoder.setRepeat(-1); | ||
encoder.setDelay(500); | ||
encoder.setQuality(10); | ||
var ws = encoder.createWriteStream(); | ||
createReadStream().pipe(ws); | ||
}); | ||
it('should pipe with write options', function(done) { | ||
function createReadStream() { | ||
var rs = new stream.Readable({ objectMode: true }); | ||
rs._read = function () { }; | ||
var n = 3; | ||
var next = after(n, finish); | ||
var frames = []; | ||
range(0, n).forEach(function (i) { | ||
png.decode(fixtures('frame' + i + '.png'), function (pixels) { | ||
frames[i] = pixels; | ||
next(); | ||
}); | ||
}); | ||
function finish() { | ||
(function next() { | ||
if (frames.length) { | ||
rs.push(frames.shift()); | ||
setImmediate(next); | ||
} else { | ||
rs.push(null); | ||
} | ||
})(); | ||
} | ||
return rs; | ||
} | ||
var encoder = new GIFEncoder(854, 480); | ||
encoder.createReadStream().pipe(concat(function (data) { | ||
var expected = fs.readFileSync(fixtures('out.gif')); | ||
expect(data).to.eql(expected); | ||
done(); | ||
})); | ||
var ws = encoder.createWriteStream({ repeat: -1, delay: 500, quality: 10 }); | ||
createReadStream().pipe(ws); | ||
}); | ||
it('should pipe a through stream', function(done) { | ||
function createReadStream() { | ||
var rs = new stream.Readable({ objectMode: true }); | ||
rs._read = function () { }; | ||
var n = 3; | ||
var next = after(n, finish); | ||
var frames = []; | ||
range(0, n).forEach(function (i) { | ||
png.decode(fixtures('frame' + i + '.png'), function (pixels) { | ||
frames[i] = pixels; | ||
next(); | ||
}); | ||
}); | ||
function finish() { | ||
(function next() { | ||
if (frames.length) { | ||
rs.push(frames.shift()); | ||
setImmediate(next); | ||
} else { | ||
rs.push(null); | ||
} | ||
})(); | ||
} | ||
return rs; | ||
} | ||
var encoder = new GIFEncoder(854, 480); | ||
createReadStream() | ||
.pipe(encoder.createWriteStream({ repeat: -1, delay: 500, quality: 10 })) | ||
.pipe(concat(function (data) { | ||
var expected = fs.readFileSync(fixtures('out.gif')); | ||
expect(data).to.eql(expected); | ||
done(); | ||
})); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
138964
20
1638
120
12