Skip to content

Commit e8f0772

Browse files
committed
speed up calcEnergySum
- moved left and right edges outside the loop, the avoid Math.min and Math.max when getting the left/right pixel indexes. - added branchless min and max functions. down to 2.7s for the dog and 3.8s for the castle.
1 parent 02001c0 commit e8f0772

2 files changed

Lines changed: 63 additions & 18 deletions

File tree

‎build/index.js‎

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ function findSeams(data, width, height, removeCount, onRemoveSeam) {
2020
return workingData;
2121
}
2222
exports.findSeams = findSeams;
23+
// filthy branchless max and min
24+
// if x - y is positive, (x - y) >> 31 will be all 0s
25+
// if x - y is negative, (x - y) >> 31 will be all 1s
26+
// & that with x - y to either get 0 or x - y
27+
function fastMin(x, y) {
28+
return y + ((x - y) & ((x - y) >> 31));
29+
}
30+
function fastMax(x, y) {
31+
return x - ((x - y) & ((x - y) >> 31));
32+
}
2333
// remove seams in remove from data, returning a new array.
2434
function removeSeam(data, width, height, remove) {
2535
const data32 = new Uint32Array(data.buffer);
@@ -118,14 +128,23 @@ function calcEnergySum(energySum, energy, width, height) {
118128
}
119129
// populate the rest of the rows
120130
for (let y = 1; y < height; y++) {
121-
// const removex = remove[y];
122-
// for (let x = Math.max(removex - y, 0); x < Math.min(removex + y, width); x++) {
123-
for (let x = 0; x < width; x++) {
124-
const left = energySum[(y - 1) * width + Math.max(x - 1, 0)];
125-
const mid = energySum[(y - 1) * width + x];
126-
const right = energySum[(y - 1) * width + Math.min(x + 1, width - 1)];
127-
energySum[y * width + x] = energy[y * width + x] + Math.min(left, mid, right);
131+
let x = 0;
132+
// left edge
133+
const lmid = energySum[(y - 1) * width + x];
134+
const lright = energySum[(y - 1) * width + x + 1];
135+
energySum[y * width + x] = energy[y * width + x] + fastMin(lmid, lright);
136+
for (x = 1; x < width - 1; x++) {
137+
const prev = energy[y * width + x];
138+
const index = (y - 1) * width + x - 1;
139+
const left = energySum[index];
140+
const mid = energySum[index + 1];
141+
const right = energySum[index + 2];
142+
energySum[y * width + x] = prev + fastMin(fastMin(left, mid), right);
128143
}
144+
// right edge
145+
const rleft = energySum[(y - 1) * width + x - 1];
146+
const rmid = energySum[(y - 1) * width + x];
147+
energySum[y * width + x] = energy[y * width + x] + fastMin(rleft, rmid);
129148
}
130149
}
131150
function findMinSeam(remove, energySum, width, height) {
@@ -141,10 +160,10 @@ function findMinSeam(remove, energySum, width, height) {
141160
remove[(height - 1)] = minx;
142161
// walk from the bottom up picking the min each time
143162
for (let y = height - 1; y >= 1; y--) {
144-
const leftx = Math.max(minx - 1, 0);
163+
const leftx = fastMax(minx - 1, 0);
145164
const left = energySum[(y - 1) * width + leftx];
146165
const mid = energySum[(y - 1) * width + minx];
147-
const rightx = Math.min(minx + 1, width - 1);
166+
const rightx = fastMin(minx + 1, width - 1);
148167
const right = energySum[(y - 1) * width + rightx];
149168
if (left < mid && left < right) {
150169
minx = leftx;

‎index.ts‎

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@ export function findSeams(
2727
return workingData;
2828
}
2929

30+
// filthy branchless max and min
31+
// if x - y is positive, (x - y) >> 31 will be all 0s
32+
// if x - y is negative, (x - y) >> 31 will be all 1s
33+
// & that with x - y to either get 0 or x - y
34+
function fastMin(x: number, y: number): number {
35+
return y + ((x - y) & ((x - y) >> 31));
36+
}
37+
38+
function fastMax(x: number, y: number): number {
39+
return x - ((x - y) & ((x - y) >> 31));
40+
}
41+
3042
// remove seams in remove from data, returning a new array.
3143
function removeSeam(
3244
data: Uint8ClampedArray,
@@ -175,14 +187,28 @@ function calcEnergySum(
175187

176188
// populate the rest of the rows
177189
for (let y = 1; y < height; y++) {
178-
// const removex = remove[y];
179-
// for (let x = Math.max(removex - y, 0); x < Math.min(removex + y, width); x++) {
180-
for (let x = 0; x < width; x++) {
181-
const left = energySum[(y - 1) * width + Math.max(x - 1, 0)];
182-
const mid = energySum[(y - 1) * width + x];
183-
const right = energySum[(y - 1) * width + Math.min(x + 1, width - 1)];
184-
energySum[y * width + x] = energy[y * width + x] + Math.min(left, mid, right);
190+
let x = 0;
191+
192+
// left edge
193+
const lmid = energySum[(y - 1) * width + x];
194+
const lright = energySum[(y - 1) * width + x + 1];
195+
energySum[y * width + x] = energy[y * width + x] + fastMin(lmid, lright);
196+
197+
for (x = 1; x < width - 1; x++) {
198+
const prev = energy[y * width + x];
199+
200+
const index = (y - 1) * width + x - 1;
201+
const left = energySum[index];
202+
const mid = energySum[index + 1];
203+
const right = energySum[index + 2];
204+
205+
energySum[y * width + x] = prev + fastMin(fastMin(left, mid), right);
185206
}
207+
208+
// right edge
209+
const rleft = energySum[(y - 1) * width + x - 1];
210+
const rmid = energySum[(y - 1) * width + x];
211+
energySum[y * width + x] = energy[y * width + x] + fastMin(rleft, rmid);
186212
}
187213
}
188214

@@ -206,12 +232,12 @@ function findMinSeam(
206232

207233
// walk from the bottom up picking the min each time
208234
for (let y = height - 1; y >= 1; y--) {
209-
const leftx = Math.max(minx - 1, 0);
235+
const leftx = fastMax(minx - 1, 0);
210236
const left = energySum[(y - 1) * width + leftx];
211237

212238
const mid = energySum[(y - 1) * width + minx];
213239

214-
const rightx = Math.min(minx + 1, width - 1);
240+
const rightx = fastMin(minx + 1, width - 1);
215241
const right = energySum[(y - 1) * width + rightx];
216242

217243
if (left < mid && left < right) {

0 commit comments

Comments
 (0)