Skip to content

Commit

Permalink
feat: add safer nativeImage.createFromBitmap(), which does not decode…
Browse files Browse the repository at this point in the history
… PNG/JPEG
  • Loading branch information
miniak committed Mar 11, 2019
1 parent 188d311 commit 3e03037
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 2 deletions.
47 changes: 47 additions & 0 deletions atom/common/api/atom_api_native_image.cc
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,52 @@ mate::Handle<NativeImage> NativeImage::CreateFromPath(
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 size_bytes = width * height * 4;

if (size_bytes != node::Buffer::Length(buffer)) {
args->ThrowError("invalid buffer size");
return mate::Handle<NativeImage>();
}

options.Get("scaleFactor", &scale_factor);

gfx::ImageSkia image_skia;

if (width > 0 && height > 0) {
SkBitmap bitmap;
bitmap.allocN32Pixels(width, height, false);
bitmap.setPixels(node::Buffer::Data(buffer));

image_skia.AddRepresentation(gfx::ImageSkiaRep(bitmap, scale_factor));
}

return Create(args->isolate(), gfx::Image(image_skia));
}

// static
mate::Handle<NativeImage> NativeImage::CreateFromBuffer(
mate::Arguments* args,
Expand Down Expand Up @@ -614,6 +660,7 @@ void Initialize(v8::Local<v8::Object> exports,
mate::Dictionary dict(context->GetIsolate(), exports);
dict.SetMethod("createEmpty", &atom::api::NativeImage::CreateEmpty);
dict.SetMethod("createFromPath", &atom::api::NativeImage::CreateFromPath);
dict.SetMethod("createFromBitmap", &atom::api::NativeImage::CreateFromBitmap);
dict.SetMethod("createFromBuffer", &atom::api::NativeImage::CreateFromBuffer);
dict.SetMethod("createFromDataURL",
&atom::api::NativeImage::CreateFromDataURL);
Expand Down
4 changes: 4 additions & 0 deletions atom/common/api/atom_api_native_image.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ class NativeImage : public mate::Wrappable<NativeImage> {
size_t length);
static mate::Handle<NativeImage> CreateFromPath(v8::Isolate* isolate,
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(
mate::Arguments* args,
v8::Local<v8::Value> buffer);
Expand Down
15 changes: 14 additions & 1 deletion docs/api/native-image.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,19 @@ let image = nativeImage.createFromPath('/Users/somebody/images/icon.png')
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()`.

### `nativeImage.createFromBuffer(buffer[, options])`

* `buffer` [Buffer][buffer]
Expand All @@ -147,7 +160,7 @@ console.log(image)

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)`

Expand Down
25 changes: 24 additions & 1 deletion spec/api-native-image-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,30 @@ describe('nativeImage module', () => {
})
})

describe('createFromBuffer(buffer, scaleFactor)', () => {
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(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)', () => {
it('returns an empty image when the buffer is empty', () => {
expect(nativeImage.createFromBuffer(Buffer.from([])).isEmpty())
})
Expand Down

0 comments on commit 3e03037

Please sign in to comment.