mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 10:30:15 +02:00
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.
640 lines
17 KiB
TypeScript
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',
|
|
});
|