Skip to content

Commit c72ee89

Browse files
committed
Bug fix: ensure all color values are consistently expressed as signed 32-bit integers (#2)
1 parent d90f52d commit c72ee89

File tree

2 files changed

+58
-31
lines changed

2 files changed

+58
-31
lines changed

lib/colorUtils.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@ module.exports = {
4242
FORCE_TRANSPARENT: Infinity,
4343

4444
/**
45-
* Creates a color on the format 0xRRGGBBAA from the specified
46-
* color components.
45+
* Creates a signed 32-bit RGBA color value from the specified color components.
4746
* @returns {number}
4847
*/
4948
from: colorUtils_from,
@@ -562,6 +561,11 @@ function parseHexColor(color) {
562561
}
563562

564563
var numeric = parseInt(hexColor, 16);
564+
565+
// numeric is now parsed as a 32-bit unsigned integer.
566+
// All return paths below must perform a bitwise operator
567+
// on numeric before returning it to ensure it is converted to a
568+
// 32-bit signed integer.
565569

566570
switch (hexColor.length) {
567571
case 3:
@@ -587,7 +591,8 @@ function parseHexColor(color) {
587591
case 6:
588592
return numeric << 8 | 0xff;
589593
case 8:
590-
return numeric;
594+
// The bitwise operation enforces the value to a 32-bit signed integer
595+
return numeric & 0xffffffff;
591596
}
592597
}
593598

@@ -624,7 +629,8 @@ function colorUtils_parse(color) {
624629

625630
// Named colors
626631
if (color in NAMED_COLORS) {
627-
return NAMED_COLORS[color];
632+
// bitwise operator is used to enforce a signed integer
633+
return NAMED_COLORS[color] & 0xffffffff;
628634
}
629635

630636
// rgb[a](red, green, blue[, alpha])

tests/colorUtils.js

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,55 @@ var tap = require("tap");
44
var colorUtils = require("../lib/colorUtils");
55

66
tap.equal(255, colorUtils.red(0xff000000));
7+
tap.equal(255, colorUtils.red(0xff000000 & 0xffffffff)); // converted to 32-bit signed int
78
tap.equal(255, colorUtils.green(0x00ff0000));
89
tap.equal(255, colorUtils.blue(0x0000ff00));
910
tap.equal(255, colorUtils.alpha(0x000000ff));
1011

11-
tap.equal("#00000000", colorUtils.format(colorUtils.parse("transparent")));
12-
tap.equal("#66cdaaff", colorUtils.format(colorUtils.parse("mediumaquaMarine")));
13-
tap.equal("#aabbccdd", colorUtils.format(colorUtils.parse("#abcd")));
14-
tap.equal("#abcdef21", colorUtils.format(colorUtils.parse("#abcdef21")));
15-
tap.equal("#aabbccff", colorUtils.format(colorUtils.parse("#abc")));
16-
tap.equal("#aabbccff", colorUtils.format(colorUtils.parse("#aabbcc")));
17-
tap.equal("#fffefdff", colorUtils.format(colorUtils.parse("rgb(255, 254, 253)")));
18-
tap.equal("#ff7f00ff", colorUtils.format(colorUtils.parse("rgb(100%, 50%, 0%)")));
19-
tap.equal("#fffefd7f", colorUtils.format(colorUtils.parse("rgb(255, 254, 253, 0.5)")));
20-
tap.equal("#fffefd7f", colorUtils.format(colorUtils.parse("rgba(255, 254, 253, 0.5)")));
21-
tap.equal("#adcbaeff", colorUtils.format(colorUtils.parse("hsl(123, 23%, 74% )")));
22-
tap.equal("#adcbaeff", colorUtils.format(colorUtils.parse("hsl(123deg, 23%, 74% )")));
23-
tap.equal("#adcbaeff", colorUtils.format(colorUtils.parse("hsl(123.000001deg, 23%, 74% )")));
24-
tap.equal("#adcbae7f", colorUtils.format(colorUtils.parse("hsl(123.000001deg, 23%, 74% , 0.5)")));
25-
tap.equal("#adcbae7f", colorUtils.format(colorUtils.parse("hsla(123.000001deg, 23%, 74% , 0.5)")));
26-
tap.equal("#bcbcbcff", colorUtils.format(colorUtils.parse("hsl(123, 0%, 74% )")));
27-
tap.equal("#000000ff", colorUtils.format(colorUtils.parse("hsl(123deg, 23%, 0% )")));
28-
tap.equal("#ffffffff", colorUtils.format(colorUtils.parse("hsl(123.000001deg, 23%, 100% )")));
29-
tap.equal("#adcbaeff", colorUtils.format(colorUtils.parse("hsl(2.146755rad, 23%, 74% )")));
30-
tap.equal("#adcbaeff", colorUtils.format(colorUtils.parse("hsl(0.3416667turn, 23%, 74% )")));
31-
tap.equal("#adcbaeff", colorUtils.format(colorUtils.parse("hsl(136.66667grad, 23%, 74% )")));
32-
tap.equal("#00bfffff", colorUtils.format(colorUtils.parse("hwb(195, 0%, 0%)")));
33-
tap.equal("#00bfffb2", colorUtils.format(colorUtils.parse("hwb(195, 0%, 0%, 0.7)")));
34-
tap.equal("#bfbfe5b2", colorUtils.format(colorUtils.parse("hwb(239.0000deg, 75%, 10%, 0.7)")));
35-
tap.equal("#9c9c9cff", colorUtils.format(colorUtils.parse("hwb(5, 91%, 57%)")));
36-
tap.equal("#000000ff", colorUtils.format(colorUtils.parse("hwb(5, 0%, 100%)")));
37-
tap.equal("#ffffffff", colorUtils.format(colorUtils.parse("hwb(5, 100%, 0%)")));
12+
function assertParsed(expected, value) {
13+
const SIGNED_MAX_VALUE = 2147483647;
14+
const SIGNED_MIN_VALUE = -2147483648;
15+
16+
var actual = colorUtils.parse(value);
17+
var actualFormatted = colorUtils.format(actual);
18+
19+
tap.assert(
20+
actual >= SIGNED_MIN_VALUE &&
21+
actual <= SIGNED_MAX_VALUE,
22+
`${value} was parsed as ${actual}, which is not a 32-bit signed integer.`);
23+
24+
tap.equal(
25+
expected, actualFormatted,
26+
`${value} was parsed as ${actualFormatted} instead of ${expected}.`);
27+
}
28+
29+
assertParsed("#fedcbaef", 0xfedcbaef);
30+
assertParsed("#00000000", 0);
31+
assertParsed("#00000000", "transparent");
32+
assertParsed("#66cdaaff", "mediumaquaMarine");
33+
assertParsed("#e0ffffff", "lightcyan");
34+
assertParsed("#aabbccdd", "#abcd");
35+
assertParsed("#abcdef21", "#abcdef21");
36+
assertParsed("#aabbccff", "#abc");
37+
assertParsed("#aabbccff", "#aabbcc");
38+
assertParsed("#fffefdff", "rgb(255, 254, 253)");
39+
assertParsed("#ff7f00ff", "rgb(100%, 50%, 0%)");
40+
assertParsed("#fffefd7f", "rgb(255, 254, 253, 0.5)");
41+
assertParsed("#fffefd7f", "rgba(255, 254, 253, 0.5)");
42+
assertParsed("#adcbaeff", "hsl(123, 23%, 74% )");
43+
assertParsed("#adcbaeff", "hsl(123deg, 23%, 74% )");
44+
assertParsed("#adcbaeff", "hsl(123.000001deg, 23%, 74% )");
45+
assertParsed("#adcbae7f", "hsl(123.000001deg, 23%, 74% , 0.5)");
46+
assertParsed("#adcbae7f", "hsla(123.000001deg, 23%, 74% , 0.5)");
47+
assertParsed("#bcbcbcff", "hsl(123, 0%, 74% )");
48+
assertParsed("#000000ff", "hsl(123deg, 23%, 0% )");
49+
assertParsed("#ffffffff", "hsl(123.000001deg, 23%, 100% )");
50+
assertParsed("#adcbaeff", "hsl(2.146755rad, 23%, 74% )");
51+
assertParsed("#adcbaeff", "hsl(0.3416667turn, 23%, 74% )");
52+
assertParsed("#adcbaeff", "hsl(136.66667grad, 23%, 74% )");
53+
assertParsed("#00bfffff", "hwb(195, 0%, 0%)");
54+
assertParsed("#00bfffb2", "hwb(195, 0%, 0%, 0.7)");
55+
assertParsed("#bfbfe5b2", "hwb(239.0000deg, 75%, 10%, 0.7)");
56+
assertParsed("#9c9c9cff", "hwb(5, 91%, 57%)");
57+
assertParsed("#000000ff", "hwb(5, 0%, 100%)");
58+
assertParsed("#ffffffff", "hwb(5, 100%, 0%)");

0 commit comments

Comments
 (0)