Skip to content

Commit a9870ad

Browse files
committed
Avoid branching in convertBlackAndWhiteToRGBA
The function is now almost 8x faster than before. And make the code in the file slightly more readable.
1 parent 2b95a8e commit a9870ad

1 file changed

Lines changed: 36 additions & 29 deletions

File tree

src/shared/image_utils.js

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -41,30 +41,36 @@ function convertBlackAndWhiteToRGBA({
4141
: [black, nonBlackColor];
4242
const widthInSource = width >> 3;
4343
const widthRemainder = width & 7;
44+
const xorMask = zeroMapping ^ oneMapping;
4445
const srcLength = src.length;
4546
dest = new Uint32Array(dest.buffer);
4647
let destPos = 0;
4748

48-
for (let i = 0; i < height; i++) {
49-
for (const max = srcPos + widthInSource; srcPos < max; srcPos++) {
50-
const elem = srcPos < srcLength ? src[srcPos] : 255;
51-
dest[destPos++] = elem & 0b10000000 ? oneMapping : zeroMapping;
52-
dest[destPos++] = elem & 0b1000000 ? oneMapping : zeroMapping;
53-
dest[destPos++] = elem & 0b100000 ? oneMapping : zeroMapping;
54-
dest[destPos++] = elem & 0b10000 ? oneMapping : zeroMapping;
55-
dest[destPos++] = elem & 0b1000 ? oneMapping : zeroMapping;
56-
dest[destPos++] = elem & 0b100 ? oneMapping : zeroMapping;
57-
dest[destPos++] = elem & 0b10 ? oneMapping : zeroMapping;
58-
dest[destPos++] = elem & 0b1 ? oneMapping : zeroMapping;
49+
for (let i = 0; i < height; ++i) {
50+
for (
51+
const max = srcPos + widthInSource;
52+
srcPos < max;
53+
++srcPos, destPos += 8
54+
) {
55+
const elem = src[srcPos];
56+
dest[destPos] = zeroMapping ^ (-((elem >> 7) & 1) & xorMask);
57+
dest[destPos + 1] = zeroMapping ^ (-((elem >> 6) & 1) & xorMask);
58+
dest[destPos + 2] = zeroMapping ^ (-((elem >> 5) & 1) & xorMask);
59+
dest[destPos + 3] = zeroMapping ^ (-((elem >> 4) & 1) & xorMask);
60+
dest[destPos + 4] = zeroMapping ^ (-((elem >> 3) & 1) & xorMask);
61+
dest[destPos + 5] = zeroMapping ^ (-((elem >> 2) & 1) & xorMask);
62+
dest[destPos + 6] = zeroMapping ^ (-((elem >> 1) & 1) & xorMask);
63+
dest[destPos + 7] = zeroMapping ^ (-(elem & 1) & xorMask);
5964
}
6065
if (widthRemainder === 0) {
6166
continue;
6267
}
6368
const elem = srcPos < srcLength ? src[srcPos++] : 255;
64-
for (let j = 0; j < widthRemainder; j++) {
65-
dest[destPos++] = elem & (1 << (7 - j)) ? oneMapping : zeroMapping;
69+
for (let j = 0; j < widthRemainder; ++j, ++destPos) {
70+
dest[destPos] = zeroMapping ^ (-((elem >> (7 - j)) & 1) & xorMask);
6671
}
6772
}
73+
6874
return { srcPos, destPos };
6975
}
7076

@@ -80,40 +86,41 @@ function convertRGBToRGBA({
8086
const len = width * height * 3;
8187
const len32 = len >> 2;
8288
const src32 = new Uint32Array(src.buffer, srcPos, len32);
89+
const alphaMask = FeatureTest.isLittleEndian ? 0xff000000 : 0xff;
8390

8491
if (FeatureTest.isLittleEndian) {
8592
// It's a way faster to do the shuffle manually instead of working
8693
// component by component with some Uint8 arrays.
8794
for (; i < len32 - 2; i += 3, destPos += 4) {
88-
const s1 = src32[i]; // R2B1G1R1
89-
const s2 = src32[i + 1]; // G3R3B2G2
90-
const s3 = src32[i + 2]; // B4G4R4B3
95+
const s1 = src32[i], // R2B1G1R1
96+
s2 = src32[i + 1], // G3R3B2G2
97+
s3 = src32[i + 2]; // B4G4R4B3
9198

92-
dest[destPos] = s1 | 0xff000000;
93-
dest[destPos + 1] = (s1 >>> 24) | (s2 << 8) | 0xff000000;
94-
dest[destPos + 2] = (s2 >>> 16) | (s3 << 16) | 0xff000000;
95-
dest[destPos + 3] = (s3 >>> 8) | 0xff000000;
99+
dest[destPos] = s1 | alphaMask;
100+
dest[destPos + 1] = (s1 >>> 24) | (s2 << 8) | alphaMask;
101+
dest[destPos + 2] = (s2 >>> 16) | (s3 << 16) | alphaMask;
102+
dest[destPos + 3] = (s3 >>> 8) | alphaMask;
96103
}
97104

98105
for (let j = i * 4, jj = srcPos + len; j < jj; j += 3) {
99106
dest[destPos++] =
100-
src[j] | (src[j + 1] << 8) | (src[j + 2] << 16) | 0xff000000;
107+
src[j] | (src[j + 1] << 8) | (src[j + 2] << 16) | alphaMask;
101108
}
102109
} else {
103110
for (; i < len32 - 2; i += 3, destPos += 4) {
104-
const s1 = src32[i]; // R1G1B1R2
105-
const s2 = src32[i + 1]; // G2B2R3G3
106-
const s3 = src32[i + 2]; // B3R4G4B4
111+
const s1 = src32[i], // R1G1B1R2
112+
s2 = src32[i + 1], // G2B2R3G3
113+
s3 = src32[i + 2]; // B3R4G4B4
107114

108-
dest[destPos] = s1 | 0xff;
109-
dest[destPos + 1] = (s1 << 24) | (s2 >>> 8) | 0xff;
110-
dest[destPos + 2] = (s2 << 16) | (s3 >>> 16) | 0xff;
111-
dest[destPos + 3] = (s3 << 8) | 0xff;
115+
dest[destPos] = s1 | alphaMask;
116+
dest[destPos + 1] = (s1 << 24) | (s2 >>> 8) | alphaMask;
117+
dest[destPos + 2] = (s2 << 16) | (s3 >>> 16) | alphaMask;
118+
dest[destPos + 3] = (s3 << 8) | alphaMask;
112119
}
113120

114121
for (let j = i * 4, jj = srcPos + len; j < jj; j += 3) {
115122
dest[destPos++] =
116-
(src[j] << 24) | (src[j + 1] << 16) | (src[j + 2] << 8) | 0xff;
123+
(src[j] << 24) | (src[j + 1] << 16) | (src[j + 2] << 8) | alphaMask;
117124
}
118125
}
119126

0 commit comments

Comments
 (0)