pngjs
pngjs copied to clipboard
saving the same pixel data twice (or more) results in an invalid additional files
I am saving a file to PNG, but the pixel data will keep updating (and I will need to save it over and over again).
Currently, the first file works out fine, and every subsequent file will be an invalid file. I also receive an error message after a few saved files ("info" lines are my own logger), this output was generated with node --trace-warnings index.js
:
info: storing current image in public/stored/1618704065758.png
info: storing current image in public/stored/1618704066763.png
info: storing current image in public/stored/1618704067767.png
info: storing current image in public/stored/1618704068781.png
info: storing current image in public/stored/1618704069783.png
info: storing current image in public/stored/1618704070792.png
info: storing current image in public/stored/1618704071793.png
(node:16828) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 end listeners added to [Stream]. Use emitter.setMaxListeners() to increase limit
at _addListener (node:events:451:17)
at exports.PNG.addListener (node:events:467:10)
at exports.PNG.Stream.pipe (node:internal/streams/legacy:38:12)
at (my own code, redacted)
at new Promise (<anonymous>)
at (my own code, redacted)
at Timeout._onTimeout ((my own code, redacted))
at listOnTimeout (node:internal/timers:556:17)
at processTimers (node:internal/timers:499:7)
(node:16828) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 close listeners added to [Stream]. Use emitter.setMaxListeners() to increase limit
at _addListener (node:events:451:17)
at exports.PNG.addListener (node:events:467:10)
at exports.PNG.Stream.pipe (node:internal/streams/legacy:39:12)
at (my own code, redacted)
at new Promise (<anonymous>)
at Canvas.store (my own code, redacted)
at Timeout._onTimeout (my own code, redacted)
at listOnTimeout (node:internal/timers:556:17)
at processTimers (node:internal/timers:499:7)
info: storing current image in public/stored/1618704072801.png
... (info lines continue) ...
Here's a pretty minimal example to illustrate my problem:
import fs from 'fs';
import { PNG } from 'pngjs';
const width = 256;
const height = 256;
const canvas = new PNG({width, height});
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
// drawing something nice, just so that we have something to look at.
canvas.data[(y * width + x) * 4 + 0] = x;
canvas.data[(y * width + x) * 4 + 1] = y;
canvas.data[(y * width + x) * 4 + 2] = 0;
canvas.data[(y * width + x) * 4 + 3] = 0xFF; // FF => fully opaque.
}
}
const out = fs.createWriteStream('file 1.png');
canvas.pack().pipe(out);
out.on('finish', () => {
const out2 = fs.createWriteStream('file 2.png');
canvas.pack().pipe(out2);
});
What currently happens
file 1.png
will show a nice demo pattern and is about 1KB in size. file 2.png
is broken and (usually) 33 bytes. If it is larger (which also happens) it's a multiple of 33 bytes.
What I would like it to do
I'd like it to store the pixel data over and over again. I would also be fine with a workaround.
I'm not sure if there is a proper way to do this, but I ran into the same issue. I worked around it by just creating a copy of the PNG before I pack it with PNG.bitblt
import fs from 'fs';
import { PNG } from 'pngjs';
const width = 256;
const height = 256;
const canvas = new PNG({ width, height });
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
// drawing something nice, just so that we have something to look at.
canvas.data[(y * width + x) * 4 + 0] = x;
canvas.data[(y * width + x) * 4 + 1] = y;
canvas.data[(y * width + x) * 4 + 2] = 0;
canvas.data[(y * width + x) * 4 + 3] = 0xFF; // FF => fully opaque.
}
}
const exportPNG = (png, fileName) => {
const pngCopy = new PNG({ width: png.width, height: png.height });
PNG.bitblt(png, pngCopy, 0, 0, png.width, png.height, 0, 0);
pngCopy.pack().pipe(fs.createWriteStream(fileName));
}
exportPNG(png, 'file 1.png');
exportPNG(png, 'file 2.png');