-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Support toBuffer("image/jpeg") and unify export configs #1152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
fc88d67
to
e5e36a4
Compare
101083e
to
02080e4
Compare
🙌 Haven't looked too closely yet, but looks super good! |
da5f1af
to
0736011
Compare
Alright, finally ready for review! Original post edited (two remaining questions). |
My vote goes to not adding it in.
Sounds good 👍 |
Phew, this is gonna make things a lot less confusing. But why would someone use Since I don't know this part of the code that well, but am I right that this is also changing the behavior of |
Mine as well. We can add it later if someone has a compelling reason.
You get the whole buffer at once instead of dealing with a stream. (Merits of each discussed starting here: #743 (comment).)
You mean vs.
No change there! There's still the synchronous version that returns a buffer if you don't provide a callback for the first arg. I'll work on making JPEG pseudo-async shortly -- I have that in some branch from a year ago. |
c1d54f1
to
a9147fe
Compare
Okay, ready to go! I'm sorry this got so big, but I decided to clean up
Removed a redundant memcpy that was called in a loop. Can someone with MacOS check that this builds please? (I enabled CPP exceptions in the gyp file and can only test on Win and Linux.) |
Builds on macOS for me! Will try to provide bigger feedback soon. |
Hi @zbjornson, I tried your branch on my macbook. The sync version
Also, after reading your comments above about
|
@jfizz thanks for trying it out and for catching the doc error. That message sounds like it comes from libjpeg... Could you post a repro case for it please, or does it happen with any canvas? |
Here is the repo case.
However, the plot thickens. I ran the above script 10 times in a row and it worked 7 / 10 times. For the 3 times it failed the error was |
Can confirm same behavior on macOS
|
Fixed docs and crash, thanks for finding. Want to give it another try please? |
Works for me! |
Not sure if it belongs in this PR or not but |
I guess |
See updated Readme.md and CHANGELOG.md Still needs more testing and possibly cleanup of the closure mess.
Vs. canvas.pngStream(). Follows node's core fs.createReadStream, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry this PR is getting old @zbjornson. I don't know this part of the code well, but it looks great.
@LinusU ok to merge?
Looks awesome! |
Also fixes a bug I introduced in Automattic#1152 (JPEG quality needs to go from 0 to 1, not 0 to 100). Fixes Automattic#1146
Also fixes a bug I introduced in Automattic#1152 (JPEG quality needs to go from 0 to 1, not 0 to 100). Fixes Automattic#1146
Broken since Automattic#1152 Fixes Automattic#1196
@chearon @LinusU @zbjornson So I was trying to use the canvas.toBuffer('image/png', { compressionLevel: 0, resolution: 200 }); And I noticed no difference in the actual PPI of the image. I went digging through this code and I found the static void parsePNGArgs(Local<Value> arg, PngClosure& pngargs) {
if (arg->IsObject()) {
Local<Object> obj = Nan::To<Object>(arg).ToLocalChecked();
Local<Value> cLevel = obj->Get(Nan::New("compressionLevel").ToLocalChecked());
if (cLevel->IsUint32()) {
uint32_t val = Nan::To<uint32_t>(cLevel).FromMaybe(0);
// See quote below from spec section 4.12.5.5.
if (val <= 9) pngargs.compressionLevel = val;
}
Local<Value> filters = obj->Get(Nan::New("filters").ToLocalChecked());
if (filters->IsUint32()) pngargs.filters = Nan::To<uint32_t>(filters).FromMaybe(0);
Local<Value> palette = obj->Get(Nan::New("palette").ToLocalChecked());
if (palette->IsUint8ClampedArray()) {
Local<Uint8ClampedArray> palette_ta = palette.As<Uint8ClampedArray>();
pngargs.nPaletteColors = palette_ta->Length();
if (pngargs.nPaletteColors % 4 != 0) {
throw "Palette length must be a multiple of 4.";
}
pngargs.nPaletteColors /= 4;
Nan::TypedArrayContents<uint8_t> _paletteColors(palette_ta);
pngargs.palette = *_paletteColors;
// Optional background color index:
Local<Value> backgroundIndexVal = obj->Get(Nan::New("backgroundIndex").ToLocalChecked());
if (backgroundIndexVal->IsUint32()) {
pngargs.backgroundIndex = static_cast<uint8_t>(Nan::To<uint32_t>(backgroundIndexVal).FromMaybe(0));
}
}
}
} |
Docs showing the new API: https://github.com/zbjornson/node-canvas/tree/tobufferjpeg#canvastobuffer
Added: support for
canvas.toBuffer("image/jpeg")
. This unblocks Missing sync canvas.toDataURL("image/jpeg") API #1146 and helps makecanvas.toBuffer()
the universal method for getting encoded data out of a canvas. Unblocks addingtoBlob
if we want it -- a later topic.Changed (breaking): PNG filter and ZLIB compression options are now named instead of positional for
canvas.toBuffer
and (undocumented)canvas.pngStream
. Less awkward API, easier support for more parameters in the future (including Output high resolution PNG #716/PNG files written with wrong resolution in header #766).Improved:
canvas.toBuffer
,canvas.jpegStream
andcanvas.pngStream
now all accept the same config objects. Previously some options were only available when streaming.Removed (breaking): The weird argument handling that allowed the compression level
"0"
(string). The logic in that section was all sort of weird, not sure if I'm misreading it.Changed (breaking): The quality argument for
jpegStream
now goes from 0 to 1 to match the quality argument incanvas.toBlob
.Question: Should there be a
canvas.toBuffer(callback, "raw")
? NO per below.There's no async work to be done here (just a memcpy). Adding this would make the API consist, but would encourage a bad usage pattern (callback-all-the-things!). There's also nocanvas.toBuffer(callback)
support for SVG or PDF canvases.image/jpeg
uses the threadpool.Question:image/jpeg
runs in the main thread currently, but that could reasonably be offloaded to the threadpool later. It's easy enough to invoke the callback in the next tick now so that if it moves to the threadpool in the future it'll be async now and in the future -- should it? One less semver major change in the future.Fixes #982