ndarray-pixels
Advanced tools
Comparing version 2.0.1 to 2.0.2
import type { NdArray } from 'ndarray'; | ||
export type GetPixelsCallback = (err: string | Event | null, pixels?: NdArray) => void; | ||
export default getPixels; | ||
declare function getPixels(path: string, callback: GetPixelsCallback): void; | ||
declare function getPixels(path: string | Uint8Array, type: string, callback: GetPixelsCallback): void; | ||
export declare function getPixelsInternal(buffer: Uint8Array, mimeType: string): Promise<NdArray<Uint8Array>>; |
import type { NdArray } from 'ndarray'; | ||
export interface SavePixelsOptions { | ||
export interface EncoderOptions { | ||
quality?: number; | ||
} | ||
export default savePixels; | ||
declare function savePixels(array: NdArray, type: 'canvas'): HTMLCanvasElement; | ||
declare function savePixels(array: NdArray, type: 'png'): Readable; | ||
declare function savePixels(array: NdArray, type: 'jpeg' | 'jpg', options?: SavePixelsOptions): Readable; | ||
declare class Readable { | ||
private _promise; | ||
constructor(_promise: Promise<Uint8Array>); | ||
on(event: 'data' | 'error' | 'end', fn: (res?: Uint8Array | Error) => void): this; | ||
static from(promise: Promise<Uint8Array>): Readable; | ||
} | ||
export declare function savePixelsInternal(pixels: NdArray<Uint8Array | Uint8ClampedArray>, mimeType: string): Promise<Uint8Array>; | ||
export declare function savePixelsInternal(pixels: NdArray<Uint8Array | Uint8ClampedArray>, mimeType: string, options?: EncoderOptions): Promise<Uint8Array>; |
@@ -14,3 +14,3 @@ import type { NdArray } from 'ndarray'; | ||
*/ | ||
declare function getPixels(data: string | Uint8Array, mimeType?: string): Promise<NdArray>; | ||
declare function getPixels(data: Uint8Array, mimeType: string): Promise<NdArray<Uint8Array>>; | ||
/** | ||
@@ -30,3 +30,3 @@ * Encodes an `ndarray` as image data in the given format. | ||
*/ | ||
declare function savePixels(pixels: NdArray, mimeType: string): Promise<Uint8Array>; | ||
declare function savePixels(pixels: NdArray<Uint8Array | Uint8ClampedArray>, mimeType: string): Promise<Uint8Array>; | ||
export { getPixels, savePixels }; |
@@ -1,2 +0,134 @@ | ||
import e from"ndarray";import t from"ndarray-ops";function a(e,t,a={}){const i=document.createElement("canvas");i.width=e.shape[0],i.height=e.shape[1];const o=i.getContext("2d"),h=o.getImageData(0,0,i.width,i.height);try{n(e,h.data)}catch(e){return s.from(Promise.reject(e))}o.putImageData(h,0,0);const p=a.quality?a.quality/100:void 0;switch(t){case"canvas":return i;case"jpg":case"jpeg":return r(i,"image/jpeg",p);case"png":return r(i,"image/png");default:throw new Error("[ndarray-pixels] Unsupported file type: "+t)}}function r(e,t,a){const r=new Promise((r,n)=>{e.toBlob(async e=>{e?r(new Uint8Array(await e.arrayBuffer())):n(new Error("[ndarray-pixels] Failed to canvas.toBlob()."))},t,a)});return s.from(r)}function n(a,r,s=-1){if(4===a.shape.length)return n(a.pick(s),r,0);if(3===a.shape.length)if(3===a.shape[2])t.assign(e(r,[a.shape[0],a.shape[1],3],[4,4*a.shape[0],1]),a),t.assigns(e(r,[a.shape[0]*a.shape[1]],[4],3),255);else if(4===a.shape[2])t.assign(e(r,[a.shape[0],a.shape[1],4],[4,4*a.shape[0],1]),a);else{if(1!==a.shape[2])throw new Error("[ndarray-pixels] Incompatible array shape.");t.assign(e(r,[a.shape[0],a.shape[1],3],[4,4*a.shape[0],1]),e(a.data,[a.shape[0],a.shape[1],3],[a.stride[0],a.stride[1],0],a.offset)),t.assigns(e(r,[a.shape[0]*a.shape[1]],[4],3),255)}else{if(2!==a.shape.length)throw new Error("[ndarray-pixels] Incompatible array shape.");t.assign(e(r,[a.shape[0],a.shape[1],3],[4,4*a.shape[0],1]),e(a.data,[a.shape[0],a.shape[1],3],[a.stride[0],a.stride[1],0],a.offset)),t.assigns(e(r,[a.shape[0]*a.shape[1]],[4],3),255)}return r}class s{constructor(e){this._promise=void 0,this._promise=e}on(e,t){return"data"===e?this._promise.then(t):"error"===e?this._promise.catch(t):"end"===e&&this._promise.finally(t),this}static from(e){return new s(e)}}async function i(t,a){return t instanceof Uint8Array&&"undefined"!=typeof Buffer&&(t=Buffer.from(t)),new Promise((r,n)=>{!function(t,a,r){if(r=r||a,t instanceof Uint8Array){if("string"!=typeof a)throw new Error("[ndarray-pixels] Type must be given for Uint8Array image data");const e=new Blob([t],{type:a});t=URL.createObjectURL(e)}const n=new Image;n.crossOrigin="anonymous",n.onload=function(){URL.revokeObjectURL(t);const a=document.createElement("canvas");a.width=n.width,a.height=n.height;const s=a.getContext("2d");s.drawImage(n,0,0);const i=s.getImageData(0,0,n.width,n.height);r(null,e(new Uint8Array(i.data),[n.width,n.height,4],[4,4*n.width,1],0))},n.onerror=e=>{URL.revokeObjectURL(t),r(e)},n.src=t}(t,a,(e,t)=>{t&&!e?r(t):n(e)})})}async function o(e,t){return new Promise((r,n)=>{const s=[],i=t.replace("image/","");a(e,i).on("data",e=>s.push(e)).on("end",()=>r(function(e){let t=0;for(const a of e)t+=a.byteLength;const a=new Uint8Array(t);let r=0;for(const t of e)a.set(t,r),r+=t.byteLength;return a}(s))).on("error",e=>n(e))})}export{i as getPixels,o as savePixels}; | ||
import ndarray from 'ndarray'; | ||
import ops from 'ndarray-ops'; | ||
function getPixelsInternal(buffer, mimeType) { | ||
// Warn for Data URIs, URLs, and file paths. Support removed in v3. | ||
if (!(buffer instanceof Uint8Array)) { | ||
throw new Error('[ndarray-pixels] Input must be Uint8Array or Buffer.'); | ||
} | ||
const blob = new Blob([buffer], { | ||
type: mimeType | ||
}); | ||
const path = URL.createObjectURL(blob); // Decode image with Canvas API. | ||
return new Promise((resolve, reject) => { | ||
const img = new Image(); | ||
img.crossOrigin = 'anonymous'; | ||
img.onload = function () { | ||
URL.revokeObjectURL(path); | ||
const canvas = document.createElement('canvas'); | ||
canvas.width = img.width; | ||
canvas.height = img.height; | ||
const context = canvas.getContext('2d'); | ||
context.drawImage(img, 0, 0); | ||
const pixels = context.getImageData(0, 0, img.width, img.height); | ||
resolve(ndarray(new Uint8Array(pixels.data), [img.width, img.height, 4], [4, 4 * img.width, 1], 0)); | ||
}; | ||
img.onerror = err => { | ||
URL.revokeObjectURL(path); | ||
reject(err); | ||
}; | ||
img.src = path; | ||
}); | ||
} | ||
function putPixelData(array, data, frame = -1) { | ||
if (array.shape.length === 4) { | ||
return putPixelData(array.pick(frame), data, 0); | ||
} else if (array.shape.length === 3) { | ||
if (array.shape[2] === 3) { | ||
ops.assign(ndarray(data, [array.shape[0], array.shape[1], 3], [4, 4 * array.shape[0], 1]), array); | ||
ops.assigns(ndarray(data, [array.shape[0] * array.shape[1]], [4], 3), 255); | ||
} else if (array.shape[2] === 4) { | ||
ops.assign(ndarray(data, [array.shape[0], array.shape[1], 4], [4, array.shape[0] * 4, 1]), array); | ||
} else if (array.shape[2] === 1) { | ||
ops.assign(ndarray(data, [array.shape[0], array.shape[1], 3], [4, 4 * array.shape[0], 1]), ndarray(array.data, [array.shape[0], array.shape[1], 3], [array.stride[0], array.stride[1], 0], array.offset)); | ||
ops.assigns(ndarray(data, [array.shape[0] * array.shape[1]], [4], 3), 255); | ||
} else { | ||
throw new Error('[ndarray-pixels] Incompatible array shape.'); | ||
} | ||
} else if (array.shape.length === 2) { | ||
ops.assign(ndarray(data, [array.shape[0], array.shape[1], 3], [4, 4 * array.shape[0], 1]), ndarray(array.data, [array.shape[0], array.shape[1], 3], [array.stride[0], array.stride[1], 0], array.offset)); | ||
ops.assigns(ndarray(data, [array.shape[0] * array.shape[1]], [4], 3), 255); | ||
} else { | ||
throw new Error('[ndarray-pixels] Incompatible array shape.'); | ||
} | ||
return data; | ||
} | ||
async function savePixelsInternal(pixels, mimeType, options = {}) { | ||
// Create HTMLCanvasElement and write pixel data. | ||
const canvas = document.createElement('canvas'); | ||
canvas.width = pixels.shape[0]; | ||
canvas.height = pixels.shape[1]; | ||
const context = canvas.getContext('2d'); | ||
const imageData = context.getImageData(0, 0, canvas.width, canvas.height); | ||
putPixelData(pixels, imageData.data); | ||
context.putImageData(imageData, 0, 0); | ||
const quality = options.quality ? options.quality / 100 : undefined; // Encode to target format. | ||
switch (mimeType) { | ||
case 'image/jpeg': | ||
return streamCanvas(canvas, 'image/jpeg', quality); | ||
default: | ||
return streamCanvas(canvas, mimeType); | ||
} | ||
} | ||
/** Creates readable stream from given HTMLCanvasElement and options. */ | ||
function streamCanvas(canvas, mimeType, quality) { | ||
return new Promise((resolve, reject) => { | ||
canvas.toBlob(async blob => { | ||
if (blob) { | ||
resolve(new Uint8Array(await blob.arrayBuffer())); | ||
} else { | ||
reject(new Error('[ndarray-pixels] Failed to canvas.toBlob().')); | ||
} | ||
}, mimeType, quality); | ||
}); | ||
} | ||
/** | ||
* Decodes image data to an `ndarray`. | ||
* | ||
* MIME type is optional when given a path or URL, and required when given a Uint8Array. | ||
* | ||
* Accepts `image/png` or `image/jpeg` in Node.js, and additional formats on browsers with | ||
* the necessary support in Canvas 2D. | ||
* | ||
* @param data | ||
* @param mimeType `image/jpeg`, `image/png`, etc. | ||
* @returns | ||
*/ | ||
async function getPixels(data, mimeType) { | ||
return getPixelsInternal(data, mimeType); | ||
} | ||
/** | ||
* Encodes an `ndarray` as image data in the given format. | ||
* | ||
* If the source `ndarray` was constructed manually with default stride, use | ||
* `ndarray.transpose(1, 0)` to reshape it and ensure an identical result from getPixels(). For an | ||
* ndarray created by getPixels(), this isn't necessary. | ||
* | ||
* Accepts `image/png` or `image/jpeg` in Node.js, and additional formats on browsers with | ||
* the necessary support in Canvas 2D. | ||
* | ||
* @param pixels ndarray of shape W x H x 4. | ||
* @param mimeType `image/jpeg`, `image/png`, etc. | ||
* @returns | ||
*/ | ||
async function savePixels(pixels, mimeType) { | ||
return savePixelsInternal(pixels, mimeType); | ||
} | ||
export { getPixels, savePixels }; | ||
//# sourceMappingURL=ndarray-pixels-browser.modern.js.map |
@@ -1,4 +0,58 @@ | ||
import getPixelsInternal from 'get-pixels'; | ||
import savePixelsInternal from 'save-pixels'; | ||
import ndarray from 'ndarray'; | ||
import sharp from 'sharp'; | ||
import ops from 'ndarray-ops'; | ||
async function getPixelsInternal(buffer, _mimeType) { | ||
// Warn for Data URIs, URLs, and file paths. Support removed in v3. | ||
if (!(buffer instanceof Uint8Array)) { | ||
throw new Error('[ndarray-pixels] Input must be Uint8Array or Buffer.'); | ||
} | ||
const { | ||
data, | ||
info | ||
} = await sharp(buffer).ensureAlpha().raw().toBuffer({ | ||
resolveWithObject: true | ||
}); | ||
return ndarray(new Uint8Array(data), [info.width, info.height, 4], [4, 4 * info.width | 0, 1], 0); | ||
} | ||
function putPixelData(array, data, frame = -1) { | ||
if (array.shape.length === 4) { | ||
return putPixelData(array.pick(frame), data, 0); | ||
} else if (array.shape.length === 3) { | ||
if (array.shape[2] === 3) { | ||
ops.assign(ndarray(data, [array.shape[0], array.shape[1], 3], [4, 4 * array.shape[0], 1]), array); | ||
ops.assigns(ndarray(data, [array.shape[0] * array.shape[1]], [4], 3), 255); | ||
} else if (array.shape[2] === 4) { | ||
ops.assign(ndarray(data, [array.shape[0], array.shape[1], 4], [4, array.shape[0] * 4, 1]), array); | ||
} else if (array.shape[2] === 1) { | ||
ops.assign(ndarray(data, [array.shape[0], array.shape[1], 3], [4, 4 * array.shape[0], 1]), ndarray(array.data, [array.shape[0], array.shape[1], 3], [array.stride[0], array.stride[1], 0], array.offset)); | ||
ops.assigns(ndarray(data, [array.shape[0] * array.shape[1]], [4], 3), 255); | ||
} else { | ||
throw new Error('[ndarray-pixels] Incompatible array shape.'); | ||
} | ||
} else if (array.shape.length === 2) { | ||
ops.assign(ndarray(data, [array.shape[0], array.shape[1], 3], [4, 4 * array.shape[0], 1]), ndarray(array.data, [array.shape[0], array.shape[1], 3], [array.stride[0], array.stride[1], 0], array.offset)); | ||
ops.assigns(ndarray(data, [array.shape[0] * array.shape[1]], [4], 3), 255); | ||
} else { | ||
throw new Error('[ndarray-pixels] Incompatible array shape.'); | ||
} | ||
return data; | ||
} | ||
async function savePixelsInternal(pixels, mimeType) { | ||
const [width, height, channels] = pixels.shape; | ||
const data = putPixelData(pixels, new Uint8Array(width * height * channels)); | ||
const format = mimeType.replace('image/', ''); | ||
return sharp(data, { | ||
raw: { | ||
width, | ||
height, | ||
channels | ||
} | ||
}).toFormat(format).toBuffer(); | ||
} | ||
/** | ||
@@ -18,16 +72,3 @@ * Decodes image data to an `ndarray`. | ||
async function getPixels(data, mimeType) { | ||
// In Node.js, get-pixels needs a Buffer and won't accept Uint8Array. | ||
if (data instanceof Uint8Array && typeof Buffer !== 'undefined') { | ||
data = Buffer.from(data); | ||
} | ||
return new Promise((resolve, reject) => { | ||
getPixelsInternal(data, mimeType, (err, pixels) => { | ||
if (pixels && !err) { | ||
resolve(pixels); | ||
} else { | ||
reject(err); | ||
} | ||
}); | ||
}); | ||
return getPixelsInternal(data); | ||
} | ||
@@ -51,28 +92,6 @@ /** | ||
async function savePixels(pixels, mimeType) { | ||
return new Promise((resolve, reject) => { | ||
const chunks = []; | ||
const internalType = mimeType.replace('image/', ''); | ||
savePixelsInternal(pixels, internalType).on('data', d => chunks.push(d)).on('end', () => resolve(concat(chunks))).on('error', e => reject(e)); | ||
}); | ||
return savePixelsInternal(pixels, mimeType); | ||
} | ||
function concat(arrays) { | ||
let totalByteLength = 0; | ||
for (const array of arrays) { | ||
totalByteLength += array.byteLength; | ||
} | ||
const result = new Uint8Array(totalByteLength); | ||
let byteOffset = 0; | ||
for (const array of arrays) { | ||
result.set(array, byteOffset); | ||
byteOffset += array.byteLength; | ||
} | ||
return result; | ||
} | ||
export { getPixels, savePixels }; | ||
//# sourceMappingURL=ndarray-pixels-node.modern.js.map |
138
package.json
{ | ||
"name": "ndarray-pixels", | ||
"version": "2.0.1", | ||
"description": "ndarray-pixels", | ||
"type": "module", | ||
"sideEffects": false, | ||
"types": "./dist/index.d.ts", | ||
"main": "./dist/ndarray-pixels-node.cjs", | ||
"module": "./dist/ndarray-pixels-browser.modern.js", | ||
"exports": { | ||
"types": "./dist/index.d.ts", | ||
"node": { | ||
"require": "./dist/ndarray-pixels-node.cjs", | ||
"default": "./dist/ndarray-pixels-node.modern.js" | ||
}, | ||
"default": { | ||
"require": "./dist/ndarray-pixels-browser.cjs", | ||
"default": "./dist/ndarray-pixels-browser.modern.js" | ||
} | ||
}, | ||
"repository": "github:donmccurdy/ndarray-pixels", | ||
"author": "Don McCurdy <dm@donmccurdy.com>", | ||
"license": "MIT", | ||
"scripts": { | ||
"dist": "yarn dist:node && yarn dist:browser", | ||
"dist:node": "microbundle build --raw --target node --format modern,cjs src/index.ts --output dist/ndarray-pixels-node.js", | ||
"dist:browser": "microbundle build --raw --target web --format modern,cjs src/index.ts --output dist/ndarray-pixels-browser.js --alias get-pixels=./browser-get-pixels.ts,save-pixels=./browser-save-pixels.ts", | ||
"clean": "rm -rf dist/*", | ||
"test": "yarn test:node && yarn test:browser", | ||
"test:node": "tape test/node.test.cjs | tap-spec", | ||
"test:browser": "browserify test/browser.test.cjs | tape-run | tap-spec", | ||
"docs": "typedoc src/index.ts --plugin typedoc-plugin-markdown --out ./docs --hideBreadcrumbs && cat docs/modules.md | tail -n +12 | replace-between --target README.md --token API && rm -rf docs", | ||
"preversion": "yarn dist && yarn test", | ||
"version": "yarn dist && yarn docs && git add -u", | ||
"postversion": "git push && git push --tags && npm publish" | ||
}, | ||
"dependencies": { | ||
"@types/ndarray": "^1.0.10", | ||
"get-pixels": "^3.3.3", | ||
"ndarray": "^1.0.19", | ||
"ndarray-ops": "^1.2.2", | ||
"save-pixels": "^2.3.6" | ||
}, | ||
"devDependencies": { | ||
"@types/get-pixels": "3.3.2", | ||
"@types/ndarray-ops": "1.2.4", | ||
"@types/node": "18.14.0", | ||
"@types/save-pixels": "2.3.2", | ||
"@types/tape": "4.13.2", | ||
"@typescript-eslint/eslint-plugin": "5.53.0", | ||
"@typescript-eslint/parser": "5.53.0", | ||
"browserify": "17.0.0", | ||
"eslint": "8.34.0", | ||
"microbundle": "0.15.1", | ||
"regenerator-runtime": "0.13.11", | ||
"replace-between": "0.0.8", | ||
"source-map-support": "0.5.21", | ||
"tap-spec": "5.0.0", | ||
"tape": "5.6.3", | ||
"tape-run": "10.0.0", | ||
"typedoc": "0.23.25", | ||
"typedoc-plugin-markdown": "3.14.0", | ||
"typescript": "4.9.5" | ||
}, | ||
"files": [ | ||
"dist/", | ||
"src/", | ||
"README.md", | ||
"LICENSE", | ||
"package.json" | ||
] | ||
"name": "ndarray-pixels", | ||
"version": "2.0.2", | ||
"description": "ndarray-pixels", | ||
"type": "module", | ||
"sideEffects": false, | ||
"types": "./dist/index.d.ts", | ||
"main": "./dist/ndarray-pixels-node.cjs", | ||
"module": "./dist/ndarray-pixels-browser.modern.js", | ||
"exports": { | ||
"types": "./dist/index.d.ts", | ||
"node": { | ||
"require": "./dist/ndarray-pixels-node.cjs", | ||
"default": "./dist/ndarray-pixels-node.modern.js" | ||
}, | ||
"default": { | ||
"require": "./dist/ndarray-pixels-browser.cjs", | ||
"default": "./dist/ndarray-pixels-browser.modern.js" | ||
} | ||
}, | ||
"repository": "github:donmccurdy/ndarray-pixels", | ||
"author": "Don McCurdy <dm@donmccurdy.com>", | ||
"license": "MIT", | ||
"scripts": { | ||
"dist": "yarn dist:node && yarn dist:browser", | ||
"dist:node": "microbundle build --raw --no-compress --target node --format modern,cjs src/index.ts --output dist/ndarray-pixels-node.js", | ||
"dist:browser": "microbundle build --raw --no-compress --target web --format modern,cjs src/index.ts --output dist/ndarray-pixels-browser.js --alias ./node-get-pixels=./browser-get-pixels.ts,./node-save-pixels=./browser-save-pixels.ts", | ||
"clean": "rm -rf dist/*", | ||
"test": "yarn test:node && yarn test:browser", | ||
"test:node": "tape test/node.test.cjs | tap-spec", | ||
"test:browser": "browserify test/browser.test.cjs | tape-run | tap-spec", | ||
"docs": "typedoc src/index.ts --plugin typedoc-plugin-markdown --out ./docs --hideBreadcrumbs && cat docs/modules.md | tail -n +12 | replace-between --target README.md --token API && rm -rf docs", | ||
"preversion": "yarn dist && yarn test", | ||
"version": "yarn dist && yarn docs && git add -u", | ||
"postversion": "git push && git push --tags && npm publish" | ||
}, | ||
"dependencies": { | ||
"@types/ndarray": "^1.0.11", | ||
"ndarray": "^1.0.19", | ||
"ndarray-ops": "^1.2.2", | ||
"sharp": "^0.32.1" | ||
}, | ||
"devDependencies": { | ||
"@types/ndarray-ops": "1.2.4", | ||
"@types/node": "20.3.1", | ||
"@types/sharp": "^0.32.0", | ||
"@types/tape": "5.6.0", | ||
"@typescript-eslint/eslint-plugin": "5.59.11", | ||
"@typescript-eslint/parser": "5.59.11", | ||
"browserify": "17.0.0", | ||
"eslint": "8.42.0", | ||
"microbundle": "0.15.1", | ||
"regenerator-runtime": "0.13.11", | ||
"replace-between": "0.0.8", | ||
"source-map-support": "0.5.21", | ||
"tap-spec": "5.0.0", | ||
"tape": "5.6.3", | ||
"tape-run": "10.0.0", | ||
"typedoc": "0.24.8", | ||
"typedoc-plugin-markdown": "3.15.3", | ||
"typescript": "5.1.3" | ||
}, | ||
"files": [ | ||
"dist/", | ||
"src/", | ||
"README.md", | ||
"LICENSE", | ||
"package.json" | ||
] | ||
} |
@@ -12,10 +12,8 @@ # ndarray-pixels | ||
In Node.js, this package uses [get-pixels](https://www.npmjs.com/package/get-pixels) and [save-pixels](https://www.npmjs.com/package/save-pixels). While both packages could be used on the web, they require polyfills for Node.js builtins. Browserify handles that automatically, but more modern bundlers do not. Moreover, the polyfills increase package size significantly. To avoid these problems, web builds of `ndarray-pixels` reimplement the same functionality with the more portable Canvas API. | ||
## Supported Formats | ||
| Platform | JPEG | PNG | Other | | ||
|----------|------|-----|-------| | ||
| Node.js | ✅ | ✅ | ❌ | | ||
| Web | ✅ | ✅ | Based on [browser support](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob) | | ||
| Platform | JPEG | PNG | Other | | ||
|----------|------|-----|-------------------------------------------------------------------------------------------------------| | ||
| Node.js | ✅ | ✅ | Based on [sharp support](https://sharp.pixelplumbing.com/) | | ||
| Web | ✅ | ✅ | Based on [browser support](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob) | | ||
@@ -85,3 +83,3 @@ ### Known Bugs | ||
▸ **getPixels**(`data`, `mimeType?`): `Promise`<`NdArray`\> | ||
▸ **getPixels**(`data`, `mimeType`): `Promise`<`NdArray`<`Uint8Array`\>\> | ||
@@ -99,12 +97,12 @@ Decodes image data to an `ndarray`. | ||
| :------ | :------ | :------ | | ||
| `data` | `string` \| `Uint8Array` | | | ||
| `mimeType?` | `string` | `image/jpeg`, `image/png`, etc. | | ||
| `data` | `Uint8Array` | | | ||
| `mimeType` | `string` | `image/jpeg`, `image/png`, etc. | | ||
#### Returns | ||
`Promise`<`NdArray`\> | ||
`Promise`<`NdArray`<`Uint8Array`\>\> | ||
#### Defined in | ||
[index.ts:17](https://github.com/donmccurdy/ndarray-pixels/blob/6f5efcc/src/index.ts#L17) | ||
[index.ts:17](https://github.com/donmccurdy/ndarray-pixels/blob/41a0530/src/index.ts#L17) | ||
@@ -130,3 +128,3 @@ ___ | ||
| :------ | :------ | :------ | | ||
| `pixels` | `NdArray`<`Data`<`number`\>\> | ndarray of shape W x H x 4. | | ||
| `pixels` | `NdArray`<`Uint8Array` \| `Uint8ClampedArray`\> | ndarray of shape W x H x 4. | | ||
| `mimeType` | `string` | `image/jpeg`, `image/png`, etc. | | ||
@@ -140,3 +138,3 @@ | ||
[index.ts:48](https://github.com/donmccurdy/ndarray-pixels/blob/6f5efcc/src/index.ts#L48) | ||
[index.ts:35](https://github.com/donmccurdy/ndarray-pixels/blob/41a0530/src/index.ts#L35) | ||
<!--- API END ---> |
import ndarray from 'ndarray'; | ||
import type { NdArray } from 'ndarray'; | ||
export type GetPixelsCallback = (err: string | Event | null, pixels?: NdArray) => void; | ||
export function getPixelsInternal( | ||
buffer: Uint8Array, | ||
mimeType: string | ||
): Promise<NdArray<Uint8Array>> { | ||
// Warn for Data URIs, URLs, and file paths. Support removed in v3. | ||
if (!(buffer instanceof Uint8Array)) { | ||
throw new Error('[ndarray-pixels] Input must be Uint8Array or Buffer.'); | ||
} | ||
export default getPixels; | ||
const blob = new Blob([buffer], { type: mimeType }); | ||
const path = URL.createObjectURL(blob); | ||
function getPixels(path: string, callback: GetPixelsCallback): void; | ||
function getPixels(path: string | Uint8Array, type: string, callback: GetPixelsCallback): void | ||
function getPixels(path: string | Uint8Array, typeOrCallback: string | GetPixelsCallback, callback?: GetPixelsCallback): void { | ||
callback = callback || typeOrCallback as GetPixelsCallback; | ||
// Construct a Blob URL for Uint8Array inputs. | ||
if (path instanceof Uint8Array) { | ||
if (typeof typeOrCallback !== 'string') { | ||
throw new Error('[ndarray-pixels] Type must be given for Uint8Array image data'); | ||
} | ||
const blob = new Blob([path], {type: typeOrCallback}); | ||
path = URL.createObjectURL(blob); | ||
} | ||
// Decode image with Canvas API. | ||
const img = new Image(); | ||
img.crossOrigin = 'anonymous'; | ||
img.onload = function() { | ||
URL.revokeObjectURL(path as string); | ||
return new Promise((resolve, reject) => { | ||
const img = new Image(); | ||
img.crossOrigin = 'anonymous'; | ||
img.onload = function () { | ||
URL.revokeObjectURL(path as string); | ||
const canvas = document.createElement('canvas'); | ||
canvas.width = img.width; | ||
canvas.height = img.height; | ||
const context = canvas.getContext('2d')!; | ||
context.drawImage(img, 0, 0); | ||
const pixels = context.getImageData(0, 0, img.width, img.height) | ||
callback!(null, ndarray(new Uint8Array(pixels.data), [img.width, img.height, 4], [4, 4*img.width, 1], 0)); | ||
} | ||
img.onerror = (err) => { | ||
URL.revokeObjectURL(path as string); | ||
callback!(err); | ||
}; | ||
img.src = path; | ||
const canvas = document.createElement('canvas'); | ||
canvas.width = img.width; | ||
canvas.height = img.height; | ||
const context = canvas.getContext('2d')!; | ||
context.drawImage(img, 0, 0); | ||
const pixels = context.getImageData(0, 0, img.width, img.height); | ||
resolve( | ||
ndarray( | ||
new Uint8Array(pixels.data), | ||
[img.width, img.height, 4], | ||
[4, 4 * img.width, 1], | ||
0 | ||
) | ||
); | ||
}; | ||
img.onerror = (err) => { | ||
URL.revokeObjectURL(path as string); | ||
reject(err); | ||
}; | ||
img.src = path; | ||
}); | ||
} |
@@ -1,21 +0,26 @@ | ||
import ndarray from 'ndarray'; | ||
import type { NdArray } from 'ndarray'; | ||
import ops from 'ndarray-ops'; | ||
import { putPixelData } from './common'; | ||
export interface SavePixelsOptions {quality?: number} | ||
export interface EncoderOptions { | ||
quality?: number; | ||
} | ||
export default savePixels; | ||
function savePixels(array: NdArray, type: 'canvas'): HTMLCanvasElement; | ||
function savePixels(array: NdArray, type: 'png'): Readable; | ||
function savePixels(array: NdArray, type: 'jpeg' | 'jpg', options?: SavePixelsOptions): Readable; | ||
function savePixels( | ||
array: NdArray, type: 'canvas' | 'png' | 'jpeg' | 'jpg', | ||
options: SavePixelsOptions = {} | ||
): Readable | HTMLCanvasElement { | ||
export async function savePixelsInternal( | ||
pixels: NdArray<Uint8Array | Uint8ClampedArray>, | ||
mimeType: string | ||
): Promise<Uint8Array>; | ||
export async function savePixelsInternal( | ||
pixels: NdArray<Uint8Array | Uint8ClampedArray>, | ||
mimeType: string, | ||
options?: EncoderOptions | ||
): Promise<Uint8Array>; | ||
export async function savePixelsInternal( | ||
pixels: NdArray<Uint8Array | Uint8ClampedArray>, | ||
mimeType: string, | ||
options: EncoderOptions = {} | ||
): Promise<Uint8Array> { | ||
// Create HTMLCanvasElement and write pixel data. | ||
const canvas = document.createElement('canvas'); | ||
canvas.width = array.shape[0]; | ||
canvas.height = array.shape[1]; | ||
canvas.width = pixels.shape[0]; | ||
canvas.height = pixels.shape[1]; | ||
@@ -25,9 +30,3 @@ const context = canvas.getContext('2d')!; | ||
try { | ||
handleData(array, imageData.data); | ||
} catch (e) { | ||
// Pass errors to stream, to match 'save-pixels' behavior. | ||
return Readable.from(Promise.reject(e)); | ||
} | ||
putPixelData(pixels, imageData.data); | ||
context.putImageData(imageData, 0, 0); | ||
@@ -38,12 +37,7 @@ | ||
// Encode to target format. | ||
switch (type) { | ||
case 'canvas': | ||
return canvas; | ||
case 'jpg': | ||
case 'jpeg': | ||
switch (mimeType) { | ||
case 'image/jpeg': | ||
return streamCanvas(canvas, 'image/jpeg', quality); | ||
case 'png': | ||
return streamCanvas(canvas, 'image/png'); | ||
default: | ||
throw new Error('[ndarray-pixels] Unsupported file type: ' + type); | ||
return streamCanvas(canvas, mimeType); | ||
} | ||
@@ -53,117 +47,20 @@ } | ||
/** Creates readable stream from given HTMLCanvasElement and options. */ | ||
function streamCanvas(canvas: HTMLCanvasElement, mimeType: string, quality?: number): Readable { | ||
const promise = new Promise<Uint8Array>((resolve, reject) => { | ||
canvas.toBlob(async (blob) => { | ||
if (blob) { | ||
resolve(new Uint8Array(await blob.arrayBuffer())); | ||
} else { | ||
reject(new Error('[ndarray-pixels] Failed to canvas.toBlob().')); | ||
} | ||
}, mimeType, quality); | ||
function streamCanvas( | ||
canvas: HTMLCanvasElement, | ||
mimeType: string, | ||
quality?: number | ||
): Promise<Uint8Array> { | ||
return new Promise<Uint8Array>((resolve, reject) => { | ||
canvas.toBlob( | ||
async (blob) => { | ||
if (blob) { | ||
resolve(new Uint8Array(await blob.arrayBuffer())); | ||
} else { | ||
reject(new Error('[ndarray-pixels] Failed to canvas.toBlob().')); | ||
} | ||
}, | ||
mimeType, | ||
quality | ||
); | ||
}); | ||
return Readable.from(promise); | ||
} | ||
function handleData( | ||
array: NdArray, | ||
data: Uint8Array | Uint8ClampedArray, | ||
frame = -1 | ||
): Uint8Array | Uint8ClampedArray { | ||
if (array.shape.length === 4) { | ||
return handleData(array.pick(frame), data, 0); | ||
} else if (array.shape.length === 3) { | ||
if (array.shape[2] === 3) { | ||
ops.assign( | ||
ndarray( | ||
data, | ||
[array.shape[0], array.shape[1], 3], | ||
[4, 4 * array.shape[0], 1] | ||
), | ||
array | ||
); | ||
ops.assigns( | ||
ndarray( | ||
data, | ||
[array.shape[0] * array.shape[1]], | ||
[4], | ||
3 | ||
), | ||
255 | ||
); | ||
} else if (array.shape[2] === 4) { | ||
ops.assign( | ||
ndarray( | ||
data, | ||
[array.shape[0], array.shape[1], 4], | ||
[4, array.shape[0] * 4, 1] | ||
), | ||
array | ||
); | ||
} else if (array.shape[2] === 1) { | ||
ops.assign( | ||
ndarray( | ||
data, | ||
[array.shape[0], array.shape[1], 3], | ||
[4, 4 * array.shape[0], 1] | ||
), | ||
ndarray( | ||
array.data, | ||
[array.shape[0], array.shape[1], 3], | ||
[array.stride[0], array.stride[1], 0], | ||
array.offset | ||
) | ||
); | ||
ops.assigns( | ||
ndarray( | ||
data, | ||
[array.shape[0] * array.shape[1]], | ||
[4], | ||
3 | ||
), | ||
255 | ||
); | ||
} else { | ||
throw new Error('[ndarray-pixels] Incompatible array shape.'); | ||
} | ||
} else if (array.shape.length === 2) { | ||
ops.assign( | ||
ndarray(data, | ||
[array.shape[0], array.shape[1], 3], | ||
[4, 4 * array.shape[0], 1]), | ||
ndarray(array.data, | ||
[array.shape[0], array.shape[1], 3], | ||
[array.stride[0], array.stride[1], 0], | ||
array.offset) | ||
); | ||
ops.assigns( | ||
ndarray(data, | ||
[array.shape[0] * array.shape[1]], | ||
[4], | ||
3), | ||
255 | ||
); | ||
} else { | ||
throw new Error('[ndarray-pixels] Incompatible array shape.'); | ||
} | ||
return data; | ||
} | ||
class Readable { | ||
constructor (private _promise: Promise<Uint8Array>) {} | ||
on(event: 'data' | 'error' | 'end', fn: (res?: Uint8Array | Error) => void): this { | ||
if (event === 'data') { | ||
this._promise.then(fn); | ||
} else if (event === 'error') { | ||
this._promise.catch(fn) | ||
} else if (event === 'end') { | ||
this._promise.finally(fn); | ||
} | ||
return this; | ||
} | ||
static from (promise: Promise<Uint8Array>): Readable { | ||
return new Readable(promise); | ||
} | ||
} |
@@ -1,4 +0,4 @@ | ||
import getPixelsInternal from 'get-pixels'; | ||
import type { NdArray } from 'ndarray'; | ||
import savePixelsInternal from 'save-pixels'; | ||
import { getPixelsInternal } from './node-get-pixels'; | ||
import { savePixelsInternal } from './node-save-pixels'; | ||
@@ -17,17 +17,4 @@ /** | ||
*/ | ||
async function getPixels (data: string | Uint8Array, mimeType?: string): Promise<NdArray> { | ||
// In Node.js, get-pixels needs a Buffer and won't accept Uint8Array. | ||
if (data instanceof Uint8Array && typeof Buffer !== 'undefined') { | ||
data = Buffer.from(data); | ||
} | ||
return new Promise((resolve, reject) => { | ||
getPixelsInternal(data, mimeType!, (err: Error | null, pixels: NdArray) => { | ||
if (pixels && !err) { | ||
resolve(pixels); | ||
} else { | ||
reject(err); | ||
} | ||
}); | ||
}); | ||
async function getPixels(data: Uint8Array, mimeType: string): Promise<NdArray<Uint8Array>> { | ||
return getPixelsInternal(data, mimeType); | ||
} | ||
@@ -49,30 +36,9 @@ | ||
*/ | ||
async function savePixels (pixels: NdArray, mimeType: string): Promise<Uint8Array> { | ||
return new Promise((resolve, reject) => { | ||
const chunks: Uint8Array[] = []; | ||
const internalType = mimeType.replace('image/', '') as 'png' | 'gif'; | ||
savePixelsInternal(pixels, internalType) | ||
.on('data', (d: Uint8Array) => chunks.push(d)) | ||
.on('end', () => resolve(concat(chunks))) | ||
.on('error', (e: Error) => reject(e)); | ||
}); | ||
async function savePixels( | ||
pixels: NdArray<Uint8Array | Uint8ClampedArray>, | ||
mimeType: string | ||
): Promise<Uint8Array> { | ||
return savePixelsInternal(pixels, mimeType); | ||
} | ||
function concat (arrays: Uint8Array[]): Uint8Array { | ||
let totalByteLength = 0; | ||
for (const array of arrays) { | ||
totalByteLength += array.byteLength; | ||
} | ||
const result = new Uint8Array(totalByteLength); | ||
let byteOffset = 0; | ||
for (const array of arrays) { | ||
result.set(array, byteOffset); | ||
byteOffset += array.byteLength; | ||
} | ||
return result; | ||
} | ||
export {getPixels, savePixels}; | ||
export { getPixels, savePixels }; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
78679
4
18
23
703
1
136
1
+ Addedsharp@^0.32.1
+ Addedb4a@1.6.7(transitive)
+ Addedbare-events@2.5.0(transitive)
+ Addedbare-fs@2.3.5(transitive)
+ Addedbare-os@2.4.4(transitive)
+ Addedbare-path@2.1.3(transitive)
+ Addedbare-stream@2.4.2(transitive)
+ Addedbase64-js@1.5.1(transitive)
+ Addedbl@4.1.0(transitive)
+ Addedbuffer@5.7.1(transitive)
+ Addedchownr@1.1.4(transitive)
+ Addedcolor@4.2.3(transitive)
+ Addedcolor-convert@2.0.1(transitive)
+ Addedcolor-name@1.1.4(transitive)
+ Addedcolor-string@1.9.1(transitive)
+ Addeddecompress-response@6.0.0(transitive)
+ Addeddeep-extend@0.6.0(transitive)
+ Addeddetect-libc@2.0.3(transitive)
+ Addedend-of-stream@1.4.4(transitive)
+ Addedexpand-template@2.0.3(transitive)
+ Addedfast-fifo@1.3.2(transitive)
+ Addedfs-constants@1.0.0(transitive)
+ Addedgithub-from-package@0.0.0(transitive)
+ Addedieee754@1.2.1(transitive)
+ Addedini@1.3.8(transitive)
+ Addedis-arrayish@0.3.2(transitive)
+ Addedmimic-response@3.1.0(transitive)
+ Addedminimist@1.2.8(transitive)
+ Addedmkdirp-classic@0.5.3(transitive)
+ Addednapi-build-utils@1.0.2(transitive)
+ Addednode-abi@3.71.0(transitive)
+ Addednode-addon-api@6.1.0(transitive)
+ Addedonce@1.4.0(transitive)
+ Addedprebuild-install@7.1.2(transitive)
+ Addedpump@3.0.2(transitive)
+ Addedqueue-tick@1.0.1(transitive)
+ Addedrc@1.2.8(transitive)
+ Addedreadable-stream@3.6.2(transitive)
+ Addedsemver@7.6.3(transitive)
+ Addedsharp@0.32.6(transitive)
+ Addedsimple-concat@1.0.1(transitive)
+ Addedsimple-get@4.0.1(transitive)
+ Addedsimple-swizzle@0.2.2(transitive)
+ Addedstreamx@2.20.2(transitive)
+ Addedstring_decoder@1.3.0(transitive)
+ Addedstrip-json-comments@2.0.1(transitive)
+ Addedtar-fs@2.1.13.0.6(transitive)
+ Addedtar-stream@2.2.03.1.7(transitive)
+ Addedtext-decoder@1.2.1(transitive)
+ Addedutil-deprecate@1.0.2(transitive)
+ Addedwrappy@1.0.2(transitive)
- Removedget-pixels@^3.3.3
- Removedsave-pixels@^2.3.6
- Removedajv@6.12.6(transitive)
- Removedasn1@0.2.6(transitive)
- Removedassert-plus@1.0.0(transitive)
- Removedasynckit@0.4.0(transitive)
- Removedaws-sign2@0.7.0(transitive)
- Removedaws4@1.13.2(transitive)
- Removedbcrypt-pbkdf@1.0.2(transitive)
- Removedcaseless@0.12.0(transitive)
- Removedcombined-stream@1.0.8(transitive)
- Removedcontentstream@1.0.0(transitive)
- Removedcore-util-is@1.0.21.0.3(transitive)
- Removeddashdash@1.14.1(transitive)
- Removeddata-uri-to-buffer@0.0.3(transitive)
- Removeddelayed-stream@1.0.0(transitive)
- Removedecc-jsbn@0.1.2(transitive)
- Removedextend@3.0.2(transitive)
- Removedextsprintf@1.3.0(transitive)
- Removedfast-deep-equal@3.1.3(transitive)
- Removedfast-json-stable-stringify@2.1.0(transitive)
- Removedforever-agent@0.6.1(transitive)
- Removedform-data@2.3.3(transitive)
- Removedget-pixels@3.3.3(transitive)
- Removedgetpass@0.1.7(transitive)
- Removedgif-encoder@0.4.3(transitive)
- Removedhar-schema@2.0.0(transitive)
- Removedhar-validator@5.1.5(transitive)
- Removedhttp-signature@1.2.0(transitive)
- Removedis-typedarray@1.0.0(transitive)
- Removedisarray@0.0.1(transitive)
- Removedisstream@0.1.2(transitive)
- Removedjpeg-js@0.4.4(transitive)
- Removedjsbn@0.1.1(transitive)
- Removedjson-schema@0.4.0(transitive)
- Removedjson-schema-traverse@0.4.1(transitive)
- Removedjson-stringify-safe@5.0.1(transitive)
- Removedjsprim@1.4.2(transitive)
- Removedmime-db@1.52.0(transitive)
- Removedmime-types@2.1.35(transitive)
- Removedndarray-pack@1.2.1(transitive)
- Removednode-bitmap@0.0.1(transitive)
- Removedoauth-sign@0.9.0(transitive)
- Removedomggif@1.0.10(transitive)
- Removedparse-data-uri@0.2.0(transitive)
- Removedperformance-now@2.1.0(transitive)
- Removedpngjs@3.4.0(transitive)
- Removedpngjs-nozlib@1.0.0(transitive)
- Removedpsl@1.14.0(transitive)
- Removedpunycode@2.3.1(transitive)
- Removedqs@6.5.3(transitive)
- Removedreadable-stream@1.0.341.1.14(transitive)
- Removedrequest@2.88.2(transitive)
- Removedsafer-buffer@2.1.2(transitive)
- Removedsave-pixels@2.3.6(transitive)
- Removedsshpk@1.18.0(transitive)
- Removedstring_decoder@0.10.31(transitive)
- Removedthrough@2.3.8(transitive)
- Removedtough-cookie@2.5.0(transitive)
- Removedtweetnacl@0.14.5(transitive)
- Removeduri-js@4.4.1(transitive)
- Removeduuid@3.4.0(transitive)
- Removedverror@1.10.0(transitive)
Updated@types/ndarray@^1.0.11