├── .gitignore ├── .npmrc ├── fixtures ├── sample1-180x135-biliniear-interpolation.png ├── sample1-180x135-nearest-neighbor.png ├── sample1-220x165-biliniear-interpolation.png ├── sample1-220x165-nearest-neighbor.png ├── sample1.png ├── sample2-172x129-biliniear-interpolation.png ├── sample2-172x129-nearest-neighbor.png ├── sample2-172x149-biliniear-interpolation.png ├── sample2-172x149-nearest-neighbor.png ├── sample2-23x97-biliniear-interpolation.png ├── sample2-23x97-nearest-neighbor.png ├── sample2-88x66-biliniear-interpolation.png ├── sample2-88x66-nearest-neighbor.png └── sample2.png ├── index.d.ts ├── index.js ├── package.json ├── readme.md ├── test.js └── util └── resize-png.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /npm-debug.log 3 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /fixtures/sample1-180x135-biliniear-interpolation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinusU/resize-image-data/b97de98ce5c506a17ee421e4ae52f79c18773bdd/fixtures/sample1-180x135-biliniear-interpolation.png -------------------------------------------------------------------------------- /fixtures/sample1-180x135-nearest-neighbor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinusU/resize-image-data/b97de98ce5c506a17ee421e4ae52f79c18773bdd/fixtures/sample1-180x135-nearest-neighbor.png -------------------------------------------------------------------------------- /fixtures/sample1-220x165-biliniear-interpolation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinusU/resize-image-data/b97de98ce5c506a17ee421e4ae52f79c18773bdd/fixtures/sample1-220x165-biliniear-interpolation.png -------------------------------------------------------------------------------- /fixtures/sample1-220x165-nearest-neighbor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinusU/resize-image-data/b97de98ce5c506a17ee421e4ae52f79c18773bdd/fixtures/sample1-220x165-nearest-neighbor.png -------------------------------------------------------------------------------- /fixtures/sample1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinusU/resize-image-data/b97de98ce5c506a17ee421e4ae52f79c18773bdd/fixtures/sample1.png -------------------------------------------------------------------------------- /fixtures/sample2-172x129-biliniear-interpolation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinusU/resize-image-data/b97de98ce5c506a17ee421e4ae52f79c18773bdd/fixtures/sample2-172x129-biliniear-interpolation.png -------------------------------------------------------------------------------- /fixtures/sample2-172x129-nearest-neighbor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinusU/resize-image-data/b97de98ce5c506a17ee421e4ae52f79c18773bdd/fixtures/sample2-172x129-nearest-neighbor.png -------------------------------------------------------------------------------- /fixtures/sample2-172x149-biliniear-interpolation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinusU/resize-image-data/b97de98ce5c506a17ee421e4ae52f79c18773bdd/fixtures/sample2-172x149-biliniear-interpolation.png -------------------------------------------------------------------------------- /fixtures/sample2-172x149-nearest-neighbor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinusU/resize-image-data/b97de98ce5c506a17ee421e4ae52f79c18773bdd/fixtures/sample2-172x149-nearest-neighbor.png -------------------------------------------------------------------------------- /fixtures/sample2-23x97-biliniear-interpolation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinusU/resize-image-data/b97de98ce5c506a17ee421e4ae52f79c18773bdd/fixtures/sample2-23x97-biliniear-interpolation.png -------------------------------------------------------------------------------- /fixtures/sample2-23x97-nearest-neighbor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinusU/resize-image-data/b97de98ce5c506a17ee421e4ae52f79c18773bdd/fixtures/sample2-23x97-nearest-neighbor.png -------------------------------------------------------------------------------- /fixtures/sample2-88x66-biliniear-interpolation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinusU/resize-image-data/b97de98ce5c506a17ee421e4ae52f79c18773bdd/fixtures/sample2-88x66-biliniear-interpolation.png -------------------------------------------------------------------------------- /fixtures/sample2-88x66-nearest-neighbor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinusU/resize-image-data/b97de98ce5c506a17ee421e4ae52f79c18773bdd/fixtures/sample2-88x66-nearest-neighbor.png -------------------------------------------------------------------------------- /fixtures/sample2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinusU/resize-image-data/b97de98ce5c506a17ee421e4ae52f79c18773bdd/fixtures/sample2.png -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import ImageData = require('@canvas/image-data') 2 | 3 | interface ImageLike { 4 | width: number 5 | height: number 6 | data: Uint8Array | Uint8ClampedArray | number[] 7 | } 8 | 9 | // FIXME: "biliniear" only for backwards compatibility, remove in next major version 10 | type Algorithm = 'nearest-neighbor' | 'bilinear-interpolation' | 'biliniear-interpolation' 11 | 12 | declare function resizeImageData(image: ImageLike, width: number, height: number, algorithm?: Algorithm): ImageData 13 | 14 | export = resizeImageData 15 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const ImageData = require('@canvas/image-data') 4 | 5 | function nearestNeighbor (src, dst) { 6 | let pos = 0 7 | 8 | for (let y = 0; y < dst.height; y++) { 9 | for (let x = 0; x < dst.width; x++) { 10 | const srcX = Math.floor(x * src.width / dst.width) 11 | const srcY = Math.floor(y * src.height / dst.height) 12 | 13 | let srcPos = ((srcY * src.width) + srcX) * 4 14 | 15 | dst.data[pos++] = src.data[srcPos++] // R 16 | dst.data[pos++] = src.data[srcPos++] // G 17 | dst.data[pos++] = src.data[srcPos++] // B 18 | dst.data[pos++] = src.data[srcPos++] // A 19 | } 20 | } 21 | } 22 | 23 | function bilinearInterpolation (src, dst) { 24 | function interpolate (k, kMin, kMax, vMin, vMax) { 25 | return Math.round((k - kMin) * vMax + (kMax - k) * vMin) 26 | } 27 | 28 | function interpolateHorizontal (offset, x, y, xMin, xMax) { 29 | const vMin = src.data[((y * src.width + xMin) * 4) + offset] 30 | if (xMin === xMax) return vMin 31 | 32 | const vMax = src.data[((y * src.width + xMax) * 4) + offset] 33 | return interpolate(x, xMin, xMax, vMin, vMax) 34 | } 35 | 36 | function interpolateVertical (offset, x, xMin, xMax, y, yMin, yMax) { 37 | const vMin = interpolateHorizontal(offset, x, yMin, xMin, xMax) 38 | if (yMin === yMax) return vMin 39 | 40 | const vMax = interpolateHorizontal(offset, x, yMax, xMin, xMax) 41 | return interpolate(y, yMin, yMax, vMin, vMax) 42 | } 43 | 44 | let pos = 0 45 | 46 | for (let y = 0; y < dst.height; y++) { 47 | for (let x = 0; x < dst.width; x++) { 48 | const srcX = x * src.width / dst.width 49 | const srcY = y * src.height / dst.height 50 | 51 | const xMin = Math.floor(srcX) 52 | const yMin = Math.floor(srcY) 53 | 54 | const xMax = Math.min(Math.ceil(srcX), src.width - 1) 55 | const yMax = Math.min(Math.ceil(srcY), src.height - 1) 56 | 57 | dst.data[pos++] = interpolateVertical(0, srcX, xMin, xMax, srcY, yMin, yMax) // R 58 | dst.data[pos++] = interpolateVertical(1, srcX, xMin, xMax, srcY, yMin, yMax) // G 59 | dst.data[pos++] = interpolateVertical(2, srcX, xMin, xMax, srcY, yMin, yMax) // B 60 | dst.data[pos++] = interpolateVertical(3, srcX, xMin, xMax, srcY, yMin, yMax) // A 61 | } 62 | } 63 | } 64 | 65 | module.exports = function resizeImageData (image, width, height, algorithm) { 66 | algorithm = algorithm || 'bilinear-interpolation' 67 | 68 | let resize 69 | switch (algorithm) { 70 | case 'nearest-neighbor': resize = nearestNeighbor; break 71 | case 'bilinear-interpolation': resize = bilinearInterpolation; break 72 | // FIXME: Only for backwards compatibility, remove in next major version 73 | case 'biliniear-interpolation': resize = bilinearInterpolation; break 74 | default: throw new Error(`Unknown algorithm: ${algorithm}`) 75 | } 76 | 77 | const result = new ImageData(width, height) 78 | 79 | resize(image, result) 80 | 81 | return result 82 | } 83 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "resize-image-data", 3 | "version": "0.3.1", 4 | "license": "MIT", 5 | "repository": "LinusU/resize-image-data", 6 | "files": [ 7 | "index.js", 8 | "index.d.ts" 9 | ], 10 | "scripts": { 11 | "test": "standard && mocha" 12 | }, 13 | "dependencies": { 14 | "@canvas/image-data": "^1.0.0" 15 | }, 16 | "devDependencies": { 17 | "lodepng": "^2.0.0", 18 | "mocha": "^6.2.2", 19 | "standard": "^14.3.1" 20 | }, 21 | "engines": { 22 | "node": ">=8.6.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Resize Image Data 2 | 3 | Resize a decoded raw image. 4 | 5 | ## Installation 6 | 7 | ```sh 8 | npm install --save resize-image-data 9 | ``` 10 | 11 | ## Usage 12 | 13 | ```js 14 | const resizeImageData = require('resize-image-data') 15 | 16 | const result = resizeImageData(image, 128, 128, 'bilinear-interpolation') 17 | 18 | console.log(result.width) 19 | //=> 128 20 | 21 | console.log(result.height) 22 | //=> 128 23 | 24 | console.log(result.data) 25 | //=> Uint8ClampedArray [ ... ] 26 | ``` 27 | 28 | ## API 29 | 30 | ### `resizeImageData(image, width, height[, algorithm])` 31 | 32 | Resize the image to the supplied width and height, using the specified algorithm. 33 | 34 | The `image` argument should be a `ImageData` instance, or any object with the following properties: 35 | 36 | - `width: Number` - The width of the image, in pixels 37 | - `height: Number` - The height of the image, in pixels 38 | - `data: Buffer | TypedArray | Array` - The image data, stored as raw pixel data in the RGBA order 39 | 40 | The following algorithms is currently supported: 41 | 42 | - `nearest-neighbor` 43 | - `bilinear-interpolation` 44 | 45 | If no algorithm is provided, it will currently default to `bilinear-interpolation`. This may however change in any subsequent release, so don't count on it being stable between even minor and patch releases. The goal is to provide the "best" experience when not supplying an algorithm, which could mean different default algorithms depending wether we are scaling up or down. 46 | 47 | Returns an `ImageData` instance. 48 | 49 | ## Related 50 | 51 | - [`rotate-image-data`](https://github.com/LinusU/rotate-image-data) - Rotate a decoded raw image 52 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | 'use strict' 4 | 5 | const fs = require('fs') 6 | const path = require('path') 7 | const assert = require('assert') 8 | 9 | const lodepng = require('lodepng') 10 | const ImageData = require('@canvas/image-data') 11 | 12 | const resizeImageData = require('./') 13 | 14 | function loadFixture (name) { 15 | return new Promise((resolve, reject) => { 16 | fs.readFile(path.join(__dirname, 'fixtures', `${name}.png`), (err, data) => { 17 | if (err) return reject(err) 18 | 19 | resolve(lodepng.decode(data)) 20 | }) 21 | }) 22 | } 23 | 24 | function addTestCase (name, width, height, algorithm) { 25 | it(`should scale ${name} to ${width}x${height} using ${algorithm}`, () => { 26 | return Promise.all([ 27 | loadFixture(name), 28 | loadFixture(`${name}-${width}x${height}-${algorithm}`) 29 | ]).then((images) => { 30 | const actual = resizeImageData(images[0], width, height, algorithm) 31 | 32 | assert.ok(actual instanceof ImageData) 33 | 34 | assert.strictEqual(actual.width, images[1].width) 35 | assert.strictEqual(actual.height, images[1].height) 36 | 37 | assert.strictEqual(actual.data.length, images[1].data.length, 'The resized data should match the target data (length)') 38 | assert.deepStrictEqual(actual.data, images[1].data, 'The resized data should match the target data (bytes)') 39 | }) 40 | }) 41 | } 42 | 43 | describe('Resize Image Data', () => { 44 | addTestCase('sample1', 180, 135, 'bilinear-interpolation') 45 | addTestCase('sample1', 180, 135, 'nearest-neighbor') 46 | addTestCase('sample1', 220, 165, 'bilinear-interpolation') 47 | addTestCase('sample1', 220, 165, 'nearest-neighbor') 48 | addTestCase('sample2', 23, 97, 'bilinear-interpolation') 49 | addTestCase('sample2', 23, 97, 'nearest-neighbor') 50 | addTestCase('sample2', 172, 129, 'bilinear-interpolation') 51 | addTestCase('sample2', 172, 129, 'nearest-neighbor') 52 | addTestCase('sample2', 172, 149, 'bilinear-interpolation') 53 | addTestCase('sample2', 172, 149, 'nearest-neighbor') 54 | addTestCase('sample2', 88, 66, 'bilinear-interpolation') 55 | addTestCase('sample2', 88, 66, 'nearest-neighbor') 56 | }) 57 | -------------------------------------------------------------------------------- /util/resize-png.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const lodepng = require('lodepng') 3 | 4 | const resizeImageData = require('../') 5 | 6 | Promise.resolve() 7 | .then(() => { 8 | console.log(`Reading file ${process.argv[2]}`) 9 | return fs.readFileSync(process.argv[2]) 10 | }) 11 | .then((source) => { 12 | console.log('Decoding source image') 13 | return lodepng.decode(source) 14 | }) 15 | .then((image) => { 16 | const sizes = process.argv[3].split('x') 17 | 18 | console.log(`Resizeing image to ${sizes[0]}x${sizes[1]} using algorithm ${process.argv[4]}`) 19 | return resizeImageData(image, sizes[0], sizes[1], process.argv[4]) 20 | }) 21 | .then((result) => { 22 | console.log('Encoding image to PNG') 23 | return lodepng.encode(result) 24 | }) 25 | .then((result) => { 26 | console.log(`Writing PNG data to ${process.argv[5]}`) 27 | fs.writeFileSync(process.argv[5], result) 28 | }) 29 | .catch((err) => { 30 | process.exitCode = 1 31 | console.error(err.stack) 32 | }) 33 | --------------------------------------------------------------------------------