Add support for bit depth with raw input and output (#2762)

* Determine input raw pixel depth from the given typed array
* Allow pixel depth to be set on raw output
This commit is contained in:
Mart
2021-08-03 15:52:54 +02:00
committed by GitHub
parent eabb671b10
commit b7add480c7
9 changed files with 123 additions and 16 deletions

View File

@@ -92,8 +92,9 @@ const debuglog = util.debuglog('sharp');
* }
* }).toFile('noise.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
* @param {(Buffer|Uint8Array|Uint8ClampedArray|Int8Array|Uint16Array|Int16Array|Uint32Array|Int32Array|Float32Array|Float64Array|string)} [input] - if present, can be
* a Buffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image data, or
* a Uint8Array / Uint8ClampedArray / Int8Array / Uint16Array / Int16Array / Uint32Array / Int32Array / Float32Array / Float64Array containing 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.
@@ -254,6 +255,7 @@ const Sharp = function (input, options) {
heifCompression: 'av1',
heifSpeed: 5,
heifChromaSubsampling: '4:4:4',
rawDepth: 'uchar',
tileSize: 256,
tileOverlap: 0,
tileContainer: 'fs',

View File

@@ -34,8 +34,7 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
throw Error('Input Buffer is empty');
}
inputDescriptor.buffer = input;
} else if (is.uint8Array(input)) {
// Uint8Array or Uint8ClampedArray
} else if (is.typedArray(input)) {
if (input.length === 0) {
throw Error('Input Bit Array is empty');
}
@@ -104,6 +103,37 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
inputDescriptor.rawHeight = inputOptions.raw.height;
inputDescriptor.rawChannels = inputOptions.raw.channels;
inputDescriptor.rawPremultiplied = !!inputOptions.raw.premultiplied;
switch (input.constructor) {
case Uint8Array:
case Uint8ClampedArray:
inputDescriptor.rawDepth = 'uchar';
break;
case Int8Array:
inputDescriptor.rawDepth = 'char';
break;
case Uint16Array:
inputDescriptor.rawDepth = 'ushort';
break;
case Int16Array:
inputDescriptor.rawDepth = 'short';
break;
case Uint32Array:
inputDescriptor.rawDepth = 'uint';
break;
case Int32Array:
inputDescriptor.rawDepth = 'int';
break;
case Float32Array:
inputDescriptor.rawDepth = 'float';
break;
case Float64Array:
inputDescriptor.rawDepth = 'double';
break;
default:
inputDescriptor.rawDepth = 'uchar';
break;
}
} else {
throw new Error('Expected width, height and channels for raw pixel input');
}

View File

@@ -49,12 +49,26 @@ const buffer = function (val) {
};
/**
* Is this value a Uint8Array or Uint8ClampedArray object?
* Is this value a typed array object?. E.g. Uint8Array or Uint8ClampedArray?
* @private
*/
const uint8Array = function (val) {
// allow both since Uint8ClampedArray simply clamps the values between 0-255
return val instanceof Uint8Array || val instanceof Uint8ClampedArray;
const typedArray = function (val) {
if (defined(val)) {
switch (val.constructor) {
case Uint8Array:
case Uint8ClampedArray:
case Int8Array:
case Uint16Array:
case Int16Array:
case Uint32Array:
case Int32Array:
case Float32Array:
case Float64Array:
return true;
}
}
return false;
};
/**
@@ -119,7 +133,7 @@ module.exports = {
fn: fn,
bool: bool,
buffer: buffer,
uint8Array: uint8Array,
typedArray: typedArray,
string: string,
number: number,
integer: integer,

View File

@@ -748,7 +748,16 @@ function heif (options) {
*
* @returns {Sharp}
*/
function raw () {
function raw (options) {
if (is.object(options)) {
if (is.defined(options.depth)) {
if (is.string(options.depth) && is.inArray(options.depth, ['char', 'uchar', 'short', 'ushort', 'int', 'uint', 'float', 'complex', 'double', 'dpcomplex'])) {
this.options.rawDepth = options.depth;
} else {
throw is.invalidParameterError('depth', 'one of: char, uchar, short, ushort, int, uint, float, complex, double, dpcomplex', options.depth);
}
}
}
return this._updateFormatOut('raw');
}