diff --git a/atom/common/api/atom_api_native_image.cc b/atom/common/api/atom_api_native_image.cc index 98eac6b2ff68..6811ab919303 100644 --- a/atom/common/api/atom_api_native_image.cc +++ b/atom/common/api/atom_api_native_image.cc @@ -15,7 +15,6 @@ #include "base/files/file_util.h" #include "base/strings/pattern.h" #include "base/strings/string_util.h" -#include "native_mate/dictionary.h" #include "native_mate/object_template_builder.h" #include "net/base/data_url.h" #include "third_party/skia/include/core/SkPixelRef.h" @@ -387,6 +386,46 @@ mate::Handle NativeImage::Crop(v8::Isolate* isolate, new NativeImage(isolate, gfx::Image(cropped))); } +void NativeImage::AddRepresentation(const mate::Dictionary& options) { + int width = 0; + int height = 0; + float scale_factor = 1.0f; + options.Get("width", &width); + options.Get("height", &height); + options.Get("scaleFactor", &scale_factor); + + bool skia_rep_added = false; + gfx::ImageSkia image_skia = image_.AsImageSkia(); + + v8::Local buffer; + GURL url; + if (options.Get("buffer", &buffer) && node::Buffer::HasInstance(buffer)) { + AddImageSkiaRep( + &image_skia, + reinterpret_cast(node::Buffer::Data(buffer)), + node::Buffer::Length(buffer), + width, height, scale_factor); + skia_rep_added = true; + } else if (options.Get("dataURL", &url)) { + std::string mime_type, charset, data; + if (net::DataURL::Parse(url, &mime_type, &charset, &data)) { + if (mime_type == "image/png" || mime_type == "image/jpeg") { + AddImageSkiaRep( + &image_skia, + reinterpret_cast(data.c_str()), + data.size(), + width, height, scale_factor); + skia_rep_added = true; + } + } + } + + // Re-initialize image when first representation is added to an empty image + if (skia_rep_added && IsEmpty()) { + gfx::Image image(image_skia); + image_.SwapRepresentations(&image); + } +} #if !defined(OS_MACOSX) void NativeImage::SetTemplateImage(bool setAsTemplate) { @@ -504,6 +543,7 @@ void NativeImage::BuildPrototype( .SetMethod("resize", &NativeImage::Resize) .SetMethod("crop", &NativeImage::Crop) .SetMethod("getAspectRatio", &NativeImage::GetAspectRatio) + .SetMethod("addRepresentation", &NativeImage::AddRepresentation) // TODO(kevinsawicki): Remove in 2.0, deprecate before then with warnings .SetMethod("toPng", &NativeImage::ToPNG) .SetMethod("toJpeg", &NativeImage::ToJPEG); diff --git a/atom/common/api/atom_api_native_image.h b/atom/common/api/atom_api_native_image.h index a6614a81b0ec..6ff7a0e29f37 100644 --- a/atom/common/api/atom_api_native_image.h +++ b/atom/common/api/atom_api_native_image.h @@ -9,6 +9,7 @@ #include #include "base/values.h" +#include "native_mate/dictionary.h" #include "native_mate/handle.h" #include "native_mate/wrappable.h" #include "ui/gfx/geometry/rect.h" @@ -85,6 +86,7 @@ class NativeImage : public mate::Wrappable { bool IsEmpty(); gfx::Size GetSize(); float GetAspectRatio(); + void AddRepresentation(const mate::Dictionary& options); // Mark the image as template image. void SetTemplateImage(bool setAsTemplate); diff --git a/docs/api/native-image.md b/docs/api/native-image.md index 0350f13bc902..100fb0e1a222 100644 --- a/docs/api/native-image.md +++ b/docs/api/native-image.md @@ -247,8 +247,8 @@ Returns `NativeImage` - The cropped image. #### `image.resize(options)` * `options` Object - * `width` Integer (optional) - * `height` Integer (optional) + * `width` Integer (optional) - Defaults to the image's width. + * `height` Integer (optional) - Defaults to the image's height * `quality` String (optional) - The desired quality of the resize image. Possible values are `good`, `better` or `best`. The default is `best`. These values express a desired quality/speed tradeoff. They are translated @@ -265,4 +265,20 @@ will be preserved in the resized image. Returns `Float` - The image's aspect ratio. +#### `image.addRepresentation(options)` + +* `options` Object + * `scaleFactor` Double - The scale factor to add the image representation for. + * `width` Integer (optional) - Defaults to 0. Required if a bitmap buffer + is specified as `buffer`. + * `height` Integer (optional) - Defaults to 0. Required if a bitmap buffer + is specified as `buffer`. + * `buffer` Buffer (optional) - The buffer containing the raw image data. + * `dataURL` String (optional) - The data URL containing either a base 64 + encoded PNG or JPEG image. + +Add an image representation for a specific scale factor. This can be used +to explicitly add different scale factor representations to an image. This +can be called on empty images. + [buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer diff --git a/spec/api-native-image-spec.js b/spec/api-native-image-spec.js index 22fe187b2d74..9b8e5c3a2e01 100644 --- a/spec/api-native-image-spec.js +++ b/spec/api-native-image-spec.js @@ -249,4 +249,75 @@ describe('nativeImage module', () => { assert.equal(nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png')).getAspectRatio(), 2.8315789699554443) }) }) + + describe('addRepresentation()', () => { + it('supports adding a buffer representation for a scale factor', () => { + const image = nativeImage.createEmpty() + image.addRepresentation({ + scaleFactor: 1.0, + buffer: nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', '1x1.png')).toPNG() + }) + image.addRepresentation({ + scaleFactor: 2.0, + buffer: nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', '2x2.jpg')).toPNG() + }) + image.addRepresentation({ + scaleFactor: 3.0, + buffer: nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', '3x3.png')).toPNG() + }) + image.addRepresentation({ + scaleFactor: 4.0, + buffer: 'invalid' + }) + + assert.equal(image.isEmpty(), false) + assert.deepEqual(image.getSize(), {width: 1, height: 1}) + assert.equal(image.toDataURL({scaleFactor: 1.0}), 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYlWNgAAIAAAUAAdafFs0AAAAASUVORK5CYII=') + assert.equal(image.toDataURL({scaleFactor: 2.0}), 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFUlEQVQYlWP8////fwYGBgYmBigAAD34BABBrq9BAAAAAElFTkSuQmCC') + assert.equal(image.toDataURL({scaleFactor: 3.0}), 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAADElEQVQYlWNgIAoAAAAnAAGZWEMnAAAAAElFTkSuQmCC') + assert.equal(image.toDataURL({scaleFactor: 4.0}), 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAADElEQVQYlWNgIAoAAAAnAAGZWEMnAAAAAElFTkSuQmCC') + }) + + it('supports adding a data URL representation for a scale factor', () => { + const image = nativeImage.createEmpty() + image.addRepresentation({ + scaleFactor: 1.0, + dataURL: nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', '1x1.png')).toDataURL() + }) + image.addRepresentation({ + scaleFactor: 2.0, + dataURL: nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', '2x2.jpg')).toDataURL() + }) + image.addRepresentation({ + scaleFactor: 3.0, + dataURL: nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', '3x3.png')).toDataURL() + }) + image.addRepresentation({ + scaleFactor: 4.0, + dataURL: 'invalid' + }) + + assert.equal(image.isEmpty(), false) + assert.deepEqual(image.getSize(), {width: 1, height: 1}) + assert.equal(image.toDataURL({scaleFactor: 1.0}), 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYlWNgAAIAAAUAAdafFs0AAAAASUVORK5CYII=') + assert.equal(image.toDataURL({scaleFactor: 2.0}), 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFUlEQVQYlWP8////fwYGBgYmBigAAD34BABBrq9BAAAAAElFTkSuQmCC') + assert.equal(image.toDataURL({scaleFactor: 3.0}), 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAADElEQVQYlWNgIAoAAAAnAAGZWEMnAAAAAElFTkSuQmCC') + assert.equal(image.toDataURL({scaleFactor: 4.0}), 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAADElEQVQYlWNgIAoAAAAnAAGZWEMnAAAAAElFTkSuQmCC') + }) + + it('supports adding a representation to an existing image', () => { + const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', '1x1.png')) + image.addRepresentation({ + scaleFactor: 2.0, + dataURL: nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', '2x2.jpg')).toDataURL() + }) + image.addRepresentation({ + scaleFactor: 2.0, + dataURL: nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', '3x3.png')).toDataURL() + }) + + assert.equal(image.toDataURL({scaleFactor: 1.0}), 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYlWNgAAIAAAUAAdafFs0AAAAASUVORK5CYII=') + assert.equal(image.toDataURL({scaleFactor: 2.0}), 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFUlEQVQYlWP8////fwYGBgYmBigAAD34BABBrq9BAAAAAElFTkSuQmCC') + }) + }) }) diff --git a/spec/fixtures/assets/2x2.jpg b/spec/fixtures/assets/2x2.jpg new file mode 100644 index 000000000000..d96fdb313314 Binary files /dev/null and b/spec/fixtures/assets/2x2.jpg differ diff --git a/spec/fixtures/assets/3x3.png b/spec/fixtures/assets/3x3.png new file mode 100644 index 000000000000..b1c1d9d688c5 Binary files /dev/null and b/spec/fixtures/assets/3x3.png differ