From 270aa760b4263029ebeb845ba0dff1a7816f71ca Mon Sep 17 00:00:00 2001 From: ozelot379 Date: Sat, 31 Aug 2019 21:56:00 +0200 Subject: [PATCH 1/3] Feature scanIterator --- packages/core/src/index.js | 22 +++++++++++++++++++++- packages/jimp/jimp.d.ts | 6 ++++++ packages/jimp/test/scan.test.js | 25 +++++++++++++++++++++++++ packages/utils/src/index.js | 12 +++++++++--- 4 files changed, 61 insertions(+), 4 deletions(-) diff --git a/packages/core/src/index.js b/packages/core/src/index.js index 7a8d6fed1..6e07836cc 100755 --- a/packages/core/src/index.js +++ b/packages/core/src/index.js @@ -2,7 +2,7 @@ import fs from 'fs'; import Path from 'path'; import EventEmitter from 'events'; -import { isNodePattern, throwError, scan } from '@jimp/utils'; +import { isNodePattern, throwError, scan, scanIterator } from '@jimp/utils'; import anyBase from 'any-base'; import mkdirp from 'mkdirp'; import pixelMatch from 'pixelmatch'; @@ -811,6 +811,26 @@ class Jimp extends EventEmitter { return false; } + + /** + * Iterate scan through a region of the bitmap + * @param {number} x the x coordinate to begin the scan at + * @param {number} y the y coordinate to begin the scan at + * @param w the width of the scan region + * @param h the height of the scan region + * @returns {IterableIterator<{x: number, y: number, idx: number, image: Jimp}>} + */ + scanIterator(x, y, w, h) { + if (typeof x !== 'number' || typeof y !== 'number') { + return throwError.call(this, 'x and y must be numbers'); + } + + if (typeof w !== 'number' || typeof h !== 'number') { + return throwError.call(this, 'w and h must be numbers'); + } + + return scanIterator(this, x, y, w, h); + } } export function addConstants(constants, jimpInstance = Jimp) { diff --git a/packages/jimp/jimp.d.ts b/packages/jimp/jimp.d.ts index 2ae0c15c4..8d4ab890d 100644 --- a/packages/jimp/jimp.d.ts +++ b/packages/jimp/jimp.d.ts @@ -177,6 +177,12 @@ export interface Jimp { f: (this: this, x: number, y: number, idx: number) => any, cb?: ImageCallback ): this; + scanIterator( + x: number, + y: number, + w: number, + h: number + ): IterableIterator<{x: number, y: number, idx: number, image: Jimp}>; crop(x: number, y: number, w: number, h: number, cb?: ImageCallback): this; cropQuiet( x: number, diff --git a/packages/jimp/test/scan.test.js b/packages/jimp/test/scan.test.js index 259a1cfce..9a8ad574c 100644 --- a/packages/jimp/test/scan.test.js +++ b/packages/jimp/test/scan.test.js @@ -24,6 +24,31 @@ describe('Scan (pixel matrix modification)', () => { .should.be.sameJGD(barsJGD, 'Color bars'); }); + it('draw bars with iterate scan', async () => { + const image = await Jimp.create(8, 3); + + for (const { x, y, idx, image } of image.scanIterator( + 0, + 0, + image.bitmap.width, + image.bitmap.height + )) { + const color = [ + [0xff, 0x00, 0x00], + [0x00, 0xff, 0x00], + [0x00, 0x00, 0xff], + [0xff, 0xff, 0x00] + ][Math.floor(x / (image.bitmap.width / 4))]; + + image.bitmap.data[idx] = color[0]; + image.bitmap.data[idx + 1] = color[1]; + image.bitmap.data[idx + 2] = color[2]; + image.bitmap.data[idx + 3] = y === 2 ? 0x7f : 0xff; + } + + image.getJGDSync().should.be.sameJGD(barsJGD, 'Color bars'); + }); + it('draw bars with (get|set)PixelColor', async () => { const image = await Jimp.read(barsJGD); diff --git a/packages/utils/src/index.js b/packages/utils/src/index.js index 97708277d..8a10b208a 100644 --- a/packages/utils/src/index.js +++ b/packages/utils/src/index.js @@ -23,6 +23,14 @@ export function throwError(error, cb) { } export function scan(image, x, y, w, h, f) { + for (const { x, y, idx, image } of scanIterator(image, x, y, w, h)) { + f.call(image, x, y, idx); + } + + return image; +} + +export function* scanIterator(image, x, y, w, h) { // round input x = Math.round(x); y = Math.round(y); @@ -32,9 +40,7 @@ export function scan(image, x, y, w, h, f) { for (let _y = y; _y < y + h; _y++) { for (let _x = x; _x < x + w; _x++) { const idx = (image.bitmap.width * _y + _x) << 2; - f.call(image, _x, _y, idx); + yield { x: _x, y: _y, idx, image }; } } - - return image; } From 489e42870d6c89a2a0721baf62a550004541a6bb Mon Sep 17 00:00:00 2001 From: ozelot379 Date: Tue, 3 Sep 2019 19:09:10 +0200 Subject: [PATCH 2/3] Redo `scan` uses scanIterator --- packages/utils/src/index.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/utils/src/index.js b/packages/utils/src/index.js index 8a10b208a..8f51fd356 100644 --- a/packages/utils/src/index.js +++ b/packages/utils/src/index.js @@ -23,8 +23,17 @@ export function throwError(error, cb) { } export function scan(image, x, y, w, h, f) { - for (const { x, y, idx, image } of scanIterator(image, x, y, w, h)) { - f.call(image, x, y, idx); + // round input + x = Math.round(x); + y = Math.round(y); + w = Math.round(w); + h = Math.round(h); + + for (let _y = y; _y < y + h; _y++) { + for (let _x = x; _x < x + w; _x++) { + const idx = (image.bitmap.width * _y + _x) << 2; + f.call(image, _x, _y, idx); + } } return image; From a9a5e3de8500ce8a011464ec6325190bf9d12751 Mon Sep 17 00:00:00 2001 From: ozelot379 Date: Thu, 5 Sep 2019 19:11:01 +0200 Subject: [PATCH 3/3] Note scanIterator in README.md --- packages/jimp/README.md | 8 ++++++++ packages/utils/README.md | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/packages/jimp/README.md b/packages/jimp/README.md index 520f50617..40ef8aec6 100644 --- a/packages/jimp/README.md +++ b/packages/jimp/README.md @@ -637,6 +637,14 @@ image.scan(0, 0, image.bitmap.width, image.bitmap.height, function(x, y, idx) { }); ``` +It's possible to make an iterator scan with a `for ... of`, if you want to `break` the scan before it reaches the end, but note, that this iterator has a huge performance implication: + +```js +for (const { x, y, idx, image } of image.scanIterator(0, 0, image.bitmap.width, image.bitmap.height)) { + +} +``` + A helper to locate a particular pixel within the raw bitmap buffer: ```js diff --git a/packages/utils/README.md b/packages/utils/README.md index c48b87f5c..01678e733 100644 --- a/packages/utils/README.md +++ b/packages/utils/README.md @@ -50,3 +50,13 @@ function removeRed(image) { }); } ``` + +### scanIterator + +It's possible to make an iterator scan with a `for ... of`, if you want to `break` the scan before it reaches the end, but note, that this iterator has a huge performance implication: + +```js +for (const { x, y, idx, image } of scanIterator(image, 0, 0, image.bitmap.width, image.bitmap.height)) { + +} +```