├── .gitignore ├── LICENSE ├── README.md ├── context_blender.js ├── package.json └── test ├── add-actual.png ├── add-ideal.png ├── color-actual.png ├── color-ideal.png ├── colorburn-actual.png ├── colorburn-ideal.png ├── colordodge-actual.png ├── colordodge-ideal.png ├── darken-actual.png ├── darken-ideal.png ├── darkercolor-actual.png ├── darkercolor-ideal.png ├── difference-actual.png ├── difference-ideal.png ├── exclusion-actual.png ├── exclusion-ideal.png ├── hardlight-actual.png ├── hardlight-ideal.png ├── hue-actual.png ├── hue-ideal.png ├── lighten-actual.png ├── lighten-ideal.png ├── lightercolor-actual.png ├── lightercolor-ideal.png ├── luminosity-actual.png ├── luminosity-ideal.png ├── multiply-actual.png ├── multiply-ideal.png ├── normal-actual.png ├── normal-ideal.png ├── over.png ├── overlay-actual.png ├── overlay-ideal.png ├── results.txt ├── run_tests.html ├── run_tests.js ├── saturation-actual.png ├── saturation-ideal.png ├── screen-actual.png ├── screen-ideal.png ├── softlight-actual.png ├── softlight-ideal.png ├── src-in-actual.png ├── src-in-ideal.png └── under.png /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | *-old.* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Context Blender JavaScript Library 2 | 3 | Copyright © 2010 Gavin Kistner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## About 2 | 3 | Adobe® Photoshop® has a variety of helpful [blend modes](http://helpx.adobe.com/photoshop/using/blending-modes.html) for compositing images from multiple RGBA layers. This small library provides the same functionality for HTML Canvas Contexts, with the goal of producing the same results as Photoshop. 4 | 5 | ## Syntax 6 | 7 | overContext.blendOnto( underContext, blendMode, offsetOptions ); 8 | - overContext : A CanvasRenderingContext2D 9 | - underContext : A CanvasRenderingContext2D 10 | - blendMode : A string with the blend mode to use, e.g. 'screen' 11 | - offsetOptions : [optional] JS Object with some/all of the following keys: 12 | destX, destY 13 | The X/Y location in the 'underContext' to blend onto; both default to 0. 14 | 15 | sourceX, sourceY 16 | The X/Y location in the 'overContext' to blend from; both default to 0. 17 | 18 | width,height 19 | The size of the box to blend; both default to 'auto', blending up to the 20 | right and bottom edges of the 'over' context. 21 | 22 | Width and height may less than specified if there is not enough space 23 | on the over or under contexts to fit the blend. 24 | 25 | 26 | ## Use 27 | 28 | ### In Node.js 29 | 30 | 1. Install the node module 31 | 32 | npm install context-blender 33 | 34 | This will also install node-canvas, which requires a working Cairo install. 35 | See https://github.com/Automattic/node-canvas#installation for more details. 36 | 37 | 2. Use the library like so in the scripts: 38 | 39 | ```javascript 40 | // Requires the canvas library and augments it for you 41 | var Canvas = require('context-blender'); 42 | 43 | var over = new Canvas(100,100).getContext('2d'); 44 | var under = new Canvas(100,100).getContext('2d'); 45 | 46 | // …drawing something to both canvas contexts, and then: 47 | 48 | // Blend all of 'over' onto 'under', starting at the upper left corner 49 | over.blendOnto(under,'screen'); 50 | 51 | // Blend all of 'over' onto 'under' (again), starting at 17,42 in 'under' 52 | over.blendOnto(under,'multiply',{destX:17,destY:42}); 53 | 54 | // Blend a 16x16 tile from 'over' onto 'under' (again), starting at 17,42 in 'under' 55 | over.blendOnto(under,'add',{destX:17,destY:42,sourceX:32,sourceY:128,width:16,height:16}); 56 | ``` 57 | 58 | ### In a Web Browser 59 | 60 | ```javascript 61 | // Likely an 'offscreen' (not in the DOM) canvas 62 | var over = someCanvas.getContext('2d'); 63 | 64 | // Usually a canvas that is shown on the page 65 | var under = anotherCanvas.getContext('2d'); 66 | 67 | // Blend all of 'over' onto 'under', starting at the upper left corner 68 | over.blendOnto(under,'screen'); 69 | 70 | // Blend all of 'over' onto 'under' (again), starting at 17,42 in 'under' 71 | over.blendOnto(under,'multiply',{destX:17,destY:42}); 72 | 73 | // Blend a 16x16 tile from 'over' onto 'under' (again), starting at 17,42 in 'under' 74 | over.blendOnto(under,'add',{destX:17,destY:42,sourceX:32,sourceY:128,width:16,height:16}); 75 | ``` 76 | 77 | 78 | ## Supported Blend Modes 79 | 80 | The following blend modes work perfectly (or as nearly as the [vagaries of the HTML Canvas](http://stackoverflow.com/questions/4309364/why-does-html-canvas-getimagedata-not-return-the-exact-same-values-that-were-ju) allow): 81 | 82 | * `normal` (or `src-over`) 83 | * `src-in` 84 | * `screen` 85 | * `multiply` 86 | * `difference` 87 | * `exclusion` 88 | 89 | The following additional blend modes mostly work as intended, but have issues when it comes to dealing with low-opacity colors. 90 | 91 | Test images are the result of blending 92 | ![](test/over.png) 93 | over top of 94 | ![](test/under.png) 95 | (where the "lighter" repetitions are the result of lowered opacity). 96 | 97 | * `add` (or `plus`) - Photoshop's _"Linear Dodge (add)"_ blend mode [does not perform addition](http://www.neilblevins.com/cg_education/additive_mode_in_photoshop/additive_mode_in_photoshop.htm) 98 | on the opacities of the two layers. I have not yet figured out what it does instead. 99 | For now, this mode performs simple numeric addition, the same as the SVG 1.2 "plus" mode. 100 | 101 | | Photoshop | context-blender | 102 | | --- | --- | 103 | | ![](test/add-ideal.png) | ![](test/add-actual.png) | 104 | * `lighten` (or `lighter`) - the result is _slightly_ too dark when the opacity falls and incorrectly 'favors' a higher-opacity source. 105 | 106 | | Photoshop | context-blender | 107 | | --- | --- | 108 | | ![](test/lighten-ideal.png) | ![](test/lighten-actual.png) | 109 | * `darken` (or `darker`) - the result is too dark when combining low-opacity regions, and does not properly 'favor' the higher-opacity source. 110 | 111 | | Photoshop | context-blender | 112 | | --- | --- | 113 | | ![](test/darken-ideal.png) | ![](test/darken-actual.png) | 114 | * `overlay` - this is correct where both the over and under images are 100% opaque; the lower the alpha of either/both images, the more the colors become too desaturated. 115 | 116 | | Photoshop | context-blender | 117 | | --- | --- | 118 | | ![](test/overlay-ideal.png) | ![](test/overlay-actual.png) | 119 | * `hardlight` - this is the opposite of "overlay" and experiences similar problems where either image is not fully opaque. 120 | 121 | | Photoshop | context-blender | 122 | | --- | --- | 123 | | ![](test/hardlight-ideal.png) | ![](test/hardlight-actual.png) | 124 | * `colordodge` (or `dodge`) - works correctly only under 100% opacity 125 | 126 | | Photoshop | context-blender | 127 | | --- | --- | 128 | | ![](test/colordodge-ideal.png) | ![](test/colordodge-actual.png) | 129 | * `colorburn` (or `burn`) - works correctly only under 100% opacity 130 | 131 | | Photoshop | context-blender | 132 | | --- | --- | 133 | | ![](test/colorburn-ideal.png) | ![](test/colorburn-actual.png) | 134 | * `softlight` 135 | 136 | | Photoshop | context-blender | 137 | | --- | --- | 138 | | ![](test/softlight-ideal.png) | ![](test/softlight-actual.png) | 139 | * `luminosity` 140 | 141 | | Photoshop | context-blender | 142 | | --- | --- | 143 | | ![](test/luminosity-ideal.png) | ![](test/luminosity-actual.png) | 144 | * `color` 145 | 146 | | Photoshop | context-blender | 147 | | --- | --- | 148 | | ![](test/color-ideal.png) | ![](test/color-actual.png) | 149 | * `hue` 150 | 151 | | Photoshop | context-blender | 152 | | --- | --- | 153 | | ![](test/hue-ideal.png) | ![](test/hue-actual.png) | 154 | * `saturation` 155 | 156 | | Photoshop | context-blender | 157 | | --- | --- | 158 | | ![](test/saturation-ideal.png) | ![](test/saturation-actual.png) | 159 | * `lightercolor` 160 | 161 | | Photoshop | context-blender | 162 | | --- | --- | 163 | | ![](test/lightercolor-ideal.png) | ![](test/lightercolor-actual.png) | 164 | * `darkercolor` 165 | 166 | | Photoshop | context-blender | 167 | | --- | --- | 168 | | ![](test/darkercolor-ideal.png) | ![](test/darkercolor-actual.png) | 169 | 170 | 171 | 172 | ## Requirements/Browser Support 173 | 174 | Should work on any user agent that supplies a `CanvasRenderingContext2D` along with `getImageData` and `putImageData`. 175 | 176 | This includes using the [`node-canvas`](https://github.com/Automattic/node-canvas) library under [Node.js](http://nodejs.org). 177 | 178 | 179 | ## About 180 | 181 | This library was created around the need solely for a one-off 'screen' blend mode to match the company-mandated style for bar graphs used internally, previously only available via a Microsoft® Excel® template. Clearly this functionality is useful in more contexts than just my one-off, so I decided to make a framework around it and encourage others to help figure out the formulae. Please, fork this project, add blend modes and/or fix math, and send me pull requests! I feel certain that the resources must exist out there on the equations Photoshop uses in the presence of alpha, but so far I have not found them. 182 | 183 | ### History 184 | 185 | #### v1.3.3 - 2014-Nov-13 186 | + Fix alpha on all blend modes to exactly match Photoshop. 187 | 188 | #### v1.3.0 - 2014-Nov-12 189 | + Release as a Node.js module. 190 | + Add blend modes: `softlight`, `luminosity`, `color`, `hue`, `saturation`, `lightercolor`, `darkercolor`. 191 | + Greatly improve the accuracy of many blend modes. 192 | 193 | _Great thanks to [Pixelero](http://pixelero.wordpress.com) for amazing contributions!_ 194 | 195 | #### v1.2.1 - 2011-Feb-9 196 | + Improve perf of `lighten` and `darken` blend modes. 197 | 198 | #### v1.2.0 - 2010-Dec-14 199 | + Add blend modes: `hardlight`, `colordodge`, `colorburn`, `darken`, `lighten`, `exclusion`. _Thanks [gingerbeardman](https://github.com/gingerbeardman)!_ 200 | 201 | #### v1.1.1 - 2010-Dec-12 202 | + Improve result of `overlay` blend mode. 203 | 204 | #### v1.1.0 - 2010-Dec-6 205 | + Added array `blendOnto.supportedBlendModes` for enumerating modes. 206 | + Added object `blendOnto.supports` for testing if a mode is supported. 207 | + Test for `getImageData()` to be present (prevent loading on excanvas). 208 | 209 | #### v1.0 - 2010-Nov-30 210 | + Initial working release. 211 | + Supported blend modes: `normal`, `screen`, `multiply`, `difference`, `src-in`, `add` 212 | - Known broken: `overlay`, `dodge` 213 | 214 | ## Reference Material 215 | * [PDF Blend Modes: Addendum (January 23, 2006)](http://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/pdf_reference_archives/blend_modes.pdf) PDF 216 | * [SVG Compositing 1.2, Part 1: Primer](http://dev.w3.org/SVG/modules/compositing/master/SVGCompositingPrimer.html) 217 | * [Custom blend modes for Flash 10](http://www.lostinactionscript.com/blog/index.php/2009/05/26/custom-blend-modes-for-flash-10/) blog post 218 | * [Blend Modes in Delphi](http://www.pegtop.net/delphi/articles/blendmodes/) blog post 219 | 220 | ### Contact 221 | 222 | To report bugs or request additional features, please use the [Context-Blender Issues](http://github.com/Phrogz/context-blender/issues) page for this project. 223 | 224 | ### License 225 | 226 | This library is released under an MIT-style license. That generally means that you are free to do almost anything you want with it as long as you give a bit of credit where credit is due. See the LICENSE file included for the actual legal limitations.s and/or fix math, and send me pull requests! I feel certain that the resources must exist out there on the equations Photoshop uses in the presence of alpha, but so far I have not found them. 227 | -------------------------------------------------------------------------------- /context_blender.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | 3 | var defaultOffsets = { 4 | destX : 0, 5 | destY : 0, 6 | sourceX : 0, 7 | sourceY : 0, 8 | width : 'auto', 9 | height : 'auto' 10 | }; 11 | 12 | if (typeof require==='function' && typeof module==='object'){ 13 | var canvas = require('canvas'); 14 | addBlendMethod(canvas.Context2d.prototype); 15 | module.exports = canvas; 16 | } else addBlendMethod(this.CanvasRenderingContext2D && this.CanvasRenderingContext2D.prototype); 17 | 18 | function addBlendMethod(object){ 19 | if (!object || typeof object.getImageData!=='function') return console.error("context blender called without a valid context prototype"); 20 | Object.defineProperty(object,'blendOnto',{value:blendOnto}); 21 | 22 | // For querying of functionality from other libraries 23 | var modes = blendOnto.supportedBlendModes = 'normal src-over screen multiply difference src-in plus add overlay hardlight colordodge dodge colorburn burn darken darker lighten lighter exclusion softlight luminosity color hue saturation lightercolor darkercolor'.split(' '); 24 | var supports = blendOnto.supports = {}; 25 | for (var i=modes.length;i--;) supports[modes[i]] = true; 26 | blendOnto.aliases = { "src-over":"normal", plus:"add", dodge:"colordodge", burn:"colorburn", darker:"darken", lighter:"lighten" }; 27 | return object; 28 | } 29 | 30 | function blendOnto(destContext,blendMode,offsetOptions){ 31 | var offsets={}; 32 | for (var key in defaultOffsets){ 33 | if (defaultOffsets.hasOwnProperty(key)){ 34 | offsets[key] = (offsetOptions && offsetOptions[key]) || defaultOffsets[key]; 35 | } 36 | } 37 | if (offsets.width =='auto') offsets.width =this.canvas.width; 38 | if (offsets.height=='auto') offsets.height=this.canvas.height; 39 | offsets.width = Math.min(offsets.width, this.canvas.width-offsets.sourceX, destContext.canvas.width-offsets.destX ); 40 | offsets.height = Math.min(offsets.height,this.canvas.height-offsets.sourceY,destContext.canvas.height-offsets.destY); 41 | 42 | var srcD = this.getImageData(offsets.sourceX,offsets.sourceY,offsets.width,offsets.height); 43 | var dstD = destContext.getImageData(offsets.destX,offsets.destY,offsets.width,offsets.height); 44 | var src = srcD.data; 45 | var dst = dstD.data; 46 | var sA, dA, len=dst.length; 47 | var sRA, sGA, sBA, dRA, dGA, dBA, dA2, 48 | r1,g1,b1, r2,g2,b2; 49 | var demultiply; 50 | 51 | function Fsoftlight(a,b) { 52 | /* 53 | http://en.wikipedia.org/wiki/Blend_modes#Soft_Light 54 | 2ab+a^2 (1-2b), if b<0.5 55 | 2a(1-b) +sqrt(a)(2b-1), otherwise 56 | */ 57 | var b2=b<<1; 58 | if (b<128) return (a*(b2+(a*(255-b2)>>8)))>>8; 59 | else return (a*(511-b2)+(Math.sqrt(a<<8)*(b2-255)))>>8; 60 | } 61 | 62 | function Foverlay(a,b) { 63 | return a<128 ? 64 | (a*b)>>7 : // (2*a*b)>>8 : 65 | 255 - (( (255 - b) * (255 - a))>>7); 66 | } 67 | 68 | function Fdodge(a,b) { 69 | return (b==255 && a==0) ? 255 : Math.min(255,(a<<8)/(255-b)); 70 | } 71 | 72 | function Fburn(a,b) { 73 | return (b==255 && a==0) ? 0 : 255-Math.min(255,((255-a)<<8)/b); 74 | } 75 | 76 | 77 | /* 78 | // yyy = similar to YCbCr 79 | 0.2990 0.5870 0.1140 80 | -0.1687 -0.3313 0.5000 81 | 0.5000 -0.4187 -0.0813 82 | */ 83 | function rgb2YCbCr(r,g,b) { 84 | return { 85 | r: 0.2990*r+0.5870*g+0.1140*b, 86 | g: -0.1687*r-0.3313*g+0.5000*b, 87 | b: 0.5000*r-0.4187*g-0.0813*b }; 88 | } 89 | 90 | /* 91 | 1.0000 -0.0000 1.4020 92 | 1.0000 -0.3441 -0.7141 93 | 1.0000 1.7720 0.0000 94 | */ 95 | function YCbCr2rgb(r,g,b) { 96 | return { 97 | r: r +1.4020*b, 98 | g: r-0.3441*g -0.7141*b, 99 | b: r+1.7720*g }; 100 | } 101 | 102 | function rgb2hsv(r,g,b) { 103 | var c=rgb2YCbCr(r,g,b); 104 | var s=Math.sqrt(c.g*c.g+c.b*c.b), 105 | h=Math.atan2(c.g,c.b); 106 | return {h:h, s:s, v:c.r }; 107 | } 108 | 109 | function hsv2rgb(h,s,v) { 110 | var g=s*Math.sin(h), 111 | b=s*Math.cos(h); 112 | return YCbCr2rgb(v,g,b); 113 | } 114 | 115 | 116 | for (var px=0;px0 ? {r:r1,g:g1,b:b1} : {r:r2,g:g2,b:b2}; 271 | dst[px] = f1*rgb.r + f2*r1 + f3*r2; 272 | dst[px+1] = f1*rgb.g + f2*g1 + f3*g2; 273 | dst[px+2] = f1*rgb.b + f2*b1 + f3*b2; 274 | break; 275 | 276 | case 'darkercolor': 277 | var rgb = 2.623*(r1-r2)+5.15*(g1-g2)+b1-b2<0 ? {r:r1,g:g1,b:b1} : {r:r2,g:g2,b:b2}; 278 | dst[px] = f1*rgb.r + f2*r1 + f3*r2; 279 | dst[px+1] = f1*rgb.g + f2*g1 + f3*g2; 280 | dst[px+2] = f1*rgb.b + f2*b1 + f3*b2; 281 | break; 282 | 283 | default: // ******* UNSUPPORTED mode, produces yellow/magenta checkerboard 284 | var col = (px/4) % this.canvas.width, 285 | row = Math.floor((px/4) / this.canvas.width), 286 | odd = (col%8<4 && row%8<4) || (col%8>3 && row%8>3); 287 | dst[px] = dst[px+3] = 255; 288 | dst[px+1] = odd ? 255 : 0; 289 | dst[px+2] = odd ? 0 : 255; 290 | } 291 | } 292 | destContext.putImageData(dstD,offsets.destX,offsets.destY); 293 | } 294 | 295 | })(); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "context-blender", 3 | "version": "1.3.3", 4 | "description": "Photoshop-style blend modes for HTML Canvas Contexts", 5 | "author": "Gavin Kistner (http://phrogz.net/)", 6 | "main": "context_blender.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/Phrogz/context-blender" 10 | }, 11 | "bugs": "http://github.com/Phrogz/context-blender/issues", 12 | "license" : "MIT", 13 | "dependencies": { 14 | "canvas": "~1.4" 15 | }, 16 | "devDependencies": { 17 | "sprintf": "~0.1" 18 | }, 19 | "readmeFilename": "README.md", 20 | "keywords": [ 21 | "html5", 22 | "canvas", 23 | "photoshop", 24 | "layer", 25 | "context", 26 | "src-over", 27 | "screen", 28 | "multiply", 29 | "difference", 30 | "src-in", 31 | "overlay", 32 | "hardlight", 33 | "colordodge", 34 | "dodge", 35 | "colorburn", 36 | "burn", 37 | "darken", 38 | "darker", 39 | "lighten", 40 | "lighter", 41 | "exclusion", 42 | "softlight", 43 | "luminosity", 44 | "color", 45 | "hue", 46 | "saturation", 47 | "lightercolor", 48 | "darkercolor" 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /test/add-actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/add-actual.png -------------------------------------------------------------------------------- /test/add-ideal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/add-ideal.png -------------------------------------------------------------------------------- /test/color-actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/color-actual.png -------------------------------------------------------------------------------- /test/color-ideal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/color-ideal.png -------------------------------------------------------------------------------- /test/colorburn-actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/colorburn-actual.png -------------------------------------------------------------------------------- /test/colorburn-ideal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/colorburn-ideal.png -------------------------------------------------------------------------------- /test/colordodge-actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/colordodge-actual.png -------------------------------------------------------------------------------- /test/colordodge-ideal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/colordodge-ideal.png -------------------------------------------------------------------------------- /test/darken-actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/darken-actual.png -------------------------------------------------------------------------------- /test/darken-ideal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/darken-ideal.png -------------------------------------------------------------------------------- /test/darkercolor-actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/darkercolor-actual.png -------------------------------------------------------------------------------- /test/darkercolor-ideal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/darkercolor-ideal.png -------------------------------------------------------------------------------- /test/difference-actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/difference-actual.png -------------------------------------------------------------------------------- /test/difference-ideal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/difference-ideal.png -------------------------------------------------------------------------------- /test/exclusion-actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/exclusion-actual.png -------------------------------------------------------------------------------- /test/exclusion-ideal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/exclusion-ideal.png -------------------------------------------------------------------------------- /test/hardlight-actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/hardlight-actual.png -------------------------------------------------------------------------------- /test/hardlight-ideal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/hardlight-ideal.png -------------------------------------------------------------------------------- /test/hue-actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/hue-actual.png -------------------------------------------------------------------------------- /test/hue-ideal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/hue-ideal.png -------------------------------------------------------------------------------- /test/lighten-actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/lighten-actual.png -------------------------------------------------------------------------------- /test/lighten-ideal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/lighten-ideal.png -------------------------------------------------------------------------------- /test/lightercolor-actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/lightercolor-actual.png -------------------------------------------------------------------------------- /test/lightercolor-ideal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/lightercolor-ideal.png -------------------------------------------------------------------------------- /test/luminosity-actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/luminosity-actual.png -------------------------------------------------------------------------------- /test/luminosity-ideal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/luminosity-ideal.png -------------------------------------------------------------------------------- /test/multiply-actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/multiply-actual.png -------------------------------------------------------------------------------- /test/multiply-ideal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/multiply-ideal.png -------------------------------------------------------------------------------- /test/normal-actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/normal-actual.png -------------------------------------------------------------------------------- /test/normal-ideal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/normal-ideal.png -------------------------------------------------------------------------------- /test/over.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/over.png -------------------------------------------------------------------------------- /test/overlay-actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/overlay-actual.png -------------------------------------------------------------------------------- /test/overlay-ideal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/overlay-ideal.png -------------------------------------------------------------------------------- /test/results.txt: -------------------------------------------------------------------------------- 1 | normal wrong: 1% delta: 781 time: 8.8ms 2 | screen wrong: 1% delta: 718 time: 8.8ms 3 | multiply wrong: 1% delta: 928 time: 9.9ms 4 | difference wrong: 0% delta: 291 time: 11.2ms 5 | src-in wrong: 0% delta: 0 time: 9.8ms 6 | add wrong: 62% delta: 310363 time: 9.6ms 7 | overlay wrong: 55% delta: 470936 time: 10.0ms 8 | hardlight wrong: 56% delta: 469943 time: 10.1ms 9 | colordodge wrong: 57% delta: 548745 time: 11.1ms 10 | colorburn wrong: 57% delta: 535776 time: 10.8ms 11 | darken wrong: 55% delta: 431696 time: 9.6ms 12 | lighten wrong: 70% delta: 503412 time: 10.1ms 13 | exclusion wrong: 0% delta: 294 time: 11.0ms 14 | softlight wrong: 56% delta: 467026 time: 10.6ms 15 | luminosity wrong: 73% delta: 559715 time: 11.8ms 16 | color wrong: 73% delta: 559677 time: 12.9ms 17 | hue wrong: 79% delta: 599536 time: 15.7ms 18 | saturation wrong: 79% delta: 595011 time: 15.3ms 19 | lightercolor wrong: 56% delta: 490698 time: 6.4ms 20 | darkercolor wrong: 56% delta: 437570 time: 5.9ms -------------------------------------------------------------------------------- /test/run_tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Context Blender Test 5 | 6 | 7 | 107 | 127 | 128 |

