1 var blendModes
= ["source-over", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn",
2 "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity"];
4 // Helper functions for separate blend mode
6 var separateBlendmodes
= ["normal", "multiply", "screen", "overlay",
7 "darken", "lighten", "colorDodge","colorBurn",
8 "hardLight", "softLight", "difference", "exclusion"];
10 var separateBlendFunctions
= {
11 normal: function(b
, s
) {
14 multiply: function(b
, s
) {
17 screen: function(b
, s
) {
20 overlay: function(b
, s
) {
21 return separateBlendFunctions
.hardLight(b
, s
);
23 darken: function(b
, s
) {
24 return Math
.min(b
, s
);
26 lighten: function(b
, s
) {
27 return Math
.max(b
, s
);
29 colorDodge: function(b
, s
) {
32 return Math
.min(1, s
/ (1 - b
));
34 colorBurn: function(b
, s
) {
37 return 1 - Math
.min(1, (1 - b
) / s
);
39 hardLight: function(b
, s
) {
41 return separateBlendFunctions
.multiply(s
, 2 * b
);
43 return separateBlendFunctions
.screen(s
, 2 * b
- 1);
45 softLight: function(b
, s
) {
48 c
= ((16 * b
- 12) * b
+ 4) * b
;
53 return b
- (1 - 2 * s
) * b
* (1 - b
);
55 return b
+ (2 * s
- 1) * (c
- b
);
57 difference: function(b
, s
) {
58 return Math
.abs(b
- s
);
60 exclusion: function(b
, s
) {
61 return s
+ b
- 2 * b
* s
;
65 function applyBlendMode(b
, s
, blendFunc
) {
66 var resultedColor
= [0, 0, 0, 255];
67 for (var i
= 0; i
< 3; ++i
)
68 resultedColor
[i
] = 255 * (s
[3] * (1 - b
[3]) * s
[i
] + b
[3] * s
[3] * blendFunc(b
[i
], s
[i
]) + (1 - s
[3]) * b
[3] * b
[i
]);
73 // Helper functions for nonseparate blend modes
75 var nonSeparateBlendModes
= ["hue", "saturation", "color", "luminosity"];
77 function luminosity(c
) {
78 return 0.3 * c
[0] + 0.59 * c
[1] + 0.11 * c
[2];
81 function clipColor(c
) {
82 var l
= luminosity(c
);
83 var n
= Math
.min(c
[0], c
[1], c
[2]);
84 var x
= Math
.max(c
[0], c
[1], c
[2]);
87 c
[0] = l
+ (((c
[0] - l
) * l
) / (l
- n
));
88 c
[1] = l
+ (((c
[1] - l
) * l
) / (l
- n
));
89 c
[2] = l
+ (((c
[1] - l
) * l
) / (l
- n
));
93 c
[0] = l
+ (((c
[0] - l
) * (1 - l
)) / (x
- l
));
94 c
[1] = l
+ (((c
[1] - l
) * (1 - l
)) / (x
- l
));
95 c
[2] = l
+ (((c
[2] - l
) * (1 - l
)) / (x
- l
));
101 function setLuminosity(c
, l
) {
102 var d
= l
- luminosity(c
);
109 function saturation(c
) {
110 return Math
.max(c
[0], c
[1], c
[2]) - Math
.min(c
[0], c
[1], c
[2]);
113 function setSaturation(c
, s
) {
114 var max
= Math
.max(c
[0], c
[1], c
[2]);
115 var min
= Math
.min(c
[0], c
[1], c
[2]);
119 for (var i
= 0; i
< 3; ++i
) {
120 if (c
[i
] == min
&& index_min
== -1) {
124 if (c
[i
] == max
&& index_max
== -1)
127 var index_mid
= 3 - index_max
- index_min
;
128 var mid
= c
[index_mid
];
132 mid
= (((mid
- min
) * s
) / (max
- min
));
140 var newColor
= [0, 0, 0];
142 newColor
[index_min
] = min
;
143 newColor
[index_mid
] = mid
;
144 newColor
[index_max
] = max
;
149 var nonSeparateBlendFunctions
= {
150 hue: function(b
, s
) {
151 var bCopy
= [b
[0], b
[1], b
[2]];
152 var sCopy
= [s
[0], s
[1], s
[2]];
153 return setLuminosity(setSaturation(sCopy
, saturation(bCopy
)), luminosity(bCopy
));
155 saturation: function(b
, s
) {
156 var bCopy
= [b
[0], b
[1], b
[2]];
157 var sCopy
= [s
[0], s
[1], s
[2]];
158 return setLuminosity(setSaturation(bCopy
, saturation(sCopy
)), luminosity(bCopy
));
160 color: function(b
, s
) {
161 var bCopy
= [b
[0], b
[1], b
[2]];
162 var sCopy
= [s
[0], s
[1], s
[2]];
163 return setLuminosity(sCopy
, luminosity(bCopy
));
165 luminosity: function(b
, s
) {
166 var bCopy
= [b
[0], b
[1], b
[2]];
167 var sCopy
= [s
[0], s
[1], s
[2]];
168 return setLuminosity(bCopy
, luminosity(sCopy
));
172 // Helper functions for drawing in canvas tests
174 function drawColorInContext(color
, context
) {
175 context
.fillStyle
= color
;
176 context
.fillRect(0, 0, 10, 10);
179 function drawBackdropColorInContext(context
) {
180 drawColorInContext("rgba(129, 255, 129, 1)", context
);
183 function drawSourceColorInContext(context
) {
184 drawColorInContext("rgba(255, 129, 129, 1)", context
);
187 function fillPathWithColorInContext(color
, context
) {
188 context
.fillStyle
= color
;
189 context
.lineTo(0, 10);
190 context
.lineTo(10, 10);
191 context
.lineTo(10, 0);
192 context
.lineTo(0, 0);
196 function fillPathWithBackdropInContext(context
) {
197 fillPathWithColorInContext("rgba(129, 255, 129, 1)", context
);
200 function fillPathWithSourceInContext(context
) {
201 fillPathWithColorInContext("rgba(255, 129, 129, 1)", context
);
204 function applyTransformsToContext(context
) {
205 context
.translate(1, 1);
206 context
.rotate(Math
.PI
/ 2);
210 function drawBackdropColorWithShadowInContext(context
) {
212 context
.shadowOffsetX
= 2;
213 context
.shadowOffsetY
= 2;
214 context
.shadowColor
= 'rgba(192, 192, 192, 1)';
215 drawBackdropColorInContext(context
);
219 function drawSourceColorRectOverShadow(context
) {
220 context
.fillStyle
= "rgba(255, 129, 129, 1)";
221 context
.fillRect(0, 0, 12, 12);
224 function drawColorImageInContext(color
, context
, callback
) {
225 var cvs
= document
.createElement("canvas");
226 var ctx
= cvs
.getContext("2d");
227 drawColorInContext(color
, ctx
);
228 var imageURL
= cvs
.toDataURL();
230 var backdropImage
= new Image();
231 backdropImage
.onload = function() {
232 context
.drawImage(this, 0, 0);
235 backdropImage
.src
= imageURL
;
238 function drawBackdropColorImageInContext(context
, callback
) {
239 drawColorImageInContext("rgba(129, 255, 129, 1)", context
, callback
);
242 function drawSourceColorImageInContext(context
, callback
) {
243 drawColorImageInContext("rgba(255, 129, 129, 1)", context
, callback
);
246 function drawColorPatternInContext(color
, context
, callback
) {
247 var cvs
= document
.createElement("canvas");
248 var ctx
= cvs
.getContext("2d");
249 drawColorInContext(color
, ctx
);
250 var imageURL
= cvs
.toDataURL();
252 var backdropImage
= new Image();
253 backdropImage
.onload = function() {
254 var pattern
= context
.createPattern(backdropImage
, 'repeat');
255 context
.rect(0, 0, 10, 10);
256 context
.fillStyle
= pattern
;
260 backdropImage
.src
= imageURL
;
263 function drawBackdropColorPatternInContext(context
, callback
) {
264 drawColorPatternInContext("rgba(129, 255, 129, 1)", context
, callback
);
267 function drawSourceColorPatternInContext(context
, callback
) {
268 drawColorPatternInContext("rgba(255, 129, 129, 1)", context
, callback
);
271 function drawGradientInContext(color1
, context
) {
272 var grad
= context
.createLinearGradient(0, 0, 10, 10);
273 grad
.addColorStop(0, color1
);
274 grad
.addColorStop(1, color1
);
275 context
.fillStyle
= grad
;
276 context
.fillRect(0, 0, 10, 10);
279 function drawBackdropColorGradientInContext(context
) {
280 drawGradientInContext("rgba(129, 255, 129, 1)", context
);
283 function drawSourceColorGradientInContext(context
) {
284 drawGradientInContext("rgba(255, 129, 129, 1)", context
);
287 function blendColors(backdrop
, source
, blendModeIndex
) {
288 if (blendModeIndex
< separateBlendmodes
.length
)
289 return separateBlendColors(backdrop
, source
, blendModeIndex
);
290 return nonSeparateBlendColors(backdrop
, source
, blendModeIndex
- separateBlendmodes
.length
);
293 function separateBlendColors(backdrop
, source
, blendModeIndex
) {
294 return applyBlendMode(backdrop
, source
, separateBlendFunctions
[separateBlendmodes
[blendModeIndex
]]);
297 function nonSeparateBlendColors(backdrop
, source
, blendModeIndex
) {
298 var expectedColor
= nonSeparateBlendFunctions
[nonSeparateBlendModes
[blendModeIndex
]](backdrop
, source
);
299 for (var i
= 0; i
< 3; ++i
)
300 expectedColor
[i
] = source
[3] * (1 - backdrop
[3]) * source
[i
] + source
[3] * backdrop
[3] * expectedColor
[i
] + (1 - source
[3]) * backdrop
[3] * backdrop
[i
];
301 return [Math
.round(255 * expectedColor
[0]), Math
.round(255 * expectedColor
[1]), Math
.round(255 * expectedColor
[2]), 255];