Skip to content

Commit

Permalink
Improve color operations performance (#1263)
Browse files Browse the repository at this point in the history
* Update phash.js - improve intToRGBA performance

Converted to bitwise operators to improve performance at high volumes.

* Update index.js - Improve performance of rgbaToInt

Converted math to bitwise operators to improve performance at high volumes.

* Update index.js - Improve performance of hasAlpha

Moved any repetitive mathematical operations outside the core loop and simplified accessors to allow for maximum JIT compile optimizations to take place.

* Update index.js - Improve performance of get/setPixelColor

Remove redundant calls to Math.round as it is called again within getPixelIndex in both cases.

* Update index.js - Fix high bit on color conversion can cause negative number

Added operation to ensure 32-bit color output is interpreted as a positive integer.

* Update index.js - Fix negative color value (again)

Final operation can "re-negate" the value - moved the conversion to unsigned int to be after alpha integration into the value.

* Update phash.js - fixed negatives the other way

Also issues with high-bit values going from int to RGBA - fixed.

* force build

---------

Co-authored-by: Andrew Lisowski <[email protected]>
  • Loading branch information
DLiblik and hipstersmoothie authored Feb 23, 2024
1 parent 7991f7e commit 65e3792
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 41 deletions.
38 changes: 14 additions & 24 deletions packages/core/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -739,10 +739,6 @@ class Jimp extends EventEmitter {
if (typeof x !== "number" || typeof y !== "number")
return throwError.call(this, "x and y must be numbers", cb);

// round input
x = Math.round(x);
y = Math.round(y);

const idx = this.getPixelIndex(x, y);
const hex = this.bitmap.data.readUInt32BE(idx);

Expand Down Expand Up @@ -771,10 +767,6 @@ class Jimp extends EventEmitter {
)
return throwError.call(this, "hex, x and y must be numbers", cb);

// round input
x = Math.round(x);
y = Math.round(y);

const idx = this.getPixelIndex(x, y);
this.bitmap.data.writeUInt32BE(hex, idx);

Expand All @@ -792,14 +784,12 @@ class Jimp extends EventEmitter {
* @return {boolean} hasAlpha whether the image contains opaque pixels
*/
hasAlpha() {
for (let yIndex = 0; yIndex < this.bitmap.height; yIndex++) {
for (let xIndex = 0; xIndex < this.bitmap.width; xIndex++) {
const idx = (this.bitmap.width * yIndex + xIndex) << 2;
const alpha = this.bitmap.data[idx + 3];
const {width, height, data} = this.bitmap;
const byteLen = (width * height) << 2;

if (alpha !== 0xff) {
return true;
}
for (let idx = 3; idx < byteLen; idx += 4) {
if (data[idx] !== 0xff) {
return true;
}
}

Expand Down Expand Up @@ -905,16 +895,16 @@ Jimp.rgbaToInt = function (r, g, b, a, cb) {
return throwError.call(this, "a must be between 0 and 255", cb);
}

r = Math.round(r);
b = Math.round(b);
g = Math.round(g);
a = Math.round(a);
let i = (r & 0xff);
i <<= 8;
i |= (g & 0xff)
i <<= 8;
i |= (b & 0xff)
i <<= 8;
i |= (a & 0xff);

const i =
r * Math.pow(256, 3) +
g * Math.pow(256, 2) +
b * Math.pow(256, 1) +
a * Math.pow(256, 0);
// Ensure sign is correct
i >>>= 0;

if (isNodePattern(cb)) {
cb.call(this, null, i);
Expand Down
29 changes: 12 additions & 17 deletions packages/core/src/modules/phash.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,24 +125,19 @@ ImagePHash.prototype.getHash = function (img) {

// DCT function stolen from http://stackoverflow.com/questions/4240490/problems-with-dct-and-idct-algorithm-in-java

/**
Convert a 32-bit integer color value to an RGBA object.
*/
function intToRGBA(i) {
const rgba = {};

rgba.r = Math.floor(i / Math.pow(256, 3));
rgba.g = Math.floor((i - rgba.r * Math.pow(256, 3)) / Math.pow(256, 2));
rgba.b = Math.floor(
(i - rgba.r * Math.pow(256, 3) - rgba.g * Math.pow(256, 2)) /
Math.pow(256, 1)
);
rgba.a = Math.floor(
(i -
rgba.r * Math.pow(256, 3) -
rgba.g * Math.pow(256, 2) -
rgba.b * Math.pow(256, 1)) /
Math.pow(256, 0)
);

return rgba;
const a = i & 0xff;
i >>>= 8;
const b = i & 0xff;
i >>>= 8;
const g = i & 0xff;
i >>>= 8;
const r = i & 0xff;

return {r, g, b, a};
}

const c = [];
Expand Down

0 comments on commit 65e3792

Please sign in to comment.