diff --git a/atom/common/api/atom_api_native_image.cc b/atom/common/api/atom_api_native_image.cc index 186978448024..6a23d214a0c6 100644 --- a/atom/common/api/atom_api_native_image.cc +++ b/atom/common/api/atom_api_native_image.cc @@ -504,6 +504,54 @@ mate::Handle NativeImage::CreateFromPath( return handle; } +// static +mate::Handle NativeImage::CreateFromBitmap( + mate::Arguments* args, + v8::Local buffer, + const mate::Dictionary& options) { + if (!node::Buffer::HasInstance(buffer)) { + args->ThrowError("buffer must be a node Buffer"); + return mate::Handle(); + } + + 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(); + } + + if (!options.Get("height", &height)) { + args->ThrowError("height is required"); + return mate::Handle(); + } + + 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(); + } + + 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::CreateFromBuffer( mate::Arguments* args, @@ -618,6 +666,7 @@ void Initialize(v8::Local 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); diff --git a/atom/common/api/atom_api_native_image.h b/atom/common/api/atom_api_native_image.h index f945e3d4df39..586f3a75cefd 100644 --- a/atom/common/api/atom_api_native_image.h +++ b/atom/common/api/atom_api_native_image.h @@ -51,6 +51,10 @@ class NativeImage : public mate::Wrappable { size_t length); static mate::Handle CreateFromPath(v8::Isolate* isolate, const base::FilePath& path); + static mate::Handle CreateFromBitmap( + mate::Arguments* args, + v8::Local buffer, + const mate::Dictionary& options); static mate::Handle CreateFromBuffer( mate::Arguments* args, v8::Local buffer); diff --git a/docs/api/native-image.md b/docs/api/native-image.md index 33c37dd3b356..763249c7133b 100644 --- a/docs/api/native-image.md +++ b/docs/api/native-image.md @@ -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] @@ -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)` diff --git a/spec/api-native-image-spec.js b/spec/api-native-image-spec.js index eb9ab0774275..7be18a976650 100644 --- a/spec/api-native-image-spec.js +++ b/spec/api-native-image-spec.js @@ -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())