/
greyAlgorithms.js
127 lines (123 loc) · 3.54 KB
/
greyAlgorithms.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/**
* @typedef {('luma709'|'luma601'|'maximum'|'minimum'|'average'|'minmax'|'red'|'green'|'blue'|'cyan'|'magenta'|'yellow'|'black'|'hue'|'saturation'|'lightness')} GreyAlgorithm
*/
export const methods = {
luma709(red, green, blue) {
// sRGB
// return red * 0.2126 + green * 0.7152 + blue * 0.0722;
// Let's do a little trick ... in order not convert the integer to a double we do
// the multiplication with integer to reach a total of 32768 and then shift the bits
// of 15 to the right
// This does a Math.floor and may lead to small (max 1) difference
// Same result, > 10% faster on the full grey conversion
return (red * 6966 + green * 23436 + blue * 2366) >> 15;
},
luma601(red, green, blue) {
// NTSC
// return this.red * 0.299 + green * 0.587 + blue * 0.114;
return (red * 9798 + green * 19235 + blue * 3735) >> 15;
},
maximum(red, green, blue) {
return Math.max(red, green, blue);
},
minimum(red, green, blue) {
return Math.min(red, green, blue);
},
average(red, green, blue) {
return ((red + green + blue) / 3) >> 0;
},
minmax(red, green, blue) {
return (Math.max(red, green, blue) + Math.min(red, green, blue)) / 2;
},
red(red) {
return red;
},
green(red, green) {
return green;
},
blue(red, green, blue) {
return blue;
},
cyan(red, green, blue, image) {
let black = methods.black(red, green, blue, image);
return ((image.maxValue - red - black) / (1 - black / image.maxValue)) >> 0;
},
magenta(red, green, blue, image) {
let black = methods.black(red, green, blue, image);
return (
((image.maxValue - green - black) / (1 - black / image.maxValue)) >> 0
);
},
yellow(red, green, blue, image) {
let black = methods.black(red, green, blue, image);
return (
((image.maxValue - blue - black) / (1 - black / image.maxValue)) >> 0
);
},
black(red, green, blue, image) {
return Math.min(
image.maxValue - red,
image.maxValue - green,
image.maxValue - blue,
);
},
hue(red, green, blue, image) {
let min = methods.min(red, green, blue);
let max = methods.max(red, green, blue);
if (max === min) {
return 0;
}
let hue = 0;
let delta = max - min;
switch (max) {
case red:
hue = (green - blue) / delta + (green < blue ? 6 : 0);
break;
case green:
hue = (blue - red) / delta + 2;
break;
case blue:
hue = (red - green) / delta + 4;
break;
default:
throw new Error('unreachable');
}
return ((hue / 6) * image.maxValue) >> 0;
},
saturation(red, green, blue, image) {
// from HSV model
let min = methods.min(red, green, blue);
let max = methods.max(red, green, blue);
let delta = max - min;
return max === 0 ? 0 : (delta / max) * image.maxValue;
},
lightness(red, green, blue) {
let min = methods.min(red, green, blue);
let max = methods.max(red, green, blue);
return (max + min) / 2;
},
};
Object.defineProperty(methods, 'luminosity', {
enumerable: false,
value: methods.lightness,
});
Object.defineProperty(methods, 'luminance', {
enumerable: false,
value: methods.lightness,
});
Object.defineProperty(methods, 'min', {
enumerable: false,
value: methods.minimum,
});
Object.defineProperty(methods, 'max', {
enumerable: false,
value: methods.maximum,
});
Object.defineProperty(methods, 'brightness', {
enumerable: false,
value: methods.maximum,
});
export const names = {};
Object.keys(methods).forEach((name) => {
names[name] = name;
});