Skip to content
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

feat: add safer nativeImage.createFromBitmap(), which does not decode PNG/JPEG #17337

Merged
merged 1 commit into from
Mar 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 49 additions & 0 deletions atom/common/api/atom_api_native_image.cc
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,54 @@ 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 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
mate::Handle<NativeImage> NativeImage::CreateFromBuffer(
mate::Arguments* args,
Expand Down Expand Up @@ -618,6 +666,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()`. The specific format is platform-dependent.

### `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
24 changes: 24 additions & 0 deletions spec/api-native-image-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)', () => {
it('returns an empty image when the buffer is empty', () => {
expect(nativeImage.createFromBuffer(Buffer.from([])).isEmpty())
Expand Down