feat: add safer nativeImage.createFromBitmap(), which does not decode PNG/JPEG (#17337)
This commit is contained in:
parent
aa8b66aae1
commit
878538f2e8
4 changed files with 91 additions and 1 deletions
|
@ -504,6 +504,54 @@ mate::Handle<NativeImage> NativeImage::CreateFromPath(
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
mate::Handle<NativeImage> NativeImage::CreateFromBitmap(
|
||||||
|
mate::Arguments* args,
|
||||||
|
v8::Local<v8::Value> buffer,
|
||||||
|
const mate::Dictionary& options) {
|
||||||
|
if (!node::Buffer::HasInstance(buffer)) {
|
||||||
|
args->ThrowError("buffer must be a node Buffer");
|
||||||
|
return mate::Handle<NativeImage>();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int width = 0;
|
||||||
|
unsigned int height = 0;
|
||||||
|
double scale_factor = 1.;
|
||||||
|
|
||||||
|
if (!options.Get("width", &width)) {
|
||||||
|
args->ThrowError("width is required");
|
||||||
|
return mate::Handle<NativeImage>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.Get("height", &height)) {
|
||||||
|
args->ThrowError("height is required");
|
||||||
|
return mate::Handle<NativeImage>();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType);
|
||||||
|
auto size_bytes = info.computeMinByteSize();
|
||||||
|
|
||||||
|
if (size_bytes != node::Buffer::Length(buffer)) {
|
||||||
|
args->ThrowError("invalid buffer size");
|
||||||
|
return mate::Handle<NativeImage>();
|
||||||
|
}
|
||||||
|
|
||||||
|
options.Get("scaleFactor", &scale_factor);
|
||||||
|
|
||||||
|
if (width == 0 || height == 0) {
|
||||||
|
return CreateEmpty(args->isolate());
|
||||||
|
}
|
||||||
|
|
||||||
|
SkBitmap bitmap;
|
||||||
|
bitmap.allocN32Pixels(width, height, false);
|
||||||
|
bitmap.setPixels(node::Buffer::Data(buffer));
|
||||||
|
|
||||||
|
gfx::ImageSkia image_skia;
|
||||||
|
image_skia.AddRepresentation(gfx::ImageSkiaRep(bitmap, scale_factor));
|
||||||
|
|
||||||
|
return Create(args->isolate(), gfx::Image(image_skia));
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
mate::Handle<NativeImage> NativeImage::CreateFromBuffer(
|
mate::Handle<NativeImage> NativeImage::CreateFromBuffer(
|
||||||
mate::Arguments* args,
|
mate::Arguments* args,
|
||||||
|
@ -618,6 +666,7 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||||
mate::Dictionary dict(context->GetIsolate(), exports);
|
mate::Dictionary dict(context->GetIsolate(), exports);
|
||||||
dict.SetMethod("createEmpty", &atom::api::NativeImage::CreateEmpty);
|
dict.SetMethod("createEmpty", &atom::api::NativeImage::CreateEmpty);
|
||||||
dict.SetMethod("createFromPath", &atom::api::NativeImage::CreateFromPath);
|
dict.SetMethod("createFromPath", &atom::api::NativeImage::CreateFromPath);
|
||||||
|
dict.SetMethod("createFromBitmap", &atom::api::NativeImage::CreateFromBitmap);
|
||||||
dict.SetMethod("createFromBuffer", &atom::api::NativeImage::CreateFromBuffer);
|
dict.SetMethod("createFromBuffer", &atom::api::NativeImage::CreateFromBuffer);
|
||||||
dict.SetMethod("createFromDataURL",
|
dict.SetMethod("createFromDataURL",
|
||||||
&atom::api::NativeImage::CreateFromDataURL);
|
&atom::api::NativeImage::CreateFromDataURL);
|
||||||
|
|
|
@ -51,6 +51,10 @@ class NativeImage : public mate::Wrappable<NativeImage> {
|
||||||
size_t length);
|
size_t length);
|
||||||
static mate::Handle<NativeImage> CreateFromPath(v8::Isolate* isolate,
|
static mate::Handle<NativeImage> CreateFromPath(v8::Isolate* isolate,
|
||||||
const base::FilePath& path);
|
const base::FilePath& path);
|
||||||
|
static mate::Handle<NativeImage> CreateFromBitmap(
|
||||||
|
mate::Arguments* args,
|
||||||
|
v8::Local<v8::Value> buffer,
|
||||||
|
const mate::Dictionary& options);
|
||||||
static mate::Handle<NativeImage> CreateFromBuffer(
|
static mate::Handle<NativeImage> CreateFromBuffer(
|
||||||
mate::Arguments* args,
|
mate::Arguments* args,
|
||||||
v8::Local<v8::Value> buffer);
|
v8::Local<v8::Value> buffer);
|
||||||
|
|
|
@ -137,6 +137,19 @@ let image = nativeImage.createFromPath('/Users/somebody/images/icon.png')
|
||||||
console.log(image)
|
console.log(image)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `nativeImage.createFromBitmap(buffer, options)`
|
||||||
|
|
||||||
|
* `buffer` [Buffer][buffer]
|
||||||
|
* `options` Object
|
||||||
|
* `width` Integer
|
||||||
|
* `height` Integer
|
||||||
|
* `scaleFactor` Double (optional) - Defaults to 1.0.
|
||||||
|
|
||||||
|
Returns `NativeImage`
|
||||||
|
|
||||||
|
Creates a new `NativeImage` instance from `buffer` that contains the raw bitmap
|
||||||
|
pixel data returned by `toBitmap()`. The specific format is platform-dependent.
|
||||||
|
|
||||||
### `nativeImage.createFromBuffer(buffer[, options])`
|
### `nativeImage.createFromBuffer(buffer[, options])`
|
||||||
|
|
||||||
* `buffer` [Buffer][buffer]
|
* `buffer` [Buffer][buffer]
|
||||||
|
@ -147,7 +160,7 @@ console.log(image)
|
||||||
|
|
||||||
Returns `NativeImage`
|
Returns `NativeImage`
|
||||||
|
|
||||||
Creates a new `NativeImage` instance from `buffer`.
|
Creates a new `NativeImage` instance from `buffer`. Tries to decode as PNG or JPEG first.
|
||||||
|
|
||||||
### `nativeImage.createFromDataURL(dataURL)`
|
### `nativeImage.createFromDataURL(dataURL)`
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,30 @@ describe('nativeImage module', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('createFromBitmap(buffer, options)', () => {
|
||||||
|
it('returns an empty image when the buffer is empty', () => {
|
||||||
|
expect(nativeImage.createFromBitmap(Buffer.from([]), { width: 0, height: 0 }).isEmpty())
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns an image created from the given buffer', () => {
|
||||||
|
const imageA = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png'))
|
||||||
|
|
||||||
|
const imageB = nativeImage.createFromBitmap(imageA.toBitmap(), imageA.getSize())
|
||||||
|
expect(imageB.getSize()).to.deep.equal({ width: 538, height: 190 })
|
||||||
|
|
||||||
|
const imageC = nativeImage.createFromBuffer(imageA.toBitmap(), { ...imageA.getSize(), scaleFactor: 2.0 })
|
||||||
|
expect(imageC.getSize()).to.deep.equal({ width: 269, height: 95 })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws on invalid arguments', () => {
|
||||||
|
expect(() => nativeImage.createFromBitmap(null, {})).to.throw('buffer must be a node Buffer')
|
||||||
|
expect(() => nativeImage.createFromBitmap([12, 14, 124, 12], {})).to.throw('buffer must be a node Buffer')
|
||||||
|
expect(() => nativeImage.createFromBitmap(Buffer.from([]), {})).to.throw('width is required')
|
||||||
|
expect(() => nativeImage.createFromBitmap(Buffer.from([]), { width: 1 })).to.throw('height is required')
|
||||||
|
expect(() => nativeImage.createFromBitmap(Buffer.from([]), { width: 1, height: 1 })).to.throw('invalid buffer size')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('createFromBuffer(buffer, options)', () => {
|
describe('createFromBuffer(buffer, options)', () => {
|
||||||
it('returns an empty image when the buffer is empty', () => {
|
it('returns an empty image when the buffer is empty', () => {
|
||||||
expect(nativeImage.createFromBuffer(Buffer.from([])).isEmpty())
|
expect(nativeImage.createFromBuffer(Buffer.from([])).isEmpty())
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue