mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +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
|
### Parameters
|
||||||
|
|
||||||
- `input` **([Buffer][2] \| [string][3])?** if present, can be
|
- `input` **([Buffer][2] \| [Uint8Array][3] \| [Uint8ClampedArray][4] \| [string][5])?** if present, can be
|
||||||
a Buffer containing JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data, or
|
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.
|
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.
|
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` **[Object][6]?** 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.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`)
|
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.
|
(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`)
|
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`)
|
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.density` **[number][8]** 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.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][6]** page number to start extracting from for multi-page input (GIF, WebP, AVIF, TIFF, PDF), zero based. (optional, default `0`)
|
- `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][6]** level to extract from a multi-level input (OpenSlide), 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][5]** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default `false`)
|
- `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][4]?** describes raw pixel input image data. See `raw()` for pixel ordering.
|
- `options.raw` **[Object][6]?** describes raw pixel input image data. See `raw()` for pixel ordering.
|
||||||
- `options.raw.width` **[number][6]?** integral number of pixels wide.
|
- `options.raw.width` **[number][8]?** integral number of pixels wide.
|
||||||
- `options.raw.height` **[number][6]?** integral number of pixels high.
|
- `options.raw.height` **[number][8]?** integral number of pixels high.
|
||||||
- `options.raw.channels` **[number][6]?** integral number of channels, between 1 and 4.
|
- `options.raw.channels` **[number][8]?** integral number of channels, between 1 and 4.
|
||||||
- `options.create` **[Object][4]?** describes a new image to be created.
|
- `options.create` **[Object][6]?** describes a new image to be created.
|
||||||
- `options.create.width` **[number][6]?** integral number of pixels wide.
|
- `options.create.width` **[number][8]?** integral number of pixels wide.
|
||||||
- `options.create.height` **[number][6]?** integral number of pixels high.
|
- `options.create.height` **[number][8]?** integral number of pixels high.
|
||||||
- `options.create.channels` **[number][6]?** integral number of channels, either 3 (RGB) or 4 (RGBA).
|
- `options.create.channels` **[number][8]?** 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.create.background` **([string][5] \| [Object][6])?** parsed by the [color][9] module to extract values for red, green, blue and alpha.
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@ -84,9 +84,24 @@ sharp({
|
|||||||
await sharp('in.gif', { animated: true }).toFile('out.webp');
|
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
|
## 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
|
[1]: http://nodejs.org/api/stream.html#stream_class_stream_duplex
|
||||||
|
|
||||||
[2]: https://nodejs.org/api/buffer.html
|
[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 => { ... });
|
.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
|
Returns **[Promise][5]<[Buffer][8]>** when no callback is provided
|
||||||
|
|
||||||
## withMetadata
|
## withMetadata
|
||||||
|
@ -90,8 +90,22 @@ const debuglog = util.debuglog('sharp');
|
|||||||
* // Convert an animated GIF to an animated WebP
|
* // Convert an animated GIF to an animated WebP
|
||||||
* await sharp('in.gif', { animated: true }).toFile('out.webp');
|
* await sharp('in.gif', { animated: true }).toFile('out.webp');
|
||||||
*
|
*
|
||||||
* @param {(Buffer|string)} [input] - if present, can be
|
* @example
|
||||||
* a Buffer containing JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data, or
|
* // 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.
|
* 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.
|
* 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.
|
* @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)) {
|
} else if (is.buffer(input)) {
|
||||||
// Buffer
|
// Buffer
|
||||||
inputDescriptor.buffer = input;
|
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)) {
|
} else if (is.plainObject(input) && !is.defined(inputOptions)) {
|
||||||
// Plain Object descriptor, e.g. create
|
// Plain Object descriptor, e.g. create
|
||||||
inputOptions = input;
|
inputOptions = input;
|
||||||
|
10
lib/is.js
10
lib/is.js
@ -48,6 +48,15 @@ const buffer = function (val) {
|
|||||||
return val instanceof Buffer;
|
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?
|
* Is this value a non-empty string?
|
||||||
* @private
|
* @private
|
||||||
@ -110,6 +119,7 @@ module.exports = {
|
|||||||
fn: fn,
|
fn: fn,
|
||||||
bool: bool,
|
bool: bool,
|
||||||
buffer: buffer,
|
buffer: buffer,
|
||||||
|
uint8Array: uint8Array,
|
||||||
string: string,
|
string: string,
|
||||||
number: number,
|
number: number,
|
||||||
integer: integer,
|
integer: integer,
|
||||||
|
@ -104,6 +104,20 @@ function toFile (fileOut, callback) {
|
|||||||
* .then(({ data, info }) => { ... })
|
* .then(({ data, info }) => { ... })
|
||||||
* .catch(err => { ... });
|
* .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 {Object} [options]
|
||||||
* @param {boolean} [options.resolveWithObject] Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
|
* @param {boolean} [options.resolveWithObject] Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
|
||||||
* @param {Function} [callback]
|
* @param {Function} [callback]
|
||||||
|
@ -72,7 +72,8 @@
|
|||||||
"Robert O'Rourke <robert@o-rourke.org>",
|
"Robert O'Rourke <robert@o-rourke.org>",
|
||||||
"Guillermo Alfonso Varela Chouciño <guillevch@gmail.com>",
|
"Guillermo Alfonso Varela Chouciño <guillevch@gmail.com>",
|
||||||
"Christian Flintrup <chr@gigahost.dk>",
|
"Christian Flintrup <chr@gigahost.dk>",
|
||||||
"Manan Jadhav <manan@motionden.com>"
|
"Manan Jadhav <manan@motionden.com>",
|
||||||
|
"Leon Radley <leon@radley.se>"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
|
"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);
|
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) {
|
it('Stream should emit info event', function (done) {
|
||||||
const readable = fs.createReadStream(fixtures.inputJpg);
|
const readable = fs.createReadStream(fixtures.inputJpg);
|
||||||
const writable = fs.createWriteStream(fixtures.outputJpg);
|
const writable = fs.createWriteStream(fixtures.outputJpg);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user