Skip to content

Commit 65e3792

Browse files
Improve color operations performance (#1263)
* 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]>
1 parent 7991f7e commit 65e3792

File tree

2 files changed

+26
-41
lines changed

2 files changed

+26
-41
lines changed

packages/core/src/index.js

+14-24
Original file line numberDiff line numberDiff line change
@@ -739,10 +739,6 @@ class Jimp extends EventEmitter {
739739
if (typeof x !== "number" || typeof y !== "number")
740740
return throwError.call(this, "x and y must be numbers", cb);
741741

742-
// round input
743-
x = Math.round(x);
744-
y = Math.round(y);
745-
746742
const idx = this.getPixelIndex(x, y);
747743
const hex = this.bitmap.data.readUInt32BE(idx);
748744

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

774-
// round input
775-
x = Math.round(x);
776-
y = Math.round(y);
777-
778770
const idx = this.getPixelIndex(x, y);
779771
this.bitmap.data.writeUInt32BE(hex, idx);
780772

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

800-
if (alpha !== 0xff) {
801-
return true;
802-
}
790+
for (let idx = 3; idx < byteLen; idx += 4) {
791+
if (data[idx] !== 0xff) {
792+
return true;
803793
}
804794
}
805795

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

908-
r = Math.round(r);
909-
b = Math.round(b);
910-
g = Math.round(g);
911-
a = Math.round(a);
898+
let i = (r & 0xff);
899+
i <<= 8;
900+
i |= (g & 0xff)
901+
i <<= 8;
902+
i |= (b & 0xff)
903+
i <<= 8;
904+
i |= (a & 0xff);
912905

913-
const i =
914-
r * Math.pow(256, 3) +
915-
g * Math.pow(256, 2) +
916-
b * Math.pow(256, 1) +
917-
a * Math.pow(256, 0);
906+
// Ensure sign is correct
907+
i >>>= 0;
918908

919909
if (isNodePattern(cb)) {
920910
cb.call(this, null, i);

packages/core/src/modules/phash.js

+12-17
Original file line numberDiff line numberDiff line change
@@ -125,24 +125,19 @@ ImagePHash.prototype.getHash = function (img) {
125125

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

128+
/**
129+
Convert a 32-bit integer color value to an RGBA object.
130+
*/
128131
function intToRGBA(i) {
129-
const rgba = {};
130-
131-
rgba.r = Math.floor(i / Math.pow(256, 3));
132-
rgba.g = Math.floor((i - rgba.r * Math.pow(256, 3)) / Math.pow(256, 2));
133-
rgba.b = Math.floor(
134-
(i - rgba.r * Math.pow(256, 3) - rgba.g * Math.pow(256, 2)) /
135-
Math.pow(256, 1)
136-
);
137-
rgba.a = Math.floor(
138-
(i -
139-
rgba.r * Math.pow(256, 3) -
140-
rgba.g * Math.pow(256, 2) -
141-
rgba.b * Math.pow(256, 1)) /
142-
Math.pow(256, 0)
143-
);
144-
145-
return rgba;
132+
const a = i & 0xff;
133+
i >>>= 8;
134+
const b = i & 0xff;
135+
i >>>= 8;
136+
const g = i & 0xff;
137+
i >>>= 8;
138+
const r = i & 0xff;
139+
140+
return {r, g, b, a};
146141
}
147142

148143
const c = [];

0 commit comments

Comments
 (0)