mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 10:30:15 +02:00
Add support for Uint8(Clamped)Array input (#2511)
This commit is contained in:
parent
bf1b326988
commit
4821a11223
@ -13,32 +13,32 @@ Implements the [stream.Duplex][1] class.
|
||||
|
||||
### Parameters
|
||||
|
||||
- `input` **([Buffer][2] \| [string][3])?** if present, can be
|
||||
a Buffer containing JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data, or
|
||||
- `input` **([Buffer][2] \| [Uint8Array][3] \| [Uint8ClampedArray][4] \| [string][5])?** if present, can be
|
||||
a Buffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data, or
|
||||
a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
|
||||
JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
||||
- `options` **[Object][4]?** if present, is an Object with optional attributes.
|
||||
- `options.failOnError` **[boolean][5]** by default halt processing and raise an error when loading invalid images.
|
||||
- `options` **[Object][6]?** if present, is an Object with optional attributes.
|
||||
- `options.failOnError` **[boolean][7]** by default halt processing and raise an error when loading invalid images.
|
||||
Set this flag to `false` if you'd rather apply a "best effort" to decode images, even if the data is corrupt or invalid. (optional, default `true`)
|
||||
- `options.limitInputPixels` **([number][6] \| [boolean][5])** Do not process input images where the number of pixels
|
||||
- `options.limitInputPixels` **([number][8] \| [boolean][7])** Do not process input images where the number of pixels
|
||||
(width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
|
||||
An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF). (optional, default `268402689`)
|
||||
- `options.sequentialRead` **[boolean][5]** Set this to `true` to use sequential rather than random access where possible.
|
||||
- `options.sequentialRead` **[boolean][7]** Set this to `true` to use sequential rather than random access where possible.
|
||||
This can reduce memory usage and might improve performance on some systems. (optional, default `false`)
|
||||
- `options.density` **[number][6]** number representing the DPI for vector images in the range 1 to 100000. (optional, default `72`)
|
||||
- `options.pages` **[number][6]** number of pages to extract for multi-page input (GIF, WebP, AVIF, TIFF, PDF), use -1 for all pages. (optional, default `1`)
|
||||
- `options.page` **[number][6]** page number to start extracting from for multi-page input (GIF, WebP, AVIF, TIFF, PDF), zero based. (optional, default `0`)
|
||||
- `options.level` **[number][6]** level to extract from a multi-level input (OpenSlide), zero based. (optional, default `0`)
|
||||
- `options.animated` **[boolean][5]** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default `false`)
|
||||
- `options.raw` **[Object][4]?** describes raw pixel input image data. See `raw()` for pixel ordering.
|
||||
- `options.raw.width` **[number][6]?** integral number of pixels wide.
|
||||
- `options.raw.height` **[number][6]?** integral number of pixels high.
|
||||
- `options.raw.channels` **[number][6]?** integral number of channels, between 1 and 4.
|
||||
- `options.create` **[Object][4]?** describes a new image to be created.
|
||||
- `options.create.width` **[number][6]?** integral number of pixels wide.
|
||||
- `options.create.height` **[number][6]?** integral number of pixels high.
|
||||
- `options.create.channels` **[number][6]?** integral number of channels, either 3 (RGB) or 4 (RGBA).
|
||||
- `options.create.background` **([string][3] \| [Object][4])?** parsed by the [color][7] module to extract values for red, green, blue and alpha.
|
||||
- `options.density` **[number][8]** number representing the DPI for vector images in the range 1 to 100000. (optional, default `72`)
|
||||
- `options.pages` **[number][8]** number of pages to extract for multi-page input (GIF, WebP, AVIF, TIFF, PDF), use -1 for all pages. (optional, default `1`)
|
||||
- `options.page` **[number][8]** page number to start extracting from for multi-page input (GIF, WebP, AVIF, TIFF, PDF), zero based. (optional, default `0`)
|
||||
- `options.level` **[number][8]** level to extract from a multi-level input (OpenSlide), zero based. (optional, default `0`)
|
||||
- `options.animated` **[boolean][7]** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default `false`)
|
||||
- `options.raw` **[Object][6]?** describes raw pixel input image data. See `raw()` for pixel ordering.
|
||||
- `options.raw.width` **[number][8]?** integral number of pixels wide.
|
||||
- `options.raw.height` **[number][8]?** integral number of pixels high.
|
||||
- `options.raw.channels` **[number][8]?** integral number of channels, between 1 and 4.
|
||||
- `options.create` **[Object][6]?** describes a new image to be created.
|
||||
- `options.create.width` **[number][8]?** integral number of pixels wide.
|
||||
- `options.create.height` **[number][8]?** integral number of pixels high.
|
||||
- `options.create.channels` **[number][8]?** integral number of channels, either 3 (RGB) or 4 (RGBA).
|
||||
- `options.create.background` **([string][5] \| [Object][6])?** parsed by the [color][9] module to extract values for red, green, blue and alpha.
|
||||
|
||||
### Examples
|
||||
|
||||
@ -84,9 +84,24 @@ sharp({
|
||||
await sharp('in.gif', { animated: true }).toFile('out.webp');
|
||||
```
|
||||
|
||||
- Throws **[Error][8]** Invalid parameters
|
||||
```javascript
|
||||
// Read a raw array of pixels and save it to a png
|
||||
const input = Uint8Array.from([255, 255, 255, 0, 0, 0]); // or Uint8ClampedArray
|
||||
const image = sharp(input, {
|
||||
// because the input does not contain its dimensions or how many channels it has
|
||||
// we need to specify it in the constructor options
|
||||
raw: {
|
||||
width: 2,
|
||||
height: 1,
|
||||
channels: 3
|
||||
}
|
||||
});
|
||||
await image.toFile('my-two-pixels.png');
|
||||
```
|
||||
|
||||
Returns **[Sharp][9]**
|
||||
- Throws **[Error][10]** Invalid parameters
|
||||
|
||||
Returns **[Sharp][11]**
|
||||
|
||||
## clone
|
||||
|
||||
@ -154,22 +169,26 @@ Promise.all(promises)
|
||||
});
|
||||
```
|
||||
|
||||
Returns **[Sharp][9]**
|
||||
Returns **[Sharp][11]**
|
||||
|
||||
[1]: http://nodejs.org/api/stream.html#stream_class_stream_duplex
|
||||
|
||||
[2]: https://nodejs.org/api/buffer.html
|
||||
|
||||
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
|
||||
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array
|
||||
|
||||
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
|
||||
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Uint8ClampedArray
|
||||
|
||||
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
||||
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
|
||||
|
||||
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
||||
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
|
||||
|
||||
[7]: https://www.npmjs.org/package/color
|
||||
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
||||
|
||||
[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
|
||||
[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
||||
|
||||
[9]: #sharp
|
||||
[9]: https://www.npmjs.org/package/color
|
||||
|
||||
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
|
||||
|
||||
[11]: #sharp
|
||||
|
@ -86,6 +86,21 @@ sharp(input)
|
||||
.catch(err => { ... });
|
||||
```
|
||||
|
||||
```javascript
|
||||
const data = await sharp('my-image.jpg')
|
||||
// output the raw pixels
|
||||
.raw()
|
||||
.toBuffer();
|
||||
|
||||
// create a more type safe way to work with the raw pixel data
|
||||
// this will not copy the data, instead it will change `data`s underlying ArrayBuffer
|
||||
// so `data` and `pixelArray` point to the same memory location
|
||||
const pixelArray = new Uint8ClampedArray(data.buffer);
|
||||
|
||||
// When you are done changing the pixelArray, sharp takes the `pixelArray` as an input
|
||||
await sharp(pixelArray).toFile('my-changed-image.jpg');
|
||||
```
|
||||
|
||||
Returns **[Promise][5]<[Buffer][8]>** when no callback is provided
|
||||
|
||||
## withMetadata
|
||||
|
@ -90,8 +90,22 @@ const debuglog = util.debuglog('sharp');
|
||||
* // Convert an animated GIF to an animated WebP
|
||||
* await sharp('in.gif', { animated: true }).toFile('out.webp');
|
||||
*
|
||||
* @param {(Buffer|string)} [input] - if present, can be
|
||||
* a Buffer containing JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data, or
|
||||
* @example
|
||||
* // Read a raw array of pixels and save it to a png
|
||||
* const input = Uint8Array.from([255, 255, 255, 0, 0, 0]); // or Uint8ClampedArray
|
||||
* const image = sharp(input, {
|
||||
* // because the input does not contain its dimensions or how many channels it has
|
||||
* // we need to specify it in the constructor options
|
||||
* raw: {
|
||||
* width: 2,
|
||||
* height: 1,
|
||||
* channels: 3
|
||||
* }
|
||||
* });
|
||||
* await image.toFile('my-two-pixels.png');
|
||||
*
|
||||
* @param {(Buffer|Uint8Array|Uint8ClampedArray|string)} [input] - if present, can be
|
||||
* a Buffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data, or
|
||||
* a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
|
||||
* JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
||||
* @param {Object} [options] - if present, is an Object with optional attributes.
|
||||
|
@ -31,6 +31,9 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
||||
} else if (is.buffer(input)) {
|
||||
// Buffer
|
||||
inputDescriptor.buffer = input;
|
||||
} else if (is.uint8Array(input)) {
|
||||
// Uint8Array or Uint8ClampedArray
|
||||
inputDescriptor.buffer = Buffer.from(input.buffer);
|
||||
} else if (is.plainObject(input) && !is.defined(inputOptions)) {
|
||||
// Plain Object descriptor, e.g. create
|
||||
inputOptions = input;
|
||||
|
10
lib/is.js
10
lib/is.js
@ -48,6 +48,15 @@ const buffer = function (val) {
|
||||
return val instanceof Buffer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Is this value a Uint8Array or Uint8ClampedArray object?
|
||||
* @private
|
||||
*/
|
||||
const uint8Array = function (val) {
|
||||
// allow both since Uint8ClampedArray simply clamps the values between 0-255
|
||||
return val instanceof Uint8Array || val instanceof Uint8ClampedArray;
|
||||
};
|
||||
|
||||
/**
|
||||
* Is this value a non-empty string?
|
||||
* @private
|
||||
@ -110,6 +119,7 @@ module.exports = {
|
||||
fn: fn,
|
||||
bool: bool,
|
||||
buffer: buffer,
|
||||
uint8Array: uint8Array,
|
||||
string: string,
|
||||
number: number,
|
||||
integer: integer,
|
||||
|
@ -104,6 +104,20 @@ function toFile (fileOut, callback) {
|
||||
* .then(({ data, info }) => { ... })
|
||||
* .catch(err => { ... });
|
||||
*
|
||||
* @example
|
||||
* const data = await sharp('my-image.jpg')
|
||||
* // output the raw pixels
|
||||
* .raw()
|
||||
* .toBuffer();
|
||||
*
|
||||
* // create a more type safe way to work with the raw pixel data
|
||||
* // this will not copy the data, instead it will change `data`s underlying ArrayBuffer
|
||||
* // so `data` and `pixelArray` point to the same memory location
|
||||
* const pixelArray = new Uint8ClampedArray(data.buffer);
|
||||
*
|
||||
* // When you are done changing the pixelArray, sharp takes the `pixelArray` as an input
|
||||
* await sharp(pixelArray).toFile('my-changed-image.jpg');
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* @param {boolean} [options.resolveWithObject] Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
|
||||
* @param {Function} [callback]
|
||||
|
@ -72,7 +72,8 @@
|
||||
"Robert O'Rourke <robert@o-rourke.org>",
|
||||
"Guillermo Alfonso Varela Chouciño <guillevch@gmail.com>",
|
||||
"Christian Flintrup <chr@gigahost.dk>",
|
||||
"Manan Jadhav <manan@motionden.com>"
|
||||
"Manan Jadhav <manan@motionden.com>",
|
||||
"Leon Radley <leon@radley.se>"
|
||||
],
|
||||
"scripts": {
|
||||
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
|
||||
|
@ -147,6 +147,38 @@ describe('Input/output', function () {
|
||||
readable.pipe(pipeline).pipe(writable);
|
||||
});
|
||||
|
||||
it('Read from Uint8Array and write to Buffer', async () => {
|
||||
const uint8array = Uint8Array.from([255, 255, 255, 0, 0, 0]);
|
||||
const { data, info } = await sharp(uint8array, {
|
||||
raw: {
|
||||
width: 2,
|
||||
height: 1,
|
||||
channels: 3
|
||||
}
|
||||
}).toBuffer({ resolveWithObject: true });
|
||||
|
||||
assert.deepStrictEqual(uint8array, new Uint8Array(data));
|
||||
assert.strictEqual(info.width, 2);
|
||||
assert.strictEqual(info.height, 1);
|
||||
});
|
||||
|
||||
it('Read from Uint8ClampedArray and output to Buffer', async () => {
|
||||
// since a Uint8ClampedArray is the same as Uint8Array but clamps the values
|
||||
// between 0-255 it seemed good to add this also
|
||||
const uint8array = Uint8ClampedArray.from([255, 255, 255, 0, 0, 0]);
|
||||
const { data, info } = await sharp(uint8array, {
|
||||
raw: {
|
||||
width: 2,
|
||||
height: 1,
|
||||
channels: 3
|
||||
}
|
||||
}).toBuffer({ resolveWithObject: true });
|
||||
|
||||
assert.deepStrictEqual(uint8array, new Uint8ClampedArray(data));
|
||||
assert.strictEqual(info.width, 2);
|
||||
assert.strictEqual(info.height, 1);
|
||||
});
|
||||
|
||||
it('Stream should emit info event', function (done) {
|
||||
const readable = fs.createReadStream(fixtures.inputJpg);
|
||||
const writable = fs.createWriteStream(fixtures.outputJpg);
|
||||
|
Loading…
x
Reference in New Issue
Block a user