129 | Blending 130 | 131 | over 132 | 133 |

134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 |
Ideal RGBAActual RGBAIdeal RGBActual RGBIdeal AlphaActual AlphaIdeal RedActual RedStats
146 | -------------------------------------------------------------------------------- /test/run_tests.js: -------------------------------------------------------------------------------- 1 | // Automatically test the correctness and performance of all blend modes. 2 | // The script requires node.js with node-canvas installed. 3 | var Canvas = require(__dirname+'/../context_blender'), 4 | Image = Canvas.Image, 5 | sprintf = require('sprintf').sprintf, 6 | fs = require('fs'); 7 | 8 | var ctx = {}; 9 | var blendModes = Canvas.Context2d.prototype.blendOnto.supportedBlendModes, 10 | blendAliases = Canvas.Context2d.prototype.blendOnto.aliases; 11 | 12 | var testLoops = 10; 13 | 14 | function go(){ 15 | for (var a='over under actual ideal'.split(' '),i=4;i--;) ctx[a[i]] = new Canvas(140,140).getContext('2d'); 16 | drawOntoContext(ctx.over, __dirname+'/over.png' ); 17 | drawOntoContext(ctx.under, __dirname+'/under.png'); 18 | 19 | for (var results={},i=blendModes.length;i--;){ 20 | var mode = blendModes[i]; 21 | if (!blendAliases[mode]) results[mode] = analyze(mode); 22 | } 23 | writeResults(results); 24 | } 25 | 26 | function analyze(mode){ 27 | ctx.ideal.clearRect(0,0,140,140); 28 | drawOntoContext(ctx.ideal, __dirname+'/'+mode+'-ideal.png'); 29 | var ideal = ctx.ideal.getImageData(0,0,140,140).data; 30 | 31 | for (var time=0,i=testLoops;i--;){ 32 | ctx.actual.clearRect(0,0,140,140); 33 | ctx.actual.drawImage(ctx.under.canvas,0,0); 34 | var start = new Date; 35 | ctx.over.blendOnto(ctx.actual,mode); 36 | time += new Date-start; 37 | } 38 | writeContextToFile(ctx.actual,__dirname+'/'+mode+'-actual.png'); 39 | var actual = ctx.actual.getImageData(0,0,140,140).data; 40 | 41 | for (var deltas=[],i=0;i3 }); 73 | var percent = 100 * wrongs.length / Math.pow(140,2); 74 | var delta = wrongs.sum(); 75 | lines.push( sprintf("%-12s wrong:%3d%% delta:%8d time:%5.1fms", result.mode, percent, delta, result.time ) ); 76 | } 77 | } 78 | fs.writeFileSync(__dirname+'/results.txt',lines.join("\n")); 79 | } 80 | 81 | Object.defineProperty(Array.prototype,'sum',{value:function(){ for (var sum=0,i=this.length;i--;) sum+=this[i]; return sum }}); 82 | Object.defineProperty(Array.prototype,'avg',{value:function(){ return this.sum()/this.length }}); 83 | Object.defineProperty(Array.prototype,'stddev',{value:function(){ for (var avg=this.avg(),sum=0,i=this.length;i--;) sum+=Math.pow(this[i]-avg,2); return Math.sqrt(sum/this.length) }}); 84 | 85 | go(); -------------------------------------------------------------------------------- /test/saturation-actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/saturation-actual.png -------------------------------------------------------------------------------- /test/saturation-ideal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/saturation-ideal.png -------------------------------------------------------------------------------- /test/screen-actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/screen-actual.png -------------------------------------------------------------------------------- /test/screen-ideal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/screen-ideal.png -------------------------------------------------------------------------------- /test/softlight-actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/softlight-actual.png -------------------------------------------------------------------------------- /test/softlight-ideal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/softlight-ideal.png -------------------------------------------------------------------------------- /test/src-in-actual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/src-in-actual.png -------------------------------------------------------------------------------- /test/src-in-ideal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/src-in-ideal.png -------------------------------------------------------------------------------- /test/under.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phrogz/context-blender/645e99c4f5f1700dad722584d463b58f85cf5c95/test/under.png --------------------------------------------------------------------------------