sharp/test/types/sharp.test-d.ts
Lovell Fuller a532659b0f Types: changes/additions relating to new v0.32.0 features
A separate commit is required as these were not part of the
initial definitions in the v0.31.3 snapshot.

From now on, new features and updates can include the relevant
TypeScript definition changes as part of the same
code/docs/tests commits.
2023-01-17 16:11:04 +00:00

640 lines
17 KiB
TypeScript

import sharp = require('../../');
import { createReadStream, createWriteStream } from 'fs';
const input: Buffer = Buffer.alloc(0);
const readableStream: NodeJS.ReadableStream = createReadStream(input);
const writableStream: NodeJS.WritableStream = createWriteStream(input);
sharp(input)
.extractChannel('green')
.toFile('input_green.jpg', (err, info) => {
// info.channels === 1
// input_green.jpg contains the green channel of the input image
});
sharp('3-channel-rgb-input.png')
.bandbool(sharp.bool.and)
.toFile('1-channel-output.png', (err, info) => {
// The output will be a single channel image where each pixel `P = R & G & B`.
// If `I(1,1) = [247, 170, 14] = [0b11110111, 0b10101010, 0b00001111]`
// then `O(1,1) = 0b11110111 & 0b10101010 & 0b00001111 = 0b00000010 = 2`.
});
sharp('input.png')
.rotate(180)
.resize(300)
.flatten({ background: '#ff6600' })
.composite([{ input: 'overlay.png', gravity: sharp.gravity.southeast, animated: false, failOn: 'warning' }])
.sharpen()
.withMetadata()
.withMetadata({
density: 96,
orientation: 8,
icc: 'some/path',
exif: { IFD0: { Copyright: 'Wernham Hogg' } },
})
.webp({
quality: 90,
})
.toBuffer()
.then((outputBuffer: Buffer) => {
// outputBuffer contains upside down, 300px wide, alpha channel flattened
// onto orange background, composited with overlay.png with SE gravity,
// sharpened, with metadata, 90% quality WebP image data. Phew!
});
sharp('input.jpg')
.resize(300, 200)
.toFile('output.jpg', (err: Error) => {
// output.jpg is a 300 pixels wide and 200 pixels high image
// containing a scaled and cropped version of input.jpg
});
sharp('input.jpg').resize({ width: 300 }).blur(false).blur(true).toFile('output.jpg');
sharp({
create: {
width: 300,
height: 200,
channels: 4,
background: { r: 255, g: 0, b: 0, alpha: 128 },
},
})
.png()
.toBuffer();
let transformer = sharp()
.resize(300)
.on('info', (info: sharp.OutputInfo) => {
console.log('Image height is ' + info.height);
});
readableStream.pipe(transformer).pipe(writableStream);
console.log(sharp.format);
console.log(sharp.versions);
console.log(sharp.vendor.current);
console.log(sharp.vendor.installed.join(', '));
sharp.queue.on('change', (queueLength: number) => {
console.log(`Queue contains ${queueLength} task(s)`);
});
let pipeline: sharp.Sharp = sharp().rotate();
pipeline.clone().resize(800, 600).pipe(writableStream);
pipeline.clone().extract({ left: 20, top: 20, width: 100, height: 100 }).pipe(writableStream);
readableStream.pipe(pipeline);
// firstWritableStream receives auto-rotated, resized readableStream
// secondWritableStream receives auto-rotated, extracted region of readableStream
const image: sharp.Sharp = sharp(input);
image
.metadata()
.then<Buffer | undefined>((metadata: sharp.Metadata) => {
if (metadata.width) {
return image
.resize(Math.round(metadata.width / 2))
.webp()
.toBuffer();
}
})
.then((data) => {
// data contains a WebP image half the width and height of the original JPEG
});
pipeline = sharp()
.rotate()
.resize(undefined, 200)
.toBuffer((err: Error, outputBuffer: Buffer, info: sharp.OutputInfo) => {
// outputBuffer contains 200px high JPEG image data,
// auto-rotated using EXIF Orientation tag
// info.width and info.height contain the dimensions of the resized image
});
readableStream.pipe(pipeline);
sharp(input)
.extract({ left: 0, top: 0, width: 100, height: 100 })
.toFile('output', (err: Error) => {
// Extract a region of the input image, saving in the same format.
});
sharp(input)
.extract({ left: 0, top: 0, width: 100, height: 100 })
.resize(200, 200)
.extract({ left: 0, top: 0, width: 100, height: 100 })
.toFile('output', (err: Error) => {
// Extract a region, resize, then extract from the resized image
});
// Resize to 140 pixels wide, then add 10 transparent pixels
// to the top, left and right edges and 20 to the bottom edge
sharp(input)
.resize(140, null, { background: { r: 0, g: 0, b: 0, alpha: 0 } })
.extend({ top: 10, bottom: 20, left: 10, right: 10 });
sharp(input)
.convolve({
width: 3,
height: 3,
kernel: [-1, 0, 1, -2, 0, 2, -1, 0, 1],
})
.raw()
.toBuffer((err: Error, data: Buffer, info: sharp.OutputInfo) => {
// data contains the raw pixel data representing the convolution
// of the input image with the horizontal Sobel operator
});
sharp('input.tiff')
.png()
.tile({
size: 512,
})
.toFile('output.dz', (err: Error, info: sharp.OutputInfo) => {
// output.dzi is the Deep Zoom XML definition
// output_files contains 512x512 tiles grouped by zoom level
});
sharp('input.tiff')
.png()
.tile({
size: 512,
center: true,
layout: 'iiif3',
id: 'https://my.image.host/iiif',
})
.toFile('output');
sharp(input)
.resize(200, 300, {
fit: 'contain',
position: 'north',
kernel: sharp.kernel.lanczos2,
background: 'white',
})
.toFile('output.tiff')
.then(() => {
// output.tiff is a 200 pixels wide and 300 pixels high image
// containing a lanczos2/nohalo scaled version, embedded on a white canvas,
// of the image data in inputBuffer
});
transformer = sharp()
.resize(200, 200, {
fit: 'cover',
position: sharp.strategy.entropy,
})
.on('error', (err: Error) => {
console.log(err);
});
// Read image data from readableStream
// Write 200px square auto-cropped image data to writableStream
readableStream.pipe(transformer).pipe(writableStream);
sharp('input.gif')
.resize(200, 300, {
fit: 'contain',
position: 'north',
background: { r: 0, g: 0, b: 0, alpha: 0 },
})
.toFormat(sharp.format.webp)
.toBuffer((err: Error, outputBuffer: Buffer) => {
if (err) {
throw err;
}
// outputBuffer contains WebP image data of a 200 pixels wide and 300 pixels high
// containing a scaled version, embedded on a transparent canvas, of input.gif
});
sharp(input)
.resize(200, 200, { fit: 'inside' })
.toFormat('jpeg')
.toBuffer()
.then((outputBuffer: Buffer) => {
// outputBuffer contains JPEG image data no wider than 200 pixels and no higher
// than 200 pixels regardless of the inputBuffer image dimensions
});
sharp(input)
.resize(100, 100)
.toFormat('jpg')
.toBuffer({ resolveWithObject: false })
.then((outputBuffer: Buffer) => {
// Resolves with a Buffer object when resolveWithObject is false
});
sharp(input)
.resize(100, 100)
.toBuffer({ resolveWithObject: true })
.then((object: { data: Buffer; info: sharp.OutputInfo }) => {
// Resolve with an object containing data Buffer and an OutputInfo object
// when resolveWithObject is true
});
sharp(input)
.resize(640, 480, { withoutEnlargement: true })
.toFormat('jpeg')
.toBuffer()
.then((outputBuffer: Buffer) => {
// outputBuffer contains JPEG image data no larger than the input
});
sharp(input)
.resize(640, 480, { withoutReduction: true })
.toFormat('jpeg')
.toBuffer()
.then((outputBuffer: Buffer) => {
// outputBuffer contains JPEG image data no smaller than the input
});
// Output to tif
sharp(input)
.resize(100, 100)
.toFormat('tif')
.toFormat('tiff')
.toFormat(sharp.format.tif)
.toFormat(sharp.format.tiff)
.toBuffer();
const stats = sharp.cache();
sharp.cache({ items: 200 });
sharp.cache({ files: 0 });
sharp.cache(false);
const threads = sharp.concurrency(); // 4
sharp.concurrency(2); // 2
sharp.concurrency(0); // 4
const counters = sharp.counters(); // { queue: 2, process: 4 }
let simd: boolean = sharp.simd();
// simd is `true` if SIMD is currently enabled
simd = sharp.simd(true);
// attempts to enable the use of SIMD, returning true if available
const vipsVersion: string = sharp.versions.vips;
if (sharp.versions.cairo) {
const cairoVersion: string = sharp.versions.cairo;
}
sharp('input.gif')
.linear(1)
.linear(1, 0)
.linear(null, 0)
.linear([0.25, 0.5, 0.75], [150, 100, 50])
.recomb([
[0.3588, 0.7044, 0.1368],
[0.299, 0.587, 0.114],
[0.2392, 0.4696, 0.0912],
])
.modulate({ brightness: 2 })
.modulate({ hue: 180 })
.modulate({ lightness: 10 })
.modulate({ brightness: 0.5, saturation: 0.5, hue: 90 });
// From https://sharp.pixelplumbing.com/api-output#examples-9
// Extract raw RGB pixel data from JPEG input
sharp('input.jpg')
.raw()
.toBuffer({ resolveWithObject: true })
.then(({ data, info }) => {
console.log(data);
console.log(info);
});
sharp(input).jpeg().jpeg({}).jpeg({
progressive: false,
chromaSubsampling: '4:4:4',
trellisQuantisation: false,
overshootDeringing: false,
optimiseScans: false,
optimizeScans: false,
optimiseCoding: false,
optimizeCoding: false,
quantisationTable: 10,
quantizationTable: 10,
mozjpeg: false,
quality: 10,
force: false,
});
sharp(input).png().png({}).png({
progressive: false,
compressionLevel: 10,
adaptiveFiltering: false,
force: false,
quality: 10,
palette: false,
colours: 10,
colors: 10,
dither: 10,
});
sharp(input)
.avif()
.avif({})
.avif({ quality: 50, lossless: false, effort: 5, chromaSubsampling: '4:2:0' })
.heif()
.heif({})
.heif({ quality: 50, compression: 'hevc', lossless: false, effort: 5, chromaSubsampling: '4:2:0' })
.toBuffer({ resolveWithObject: true })
.then(({ data, info }) => {
console.log(data);
console.log(info);
});
sharp(input)
.gif()
.gif({})
.gif({ loop: 0, delay: [], force: true })
.gif({ delay: 30 })
.gif({ reuse: true })
.gif({ reuse: false })
.gif({ progressive: true })
.gif({ progressive: false })
.toBuffer({ resolveWithObject: true })
.then(({ data, info }) => {
console.log(data);
console.log(info);
});
sharp(input)
.tiff({ compression: 'packbits' })
.toBuffer({ resolveWithObject: true })
.then(({ data, info }) => {
console.log(data);
console.log(info);
});
sharp('input.jpg')
.stats()
.then(stats => {
const {
sharpness,
dominant: { r, g, b },
} = stats;
console.log(sharpness);
console.log(`${r}, ${g}, ${b}`);
});
// From https://sharp.pixelplumbing.com/api-output#examples-9
// Extract alpha channel as raw pixel data from PNG input
sharp('input.png').ensureAlpha().ensureAlpha(0).extractChannel(3).toColourspace('b-w').raw().toBuffer();
// From https://sharp.pixelplumbing.com/api-constructor#examples-4
// Convert an animated GIF to an animated WebP
sharp('in.gif', { animated: true }).toFile('out.webp');
// From https://github.com/lovell/sharp/issues/2701
// Type support for limitInputPixels
sharp({
create: {
background: 'red',
channels: 4,
height: 25000,
width: 25000,
},
limitInputPixels: false,
})
.toFormat('png')
.toBuffer()
.then(largeImage => sharp(input).composite([{ input: largeImage, limitInputPixels: false }]));
// Taken from API documentation at
// https://sharp.pixelplumbing.com/api-operation#clahe
// introducted
sharp('input.jpg').clahe({ width: 10, height: 10 }).toFile('output.jpg');
sharp('input.jpg').clahe({ width: 10, height: 10, maxSlope: 5 }).toFile('outfile.jpg');
// Support `unlimited` input option
sharp('input.png', { unlimited: true }).resize(320, 240).toFile('outfile.png');
// Support `subifd` input option for tiffs
sharp('input.tiff', { subifd: 3 }).resize(320, 240).toFile('outfile.png');
// Support creating with noise
sharp({
create: {
background: 'red',
channels: 4,
height: 100,
width: 100,
noise: {
type: 'gaussian',
mean: 128,
sigma: 30,
},
},
})
.png()
.toFile('output.png');
sharp(new Uint8Array(input.buffer)).toFile('output.jpg');
// Support for negate options
sharp('input.png').negate({ alpha: false }).toFile('output.png');
// From https://github.com/lovell/sharp/pull/2704
// Type support for pipelineColourspace
sharp(input)
.pipelineColourspace('rgb16')
.resize(320, 240)
.gamma()
.toColourspace('srgb') // this is the default, but included here for clarity
.toBuffer();
// From https://github.com/lovell/sharp/pull/1439
// Second parameter to gamma operation for different output gamma
sharp(input)
.resize(129, 111)
.gamma(2.2, 3.0)
.toBuffer(err => {
if (err) throw err;
});
// Support for raw depth specification
sharp('16bpc.png')
.toColourspace('rgb16')
.raw({ depth: 'ushort' })
.toBuffer((error, data, { width, height, channels, size }) => {
console.log((size / width / height / channels) * 8);
console.log(new Uint16Array(data.buffer));
});
// Output channels are constrained from 1-4, can be used as raw input
sharp(input)
.toBuffer({ resolveWithObject: true })
.then(result => {
const newImg = sharp(result.data, {
raw: {
channels: result.info.channels,
width: result.info.width,
height: result.info.height,
},
});
return newImg.toBuffer();
});
// Support for specifying a timeout
sharp('someImage.png').timeout({ seconds: 30 }).resize(300, 300).toBuffer();
// Support for `effort` in different formats
sharp('input.tiff').png({ effort: 9 }).toFile('out.png');
sharp('input.tiff').webp({ effort: 9 }).toFile('out.webp');
sharp('input.tiff').avif({ effort: 9 }).toFile('out.avif');
sharp('input.tiff').heif({ effort: 9 }).toFile('out.heif');
sharp('input.tiff').gif({ effort: 9 }).toFile('out.gif');
// Support for `colors`/`colours` for gif output
sharp('input.gif').gif({ colors: 16 }).toFile('out.gif');
sharp('input.gif').gif({ colours: 16 }).toFile('out.gif');
// Support for `dither` for gif/png output
sharp('input.gif').gif({ dither: 0.5 }).toFile('out.gif');
sharp('input.gif').png({ dither: 0.5 }).toFile('out.png');
// Support for `interFrameMaxError` for gif output
sharp('input.gif').gif({ interFrameMaxError: 0 }).toFile('out.gif');
// Support for `interPaletteMaxError` for gif output
sharp('input.gif').gif({ interPaletteMaxError: 0 }).toFile('out.gif');
// Support for `resolutionUnit` for tiff output
sharp('input.tiff').tiff({ resolutionUnit: 'cm' }).toFile('out.tiff');
// Support for `jp2` output with different options
sharp('input.tiff').jp2().toFile('out.jp2');
sharp('input.tiff').jp2({ quality: 50 }).toFile('out.jp2');
sharp('input.tiff').jp2({ lossless: true }).toFile('out.jp2');
sharp('input.tiff').jp2({ tileWidth: 128, tileHeight: 128 }).toFile('out.jp2');
sharp('input.tiff').jp2({ chromaSubsampling: '4:2:0' }).toFile('out.jp2');
// Support for `jxl` output with different options
sharp('input.tiff').jxl().toFile('out.jxl');
sharp('input.tiff').jxl({ distance: 15.0 }).toFile('out.jxl');
sharp('input.tiff').jxl({ quality: 50 }).toFile('out.jxl');
sharp('input.tiff').jxl({ decodingTier: 4 }).toFile('out.jxl');
sharp('input.tiff').jxl({ lossless: true }).toFile('out.jxl');
sharp('input.tiff').jxl({ effort: 7 }).toFile('out.jxl');
// Support `minSize` and `mixed` webp options
sharp('input.tiff').webp({ minSize: 1000, mixed: true }).toFile('out.gif');
// 'failOn' input param
sharp('input.tiff', { failOn: 'none' });
sharp('input.tiff', { failOn: 'truncated' });
sharp('input.tiff', { failOn: 'error' });
sharp('input.tiff', { failOn: 'warning' });
// Sharpen operation taking an object instead of three params
sharp('input.tiff').sharpen().toBuffer();
sharp('input.tiff').sharpen({ sigma: 2 }).toBuffer();
sharp('input.tiff')
.sharpen({
sigma: 2,
m1: 0,
m2: 3,
x1: 3,
y2: 15,
y3: 15,
})
.toBuffer();
// Affine operator + interpolator hash
sharp().affine(
[
[1, 0.3],
[0.1, 0.7],
],
{
background: 'white',
interpolator: sharp.interpolators.nohalo,
},
);
sharp().affine([1, 1, 1, 1], {
background: 'white',
idx: 0,
idy: 0,
odx: 0,
ody: 0,
});
const bicubic: string = sharp.interpolators.bicubic;
const bilinear: string = sharp.interpolators.bilinear;
const locallyBoundedBicubic: string = sharp.interpolators.locallyBoundedBicubic;
const nearest: string = sharp.interpolators.nearest;
const nohalo: string = sharp.interpolators.nohalo;
const vertexSplitQuadraticBasisSpline: string = sharp.interpolators.vertexSplitQuadraticBasisSpline;
// Triming
sharp(input).trim('#000').toBuffer();
sharp(input).trim(10).toBuffer();
sharp(input).trim({ background: '#bf1942', threshold: 30 }).toBuffer();
// Text input
sharp({
text: {
text: 'Hello world',
align: 'centre',
dpi: 72,
font: 'Arial',
fontfile: 'path/to/arial.ttf',
height: 500,
width: 500,
rgba: true,
justify: true,
spacing: 10,
wrap: 'charWord',
},
})
.png()
.toBuffer({ resolveWithObject: true })
.then(out => {
console.log(out.info.textAutofitDpi);
});
// Text composite
sharp('input.png').composite([
{
input: {
text: {
text: 'Okay then',
font: 'Comic Sans',
},
},
},
]);
// From https://github.com/lovell/sharp/pull/1835
sharp('input.png').composite([
{
input: {
text: {
text: 'Okay then',
font: 'Comic Sans',
},
},
blend: 'color-burn',
top: 0,
left: 0,
premultiplied: true,
},
]);
// https://github.com/lovell/sharp/pull/402
(['fs', 'zip'] as const).forEach(container => {
sharp().tile({ container });
});
// From https://github.com/lovell/sharp/issues/2238
sharp('input.png').tile({
basename: 'output.dz.tiles',
});