I am trying to create an image (ideally to be output as a png) from raw pixel data, but I'm struggling to understand the desired format
const rs = Readable();
const transformer = sharp({
raw: {
width,
height,
channels: 3,
},
});
rs.pipe(transformer).pipe(createWriteStream(outputPath));
I have tried setting _read on rs to push Uint8Arrays, Buffers, and other types representing RGB data, but I inevitably get the Error: Unexpected data on Writable Stream error
I'm stumped, any help would be greatly appreciated
Hello, this approach should be supported but sadly, as you point out, it looks like it isn't currently working. There are no unit tests for stream-based input of raw pixel data so this could be a regression, oops.
Luckily a small rejig of the logic relating to raw input in _createInputDescriptor should fix this. Happy to accept/help with a PR if you're able.
Thanks for the quick reply, I鈥檒l start working on it today
@lovell Hey, I've got sharp building locally, tests pass and all that, ready to take a stab at this
But I'm a stream noob and I'm not sure how this change should go in
_createInputDescriptor with new functionality in is to check for a readable stream or is the intended input a buffer (coming from the stream)? this.options.input.buffer on instantiation? It seems to be referred to often in the path that's currently followed, but I'm unclear on how and where it's set in this caseUint8Arrays like [0, 0, 0] (for three channels), but again, I have no idea what the intention is Fantastic, thanks for looking into this.
When Stream-based input is expected, inputDescriptor.buffer becomes an array ready for chunks to be pushed to in _write() - there's an internal _isStreamInput() helper function to test for this.
There's probably scope for simplification (and definitely a need for testing) but something like the following might work:
} else if (!is.defined(input) && is.object(containerOptions) && containerOptions.allowStream) {
// Stream
inputDescriptor.buffer = [];
+ } else if (is.object(input) && is.object(input.raw) && is.object(containerOptions) && containerOptions.allowStream) {
+ // Raw Stream
+ inputDescriptor.buffer = [];
} else {
throw new Error('Unsupported input ' + typeof input);
For anyone having trouble loading a raw stream, you can can do it by using null in the constructor.
Example:
const pipeline = sharp(null, { raw }).jpeg();
inStream.pipe(pipeline).pipe(outStream);
Commit 83cdb55 adds a test for this, plus fixes the problem.
This will be in v0.22.0 - thanks for reporting!
v0.22.0 now available.
Most helpful comment
v0.22.0 now available.