Skip to content

Commit

Permalink
Feature scanIterator (#781)
Browse files Browse the repository at this point in the history
* Feature scanIterator

* Redo `scan` uses scanIterator

* Note scanIterator in README.md
  • Loading branch information
ozelot379 authored and hipstersmoothie committed Sep 6, 2019
1 parent aa93772 commit b2f8dfb
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 1 deletion.
22 changes: 21 additions & 1 deletion packages/core/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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) {
Expand Down
8 changes: 8 additions & 0 deletions packages/jimp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions packages/jimp/jimp.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
25 changes: 25 additions & 0 deletions packages/jimp/test/scan.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
10 changes: 10 additions & 0 deletions packages/utils/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {

}
```
15 changes: 15 additions & 0 deletions packages/utils/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,18 @@ export function scan(image, x, y, w, h, f) {

return image;
}

export function* scanIterator(image, x, y, w, h) {
// 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;
yield { x: _x, y: _y, idx, image };
}
}
}

0 comments on commit b2f8dfb

Please sign in to comment.