mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Create blank image (width, height, channels, background) #470
This commit is contained in:
parent
701b1c4216
commit
1aa053ce6f
@ -25,6 +25,11 @@ If both `top` and `left` options are provided, they take precedence over `gravit
|
||||
- `options.raw.width` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
|
||||
- `options.raw.height` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
|
||||
- `options.raw.channels` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
|
||||
- `options.create` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** describes a blank overlay to be created.
|
||||
- `options.create.width` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
|
||||
- `options.create.height` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
|
||||
- `options.create.channels` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** 3-4
|
||||
- `options.create.background` **([String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object))?** parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
|
||||
|
||||
**Examples**
|
||||
|
||||
|
@ -17,10 +17,15 @@
|
||||
JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when null or undefined.
|
||||
- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** if present, is an Object with optional attributes.
|
||||
- `options.density` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** integral number representing the DPI for vector images. (optional, default `72`)
|
||||
- `options.raw` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** describes raw pixel image data. See `raw()` for pixel ordering.
|
||||
- `options.raw` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** describes raw pixel input image data. See `raw()` for pixel ordering.
|
||||
- `options.raw.width` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
|
||||
- `options.raw.height` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
|
||||
- `options.raw.channels` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
|
||||
- `options.raw.channels` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** 1-4
|
||||
- `options.create` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** describes a new image to be created.
|
||||
- `options.create.width` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
|
||||
- `options.create.height` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
|
||||
- `options.create.channels` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** 3-4
|
||||
- `options.create.background` **([String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object))?** parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
|
||||
|
||||
**Examples**
|
||||
|
||||
@ -46,6 +51,21 @@ var transformer = sharp()
|
||||
readableStream.pipe(transformer).pipe(writableStream);
|
||||
```
|
||||
|
||||
```javascript
|
||||
// Create a blank 300x200 PNG image of semi-transluent red pixels
|
||||
sharp(null, {
|
||||
create: {
|
||||
width: 300,
|
||||
height: 200,
|
||||
channels: 4,
|
||||
background: { r: 255, g: 0, b: 0, alpha: 128 }
|
||||
}
|
||||
})
|
||||
.png()
|
||||
.toBuffer()
|
||||
.then( ... );
|
||||
```
|
||||
|
||||
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
|
||||
|
||||
Returns **[Sharp](#sharp)**
|
||||
|
@ -10,6 +10,10 @@ Requires libvips v8.4.2.
|
||||
[#143](https://github.com/lovell/sharp/issues/143)
|
||||
[@salzhrani](https://github.com/salzhrani)
|
||||
|
||||
* Create blank image of given width, height, channels and background.
|
||||
[#470](https://github.com/lovell/sharp/issues/470)
|
||||
[@pjarts](https://github.com/pjarts)
|
||||
|
||||
#### v0.17.2 - 11<sup>th</sup> February 2017
|
||||
|
||||
* Ensure Readable side of Stream can start flowing after Writable side has finished.
|
||||
|
@ -38,6 +38,11 @@ const is = require('./is');
|
||||
* @param {Number} [options.raw.width]
|
||||
* @param {Number} [options.raw.height]
|
||||
* @param {Number} [options.raw.channels]
|
||||
* @param {Object} [options.create] - describes a blank overlay to be created.
|
||||
* @param {Number} [options.create.width]
|
||||
* @param {Number} [options.create.height]
|
||||
* @param {Number} [options.create.channels] - 3-4
|
||||
* @param {String|Object} [options.create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid parameters
|
||||
*/
|
||||
|
@ -54,16 +54,35 @@ let versions = {
|
||||
* });
|
||||
* readableStream.pipe(transformer).pipe(writableStream);
|
||||
*
|
||||
* @example
|
||||
* // Create a blank 300x200 PNG image of semi-transluent red pixels
|
||||
* sharp(null, {
|
||||
* create: {
|
||||
* width: 300,
|
||||
* height: 200,
|
||||
* channels: 4,
|
||||
* background: { r: 255, g: 0, b: 0, alpha: 128 }
|
||||
* }
|
||||
* })
|
||||
* .png()
|
||||
* .toBuffer()
|
||||
* .then( ... );
|
||||
*
|
||||
* @param {(Buffer|String)} [input] - if present, can be
|
||||
* a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
|
||||
* a String containing the path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
|
||||
* JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when null or undefined.
|
||||
* @param {Object} [options] - if present, is an Object with optional attributes.
|
||||
* @param {Number} [options.density=72] - integral number representing the DPI for vector images.
|
||||
* @param {Object} [options.raw] - describes raw pixel image data. See `raw()` for pixel ordering.
|
||||
* @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
|
||||
* @param {Number} [options.raw.width]
|
||||
* @param {Number} [options.raw.height]
|
||||
* @param {Number} [options.raw.channels]
|
||||
* @param {Number} [options.raw.channels] - 1-4
|
||||
* @param {Object} [options.create] - describes a new image to be created.
|
||||
* @param {Number} [options.create.width]
|
||||
* @param {Number} [options.create.height]
|
||||
* @param {Number} [options.create.channels] - 3-4
|
||||
* @param {String|Object} [options.create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid parameters
|
||||
*/
|
||||
|
25
lib/input.js
25
lib/input.js
@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const util = require('util');
|
||||
const color = require('color');
|
||||
const is = require('./is');
|
||||
const sharp = require('../build/Release/sharp.node');
|
||||
|
||||
@ -46,6 +47,30 @@ const _createInputDescriptor = function _createInputDescriptor (input, inputOpti
|
||||
throw new Error('Expected width, height and channels for raw pixel input');
|
||||
}
|
||||
}
|
||||
// Create new image
|
||||
if (is.defined(inputOptions.create)) {
|
||||
if (
|
||||
is.object(inputOptions.create) &&
|
||||
is.integer(inputOptions.create.width) && is.inRange(inputOptions.create.width, 1, this.constructor.maximum.width) &&
|
||||
is.integer(inputOptions.create.height) && is.inRange(inputOptions.create.height, 1, this.constructor.maximum.height) &&
|
||||
is.integer(inputOptions.create.channels) && is.inRange(inputOptions.create.channels, 3, 4) &&
|
||||
is.defined(inputOptions.create.background)
|
||||
) {
|
||||
inputDescriptor.createWidth = inputOptions.create.width;
|
||||
inputDescriptor.createHeight = inputOptions.create.height;
|
||||
inputDescriptor.createChannels = inputOptions.create.channels;
|
||||
const background = color(inputOptions.create.background);
|
||||
inputDescriptor.createBackground = [
|
||||
background.red(),
|
||||
background.green(),
|
||||
background.blue(),
|
||||
Math.round(background.alpha() * 255)
|
||||
];
|
||||
delete inputDescriptor.buffer;
|
||||
} else {
|
||||
throw new Error('Expected width, height, channels and background to create a new input image');
|
||||
}
|
||||
}
|
||||
} else if (is.defined(inputOptions)) {
|
||||
throw new Error('Invalid input options ' + inputOptions);
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ namespace sharp {
|
||||
InputDescriptor *descriptor = new InputDescriptor;
|
||||
if (HasAttr(input, "file")) {
|
||||
descriptor->file = AttrAsStr(input, "file");
|
||||
} else {
|
||||
} else if (HasAttr(input, "buffer")) {
|
||||
v8::Local<v8::Object> buffer = AttrAs<v8::Object>(input, "buffer");
|
||||
descriptor->bufferLength = node::Buffer::Length(buffer);
|
||||
descriptor->buffer = node::Buffer::Data(buffer);
|
||||
@ -60,6 +60,16 @@ namespace sharp {
|
||||
descriptor->rawWidth = AttrTo<uint32_t>(input, "rawWidth");
|
||||
descriptor->rawHeight = AttrTo<uint32_t>(input, "rawHeight");
|
||||
}
|
||||
// Create new image
|
||||
if (HasAttr(input, "createChannels")) {
|
||||
descriptor->createChannels = AttrTo<uint32_t>(input, "createChannels");
|
||||
descriptor->createWidth = AttrTo<uint32_t>(input, "createWidth");
|
||||
descriptor->createHeight = AttrTo<uint32_t>(input, "createHeight");
|
||||
v8::Local<v8::Object> createBackground = AttrAs<v8::Object>(input, "createBackground");
|
||||
for (unsigned int i = 0; i < 4; i++) {
|
||||
descriptor->createBackground[i] = AttrTo<double>(createBackground, i);
|
||||
}
|
||||
}
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
@ -192,7 +202,6 @@ namespace sharp {
|
||||
VImage image;
|
||||
ImageType imageType;
|
||||
if (descriptor->buffer != nullptr) {
|
||||
// From buffer
|
||||
if (descriptor->rawChannels > 0) {
|
||||
// Raw, uncompressed pixel data
|
||||
image = VImage::new_from_memory(descriptor->buffer, descriptor->bufferLength,
|
||||
@ -227,26 +236,41 @@ namespace sharp {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// From filesystem
|
||||
imageType = DetermineImageType(descriptor->file.data());
|
||||
if (imageType != ImageType::UNKNOWN) {
|
||||
try {
|
||||
vips::VOption *option = VImage::option()->set("access", accessMethod);
|
||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
|
||||
option->set("dpi", static_cast<double>(descriptor->density));
|
||||
}
|
||||
if (imageType == ImageType::MAGICK) {
|
||||
option->set("density", std::to_string(descriptor->density).data());
|
||||
}
|
||||
image = VImage::new_from_file(descriptor->file.data(), option);
|
||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
||||
SetDensity(image, descriptor->density);
|
||||
}
|
||||
} catch (...) {
|
||||
throw vips::VError("Input file has corrupt header");
|
||||
if (descriptor->createChannels > 0) {
|
||||
// Create new image
|
||||
std::vector<double> background = {
|
||||
descriptor->createBackground[0],
|
||||
descriptor->createBackground[1],
|
||||
descriptor->createBackground[2]
|
||||
};
|
||||
if (descriptor->createChannels == 4) {
|
||||
background.push_back(descriptor->createBackground[3]);
|
||||
}
|
||||
image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight).new_from_image(background);
|
||||
image.get_image()->Type = VIPS_INTERPRETATION_sRGB;
|
||||
imageType = ImageType::RAW;
|
||||
} else {
|
||||
throw vips::VError("Input file is missing or of an unsupported image format");
|
||||
// From filesystem
|
||||
imageType = DetermineImageType(descriptor->file.data());
|
||||
if (imageType != ImageType::UNKNOWN) {
|
||||
try {
|
||||
vips::VOption *option = VImage::option()->set("access", accessMethod);
|
||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
|
||||
option->set("dpi", static_cast<double>(descriptor->density));
|
||||
}
|
||||
if (imageType == ImageType::MAGICK) {
|
||||
option->set("density", std::to_string(descriptor->density).data());
|
||||
}
|
||||
image = VImage::new_from_file(descriptor->file.data(), option);
|
||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
||||
SetDensity(image, descriptor->density);
|
||||
}
|
||||
} catch (...) {
|
||||
throw vips::VError("Input file has corrupt header");
|
||||
}
|
||||
} else {
|
||||
throw vips::VError("Input file is missing or of an unsupported image format");
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::make_tuple(image, imageType);
|
||||
|
14
src/common.h
14
src/common.h
@ -52,6 +52,10 @@ namespace sharp {
|
||||
int rawChannels;
|
||||
int rawWidth;
|
||||
int rawHeight;
|
||||
int createChannels;
|
||||
int createWidth;
|
||||
int createHeight;
|
||||
double createBackground[4];
|
||||
|
||||
InputDescriptor():
|
||||
buffer(nullptr),
|
||||
@ -59,7 +63,15 @@ namespace sharp {
|
||||
density(72),
|
||||
rawChannels(0),
|
||||
rawWidth(0),
|
||||
rawHeight(0) {}
|
||||
rawHeight(0),
|
||||
createChannels(0),
|
||||
createWidth(0),
|
||||
createHeight(0) {
|
||||
createBackground[0] = 0.0;
|
||||
createBackground[1] = 0.0;
|
||||
createBackground[2] = 0.0;
|
||||
createBackground[3] = 255.0;
|
||||
}
|
||||
};
|
||||
|
||||
// Convenience methods to access the attributes of a v8::Object
|
||||
|
@ -1100,7 +1100,7 @@ NAN_METHOD(pipeline) {
|
||||
// Background colour
|
||||
v8::Local<v8::Object> background = AttrAs<v8::Object>(options, "background");
|
||||
for (unsigned int i = 0; i < 4; i++) {
|
||||
baton->background[i] = AttrTo<uint32_t>(background, i);
|
||||
baton->background[i] = AttrTo<double>(background, i);
|
||||
}
|
||||
// Overlay options
|
||||
if (HasAttr(options, "overlay")) {
|
||||
|
BIN
test/fixtures/expected/create-rgb.jpg
vendored
Normal file
BIN
test/fixtures/expected/create-rgb.jpg
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 271 B |
BIN
test/fixtures/expected/create-rgba.png
vendored
Normal file
BIN
test/fixtures/expected/create-rgba.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 105 B |
@ -1156,6 +1156,66 @@ describe('Input/output', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('create new image', function () {
|
||||
it('RGB', function (done) {
|
||||
const create = {
|
||||
width: 10,
|
||||
height: 20,
|
||||
channels: 3,
|
||||
background: { r: 0, g: 255, b: 0 }
|
||||
};
|
||||
sharp(null, { create: create })
|
||||
.jpeg()
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(create.width, info.width);
|
||||
assert.strictEqual(create.height, info.height);
|
||||
assert.strictEqual(create.channels, info.channels);
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
fixtures.assertSimilar(fixtures.expected('create-rgb.jpg'), data, done);
|
||||
});
|
||||
});
|
||||
it('RGBA', function (done) {
|
||||
const create = {
|
||||
width: 20,
|
||||
height: 10,
|
||||
channels: 4,
|
||||
background: { r: 255, g: 0, b: 0, alpha: 128 }
|
||||
};
|
||||
sharp(null, { create: create })
|
||||
.png()
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(create.width, info.width);
|
||||
assert.strictEqual(create.height, info.height);
|
||||
assert.strictEqual(create.channels, info.channels);
|
||||
assert.strictEqual('png', info.format);
|
||||
fixtures.assertSimilar(fixtures.expected('create-rgba.png'), data, done);
|
||||
});
|
||||
});
|
||||
it('Invalid channels', function () {
|
||||
const create = {
|
||||
width: 10,
|
||||
height: 20,
|
||||
channels: 2,
|
||||
background: { r: 0, g: 0, b: 0 }
|
||||
};
|
||||
assert.throws(function () {
|
||||
sharp(null, { create: create });
|
||||
});
|
||||
});
|
||||
it('Missing background', function () {
|
||||
const create = {
|
||||
width: 10,
|
||||
height: 20,
|
||||
channels: 3
|
||||
};
|
||||
assert.throws(function () {
|
||||
sharp(null, { create: create });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Queue length change events', function (done) {
|
||||
let eventCounter = 0;
|
||||
const queueListener = function (queueLength) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user