Skip to content

Commit 6143a3c

Browse files
Remove memory allocation for interpolateColors following FIXME comment (#1077)
* Remove memoery alloction for InterpolateColors following FIXME comment * Remove old code * Prevent divison by zero * Extend unit tests --------- Co-authored-by: lukasmasuch <[email protected]>
1 parent 8257c8f commit 6143a3c

File tree

2 files changed

+47
-15
lines changed

2 files changed

+47
-15
lines changed

packages/core/src/internal/data-grid/color-parser.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -98,24 +98,27 @@ export function interpolateColors(leftColor: string, rightColor: string, val: nu
9898
if (val >= 1) return rightColor;
9999

100100
// Parse to rgba returns straight alpha colors, for interpolation we want pre-multiplied alpha
101-
// FIXME: This can be faster if instead of makign an array we just use variables. No memory allocation.
102-
const left = [...parseToRgba(leftColor)];
103-
left[0] = left[0] * left[3];
104-
left[1] = left[1] * left[3];
105-
left[2] = left[2] * left[3];
106-
const right = [...parseToRgba(rightColor)];
107-
right[0] = right[0] * right[3];
108-
right[1] = right[1] * right[3];
109-
right[2] = right[2] * right[3];
101+
const [lr, lg, lb, la] = parseToRgba(leftColor);
102+
const [rr, rg, rb, ra] = parseToRgba(rightColor);
103+
104+
const leftR = lr * la;
105+
const leftG = lg * la;
106+
const leftB = lb * la;
107+
108+
const rightR = rr * ra;
109+
const rightG = rg * ra;
110+
const rightB = rb * ra;
110111

111112
const hScaler = val;
112113
const nScaler = 1 - val;
113114

114-
const a = left[3] * nScaler + right[3] * hScaler;
115+
const a = la * nScaler + ra * hScaler;
116+
// If both colors are fully transparent the resulting alpha can be 0, avoid dividing by 0
117+
if (a === 0) return "rgba(0, 0, 0, 0)";
115118
// now we need to divide the alpha back out to get linear alpha back for the final result
116-
const r = Math.floor((left[0] * nScaler + right[0] * hScaler) / a);
117-
const g = Math.floor((left[1] * nScaler + right[1] * hScaler) / a);
118-
const b = Math.floor((left[2] * nScaler + right[2] * hScaler) / a);
119+
const r = Math.floor((leftR * nScaler + rightR * hScaler) / a);
120+
const g = Math.floor((leftG * nScaler + rightG * hScaler) / a);
121+
const b = Math.floor((leftB * nScaler + rightB * hScaler) / a);
119122
return `rgba(${r}, ${g}, ${b}, ${a})`;
120123
}
121124

packages/core/test/color-parser.test.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,41 @@
11
/* eslint-disable sonarjs/no-duplicate-string */
2+
import { describe, expect, test } from "vitest";
23
import {
34
blend,
5+
getLuminance,
46
interpolateColors,
57
parseToRgba,
68
withAlpha,
7-
getLuminance,
89
} from "../src/internal/data-grid/color-parser.js";
9-
import { expect, describe, test } from "vitest";
1010

1111
describe("interpolateColors", () => {
1212
test("Smoke test", () => {
1313
expect(interpolateColors("rgba(0, 0, 0, 1)", "rgba(255, 255, 255, 1)", 0.5)).toEqual("rgba(127, 127, 127, 1)");
1414
});
15+
16+
test("Fully transparent inputs do not produce NaN (alpha 0)", () => {
17+
expect(interpolateColors("rgba(0, 0, 0, 0)", "rgba(0, 0, 0, 0)", 0.5)).toEqual("rgba(0, 0, 0, 0)");
18+
});
19+
20+
test("Pre-multiplied alpha behavior with one side transparent", () => {
21+
// Left transparent, right opaque white
22+
expect(interpolateColors("rgba(0, 0, 0, 0)", "rgba(255, 255, 255, 1)", 0.5)).toEqual(
23+
"rgba(255, 255, 255, 0.5)"
24+
);
25+
// Right transparent, left opaque white (symmetry check)
26+
expect(interpolateColors("rgba(255, 255, 255, 1)", "rgba(0, 0, 0, 0)", 0.5)).toEqual(
27+
"rgba(255, 255, 255, 0.5)"
28+
);
29+
});
30+
31+
test("Clamp outside range: val <= 0 returns left, val >= 1 returns right", () => {
32+
expect(interpolateColors("rgba(10, 20, 30, 1)", "rgba(200, 210, 220, 1)", -0.25)).toEqual(
33+
"rgba(10, 20, 30, 1)"
34+
);
35+
expect(interpolateColors("rgba(10, 20, 30, 1)", "rgba(200, 210, 220, 1)", 1.25)).toEqual(
36+
"rgba(200, 210, 220, 1)"
37+
);
38+
});
1539
});
1640

1741
describe("parseToRgba", () => {
@@ -36,4 +60,9 @@ describe("getLuminance", () => {
3660
test("Smoke test", () => {
3761
expect(getLuminance("rgba(255, 255, 255, 1)")).toEqual(1);
3862
});
63+
64+
test("Transparent has zero luminance", () => {
65+
expect(getLuminance("rgba(0, 0, 0, 0)")).toEqual(0);
66+
expect(getLuminance("transparent")).toEqual(0);
67+
});
3968
});

0 commit comments

Comments
 (0)