├── zorrosvg ├── .htaccess ├── img │ ├── example.png │ ├── zorrosvg.jpg │ ├── transparentpattern.gif │ ├── transparentpattern.png │ ├── zorro.svg │ ├── example2.svg │ ├── zorro2.svg │ ├── example4.svg │ ├── example1.svg │ └── example3.svg ├── README.md ├── index.html └── js │ └── zorrosvgmaskmaker.js ├── blur ├── stackblur.pdf ├── IntegralImage.js ├── FastBlur.js ├── StackBoxBlur.js ├── StackBlur.js └── CompoundBlur.js └── README.md /zorrosvg/.htaccess: -------------------------------------------------------------------------------- 1 | AddType image/svg+xml .svg .svgz -------------------------------------------------------------------------------- /blur/stackblur.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Quasimondo/QuasimondoJS/HEAD/blur/stackblur.pdf -------------------------------------------------------------------------------- /zorrosvg/img/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Quasimondo/QuasimondoJS/HEAD/zorrosvg/img/example.png -------------------------------------------------------------------------------- /zorrosvg/img/zorrosvg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Quasimondo/QuasimondoJS/HEAD/zorrosvg/img/zorrosvg.jpg -------------------------------------------------------------------------------- /zorrosvg/img/transparentpattern.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Quasimondo/QuasimondoJS/HEAD/zorrosvg/img/transparentpattern.gif -------------------------------------------------------------------------------- /zorrosvg/img/transparentpattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Quasimondo/QuasimondoJS/HEAD/zorrosvg/img/transparentpattern.png -------------------------------------------------------------------------------- /zorrosvg/README.md: -------------------------------------------------------------------------------- 1 | ZorroSVG 2 | ============ 3 | 4 | The source code for: http://quasimondo.com/ZorroSVG/ 5 | 6 | This is not cleaned up or documented at all, but if you are interested in making a standalone version 7 | or your own flavour of it, you'll find all of the required code inside of js/zorrosvgmaskmaker.js 8 | 9 | I've not added all the jQuery/foundation etc. code to the repository - I mean it's shameful enough that 10 | I used jQuery for the UI in the first place ;-) 11 | 12 | Please let me know whenever you make improvements to the code or port it. 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | QuasimondoJS 2 | ============ 3 | 4 | A collection of more or less useful Javascript utils and snippets 5 | 6 | 7 | 8 | ZorroSVG - Put a Mask on it. 9 | 10 | Converts transparent PNGs to SVGs and can shrink file sizes considerably. 11 | http://quasimondo.com/ZorroSVG/ 12 | 13 | 14 | 15 | Various Blurs: 16 | 17 | Stack Blur (best quality/speed ratio): 18 | http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html 19 | 20 | An explanation how the Stackblur Algorithm works can be found here: 21 | https://github.com/Quasimondo/QuasimondoJS/blob/master/blur/stackblur.pdf?raw=true 22 | 23 | Compound Blur with variable radii: 24 | http://www.quasimondo.com/CompoundBlurForCanvas/CompoundBlurDemo.html 25 | 26 | Superfast Blur (fastest, but not so beautiful): 27 | http://www.quasimondo.com/BoxBlurForCanvas/FastBlurDemo.html 28 | 29 | Stack Box Blur (another fast variant): 30 | http://www.quasimondo.com/BoxBlurForCanvas/FastBlur2Demo.html 31 | 32 | Integral Image Blur (and another fast variant): 33 | http://www.quasimondo.com/IntegralImageForCanvas/IntegralImageBlurDemo.html 34 | 35 | -------------------------------------------------------------------------------- /zorrosvg/img/zorro.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | ]> 9 | 11 | 12 | 13 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 38 | 39 | 41 | 45 | 46 | 47 | 48 | 49 | 50 | 57 | 59 | 60 | 61 | 67 | 68 | 69 | 71 | 82 | 83 | 84 | 86 | 87 | 89 | 91 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /zorrosvg/img/example2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blur/IntegralImage.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Integral Image 4 | 5 | Version: 0.4 6 | Author: Mario Klingemann 7 | Contact: mario@quasimondo.com 8 | Website: http://www.quasimondo.com/IntegralImageForCanvas 9 | Twitter: @quasimondo 10 | 11 | In case you find this class useful - especially in commercial projects - 12 | I am not totally unhappy for a small donation to my PayPal account 13 | mario@quasimondo.de 14 | 15 | 16 | Copyright (c) 2011 Mario Klingemann 17 | 18 | Permission is hereby granted, free of charge, to any person 19 | obtaining a copy of this software and associated documentation 20 | files (the "Software"), to deal in the Software without 21 | restriction, including without limitation the rights to use, 22 | copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the 24 | Software is furnished to do so, subject to the following 25 | conditions: 26 | 27 | The above copyright notice and this permission notice shall be 28 | included in all copies or substantial portions of the Software. 29 | 30 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 31 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 32 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 33 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 34 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 35 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 36 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 37 | OTHER DEALINGS IN THE SOFTWARE. 38 | */ 39 | 40 | function integralBlurImage( imageID, canvasID, radius, blurAlphaChannel, iterations ) 41 | { 42 | 43 | var img = document.getElementById( imageID ); 44 | var w = img.naturalWidth; 45 | var h = img.naturalHeight; 46 | 47 | var canvas = document.getElementById( canvasID ); 48 | 49 | canvas.style.width = w + "px"; 50 | canvas.style.height = h + "px"; 51 | canvas.width = w; 52 | canvas.height = h; 53 | 54 | var context = canvas.getContext("2d"); 55 | context.clearRect( 0, 0, w, h ); 56 | context.drawImage( img, 0, 0 ); 57 | 58 | if ( isNaN(radius) || radius < 1 ) return; 59 | 60 | if ( blurAlphaChannel ) 61 | { 62 | integralBlurCanvasRGBA( canvasID, 0, 0, w, h, radius, iterations ); 63 | } else { 64 | integralBlurCanvasRGB( canvasID, 0, 0, w, h, radius, iterations ); 65 | } 66 | } 67 | 68 | function integralBlurCanvasRGB( id, top_x, top_y, width, height, radius, iterations ) 69 | { 70 | 71 | var ii = integralImageFromCanvasRGB( id, 0, 0, width, height ); 72 | if ( ii == null ) return; 73 | var imageData = ii.imageData; 74 | var pixels = imageData.pixels; 75 | var context = ii.context; 76 | blurIntegralImageRGB( ii, radius ); 77 | 78 | while ( --iterations > 0 ) 79 | { 80 | ii = calculateIntegralImageRGB( pixels, width, height ); 81 | ii.imageData = imageData; 82 | ii.context = context; 83 | blurIntegralImageRGB( ii, radius ); 84 | } 85 | 86 | context.putImageData( imageData, top_x, top_y ); 87 | 88 | } 89 | 90 | 91 | function integralBlurCanvasRGBA( id, top_x, top_y, width, height, radius, iterations ) 92 | { 93 | 94 | var ii = integralImageFromCanvasRGBA( id, 0, 0, width, height ); 95 | if ( ii == null ) return; 96 | var imageData = ii.imageData; 97 | var pixels = ii.pixels; 98 | var context = ii.context; 99 | blurIntegralImageRGBA( ii, radius ); 100 | 101 | while ( --iterations > 0 ) 102 | { 103 | ii = calculateIntegralImageRGBA( pixels, width, height ); 104 | ii.imageData = imageData; 105 | ii.context = context; 106 | blurIntegralImageRGBA( ii, radius ); 107 | } 108 | 109 | context.putImageData( imageData, top_x, top_y ); 110 | 111 | } 112 | 113 | function updateCanvas( integralImage ) 114 | { 115 | integralImage.context.putImageData( integralImage.imageData, 0, 0 ); 116 | } 117 | 118 | function blurIntegralImageRGBA( integralImage, radius ) 119 | { 120 | var dx1,dx2,dy1,dy2,dy,idx1,idx2,idx3,idx4,area; 121 | 122 | var ii = integralImage; 123 | var width = ii.width; 124 | var height = ii.height; 125 | var pixels = ii.pixels; 126 | var i1 = 0; 127 | var i2 = 0; 128 | var r = ii.r; 129 | var g = ii.g; 130 | var b = ii.b; 131 | var a = ii.a; 132 | var pa; 133 | var iw = width + 1; 134 | 135 | for ( var y = 0; y < height; y++ ) 136 | { 137 | dy1 = ( y < radius ? -y : -radius ); 138 | dy2 = ( y >= height - radius ? height - y : radius ); 139 | dy = dy2 - dy1; 140 | dy1 *= iw; 141 | dy2 *= iw; 142 | for ( var x = 0; x < width; x++ ) 143 | { 144 | dx1 = ( x < radius ? -x : -radius ); 145 | dx2 = ( x >= width - radius ? width - x : radius ); 146 | area = 1 / ((dx2 - dx1) * dy); 147 | 148 | dx1 += i1; 149 | dx2 += i1; 150 | 151 | idx1 = dx1+dy1; 152 | idx2 = dx2+dy2; 153 | idx3 = dx1+dy2; 154 | idx4 = dx2+dy1; 155 | 156 | pa = (( a[idx1] + a[idx2] - a[idx3] - a[idx4] ) * area ) | 0; 157 | if ( pa > 0 ) 158 | { 159 | pa = 255 / pa; 160 | pixels[i2++] = (( r[idx1] + r[idx2] - r[idx3] - r[idx4] ) * area * pa) | 0; 161 | pixels[i2++] = (( g[idx1] + g[idx2] - g[idx3] - g[idx4] ) * area * pa) | 0; 162 | pixels[i2++] = (( b[idx1] + b[idx2] - b[idx3] - b[idx4] ) * area * pa) | 0; 163 | pixels[i2++] = pa; 164 | } else { 165 | pixels[i2++] = pixels[i2++] = pixels[i2++] = pixels[i2++] = 0; 166 | } 167 | i1++; 168 | } 169 | i1++; 170 | } 171 | } 172 | 173 | 174 | 175 | function blurIntegralImageRGB( integralImage, radius ) 176 | { 177 | var dx1,dx2,dy1,dy2,dy,idx1,idx2,idx3,idx4,area; 178 | 179 | var ii = integralImage; 180 | var width = ii.width; 181 | var height = ii.height; 182 | var pixels = ii.pixels; 183 | var i1 = 0; 184 | var i2 = 0; 185 | var r = ii.r; 186 | var g = ii.g; 187 | var b = ii.b; 188 | var iw = width + 1; 189 | 190 | for ( var y = 0; y < height; y++ ) { 191 | dy1 = ( y < radius ? -y : -radius ); 192 | dy2 = ( y >= height - radius ? height - y -1 : radius ); 193 | dy = dy2 - dy1; 194 | dy1 *= iw; 195 | dy2 *= iw; 196 | 197 | for ( var x = 0; x < width; x++ ) { 198 | dx1 = ( x < radius ? -x : -radius ); 199 | dx2 = ( x >= width - radius ? width - x - 1: radius ); 200 | 201 | area = 1 / ((dx2 - dx1) * dy); 202 | 203 | dx1 += i1; 204 | dx2 += i1; 205 | 206 | idx1 = dx1+dy1; 207 | idx2 = dx2+dy2; 208 | idx3 = dx1+dy2; 209 | idx4 = dx2+dy1; 210 | 211 | pixels[i2++] = (( r[idx1] + r[idx2] - r[idx3] - r[idx4] ) * area ) | 0; 212 | pixels[i2++] = (( g[idx1] + g[idx2] - g[idx3] - g[idx4] ) * area ) | 0; 213 | pixels[i2++] = (( b[idx1] + b[idx2] - b[idx3] - b[idx4] ) * area ) | 0; 214 | i2++ 215 | i1++; 216 | } 217 | i1++; 218 | } 219 | 220 | } 221 | 222 | function integralImageFromImage( imageID, canvasID, includeAlphaChannel ) 223 | { 224 | 225 | var img = document.getElementById( imageID ); 226 | var w = img.naturalWidth; 227 | var h = img.naturalHeight; 228 | 229 | var canvas = document.getElementById( canvasID ); 230 | 231 | canvas.style.width = w + "px"; 232 | canvas.style.height = h + "px"; 233 | canvas.width = w; 234 | canvas.height = h; 235 | 236 | var context = canvas.getContext("2d"); 237 | context.clearRect( 0, 0, w, h ); 238 | context.drawImage( img, 0, 0 ); 239 | 240 | if ( includeAlphaChannel ) 241 | { 242 | return integralImageFromCanvasRGBA( canvasID, 0, 0, w, h ); 243 | } else { 244 | return integralImageFromCanvasRGB( canvasID, 0, 0, w, h ); 245 | } 246 | } 247 | 248 | function integralImageFromCanvasRGB( id, top_x, top_y, width, height ) 249 | { 250 | var pixelData = getCanvasPixels( id, top_x, top_y, width, height ); 251 | if ( pixelData == null ) return; 252 | 253 | var ii = calculateIntegralImageRGB( pixelData.pixels, width, height ); 254 | ii.context = pixelData.context; 255 | ii.imageData = pixelData.imageData; 256 | return ii; 257 | } 258 | 259 | function integralImageFromCanvasRGBA( id, top_x, top_y, width, height ) 260 | { 261 | var pixelData = getCanvasPixels( id, top_x, top_y, width, height ); 262 | if ( pixelData == null ) return; 263 | 264 | var ii = calculateIntegralImageRGBA( pixelData.pixels, width, height ); 265 | ii.context = pixelData.context; 266 | ii.imageData = pixelData.imageData; 267 | return ii; 268 | } 269 | 270 | function getCanvasPixels( canvasID, top_x, top_y, width, height ) 271 | { 272 | var result = { id:canvasID, top_x:top_x, top_y:top_y, width:width, height:height }; 273 | result.canvas = document.getElementById( canvasID ); 274 | result.context = result.canvas.getContext("2d"); 275 | 276 | try { 277 | result.imageData = result.context.getImageData( top_x, top_y, width, height ); 278 | } catch(e) { 279 | //throw new Error("unable to access image data: " + e); 280 | return null; 281 | } 282 | result.pixels = result.imageData.data; 283 | return result; 284 | } 285 | 286 | function calculateIntegralImageRGB( pixels, width, height ) 287 | { 288 | var r = []; 289 | var g = []; 290 | var b = []; 291 | 292 | var i = 0; 293 | var j = 0; 294 | for ( y=0; y < height; y++ ) 295 | { 296 | rsum = pixels[i++]; 297 | gsum = pixels[i++]; 298 | bsum = pixels[i++]; 299 | i++; 300 | for ( x = 0; x < width; x++ ) 301 | { 302 | r[j] = rsum; 303 | g[j] = gsum; 304 | b[j++] = bsum; 305 | 306 | rsum += pixels[i++]; 307 | gsum += pixels[i++]; 308 | bsum += pixels[i++]; 309 | i++; 310 | } 311 | 312 | r[j] = rsum; 313 | g[j] = gsum; 314 | b[j++] = bsum; 315 | i-=4; 316 | } 317 | 318 | var j1 = width + 1; 319 | var w1 = j1; 320 | var k = j1 * ( height + 1 ); 321 | var j2 = j1 - w1; 322 | while ( j1 < k ) 323 | { 324 | r[j1] += r[j2]; 325 | g[j1] += g[j2]; 326 | b[j1] += b[j2]; 327 | j1++,j2++; 328 | } 329 | 330 | return { r:r, g:g, b:b, width:width, height:height, pixels:pixels }; 331 | 332 | } 333 | 334 | function calculateIntegralImageRGBA( pixels, width, height ) 335 | { 336 | 337 | var r = []; 338 | var g = []; 339 | var b = []; 340 | var a = []; 341 | var i = 0; 342 | var j = 0; 343 | 344 | for ( y=0; y < height; y++ ) 345 | { 346 | rsum = pixels[i++]; 347 | gsum = pixels[i++]; 348 | bsum = pixels[i++]; 349 | asum = pixels[i++]; 350 | 351 | for ( x = 0; x < width; x++ ) 352 | { 353 | r[j] = rsum; 354 | g[j] = gsum; 355 | b[j] = bsum; 356 | a[j++] = asum; 357 | 358 | rsum += pixels[i++]; 359 | gsum += pixels[i++]; 360 | bsum += pixels[i++]; 361 | asum += pixels[i++]; 362 | } 363 | 364 | r[j] = rsum; 365 | g[j] = gsum; 366 | b[j] = bsum; 367 | a[j++] = asum; 368 | i-=4; 369 | } 370 | 371 | var j1 = width + 1; 372 | var w1 = j1; 373 | var k = j1 * ( height + 1 ); 374 | var j2 = j1 - w1; 375 | while ( j1 < k ) 376 | { 377 | r[j1] += r[j2]; 378 | g[j1] += g[j2]; 379 | b[j1] += b[j2]; 380 | a[j1] += a[j2]; 381 | j1++, j2++; 382 | } 383 | 384 | return { r:r, g:g, b:b, a:a, width:width, height:height, pixels:pixels }; 385 | } 386 | 387 | 388 | 389 | -------------------------------------------------------------------------------- /zorrosvg/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ZorroSVG - Put a Mask on it 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 35 | 36 |
37 |
38 |

ZorroSVG - Put a Mask on it

39 |

The transparency of PNGs for the file size of JPEGs

40 |
41 |
42 |
43 | 44 |
45 |
46 | 47 |
48 |
49 |
50 |
51 |
52 |

Try it

53 |

Drag a transparent PNG or GIF from your desktop onto this page or use the file picker 54 | and see how many kB ZorroSVG can save you.

55 | 56 |
57 |
58 |
59 | 60 |
61 |
62 | 63 |
64 | 65 |
66 |
Original Transparent PNG
67 |
68 |
69 | 70 |
71 |
72 |
73 |
74 |
ZorroSVG (live rendered)
75 |
76 | Change JPEG Compression Quality here to find the optimum quality/size ratio:
77 |

78 | 79 |

Images created with ZorroSVG can be used like any other image. Simply embed the transparent bitmap in your html like this:

80 |
<img src="yourImage.svg" />
81 |

If you want to be on the safe side you can add a fallback which will display the old png in case the browser does not support SVG. Your html tag would look like this:

82 |
<img src="yourImage.svg" onerror="this.src='yourOriginal.png'"/>
83 |

And if you really care about users that still use mobile-Safari on iOS 7 you will have to use an object tag instead of the img tag:

84 |
<object data="yourImage.svg" type="image/svg+xml"/>
85 |
86 |
87 |
88 |
89 |
90 |
91 |

The small print

92 |

ZorroSVG has some minor pitfalls you should be a aware of: 93 |

    94 |
  • It only runs on browsers that support SVG 1.1 - which all the latest browsers do. 95 |
  • Right now there are some subtle brightness differences in semi-transparent areas (maybe you can spot them in the example image's soft shadow). They might be gamma or color-profile related and I am still trying to improve on this. 96 |
  • Some (older) servers do not serve the correct response headers for SVG files. In that case you have to add a .htaccess file to your image folder which contains the line:
    AddType image/svg+xml .svg .svgz
    97 |
  • There are cases where the size of the original PNG is actually smaller than the SVG version. That is usually the case with images that have large monochrome areas or certain types of linear gradients. In these cases PNG can use some very efficient compression methods that are better suited than JPEG. Well, you can't win all the time. 98 |
  • Before somebody points it out: I am not a big fan of the base64 encoding that I have to use to embed the JPEG inside the SVG since it increases the size of the data by a factor of 1.37. For that reason I originally had allowed the option to save the JPEG as a separate file which gets loaded automatically by the SVG code. The problem was that the handling of relative image paths inside of SVGs does not seem to be consistent across browsers. But feel free to check out the Javascript of this page - the code to generate a two map version is still there. 99 |
  • Right now this map creation tool does not work on Internet Explorer (something weird with jQuery I still have to figure out - any pointers how to fix it are more than welcome). 100 |
  • The SVG file download does not seem to work in Safari. Well, this can fortunately be avoided by using a proper browser instead. 101 |

102 |
103 |
104 |
105 |
106 |
107 |
108 |

A few more examples:

109 | 110 | 111 | 112 | 113 |
114 |
115 |
116 |
117 |
118 |
119 |

Version History

120 |

v1.0 121 |

    122 |
  • Initial Release 123 |
124 |

125 |

v1.1 126 |

    127 |
  • Applying gamma correction to the alpha mask to correct opacity differences between the original PNG and the SVG 128 |
129 |

130 |

v1.2 131 |

    132 |
  • Removed the gamma correction code for iPad and iPhone - it looks like the behaviour of the semi-transparent pixels is platform specific - any feedback on browsers that still show different results for the PNG and the SVG is more than welcome. 133 |
134 |

135 |
136 |
137 |
138 | 139 |
140 | 141 |
142 |

Transparent PNGs are great, but they come at a price: file size. Wouldn't it be nice if there were transparent JPEGs instead? Unfortunately there is no such thing, but ZorroSVG gives you the power of transparent PNGs for the size of a JPEG.

143 |

It achieves this by converting your PNG to an SVG which uses the compositing capabilites of SVG to create a masked bitmap on-the-fly using JPEGs for both the image and its mask - achieving most of the time a much better compression than PNG. Since all the compositing and masking happens inside the SVG there is no JavaScript required. And it will work on any current browser.

144 |

The SVGs created by ZorroSVG can be used like any other image by simply using the <img> tag to embed it into your page.

145 |

The tool on this page helps you to convert your current PNGs to the ZorroSVG image format. Give it a try!

146 | 151 |
152 | 153 | 154 |
155 | 156 |
157 | 158 | 159 |
160 |
161 |
162 | 163 | 168 | 169 | 170 | 171 | 172 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /blur/FastBlur.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Superfast Blur - a fast Box Blur For Canvas 4 | 5 | Version: 0.5 6 | Author: Mario Klingemann 7 | Contact: mario@quasimondo.com 8 | Website: http://www.quasimondo.com/BoxBlurForCanvas 9 | Twitter: @quasimondo 10 | 11 | In case you find this class useful - especially in commercial projects - 12 | I am not totally unhappy for a small donation to my PayPal account 13 | mario@quasimondo.de 14 | 15 | Or support me on flattr: 16 | https://flattr.com/thing/140066/Superfast-Blur-a-pretty-fast-Box-Blur-Effect-for-CanvasJavascript 17 | 18 | Copyright (c) 2011 Mario Klingemann 19 | 20 | Permission is hereby granted, free of charge, to any person 21 | obtaining a copy of this software and associated documentation 22 | files (the "Software"), to deal in the Software without 23 | restriction, including without limitation the rights to use, 24 | copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the 26 | Software is furnished to do so, subject to the following 27 | conditions: 28 | 29 | The above copyright notice and this permission notice shall be 30 | included in all copies or substantial portions of the Software. 31 | 32 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 33 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 34 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 35 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 36 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 37 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 38 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 39 | OTHER DEALINGS IN THE SOFTWARE. 40 | */ 41 | var mul_table = [ 1,57,41,21,203,34,97,73,227,91,149,62,105,45,39,137,241,107,3,173,39,71,65,238,219,101,187,87,81,151,141,133,249,117,221,209,197,187,177,169,5,153,73,139,133,127,243,233,223,107,103,99,191,23,177,171,165,159,77,149,9,139,135,131,253,245,119,231,224,109,211,103,25,195,189,23,45,175,171,83,81,79,155,151,147,9,141,137,67,131,129,251,123,30,235,115,113,221,217,53,13,51,50,49,193,189,185,91,179,175,43,169,83,163,5,79,155,19,75,147,145,143,35,69,17,67,33,65,255,251,247,243,239,59,29,229,113,111,219,27,213,105,207,51,201,199,49,193,191,47,93,183,181,179,11,87,43,85,167,165,163,161,159,157,155,77,19,75,37,73,145,143,141,35,138,137,135,67,33,131,129,255,63,250,247,61,121,239,237,117,29,229,227,225,111,55,109,216,213,211,209,207,205,203,201,199,197,195,193,48,190,47,93,185,183,181,179,178,176,175,173,171,85,21,167,165,41,163,161,5,79,157,78,154,153,19,75,149,74,147,73,144,143,71,141,140,139,137,17,135,134,133,66,131,65,129,1]; 42 | 43 | 44 | var shg_table = [0,9,10,10,14,12,14,14,16,15,16,15,16,15,15,17,18,17,12,18,16,17,17,19,19,18,19,18,18,19,19,19,20,19,20,20,20,20,20,20,15,20,19,20,20,20,21,21,21,20,20,20,21,18,21,21,21,21,20,21,17,21,21,21,22,22,21,22,22,21,22,21,19,22,22,19,20,22,22,21,21,21,22,22,22,18,22,22,21,22,22,23,22,20,23,22,22,23,23,21,19,21,21,21,23,23,23,22,23,23,21,23,22,23,18,22,23,20,22,23,23,23,21,22,20,22,21,22,24,24,24,24,24,22,21,24,23,23,24,21,24,23,24,22,24,24,22,24,24,22,23,24,24,24,20,23,22,23,24,24,24,24,24,24,24,23,21,23,22,23,24,24,24,22,24,24,24,23,22,24,24,25,23,25,25,23,24,25,25,24,22,25,25,25,24,23,24,25,25,25,25,25,25,25,25,25,25,25,25,23,25,23,24,25,25,25,25,25,25,25,25,25,24,22,25,25,23,25,25,20,24,25,24,25,25,22,24,25,24,25,24,25,25,24,25,25,25,25,22,25,25,25,24,25,24,25,18]; 45 | 46 | 47 | function boxBlurImage( imageID, canvasID, radius, blurAlphaChannel, iterations ){ 48 | 49 | var img = document.getElementById( imageID ); 50 | var w = img.naturalWidth; 51 | var h = img.naturalHeight; 52 | 53 | var canvas = document.getElementById( canvasID ); 54 | 55 | canvas.style.width = w + "px"; 56 | canvas.style.height = h + "px"; 57 | canvas.width = w; 58 | canvas.height = h; 59 | 60 | var context = canvas.getContext("2d"); 61 | context.clearRect( 0, 0, w, h ); 62 | context.drawImage( img, 0, 0 ); 63 | 64 | if ( isNaN(radius) || radius < 1 ) return; 65 | 66 | if ( blurAlphaChannel ) 67 | { 68 | boxBlurCanvasRGBA( canvasID, 0, 0, w, h, radius, iterations ); 69 | } else { 70 | boxBlurCanvasRGB( canvasID, 0, 0, w, h, radius, iterations ); 71 | } 72 | 73 | } 74 | 75 | 76 | function boxBlurCanvasRGBA( id, top_x, top_y, width, height, radius, iterations ){ 77 | if ( isNaN(radius) || radius < 1 ) return; 78 | 79 | radius |= 0; 80 | 81 | if ( isNaN(iterations) ) iterations = 1; 82 | iterations |= 0; 83 | if ( iterations > 3 ) iterations = 3; 84 | if ( iterations < 1 ) iterations = 1; 85 | 86 | var canvas = document.getElementById( id ); 87 | var context = canvas.getContext("2d"); 88 | var imageData; 89 | 90 | try { 91 | try { 92 | imageData = context.getImageData( top_x, top_y, width, height ); 93 | } catch(e) { 94 | 95 | // NOTE: this part is supposedly only needed if you want to work with local files 96 | // so it might be okay to remove the whole try/catch block and just use 97 | // imageData = context.getImageData( top_x, top_y, width, height ); 98 | try { 99 | netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); 100 | imageData = context.getImageData( top_x, top_y, width, height ); 101 | } catch(e) { 102 | alert("Cannot access local image"); 103 | throw new Error("unable to access local image data: " + e); 104 | return; 105 | } 106 | } 107 | } catch(e) { 108 | alert("Cannot access image"); 109 | throw new Error("unable to access image data: " + e); 110 | return; 111 | } 112 | 113 | var pixels = imageData.data; 114 | 115 | var rsum,gsum,bsum,asum,x,y,i,p,p1,p2,yp,yi,yw,idx,pa; 116 | var wm = width - 1; 117 | var hm = height - 1; 118 | var wh = width * height; 119 | var rad1 = radius + 1; 120 | 121 | var mul_sum = mul_table[radius]; 122 | var shg_sum = shg_table[radius]; 123 | 124 | var r = []; 125 | var g = []; 126 | var b = []; 127 | var a = []; 128 | 129 | var vmin = []; 130 | var vmax = []; 131 | 132 | while ( iterations-- > 0 ){ 133 | yw = yi = 0; 134 | 135 | for ( y=0; y < height; y++ ){ 136 | rsum = pixels[yw] * rad1; 137 | gsum = pixels[yw+1] * rad1; 138 | bsum = pixels[yw+2] * rad1; 139 | asum = pixels[yw+3] * rad1; 140 | 141 | 142 | for( i = 1; i <= radius; i++ ){ 143 | p = yw + (((i > wm ? wm : i )) << 2 ); 144 | rsum += pixels[p++]; 145 | gsum += pixels[p++]; 146 | bsum += pixels[p++]; 147 | asum += pixels[p] 148 | } 149 | 150 | for ( x = 0; x < width; x++ ) { 151 | r[yi] = rsum; 152 | g[yi] = gsum; 153 | b[yi] = bsum; 154 | a[yi] = asum; 155 | 156 | if( y==0) { 157 | vmin[x] = ( ( p = x + rad1) < wm ? p : wm ) << 2; 158 | vmax[x] = ( ( p = x - radius) > 0 ? p << 2 : 0 ); 159 | } 160 | 161 | p1 = yw + vmin[x]; 162 | p2 = yw + vmax[x]; 163 | 164 | rsum += pixels[p1++] - pixels[p2++]; 165 | gsum += pixels[p1++] - pixels[p2++]; 166 | bsum += pixels[p1++] - pixels[p2++]; 167 | asum += pixels[p1] - pixels[p2]; 168 | 169 | yi++; 170 | } 171 | yw += ( width << 2 ); 172 | } 173 | 174 | for ( x = 0; x < width; x++ ) { 175 | yp = x; 176 | rsum = r[yp] * rad1; 177 | gsum = g[yp] * rad1; 178 | bsum = b[yp] * rad1; 179 | asum = a[yp] * rad1; 180 | 181 | for( i = 1; i <= radius; i++ ) { 182 | yp += ( i > hm ? 0 : width ); 183 | rsum += r[yp]; 184 | gsum += g[yp]; 185 | bsum += b[yp]; 186 | asum += a[yp]; 187 | } 188 | 189 | yi = x << 2; 190 | for ( y = 0; y < height; y++) { 191 | 192 | pixels[yi+3] = pa = (asum * mul_sum) >>> shg_sum; 193 | if ( pa > 0 ) 194 | { 195 | pa = 255 / pa; 196 | pixels[yi] = ((rsum * mul_sum) >>> shg_sum) * pa; 197 | pixels[yi+1] = ((gsum * mul_sum) >>> shg_sum) * pa; 198 | pixels[yi+2] = ((bsum * mul_sum) >>> shg_sum) * pa; 199 | } else { 200 | pixels[yi] = pixels[yi+1] = pixels[yi+2] = 0; 201 | } 202 | if( x == 0 ) { 203 | vmin[y] = ( ( p = y + rad1) < hm ? p : hm ) * width; 204 | vmax[y] = ( ( p = y - radius) > 0 ? p * width : 0 ); 205 | } 206 | 207 | p1 = x + vmin[y]; 208 | p2 = x + vmax[y]; 209 | 210 | rsum += r[p1] - r[p2]; 211 | gsum += g[p1] - g[p2]; 212 | bsum += b[p1] - b[p2]; 213 | asum += a[p1] - a[p2]; 214 | 215 | yi += width << 2; 216 | } 217 | } 218 | } 219 | 220 | context.putImageData( imageData, top_x, top_y ); 221 | 222 | } 223 | 224 | function boxBlurCanvasRGB( id, top_x, top_y, width, height, radius, iterations ){ 225 | if ( isNaN(radius) || radius < 1 ) return; 226 | 227 | radius |= 0; 228 | 229 | if ( isNaN(iterations) ) iterations = 1; 230 | iterations |= 0; 231 | if ( iterations > 3 ) iterations = 3; 232 | if ( iterations < 1 ) iterations = 1; 233 | 234 | var canvas = document.getElementById( id ); 235 | var context = canvas.getContext("2d"); 236 | var imageData; 237 | 238 | try { 239 | try { 240 | imageData = context.getImageData( top_x, top_y, width, height ); 241 | } catch(e) { 242 | 243 | // NOTE: this part is supposedly only needed if you want to work with local files 244 | // so it might be okay to remove the whole try/catch block and just use 245 | // imageData = context.getImageData( top_x, top_y, width, height ); 246 | try { 247 | netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); 248 | imageData = context.getImageData( top_x, top_y, width, height ); 249 | } catch(e) { 250 | alert("Cannot access local image"); 251 | throw new Error("unable to access local image data: " + e); 252 | return; 253 | } 254 | } 255 | } catch(e) { 256 | alert("Cannot access image"); 257 | throw new Error("unable to access image data: " + e); 258 | return; 259 | } 260 | 261 | var pixels = imageData.data; 262 | 263 | var rsum,gsum,bsum,asum,x,y,i,p,p1,p2,yp,yi,yw,idx; 264 | var wm = width - 1; 265 | var hm = height - 1; 266 | var wh = width * height; 267 | var rad1 = radius + 1; 268 | 269 | var r = []; 270 | var g = []; 271 | var b = []; 272 | 273 | var mul_sum = mul_table[radius]; 274 | var shg_sum = shg_table[radius]; 275 | 276 | var vmin = []; 277 | var vmax = []; 278 | 279 | while ( iterations-- > 0 ){ 280 | yw = yi = 0; 281 | 282 | for ( y=0; y < height; y++ ){ 283 | rsum = pixels[yw] * rad1; 284 | gsum = pixels[yw+1] * rad1; 285 | bsum = pixels[yw+2] * rad1; 286 | 287 | for( i = 1; i <= radius; i++ ){ 288 | p = yw + (((i > wm ? wm : i )) << 2 ); 289 | rsum += pixels[p++]; 290 | gsum += pixels[p++]; 291 | bsum += pixels[p++]; 292 | } 293 | 294 | for ( x = 0; x < width; x++ ){ 295 | r[yi] = rsum; 296 | g[yi] = gsum; 297 | b[yi] = bsum; 298 | 299 | if( y==0) { 300 | vmin[x] = ( ( p = x + rad1) < wm ? p : wm ) << 2; 301 | vmax[x] = ( ( p = x - radius) > 0 ? p << 2 : 0 ); 302 | } 303 | 304 | p1 = yw + vmin[x]; 305 | p2 = yw + vmax[x]; 306 | 307 | rsum += pixels[p1++] - pixels[p2++]; 308 | gsum += pixels[p1++] - pixels[p2++]; 309 | bsum += pixels[p1++] - pixels[p2++]; 310 | 311 | yi++; 312 | } 313 | yw += ( width << 2 ); 314 | } 315 | 316 | for ( x = 0; x < width; x++ ){ 317 | yp = x; 318 | rsum = r[yp] * rad1; 319 | gsum = g[yp] * rad1; 320 | bsum = b[yp] * rad1; 321 | 322 | for( i = 1; i <= radius; i++ ){ 323 | yp += ( i > hm ? 0 : width ); 324 | rsum += r[yp]; 325 | gsum += g[yp]; 326 | bsum += b[yp]; 327 | } 328 | 329 | yi = x << 2; 330 | for ( y = 0; y < height; y++){ 331 | pixels[yi] = (rsum * mul_sum) >>> shg_sum; 332 | pixels[yi+1] = (gsum * mul_sum) >>> shg_sum; 333 | pixels[yi+2] = (bsum * mul_sum) >>> shg_sum; 334 | 335 | if( x == 0 ) { 336 | vmin[y] = ( ( p = y + rad1) < hm ? p : hm ) * width; 337 | vmax[y] = ( ( p = y - radius) > 0 ? p * width : 0 ); 338 | } 339 | 340 | p1 = x + vmin[y]; 341 | p2 = x + vmax[y]; 342 | 343 | rsum += r[p1] - r[p2]; 344 | gsum += g[p1] - g[p2]; 345 | bsum += b[p1] - b[p2]; 346 | 347 | yi += width << 2; 348 | } 349 | } 350 | } 351 | context.putImageData( imageData, top_x, top_y ); 352 | 353 | } -------------------------------------------------------------------------------- /zorrosvg/js/zorrosvgmaskmaker.js: -------------------------------------------------------------------------------- 1 | /* 2 | * ZorroSVG Mask Maker 3 | * 4 | * Visit http://quasimondo.com/ZorroSVG documentation, updates and examples. 5 | * 6 | * Copyright (c) 2014 Mario Klingemann 7 | * 8 | * Permission is hereby granted, free of charge, to any person 9 | * obtaining a copy of this software and associated documentation 10 | * files (the "Software"), to deal in the Software without 11 | * restriction, including without limitation the rights to use, 12 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the 14 | * Software is furnished to do so, subject to the following 15 | * conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be 18 | * included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | * OTHER DEALINGS IN THE SOFTWARE. 28 | */ 29 | 30 | var currentImage; 31 | var isLittleEndian; 32 | var currentMaskCanvas; 33 | var newImageData; 34 | var sourceFileSize; 35 | var exampleReady = false; 36 | var jqueryReady = false; 37 | var currentFileName = "example"; 38 | var currentEmbeddedSVG; 39 | var rawSVG; 40 | 41 | $(function() { 42 | 43 | if ( $( "#qualitySlider" ).slider != null ) 44 | { 45 | $( "#qualitySlider" ).slider({ 46 | range: false, 47 | min: 0, 48 | max: 1, 49 | step: 0.01, 50 | value: 0.65, 51 | change: onJpegQualityChanged 52 | } 53 | ); 54 | } 55 | 56 | document.getElementById('files').addEventListener('change', handleFileSelect, false); 57 | 58 | jqueryReady = true; 59 | if ( exampleReady) createMaskedMap(); 60 | 61 | }); 62 | 63 | function onExampleLoaded(img) 64 | { 65 | currentImage = img; 66 | sourceFileSize = 100864; 67 | exampleReady = true; 68 | if ( jqueryReady) createMaskedMap(); 69 | } 70 | 71 | function handleFileSelect(evt) 72 | { 73 | var file = evt.target.files[0]; 74 | 75 | var parts = file.name.split("."); 76 | parts.pop(); 77 | currentFileName = parts.join("."); 78 | 79 | sourceFileSize = file.size; 80 | var reader = new FileReader(); 81 | reader.readAsDataURL(file); 82 | 83 | addEventHandler(reader, 'loadend', function(e, file) { 84 | var bin = this.result; 85 | currentImage = document.createElement("img"); 86 | currentImage.file = file; 87 | currentImage.src = bin; 88 | currentImage.onload = createMaskedMap; 89 | currentImage.style.verticalAlign = "top"; 90 | drop.innerHTML = ""; 91 | drop.appendChild(currentImage); 92 | }); 93 | } 94 | 95 | 96 | 97 | 98 | if(window.FileReader) 99 | { 100 | var drop; 101 | addEventHandler(window, 'load', function() { 102 | drop = document.getElementById('drop'); 103 | 104 | function cancel(e) { 105 | if (e.preventDefault) { e.preventDefault(); } 106 | return false; 107 | } 108 | 109 | // Tells the browser that we *can* drop on this target 110 | addEventHandler(window, 'dragover', cancel); 111 | addEventHandler(window, 'dragenter', cancel); 112 | 113 | addEventHandler(window, 'drop', function (e) { 114 | e = e || window.event; // get window.event if e argument missing (in IE) 115 | if (e.preventDefault) { e.preventDefault(); } // stops the browser from redirecting off to the image. 116 | 117 | var dt = e.dataTransfer; 118 | var file = dt.files[0]; 119 | sourceFileSize = file.size; 120 | 121 | 122 | var reader = new FileReader(); 123 | reader.readAsDataURL(file); 124 | 125 | addEventHandler(reader, 'loadend', function(e, file) { 126 | var bin = this.result; 127 | 128 | 129 | 130 | currentImage = document.createElement("img"); 131 | currentImage.file = file; 132 | currentImage.src = bin; 133 | currentImage.onload = createMaskedMap; 134 | drop.innerHTML = ""; 135 | drop.appendChild(currentImage); 136 | 137 | 138 | }.bindToEventHandler(file)); 139 | 140 | return false; 141 | }); 142 | 143 | 144 | Function.prototype.bindToEventHandler = function bindToEventHandler() { 145 | var handler = this; 146 | var boundParameters = Array.prototype.slice.call(arguments); 147 | //create closure 148 | return function(e) { 149 | e = e || window.event; // get window.event if e argument missing (in IE) 150 | boundParameters.unshift(e); 151 | handler.apply(this, boundParameters); 152 | } 153 | }; 154 | }); 155 | 156 | } else { 157 | document.getElementById('compressedStatus').innerHTML = 'Your browser does not support the HTML5 FileReader.'; 158 | } 159 | 160 | 161 | 162 | function addEventHandler(obj, evt, handler) { 163 | if ( obj == null ) return; 164 | if(obj.addEventListener) { 165 | // W3C method 166 | obj.addEventListener(evt, handler, false); 167 | } else if(obj.attachEvent) { 168 | // IE method. 169 | obj.attachEvent('on'+evt, handler); 170 | } else { 171 | // Old school method. 172 | obj['on'+evt] = handler; 173 | } 174 | } 175 | 176 | function createMaskedMap(event) 177 | { 178 | try 179 | { 180 | if ( isLittleEndian == undefined ) 181 | { 182 | // Determine whether Uint32 is little- or big-endian. 183 | var endianTest = new ArrayBuffer(4); 184 | var e8 = new Uint8ClampedArray(endianTest); 185 | var e32 = new Uint32Array(endianTest); 186 | e32[0] = 0x000000ff; 187 | isLittleEndian = (e8[3] === 0xff); 188 | } 189 | } catch (error) 190 | { 191 | createMaskedMapForDumbBrowsers(); 192 | return; 193 | } 194 | 195 | 196 | var userAgent = window.navigator.userAgent; 197 | var applyGammaCorrection = true; 198 | if (userAgent.match(/iPad/i) || userAgent.match(/iPhone/i)) { 199 | applyGammaCorrection = false; 200 | } 201 | 202 | var img = currentImage; 203 | var canvas = document.createElement("canvas"); 204 | 205 | canvas.width = img.naturalWidth || img.width; 206 | canvas.height = (img.naturalHeight || img.height )* 2; 207 | var context = canvas.getContext("2d"); 208 | 209 | context.drawImage(img, 0, 0); 210 | context.drawImage(img, 0, img.naturalHeight || img.height ); 211 | 212 | 213 | var imagedata = context.getImageData(0, 0, canvas.width, canvas.height); 214 | var buf32 = new Uint32Array(imagedata.data.buffer); 215 | var l = buf32.length; 216 | var l2 = l>>1; 217 | if ( !isLittleEndian ) 218 | { 219 | if ( applyGammaCorrection ) 220 | { 221 | for ( var i = l; --i >= l2; ) 222 | { 223 | //Gamma correction for semi transparent pixels: 224 | var b = (Math.pow(((buf32[i] >>> 24) & 0xff) / 255,0.45 ) * 255) | 0; 225 | buf32[i] = 0xff000000 | ( b << 16) | ( b << 8) | b; 226 | } 227 | } else { 228 | for ( var i = l; --i >= l2; ) 229 | { 230 | //No Gamma correction on some platforms 231 | var b = (buf32[i] >>> 24) & 0xff; 232 | buf32[i] = 0xff000000 | ( b << 16) | ( b << 8) | b; 233 | } 234 | } 235 | 236 | for ( i = l2; --i > -l; ) 237 | { 238 | buf32[i] |= 0xff000000; 239 | } 240 | } else { 241 | if ( applyGammaCorrection ) 242 | { 243 | for ( var i = l; --i >= l2; ) 244 | { 245 | //Gamma correction for semi transparent pixels: 246 | var b = (Math.pow(( buf32[i] & 0xff) / 255,0.45 ) * 255) | 0; 247 | buf32[i] = 0xff | ( b << 24) | ( b << 16) | (b<<8); 248 | } 249 | } else { 250 | for ( var i = l; --i >= l2; ) 251 | { 252 | //No Gamma correction on some platforms 253 | var b = buf32[i] & 0xff; 254 | buf32[i] = 0xff | ( b << 24) | ( b << 16) | (b<<8); 255 | } 256 | } 257 | 258 | for ( i = l2; --i > -l; ) 259 | { 260 | buf32[i] |= 0xff; 261 | } 262 | 263 | } 264 | 265 | currentMaskCanvas = document.createElement("canvas"); 266 | currentMaskCanvas.width = img.naturalWidth; 267 | currentMaskCanvas.height = img.naturalHeight * 2; 268 | var context2 = currentMaskCanvas.getContext("2d"); 269 | context2.putImageData(imagedata, 0, 0); 270 | if ( $( "#qualitySlider" ).slider != null ) 271 | { 272 | compress($( "#qualitySlider" ).slider( "option", "value" )); 273 | } else { 274 | compress(0.5); 275 | } 276 | 277 | } 278 | 279 | function createMaskedMapForDumbBrowsers() 280 | { 281 | 282 | var img = currentImage; 283 | var canvas = document.createElement("canvas"); 284 | canvas.width = img.naturalWidth; 285 | canvas.height = img.naturalHeight * 2; 286 | var context = canvas.getContext("2d"); 287 | context.drawImage(img, 0, 0); 288 | context.drawImage(img, 0, img.naturalHeight); 289 | 290 | 291 | var imagedata = context.getImageData(0, 0, canvas.width, canvas.height); 292 | var buf8 = imagedata.data; 293 | 294 | var l = buf8.length; 295 | var l2 = l>>1; 296 | 297 | for ( var i = l; --i >= l2; ) 298 | { 299 | var b = buf8[i+3]; 300 | buf8[i+3] = 0xff; 301 | buf8[i] = b; 302 | buf8[i+1] = b; 303 | buf8[i+2] = b; 304 | } 305 | 306 | for ( i = l2; --i > -l; ) 307 | { 308 | buf8[i+3] = 0xff; 309 | } 310 | 311 | 312 | 313 | currentMaskCanvas = document.createElement("canvas"); 314 | currentMaskCanvas.width = img.naturalWidth; 315 | currentMaskCanvas.height = img.naturalHeight * 2; 316 | var context2 = currentMaskCanvas.getContext("2d"); 317 | context2.putImageData(imagedata, 0, 0); 318 | 319 | if ( $( "#qualitySlider" ).slider != null ) 320 | { 321 | compress($( "#qualitySlider" ).slider( "option", "value" )); 322 | } else { 323 | compress(0.5); 324 | } 325 | 326 | } 327 | 328 | 329 | function onJpegQualityChanged(event, ui) 330 | { 331 | if ( $( "#qualitySlider" ).slider != null ) 332 | { 333 | compress($( "#qualitySlider" ).slider( "option", "value" )); 334 | } else { 335 | compress(0.5); 336 | } 337 | } 338 | 339 | function compress(quality){ 340 | if ( currentMaskCanvas == undefined ) return; 341 | 342 | compressed.innerHTML = ""; 343 | currentEmbeddedSVG = maskedImageToSVG(quality,true); 344 | 345 | if ( currentEmbeddedSVG.outerHTML ) 346 | { 347 | rawSVG = currentEmbeddedSVG.outerHTML.toString() 348 | } else { 349 | rawSVG = new XMLSerializer().serializeToString(currentEmbeddedSVG); 350 | } 351 | 352 | 353 | var is_chrome = navigator.userAgent.indexOf('Chrome') > -1; 354 | var is_safari = navigator.userAgent.indexOf("Safari") > -1; 355 | if ((is_chrome)&&(is_safari)) {is_safari=false;} 356 | 357 | if (!is_safari ) 358 | { 359 | var img = new Image(); 360 | img.style.height= "auto"; 361 | img.style.maxWidth= "592px"; 362 | img.style.height= "auto"; 363 | img.src ="data:image/svg+xml;utf-8,"+escape(rawSVG); 364 | img.width = currentImage.width; 365 | img.height = currentImage.height; 366 | compressed.appendChild(img); 367 | } else { 368 | 369 | currentEmbeddedSVG.width = currentImage.width; 370 | currentEmbeddedSVG.height = currentImage.height; 371 | 372 | compressed.appendChild(currentEmbeddedSVG); 373 | } 374 | 375 | 376 | var size1 = rawSVG.length; 377 | newImageData = currentMaskCanvas.toDataURL("image/jpeg", quality); 378 | document.getElementById('compressedStatus').innerHTML = 379 | "

Original PNG: "+ Math.round( sourceFileSize / 1024)+"kB"+ 380 | " - ZorroSVG: "+ Math.round(size1 / 1024)+"kB, that's "+ 381 | ""+ Math.round(size1 / sourceFileSize * 100)+"% of the original size

"+ 382 | (size1 > sourceFileSize ? "In this case, you rather shouldn't, but if you insist you still can":"")+ 383 | "

=> Download converted SVG image

"; 384 | 385 | var a = document.getElementById("singlesvg"); 386 | a.onclick = download; 387 | a.href = "#"; 388 | 389 | document.getElementById("originalSize").innerHTML = "Original Transparent PNG: "+Math.round( sourceFileSize / 1024)+"kB"; 390 | document.getElementById("zorroSize").innerHTML = "ZorroSVG: "+Math.round(size1 / 1024)+"kB (live rendered)"; 391 | 392 | } 393 | 394 | function download(e) { 395 | e.preventDefault(); 396 | var blob = new Blob([rawSVG], {type: "image/svg+xml"}); 397 | saveAs(blob, currentFileName+".svg"); 398 | } 399 | 400 | function maskedImageToSVG(quality, embedded) { 401 | 402 | var svg = document.createElementNS("http://www.w3.org/2000/svg","svg"); 403 | svg.setAttribute('xmlns',"http://www.w3.org/2000/svg" ); 404 | svg.setAttribute('width', currentMaskCanvas.width); 405 | svg.setAttribute('height', currentMaskCanvas.height*0.5); 406 | svg.setAttribute('viewBox', "0 0 "+currentMaskCanvas.width+" "+(currentMaskCanvas.height*0.5)); 407 | 408 | svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink"); 409 | 410 | 411 | var defs = document.createElementNS("http://www.w3.org/2000/svg", 'defs'); 412 | svg.appendChild(defs); 413 | 414 | var filter = document.createElementNS("http://www.w3.org/2000/svg", 'filter'); 415 | filter.setAttribute("id","a"); 416 | defs.appendChild(filter); 417 | 418 | var feOffset = document.createElementNS("http://www.w3.org/2000/svg", 'feOffset'); 419 | feOffset.setAttribute("dy",-currentMaskCanvas.height*0.5); 420 | feOffset.setAttribute("in","SourceGraphic" ); 421 | feOffset.setAttribute("result","b"); 422 | filter.appendChild(feOffset); 423 | 424 | var feColorMatrix = document.createElementNS("http://www.w3.org/2000/svg", 'feColorMatrix'); 425 | feColorMatrix.setAttribute("in","b" ); 426 | feColorMatrix.setAttribute("result","b"); 427 | feColorMatrix.setAttribute("type","matrix"); 428 | feColorMatrix.setAttribute("values","0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0"); 429 | filter.appendChild(feColorMatrix); 430 | 431 | var feComposite = document.createElementNS("http://www.w3.org/2000/svg", 'feComposite'); 432 | feComposite.setAttribute("in","SourceGraphic" ); 433 | feComposite.setAttribute("in2","b" ); 434 | feComposite.setAttribute("operator","in" ); 435 | filter.appendChild(feComposite); 436 | 437 | var image = document.createElementNS("http://www.w3.org/2000/svg", 'image'); 438 | image.setAttribute("width","100%"); 439 | image.setAttribute("height","200%"); 440 | 441 | if ( embedded ) 442 | image.setAttributeNS("http://www.w3.org/1999/xlink", "A:href",currentMaskCanvas.toDataURL("image/jpeg", quality)); 443 | else 444 | image.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href",currentFileName+".jpg"); 445 | 446 | image.setAttribute("filter","url(#a)"); 447 | svg.appendChild(image); 448 | return svg; 449 | } 450 | 451 | -------------------------------------------------------------------------------- /zorrosvg/img/zorro2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | 34 | 35 | 37 | 41 | 42 | 43 | 44 | 45 | 46 | 53 | 55 | 56 | 58 | 64 | 65 | 66 | 68 | 79 | 80 | 81 | 83 | 84 | 86 | 88 | 95 | 96 | 97 | 98 | 103 | 111 | 116 | 121 | 129 | 140 | 145 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /blur/StackBoxBlur.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | StackBoxBlur - a fast almost Box Blur For Canvas 4 | 5 | Version: 0.3 6 | Author: Mario Klingemann 7 | Contact: mario@quasimondo.com 8 | Website: http://www.quasimondo.com/ 9 | Twitter: @quasimondo 10 | 11 | In case you find this class useful - especially in commercial projects - 12 | I am not totally unhappy for a small donation to my PayPal account 13 | mario@quasimondo.de 14 | 15 | Copyright (c) 2010 Mario Klingemann 16 | 17 | Permission is hereby granted, free of charge, to any person 18 | obtaining a copy of this software and associated documentation 19 | files (the "Software"), to deal in the Software without 20 | restriction, including without limitation the rights to use, 21 | copy, modify, merge, publish, distribute, sublicense, and/or sell 22 | copies of the Software, and to permit persons to whom the 23 | Software is furnished to do so, subject to the following 24 | conditions: 25 | 26 | The above copyright notice and this permission notice shall be 27 | included in all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 30 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 31 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 32 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 33 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 34 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 35 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 36 | OTHER DEALINGS IN THE SOFTWARE. 37 | */ 38 | /* 39 | var mul_table = [ 1,57,41,21,203,34,97,73,227,91,149,62,105,45,39,137,241,107,3,173,39,71,65,238,219,101,187,87,81,151,141,133,249,117,221,209,197,187,177,169,5,153,73,139,133,127,243,233,223,107,103,99,191,23,177,171,165,159,77,149,9,139,135,131,253,245,119,231,224,109,211,103,25,195,189,23,45,175,171,83,81,79,155,151,147,9,141,137,67,131,129,251,123,30,235,115,113,221,217,53,13,51,50,49,193,189,185,91,179,175,43,169,83,163,5,79,155,19,75,147,145,143,35,69,17,67,33,65,255,251,247,243,239,59,29,229,113,111,219,27,213,105,207,51,201,199,49,193,191,47,93,183,181,179,11,87,43,85,167,165,163,161,159,157,155,77,19,75,37,73,145,143,141,35,138,137,135,67,33,131,129,255,63,250,247,61,121,239,237,117,29,229,227,225,111,55,109,216,213,211,209,207,205,203,201,199,197,195,193,48,190,47,93,185,183,181,179,178,176,175,173,171,85,21,167,165,41,163,161,5,79,157,78,154,153,19,75,149,74,147,73,144,143,71,141,140,139,137,17,135,134,133,66,131,65,129,1]; 40 | 41 | 42 | var shg_table = [0,9,10,10,14,12,14,14,16,15,16,15,16,15,15,17,18,17,12,18,16,17,17,19,19,18,19,18,18,19,19,19,20,19,20,20,20,20,20,20,15,20,19,20,20,20,21,21,21,20,20,20,21,18,21,21,21,21,20,21,17,21,21,21,22,22,21,22,22,21,22,21,19,22,22,19,20,22,22,21,21,21,22,22,22,18,22,22,21,22,22,23,22,20,23,22,22,23,23,21,19,21,21,21,23,23,23,22,23,23,21,23,22,23,18,22,23,20,22,23,23,23,21,22,20,22,21,22,24,24,24,24,24,22,21,24,23,23,24,21,24,23,24,22,24,24,22,24,24,22,23,24,24,24,20,23,22,23,24,24,24,24,24,24,24,23,21,23,22,23,24,24,24,22,24,24,24,23,22,24,24,25,23,25,25,23,24,25,25,24,22,25,25,25,24,23,24,25,25,25,25,25,25,25,25,25,25,25,25,23,25,23,24,25,25,25,25,25,25,25,25,25,24,22,25,25,23,25,25,20,24,25,24,25,25,22,24,25,24,25,24,25,25,24,25,25,25,25,22,25,25,25,24,25,24,25,18]; 43 | */ 44 | 45 | var mul_table = [ 1,171,205,293,57,373,79,137,241,27,391,357,41,19,283,265,497,469,443,421,25,191,365,349,335,161,155,149,9,278,269,261,505,245,475,231,449,437,213,415,405,395,193,377,369,361,353,345,169,331,325,319,313,307,301,37,145,285,281,69,271,267,263,259,509,501,493,243,479,118,465,459,113,446,55,435,429,423,209,413,51,403,199,393,97,3,379,375,371,367,363,359,355,351,347,43,85,337,333,165,327,323,5,317,157,311,77,305,303,75,297,294,73,289,287,71,141,279,277,275,68,135,67,133,33,262,260,129,511,507,503,499,495,491,61,121,481,477,237,235,467,232,115,457,227,451,7,445,221,439,218,433,215,427,425,211,419,417,207,411,409,203,202,401,399,396,197,49,389,387,385,383,95,189,47,187,93,185,23,183,91,181,45,179,89,177,11,175,87,173,345,343,341,339,337,21,167,83,331,329,327,163,81,323,321,319,159,79,315,313,39,155,309,307,153,305,303,151,75,299,149,37,295,147,73,291,145,289,287,143,285,71,141,281,35,279,139,69,275,137,273,17,271,135,269,267,133,265,33,263,131,261,130,259,129,257,1]; 46 | 47 | 48 | var shg_table = [0,9,10,11,9,12,10,11,12,9,13,13,10,9,13,13,14,14,14,14,10,13,14,14,14,13,13,13,9,14,14,14,15,14,15,14,15,15,14,15,15,15,14,15,15,15,15,15,14,15,15,15,15,15,15,12,14,15,15,13,15,15,15,15,16,16,16,15,16,14,16,16,14,16,13,16,16,16,15,16,13,16,15,16,14,9,16,16,16,16,16,16,16,16,16,13,14,16,16,15,16,16,10,16,15,16,14,16,16,14,16,16,14,16,16,14,15,16,16,16,14,15,14,15,13,16,16,15,17,17,17,17,17,17,14,15,17,17,16,16,17,16,15,17,16,17,11,17,16,17,16,17,16,17,17,16,17,17,16,17,17,16,16,17,17,17,16,14,17,17,17,17,15,16,14,16,15,16,13,16,15,16,14,16,15,16,12,16,15,16,17,17,17,17,17,13,16,15,17,17,17,16,15,17,17,17,16,15,17,17,14,16,17,17,16,17,17,16,15,17,16,14,17,16,15,17,16,17,17,16,17,15,16,17,14,17,16,15,17,16,17,13,17,16,17,17,16,17,14,17,16,17,16,17,16,17,9 49 | ]; 50 | 51 | function stackBoxBlurImage( imageID, canvasID, radius, blurAlphaChannel, iterations ) 52 | { 53 | 54 | var img = document.getElementById( imageID ); 55 | var w = img.naturalWidth; 56 | var h = img.naturalHeight; 57 | 58 | var canvas = document.getElementById( canvasID ); 59 | 60 | canvas.style.width = w + "px"; 61 | canvas.style.height = h + "px"; 62 | canvas.width = w; 63 | canvas.height = h; 64 | 65 | var context = canvas.getContext("2d"); 66 | context.clearRect( 0, 0, w, h ); 67 | context.drawImage( img, 0, 0 ); 68 | 69 | if ( isNaN(radius) || radius < 1 ) return; 70 | 71 | if ( blurAlphaChannel ) 72 | stackBoxBlurCanvasRGBA( canvasID, 0, 0, w, h, radius, iterations ); 73 | else 74 | stackBoxBlurCanvasRGB( canvasID, 0, 0, w, h, radius, iterations ); 75 | } 76 | 77 | 78 | function stackBoxBlurCanvasRGBA( id, top_x, top_y, width, height, radius, iterations ) 79 | { 80 | if ( isNaN(radius) || radius < 1 ) return; 81 | radius |= 0; 82 | 83 | if ( isNaN(iterations) ) iterations = 1; 84 | iterations |= 0; 85 | if ( iterations > 3 ) iterations = 3; 86 | if ( iterations < 1 ) iterations = 1; 87 | 88 | var canvas = document.getElementById( id ); 89 | var context = canvas.getContext("2d"); 90 | var imageData; 91 | 92 | try { 93 | try { 94 | imageData = context.getImageData( top_x, top_y, width, height ); 95 | } catch(e) { 96 | 97 | // NOTE: this part is supposedly only needed if you want to work with local files 98 | // so it might be okay to remove the whole try/catch block and just use 99 | // imageData = context.getImageData( top_x, top_y, width, height ); 100 | try { 101 | netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); 102 | imageData = context.getImageData( top_x, top_y, width, height ); 103 | } catch(e) { 104 | alert("Cannot access local image"); 105 | throw new Error("unable to access local image data: " + e); 106 | return; 107 | } 108 | } 109 | } catch(e) { 110 | alert("Cannot access image"); 111 | throw new Error("unable to access image data: " + e); 112 | } 113 | 114 | var pixels = imageData.data; 115 | 116 | var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, a_sum, 117 | r_out_sum, g_out_sum, b_out_sum, a_out_sum, 118 | r_in_sum, g_in_sum, b_in_sum, a_in_sum, 119 | pr, pg, pb, pa, rbs; 120 | 121 | var div = radius + radius + 1; 122 | var w4 = width << 2; 123 | var widthMinus1 = width - 1; 124 | var heightMinus1 = height - 1; 125 | var radiusPlus1 = radius + 1; 126 | 127 | var stackStart = new BlurStack(); 128 | 129 | var stack = stackStart; 130 | for ( i = 1; i < div; i++ ) 131 | { 132 | stack = stack.next = new BlurStack(); 133 | if ( i == radiusPlus1 ) var stackEnd = stack; 134 | } 135 | stack.next = stackStart; 136 | var stackIn = null; 137 | 138 | 139 | 140 | var mul_sum = mul_table[radius]; 141 | var shg_sum = shg_table[radius]; 142 | while ( iterations-- > 0 ) { 143 | yw = yi = 0; 144 | for ( y = height; --y > -1; ) 145 | { 146 | r_sum = radiusPlus1 * ( pr = pixels[yi] ); 147 | g_sum = radiusPlus1 * ( pg = pixels[yi+1] ); 148 | b_sum = radiusPlus1 * ( pb = pixels[yi+2] ); 149 | a_sum = radiusPlus1 * ( pa = pixels[yi+3] ); 150 | 151 | stack = stackStart; 152 | 153 | for( i = radiusPlus1; --i > -1; ) 154 | { 155 | stack.r = pr; 156 | stack.g = pg; 157 | stack.b = pb; 158 | stack.a = pa; 159 | stack = stack.next; 160 | } 161 | 162 | for( i = 1; i < radiusPlus1; i++ ) 163 | { 164 | p = yi + (( widthMinus1 < i ? widthMinus1 : i ) << 2 ); 165 | r_sum += ( stack.r = pixels[p]); 166 | g_sum += ( stack.g = pixels[p+1]); 167 | b_sum += ( stack.b = pixels[p+2]); 168 | a_sum += ( stack.a = pixels[p+3]); 169 | 170 | stack = stack.next; 171 | } 172 | 173 | stackIn = stackStart; 174 | for ( x = 0; x < width; x++ ) 175 | { 176 | pixels[yi++] = (r_sum * mul_sum) >>> shg_sum; 177 | pixels[yi++] = (g_sum * mul_sum) >>> shg_sum; 178 | pixels[yi++] = (b_sum * mul_sum) >>> shg_sum; 179 | pixels[yi++] = (a_sum * mul_sum) >>> shg_sum; 180 | 181 | p = ( yw + ( ( p = x + radius + 1 ) < widthMinus1 ? p : widthMinus1 ) ) << 2; 182 | 183 | r_sum -= stackIn.r - ( stackIn.r = pixels[p]); 184 | g_sum -= stackIn.g - ( stackIn.g = pixels[p+1]); 185 | b_sum -= stackIn.b - ( stackIn.b = pixels[p+2]); 186 | a_sum -= stackIn.a - ( stackIn.a = pixels[p+3]); 187 | 188 | stackIn = stackIn.next; 189 | 190 | } 191 | yw += width; 192 | } 193 | 194 | 195 | for ( x = 0; x < width; x++ ) 196 | { 197 | yi = x << 2; 198 | 199 | r_sum = radiusPlus1 * ( pr = pixels[yi]); 200 | g_sum = radiusPlus1 * ( pg = pixels[yi+1]); 201 | b_sum = radiusPlus1 * ( pb = pixels[yi+2]); 202 | a_sum = radiusPlus1 * ( pa = pixels[yi+3]); 203 | 204 | stack = stackStart; 205 | 206 | for( i = 0; i < radiusPlus1; i++ ) 207 | { 208 | stack.r = pr; 209 | stack.g = pg; 210 | stack.b = pb; 211 | stack.a = pa; 212 | stack = stack.next; 213 | } 214 | 215 | yp = width; 216 | 217 | for( i = 1; i <= radius; i++ ) 218 | { 219 | yi = ( yp + x ) << 2; 220 | 221 | r_sum += ( stack.r = pixels[yi]); 222 | g_sum += ( stack.g = pixels[yi+1]); 223 | b_sum += ( stack.b = pixels[yi+2]); 224 | a_sum += ( stack.a = pixels[yi+3]); 225 | 226 | stack = stack.next; 227 | 228 | if( i < heightMinus1 ) 229 | { 230 | yp += width; 231 | } 232 | } 233 | 234 | yi = x; 235 | stackIn = stackStart; 236 | for ( y = 0; y < height; y++ ) 237 | { 238 | p = yi << 2; 239 | pixels[p+3] = pa =(a_sum * mul_sum) >>> shg_sum; 240 | if ( pa > 0 ) 241 | { 242 | pa = 255 / pa; 243 | pixels[p] = ((r_sum * mul_sum) >>> shg_sum ) * pa; 244 | pixels[p+1] = ((g_sum * mul_sum) >>> shg_sum ) * pa; 245 | pixels[p+2] = ((b_sum * mul_sum) >>> shg_sum ) * pa; 246 | } else { 247 | pixels[p] = pixels[p+1] = pixels[p+2] = 0 248 | } 249 | 250 | p = ( x + (( ( p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1 ) * width )) << 2; 251 | 252 | r_sum -= stackIn.r - ( stackIn.r = pixels[p]); 253 | g_sum -= stackIn.g - ( stackIn.g = pixels[p+1]); 254 | b_sum -= stackIn.b - ( stackIn.b = pixels[p+2]); 255 | a_sum -= stackIn.a - ( stackIn.a = pixels[p+3]); 256 | 257 | stackIn = stackIn.next; 258 | 259 | yi += width; 260 | } 261 | } 262 | } 263 | context.putImageData( imageData, top_x, top_y ); 264 | 265 | } 266 | 267 | 268 | function stackBoxBlurCanvasRGB( id, top_x, top_y, width, height, radius, iterations ) 269 | { 270 | if ( isNaN(radius) || radius < 1 ) return; 271 | radius |= 0; 272 | 273 | if ( isNaN(iterations) ) iterations = 1; 274 | iterations |= 0; 275 | if ( iterations > 3 ) iterations = 3; 276 | if ( iterations < 1 ) iterations = 1; 277 | 278 | var canvas = document.getElementById( id ); 279 | var context = canvas.getContext("2d"); 280 | var imageData; 281 | 282 | try { 283 | try { 284 | imageData = context.getImageData( top_x, top_y, width, height ); 285 | } catch(e) { 286 | 287 | // NOTE: this part is supposedly only needed if you want to work with local files 288 | // so it might be okay to remove the whole try/catch block and just use 289 | // imageData = context.getImageData( top_x, top_y, width, height ); 290 | try { 291 | netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); 292 | imageData = context.getImageData( top_x, top_y, width, height ); 293 | } catch(e) { 294 | alert("Cannot access local image"); 295 | throw new Error("unable to access local image data: " + e); 296 | return; 297 | } 298 | } 299 | } catch(e) { 300 | alert("Cannot access image"); 301 | throw new Error("unable to access image data: " + e); 302 | } 303 | 304 | var pixels = imageData.data; 305 | 306 | var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, 307 | r_out_sum, g_out_sum, b_out_sum, 308 | r_in_sum, g_in_sum, b_in_sum, 309 | pr, pg, pb, rbs; 310 | 311 | var div = radius + radius + 1; 312 | var w4 = width << 2; 313 | var widthMinus1 = width - 1; 314 | var heightMinus1 = height - 1; 315 | var radiusPlus1 = radius + 1; 316 | 317 | var stackStart = new BlurStack(); 318 | var stack = stackStart; 319 | for ( i = 1; i < div; i++ ) 320 | { 321 | stack = stack.next = new BlurStack(); 322 | if ( i == radiusPlus1 ) var stackEnd = stack; 323 | } 324 | stack.next = stackStart; 325 | var stackIn = null; 326 | 327 | 328 | 329 | var mul_sum = mul_table[radius]; 330 | var shg_sum = shg_table[radius]; 331 | 332 | while ( iterations-- > 0 ) { 333 | yw = yi = 0; 334 | 335 | for ( y = height; --y >-1; ) 336 | { 337 | r_sum = radiusPlus1 * ( pr = pixels[yi] ); 338 | g_sum = radiusPlus1 * ( pg = pixels[yi+1] ); 339 | b_sum = radiusPlus1 * ( pb = pixels[yi+2] ); 340 | 341 | stack = stackStart; 342 | 343 | for( i = radiusPlus1; --i > -1; ) 344 | { 345 | stack.r = pr; 346 | stack.g = pg; 347 | stack.b = pb; 348 | stack = stack.next; 349 | } 350 | 351 | for( i = 1; i < radiusPlus1; i++ ) 352 | { 353 | p = yi + (( widthMinus1 < i ? widthMinus1 : i ) << 2 ); 354 | r_sum += ( stack.r = pixels[p++]); 355 | g_sum += ( stack.g = pixels[p++]); 356 | b_sum += ( stack.b = pixels[p]); 357 | 358 | stack = stack.next; 359 | } 360 | 361 | stackIn = stackStart; 362 | for ( x = 0; x < width; x++ ) 363 | { 364 | pixels[yi++] = (r_sum * mul_sum) >>> shg_sum; 365 | pixels[yi++] = (g_sum * mul_sum) >>> shg_sum; 366 | pixels[yi++] = (b_sum * mul_sum) >>> shg_sum; 367 | yi++; 368 | 369 | p = ( yw + ( ( p = x + radius + 1 ) < widthMinus1 ? p : widthMinus1 ) ) << 2; 370 | 371 | r_sum -= stackIn.r - ( stackIn.r = pixels[p++]); 372 | g_sum -= stackIn.g - ( stackIn.g = pixels[p++]); 373 | b_sum -= stackIn.b - ( stackIn.b = pixels[p]); 374 | 375 | stackIn = stackIn.next; 376 | } 377 | yw += width; 378 | } 379 | 380 | 381 | for ( x = 0; x < width; x++ ) 382 | { 383 | yi = x << 2; 384 | 385 | r_sum = radiusPlus1 * ( pr = pixels[yi++]); 386 | g_sum = radiusPlus1 * ( pg = pixels[yi++]); 387 | b_sum = radiusPlus1 * ( pb = pixels[yi]); 388 | 389 | stack = stackStart; 390 | 391 | for( i = 0; i < radiusPlus1; i++ ) 392 | { 393 | stack.r = pr; 394 | stack.g = pg; 395 | stack.b = pb; 396 | stack = stack.next; 397 | } 398 | 399 | yp = width; 400 | 401 | for( i = 1; i <= radius; i++ ) 402 | { 403 | yi = ( yp + x ) << 2; 404 | 405 | r_sum += ( stack.r = pixels[yi++]); 406 | g_sum += ( stack.g = pixels[yi++]); 407 | b_sum += ( stack.b = pixels[yi]); 408 | 409 | stack = stack.next; 410 | 411 | if ( i < heightMinus1 ) yp += width; 412 | } 413 | 414 | yi = x; 415 | stackIn = stackStart; 416 | for ( y = 0; y < height; y++ ) 417 | { 418 | p = yi << 2; 419 | pixels[p] = (r_sum * mul_sum) >>> shg_sum; 420 | pixels[p+1] = (g_sum * mul_sum) >>> shg_sum; 421 | pixels[p+2] = (b_sum * mul_sum) >>> shg_sum; 422 | 423 | p = ( x + (( ( p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1 ) * width )) << 2; 424 | 425 | r_sum -= stackIn.r - ( stackIn.r = pixels[p]); 426 | g_sum -= stackIn.g - ( stackIn.g = pixels[p+1]); 427 | b_sum -= stackIn.b - ( stackIn.b = pixels[p+2]); 428 | 429 | stackIn = stackIn.next; 430 | 431 | yi += width; 432 | } 433 | } 434 | } 435 | context.putImageData( imageData, top_x, top_y ); 436 | 437 | } 438 | 439 | function BlurStack() 440 | { 441 | this.r = 0; 442 | this.g = 0; 443 | this.b = 0; 444 | this.a = 0; 445 | this.next = null; 446 | } 447 | 448 | -------------------------------------------------------------------------------- /zorrosvg/img/example4.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blur/StackBlur.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | StackBlur - a fast almost Gaussian Blur For Canvas 4 | 5 | Version: 0.6 6 | Author: Mario Klingemann 7 | Contact: mario@quasimondo.com 8 | Website: http://www.quasimondo.com/StackBlurForCanvas 9 | Twitter: @quasimondo 10 | 11 | In case you find this class useful - especially in commercial projects - 12 | I am not totally unhappy for a small donation to my PayPal account 13 | mario@quasimondo.de 14 | 15 | Or support me on flattr: 16 | https://flattr.com/thing/72791/StackBlur-a-fast-almost-Gaussian-Blur-Effect-for-CanvasJavascript 17 | 18 | Copyright (c) 2010 Mario Klingemann 19 | 20 | Permission is hereby granted, free of charge, to any person 21 | obtaining a copy of this software and associated documentation 22 | files (the "Software"), to deal in the Software without 23 | restriction, including without limitation the rights to use, 24 | copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the 26 | Software is furnished to do so, subject to the following 27 | conditions: 28 | 29 | The above copyright notice and this permission notice shall be 30 | included in all copies or substantial portions of the Software. 31 | 32 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 33 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 34 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 35 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 36 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 37 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 38 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 39 | OTHER DEALINGS IN THE SOFTWARE. 40 | */ 41 | 42 | var mul_table = [ 43 | 512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512, 44 | 454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512, 45 | 482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456, 46 | 437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512, 47 | 497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328, 48 | 320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456, 49 | 446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335, 50 | 329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512, 51 | 505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405, 52 | 399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328, 53 | 324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271, 54 | 268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456, 55 | 451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388, 56 | 385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335, 57 | 332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292, 58 | 289,287,285,282,280,278,275,273,271,269,267,265,263,261,259]; 59 | 60 | 61 | var shg_table = [ 62 | 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 63 | 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 64 | 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 65 | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 66 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 67 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 68 | 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 69 | 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 70 | 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 71 | 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 72 | 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 73 | 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 74 | 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 75 | 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 76 | 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 77 | 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 ]; 78 | 79 | function stackBlurImage( imageIDOrElement, canvasIDOrElement, radius, blurAlphaChannel ) 80 | { 81 | 82 | var img = stackBlurGetElement( imageIDOrElement ); 83 | var w = img.naturalWidth; 84 | var h = img.naturalHeight; 85 | 86 | var canvas = stackBlurGetElement( canvasIDOrElement ); 87 | 88 | canvas.style.width = w + "px"; 89 | canvas.style.height = h + "px"; 90 | canvas.width = w; 91 | canvas.height = h; 92 | 93 | var context = canvas.getContext("2d"); 94 | context.clearRect( 0, 0, w, h ); 95 | context.drawImage( img, 0, 0 ); 96 | 97 | if ( isNaN(radius) || radius < 1 ) return; 98 | 99 | if ( blurAlphaChannel ) 100 | stackBlurCanvasRGBA( canvasIDOrElement, 0, 0, w, h, radius ); 101 | else 102 | stackBlurCanvasRGB( canvasIDOrElement, 0, 0, w, h, radius ); 103 | } 104 | 105 | 106 | function stackBlurCanvasRGBA( canvasIDOrElement, top_x, top_y, width, height, radius ) 107 | { 108 | var canvas = stackBlurGetElement( canvasIDOrElement ); 109 | var context = canvas.getContext("2d"); 110 | var imageData; 111 | 112 | try { 113 | try { 114 | imageData = context.getImageData( top_x, top_y, width, height ); 115 | } catch(e) { 116 | 117 | // NOTE: this part is supposedly only needed if you want to work with local files 118 | // so it might be okay to remove the whole try/catch block and just use 119 | // imageData = context.getImageData( top_x, top_y, width, height ); 120 | try { 121 | netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); 122 | imageData = context.getImageData( top_x, top_y, width, height ); 123 | } catch(e) { 124 | alert("Cannot access local image"); 125 | throw new Error("unable to access local image data: " + e); 126 | return; 127 | } 128 | } 129 | } catch(e) { 130 | alert("Cannot access image"); 131 | throw new Error("unable to access image data: " + e); 132 | } 133 | 134 | imageData = stackBlurImageDataRGBA( imageData, radius ); 135 | context.putImageData( imageData, top_x, top_y ); 136 | } 137 | 138 | 139 | function stackBlurImageDataRGBA( imageData, radius ) 140 | { 141 | if ( isNaN(radius) || radius < 1 ) return; 142 | radius |= 0; 143 | 144 | var pixels = imageData.data; 145 | var width = imageData.width; 146 | var height = imageData.height; 147 | 148 | var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, a_sum, 149 | r_out_sum, g_out_sum, b_out_sum, a_out_sum, 150 | r_in_sum, g_in_sum, b_in_sum, a_in_sum, 151 | pr, pg, pb, pa, rbs; 152 | 153 | var div = radius + radius + 1; 154 | var w4 = width << 2; 155 | var widthMinus1 = width - 1; 156 | var heightMinus1 = height - 1; 157 | var radiusPlus1 = radius + 1; 158 | var sumFactor = radiusPlus1 * ( radiusPlus1 + 1 ) / 2; 159 | 160 | var stackStart = new BlurStack(); 161 | var stack = stackStart; 162 | for ( i = 1; i < div; i++ ) 163 | { 164 | stack = stack.next = new BlurStack(); 165 | if ( i == radiusPlus1 ) var stackEnd = stack; 166 | } 167 | stack.next = stackStart; 168 | var stackIn = null; 169 | var stackOut = null; 170 | 171 | yw = yi = 0; 172 | 173 | var mul_sum = mul_table[radius]; 174 | var shg_sum = shg_table[radius]; 175 | 176 | for ( y = 0; y < height; y++ ) 177 | { 178 | r_in_sum = g_in_sum = b_in_sum = a_in_sum = r_sum = g_sum = b_sum = a_sum = 0; 179 | 180 | r_out_sum = radiusPlus1 * ( pr = pixels[yi] ); 181 | g_out_sum = radiusPlus1 * ( pg = pixels[yi+1] ); 182 | b_out_sum = radiusPlus1 * ( pb = pixels[yi+2] ); 183 | a_out_sum = radiusPlus1 * ( pa = pixels[yi+3] ); 184 | 185 | r_sum += sumFactor * pr; 186 | g_sum += sumFactor * pg; 187 | b_sum += sumFactor * pb; 188 | a_sum += sumFactor * pa; 189 | 190 | stack = stackStart; 191 | 192 | for( i = 0; i < radiusPlus1; i++ ) 193 | { 194 | stack.r = pr; 195 | stack.g = pg; 196 | stack.b = pb; 197 | stack.a = pa; 198 | stack = stack.next; 199 | } 200 | 201 | for( i = 1; i < radiusPlus1; i++ ) 202 | { 203 | p = yi + (( widthMinus1 < i ? widthMinus1 : i ) << 2 ); 204 | r_sum += ( stack.r = ( pr = pixels[p])) * ( rbs = radiusPlus1 - i ); 205 | g_sum += ( stack.g = ( pg = pixels[p+1])) * rbs; 206 | b_sum += ( stack.b = ( pb = pixels[p+2])) * rbs; 207 | a_sum += ( stack.a = ( pa = pixels[p+3])) * rbs; 208 | 209 | r_in_sum += pr; 210 | g_in_sum += pg; 211 | b_in_sum += pb; 212 | a_in_sum += pa; 213 | 214 | stack = stack.next; 215 | } 216 | 217 | 218 | stackIn = stackStart; 219 | stackOut = stackEnd; 220 | for ( x = 0; x < width; x++ ) 221 | { 222 | pixels[yi] = (r_sum * mul_sum) >> shg_sum; 223 | pixels[yi+1] = (g_sum * mul_sum) >> shg_sum; 224 | pixels[yi+2] = (b_sum * mul_sum) >> shg_sum; 225 | pixels[yi+3] = (a_sum * mul_sum) >> shg_sum; 226 | 227 | r_sum -= r_out_sum; 228 | g_sum -= g_out_sum; 229 | b_sum -= b_out_sum; 230 | a_sum -= a_out_sum; 231 | 232 | r_out_sum -= stackIn.r; 233 | g_out_sum -= stackIn.g; 234 | b_out_sum -= stackIn.b; 235 | a_out_sum -= stackIn.a; 236 | 237 | p = ( yw + ( ( p = x + radius + 1 ) < widthMinus1 ? p : widthMinus1 ) ) << 2; 238 | 239 | r_in_sum += ( stackIn.r = pixels[p]); 240 | g_in_sum += ( stackIn.g = pixels[p+1]); 241 | b_in_sum += ( stackIn.b = pixels[p+2]); 242 | a_in_sum += ( stackIn.a = pixels[p+3]); 243 | 244 | r_sum += r_in_sum; 245 | g_sum += g_in_sum; 246 | b_sum += b_in_sum; 247 | a_sum += a_in_sum; 248 | 249 | stackIn = stackIn.next; 250 | 251 | r_out_sum += ( pr = stackOut.r ); 252 | g_out_sum += ( pg = stackOut.g ); 253 | b_out_sum += ( pb = stackOut.b ); 254 | a_out_sum += ( pa = stackOut.a ); 255 | 256 | r_in_sum -= pr; 257 | g_in_sum -= pg; 258 | b_in_sum -= pb; 259 | a_in_sum -= pa; 260 | 261 | stackOut = stackOut.next; 262 | 263 | yi += 4; 264 | } 265 | yw += width; 266 | } 267 | 268 | 269 | for ( x = 0; x < width; x++ ) 270 | { 271 | g_in_sum = b_in_sum = a_in_sum = r_in_sum = g_sum = b_sum = a_sum = r_sum = 0; 272 | 273 | yi = x << 2; 274 | r_out_sum = radiusPlus1 * ( pr = pixels[yi]); 275 | g_out_sum = radiusPlus1 * ( pg = pixels[yi+1]); 276 | b_out_sum = radiusPlus1 * ( pb = pixels[yi+2]); 277 | a_out_sum = radiusPlus1 * ( pa = pixels[yi+3]); 278 | 279 | r_sum += sumFactor * pr; 280 | g_sum += sumFactor * pg; 281 | b_sum += sumFactor * pb; 282 | a_sum += sumFactor * pa; 283 | 284 | stack = stackStart; 285 | 286 | for( i = 0; i < radiusPlus1; i++ ) 287 | { 288 | stack.r = pr; 289 | stack.g = pg; 290 | stack.b = pb; 291 | stack.a = pa; 292 | stack = stack.next; 293 | } 294 | 295 | yp = width; 296 | 297 | for( i = 1; i <= radius; i++ ) 298 | { 299 | yi = ( yp + x ) << 2; 300 | 301 | r_sum += ( stack.r = ( pr = pixels[yi])) * ( rbs = radiusPlus1 - i ); 302 | g_sum += ( stack.g = ( pg = pixels[yi+1])) * rbs; 303 | b_sum += ( stack.b = ( pb = pixels[yi+2])) * rbs; 304 | a_sum += ( stack.a = ( pa = pixels[yi+3])) * rbs; 305 | 306 | r_in_sum += pr; 307 | g_in_sum += pg; 308 | b_in_sum += pb; 309 | a_in_sum += pa; 310 | 311 | stack = stack.next; 312 | 313 | if( i < heightMinus1 ) 314 | { 315 | yp += width; 316 | } 317 | } 318 | 319 | yi = x; 320 | stackIn = stackStart; 321 | stackOut = stackEnd; 322 | for ( y = 0; y < height; y++ ) 323 | { 324 | p = yi << 2; 325 | pixels[p+3] = pa = (a_sum * mul_sum) >> shg_sum; 326 | if ( pa > 0 ) 327 | { 328 | pa = 255 / pa; 329 | pixels[p] = ((r_sum * mul_sum) >> shg_sum ) * pa; 330 | pixels[p+1] = ((g_sum * mul_sum) >> shg_sum ) * pa; 331 | pixels[p+2] = ((b_sum * mul_sum) >> shg_sum ) * pa; 332 | } else { 333 | pixels[p] = pixels[p+1] = pixels[p+2] = 0; 334 | } 335 | 336 | r_sum -= r_out_sum; 337 | g_sum -= g_out_sum; 338 | b_sum -= b_out_sum; 339 | a_sum -= a_out_sum; 340 | 341 | r_out_sum -= stackIn.r; 342 | g_out_sum -= stackIn.g; 343 | b_out_sum -= stackIn.b; 344 | a_out_sum -= stackIn.a; 345 | 346 | p = ( x + (( ( p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1 ) * width )) << 2; 347 | 348 | r_sum += ( r_in_sum += ( stackIn.r = pixels[p])); 349 | g_sum += ( g_in_sum += ( stackIn.g = pixels[p+1])); 350 | b_sum += ( b_in_sum += ( stackIn.b = pixels[p+2])); 351 | a_sum += ( a_in_sum += ( stackIn.a = pixels[p+3])); 352 | 353 | stackIn = stackIn.next; 354 | 355 | r_out_sum += ( pr = stackOut.r ); 356 | g_out_sum += ( pg = stackOut.g ); 357 | b_out_sum += ( pb = stackOut.b ); 358 | a_out_sum += ( pa = stackOut.a ); 359 | 360 | r_in_sum -= pr; 361 | g_in_sum -= pg; 362 | b_in_sum -= pb; 363 | a_in_sum -= pa; 364 | 365 | stackOut = stackOut.next; 366 | 367 | yi += width; 368 | } 369 | } 370 | 371 | return imageData; 372 | 373 | } 374 | 375 | 376 | function stackBlurCanvasRGB( canvasIDOrElement, top_x, top_y, width, height, radius ) 377 | { 378 | if ( isNaN(radius) || radius < 1 ) return; 379 | radius |= 0; 380 | 381 | var canvas = stackBlurGetElement( canvasIDOrElement ); 382 | var context = canvas.getContext("2d"); 383 | var imageData; 384 | 385 | try { 386 | try { 387 | imageData = context.getImageData( top_x, top_y, width, height ); 388 | } catch(e) { 389 | 390 | // NOTE: this part is supposedly only needed if you want to work with local files 391 | // so it might be okay to remove the whole try/catch block and just use 392 | // imageData = context.getImageData( top_x, top_y, width, height ); 393 | try { 394 | netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); 395 | imageData = context.getImageData( top_x, top_y, width, height ); 396 | } catch(e) { 397 | alert("Cannot access local image"); 398 | throw new Error("unable to access local image data: " + e); 399 | return; 400 | } 401 | } 402 | } catch(e) { 403 | alert("Cannot access image"); 404 | throw new Error("unable to access image data: " + e); 405 | } 406 | 407 | var pixels = imageData.data; 408 | 409 | var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, 410 | r_out_sum, g_out_sum, b_out_sum, 411 | r_in_sum, g_in_sum, b_in_sum, 412 | pr, pg, pb, rbs; 413 | 414 | var div = radius + radius + 1; 415 | var w4 = width << 2; 416 | var widthMinus1 = width - 1; 417 | var heightMinus1 = height - 1; 418 | var radiusPlus1 = radius + 1; 419 | var sumFactor = radiusPlus1 * ( radiusPlus1 + 1 ) / 2; 420 | 421 | var stackStart = new BlurStack(); 422 | var stack = stackStart; 423 | for ( i = 1; i < div; i++ ) 424 | { 425 | stack = stack.next = new BlurStack(); 426 | if ( i == radiusPlus1 ) var stackEnd = stack; 427 | } 428 | stack.next = stackStart; 429 | var stackIn = null; 430 | var stackOut = null; 431 | 432 | yw = yi = 0; 433 | 434 | var mul_sum = mul_table[radius]; 435 | var shg_sum = shg_table[radius]; 436 | 437 | for ( y = 0; y < height; y++ ) 438 | { 439 | r_in_sum = g_in_sum = b_in_sum = r_sum = g_sum = b_sum = 0; 440 | 441 | r_out_sum = radiusPlus1 * ( pr = pixels[yi] ); 442 | g_out_sum = radiusPlus1 * ( pg = pixels[yi+1] ); 443 | b_out_sum = radiusPlus1 * ( pb = pixels[yi+2] ); 444 | 445 | r_sum += sumFactor * pr; 446 | g_sum += sumFactor * pg; 447 | b_sum += sumFactor * pb; 448 | 449 | stack = stackStart; 450 | 451 | for( i = 0; i < radiusPlus1; i++ ) 452 | { 453 | stack.r = pr; 454 | stack.g = pg; 455 | stack.b = pb; 456 | stack = stack.next; 457 | } 458 | 459 | for( i = 1; i < radiusPlus1; i++ ) 460 | { 461 | p = yi + (( widthMinus1 < i ? widthMinus1 : i ) << 2 ); 462 | r_sum += ( stack.r = ( pr = pixels[p])) * ( rbs = radiusPlus1 - i ); 463 | g_sum += ( stack.g = ( pg = pixels[p+1])) * rbs; 464 | b_sum += ( stack.b = ( pb = pixels[p+2])) * rbs; 465 | 466 | r_in_sum += pr; 467 | g_in_sum += pg; 468 | b_in_sum += pb; 469 | 470 | stack = stack.next; 471 | } 472 | 473 | 474 | stackIn = stackStart; 475 | stackOut = stackEnd; 476 | for ( x = 0; x < width; x++ ) 477 | { 478 | pixels[yi] = (r_sum * mul_sum) >> shg_sum; 479 | pixels[yi+1] = (g_sum * mul_sum) >> shg_sum; 480 | pixels[yi+2] = (b_sum * mul_sum) >> shg_sum; 481 | 482 | r_sum -= r_out_sum; 483 | g_sum -= g_out_sum; 484 | b_sum -= b_out_sum; 485 | 486 | r_out_sum -= stackIn.r; 487 | g_out_sum -= stackIn.g; 488 | b_out_sum -= stackIn.b; 489 | 490 | p = ( yw + ( ( p = x + radius + 1 ) < widthMinus1 ? p : widthMinus1 ) ) << 2; 491 | 492 | r_in_sum += ( stackIn.r = pixels[p]); 493 | g_in_sum += ( stackIn.g = pixels[p+1]); 494 | b_in_sum += ( stackIn.b = pixels[p+2]); 495 | 496 | r_sum += r_in_sum; 497 | g_sum += g_in_sum; 498 | b_sum += b_in_sum; 499 | 500 | stackIn = stackIn.next; 501 | 502 | r_out_sum += ( pr = stackOut.r ); 503 | g_out_sum += ( pg = stackOut.g ); 504 | b_out_sum += ( pb = stackOut.b ); 505 | 506 | r_in_sum -= pr; 507 | g_in_sum -= pg; 508 | b_in_sum -= pb; 509 | 510 | stackOut = stackOut.next; 511 | 512 | yi += 4; 513 | } 514 | yw += width; 515 | } 516 | 517 | 518 | for ( x = 0; x < width; x++ ) 519 | { 520 | g_in_sum = b_in_sum = r_in_sum = g_sum = b_sum = r_sum = 0; 521 | 522 | yi = x << 2; 523 | r_out_sum = radiusPlus1 * ( pr = pixels[yi]); 524 | g_out_sum = radiusPlus1 * ( pg = pixels[yi+1]); 525 | b_out_sum = radiusPlus1 * ( pb = pixels[yi+2]); 526 | 527 | r_sum += sumFactor * pr; 528 | g_sum += sumFactor * pg; 529 | b_sum += sumFactor * pb; 530 | 531 | stack = stackStart; 532 | 533 | for( i = 0; i < radiusPlus1; i++ ) 534 | { 535 | stack.r = pr; 536 | stack.g = pg; 537 | stack.b = pb; 538 | stack = stack.next; 539 | } 540 | 541 | yp = width; 542 | 543 | for( i = 1; i <= radius; i++ ) 544 | { 545 | yi = ( yp + x ) << 2; 546 | 547 | r_sum += ( stack.r = ( pr = pixels[yi])) * ( rbs = radiusPlus1 - i ); 548 | g_sum += ( stack.g = ( pg = pixels[yi+1])) * rbs; 549 | b_sum += ( stack.b = ( pb = pixels[yi+2])) * rbs; 550 | 551 | r_in_sum += pr; 552 | g_in_sum += pg; 553 | b_in_sum += pb; 554 | 555 | stack = stack.next; 556 | 557 | if( i < heightMinus1 ) 558 | { 559 | yp += width; 560 | } 561 | } 562 | 563 | yi = x; 564 | stackIn = stackStart; 565 | stackOut = stackEnd; 566 | for ( y = 0; y < height; y++ ) 567 | { 568 | p = yi << 2; 569 | pixels[p] = (r_sum * mul_sum) >> shg_sum; 570 | pixels[p+1] = (g_sum * mul_sum) >> shg_sum; 571 | pixels[p+2] = (b_sum * mul_sum) >> shg_sum; 572 | 573 | r_sum -= r_out_sum; 574 | g_sum -= g_out_sum; 575 | b_sum -= b_out_sum; 576 | 577 | r_out_sum -= stackIn.r; 578 | g_out_sum -= stackIn.g; 579 | b_out_sum -= stackIn.b; 580 | 581 | p = ( x + (( ( p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1 ) * width )) << 2; 582 | 583 | r_sum += ( r_in_sum += ( stackIn.r = pixels[p])); 584 | g_sum += ( g_in_sum += ( stackIn.g = pixels[p+1])); 585 | b_sum += ( b_in_sum += ( stackIn.b = pixels[p+2])); 586 | 587 | stackIn = stackIn.next; 588 | 589 | r_out_sum += ( pr = stackOut.r ); 590 | g_out_sum += ( pg = stackOut.g ); 591 | b_out_sum += ( pb = stackOut.b ); 592 | 593 | r_in_sum -= pr; 594 | g_in_sum -= pg; 595 | b_in_sum -= pb; 596 | 597 | stackOut = stackOut.next; 598 | 599 | yi += width; 600 | } 601 | } 602 | 603 | context.putImageData( imageData, top_x, top_y ); 604 | 605 | } 606 | 607 | function BlurStack() 608 | { 609 | this.r = 0; 610 | this.g = 0; 611 | this.b = 0; 612 | this.a = 0; 613 | this.next = null; 614 | } 615 | 616 | function stackBlurGetElement( elementOrID ) 617 | { 618 | if ( elementOrID.nodeType == 1 ) 619 | return elementOrID; 620 | 621 | return document.getElementById( elementOrID ); 622 | } 623 | -------------------------------------------------------------------------------- /zorrosvg/img/example1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blur/CompoundBlur.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | CompoundBlur - Blurring with varying radii for Canvas 4 | 5 | Version: 0.1 6 | Author: Mario Klingemann 7 | Contact: mario@quasimondo.com 8 | Website: http://www.quasimondo.com/StackBlurForCanvas 9 | Twitter: @quasimondo 10 | 11 | In case you find this class useful - especially in commercial projects - 12 | I am not totally unhappy for a small donation to my PayPal account 13 | mario@quasimondo.de 14 | 15 | Copyright (c) 2011 Mario Klingemann 16 | 17 | Permission is hereby granted, free of charge, to any person 18 | obtaining a copy of this software and associated documentation 19 | files (the "Software"), to deal in the Software without 20 | restriction, including without limitation the rights to use, 21 | copy, modify, merge, publish, distribute, sublicense, and/or sell 22 | copies of the Software, and to permit persons to whom the 23 | Software is furnished to do so, subject to the following 24 | conditions: 25 | 26 | The above copyright notice and this permission notice shall be 27 | included in all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 30 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 31 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 32 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 33 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 34 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 35 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 36 | OTHER DEALINGS IN THE SOFTWARE. 37 | */ 38 | 39 | var mul_table = [ 40 | 512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512, 41 | 454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512, 42 | 482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456, 43 | 437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512, 44 | 497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328, 45 | 320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456, 46 | 446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335, 47 | 329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512, 48 | 505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405, 49 | 399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328, 50 | 324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271, 51 | 268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456, 52 | 451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388, 53 | 385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335, 54 | 332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292, 55 | 289,287,285,282,280,278,275,273,271,269,267,265,263,261,259]; 56 | 57 | 58 | var shg_table = [ 59 | 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 60 | 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 61 | 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 62 | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 63 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 64 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 65 | 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 66 | 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 67 | 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 68 | 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 69 | 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 70 | 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 71 | 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 72 | 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 73 | 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 74 | 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 ]; 75 | 76 | 77 | function compoundBlurImage( imageID, canvasID, radiusData, minRadius, increaseFactor, blurLevels, blurAlphaChannel ) 78 | { 79 | 80 | var img = document.getElementById( imageID ); 81 | var w = img.naturalWidth; 82 | var h = img.naturalHeight; 83 | 84 | var canvas = document.getElementById( canvasID ); 85 | 86 | canvas.style.width = w + "px"; 87 | canvas.style.height = h + "px"; 88 | canvas.width = w; 89 | canvas.height = h; 90 | 91 | var context = canvas.getContext("2d"); 92 | context.clearRect( 0, 0, w, h ); 93 | context.drawImage( img, 0, 0 ); 94 | 95 | if ( isNaN(minRadius) || minRadius <= 0 || isNaN(increaseFactor) || increaseFactor == 0 ) return; 96 | 97 | if ( blurAlphaChannel ) 98 | compundBlurCanvasRGBA( canvasID, 0, 0, w, h, radiusData, minRadius, increaseFactor, blurLevels ); 99 | else 100 | compundBlurCanvasRGB( canvasID, 0, 0, w, h, radiusData, minRadius, increaseFactor, blurLevels ); 101 | } 102 | 103 | function getLinearGradientMap( width, height, centerX, centerY, angle, length, mirrored ) 104 | { 105 | var cnv = document.createElement('canvas'); 106 | cnv.width = width; 107 | cnv.height = height; 108 | 109 | var x1 = centerX + Math.cos( angle ) * length * 0.5; 110 | var y1 = centerY + Math.sin( angle ) * length * 0.5; 111 | 112 | var x2 = centerX - Math.cos( angle ) * length * 0.5; 113 | var y2 = centerY - Math.sin( angle ) * length * 0.5; 114 | 115 | var context = cnv.getContext("2d"); 116 | var gradient = context.createLinearGradient(x1, y1, x2, y2); 117 | if ( !mirrored ) 118 | { 119 | gradient.addColorStop(0, "white"); 120 | gradient.addColorStop(1, "black"); 121 | } else { 122 | gradient.addColorStop(0, "white"); 123 | gradient.addColorStop(0.5, "black"); 124 | gradient.addColorStop(1, "white"); 125 | } 126 | context.fillStyle = gradient; 127 | context.fillRect(0, 0, width, height ); 128 | return context.getImageData( 0, 0, width, height ); 129 | } 130 | 131 | function getRadialGradientMap( width, height, centerX, centerY, radius1, radius2 ) 132 | { 133 | var cnv = document.createElement('canvas'); 134 | cnv.width = width; 135 | cnv.height = height; 136 | 137 | 138 | var context = cnv.getContext("2d"); 139 | var gradient = context.createRadialGradient(centerX, centerY, radius1, centerX, centerY, radius2); 140 | 141 | gradient.addColorStop(1, "white"); 142 | gradient.addColorStop(0, "black"); 143 | 144 | context.fillStyle = gradient; 145 | context.fillRect(0, 0, width, height ); 146 | return context.getImageData( 0, 0, width, height ); 147 | } 148 | 149 | function compundBlurCanvasRGB( id, top_x, top_y, width, height, radiusData, minRadius, increaseFactor, blurLevels ) 150 | { 151 | if ( isNaN(minRadius) || minRadius <= 0 || isNaN(increaseFactor) || increaseFactor == 0 ) return; 152 | 153 | var canvas = document.getElementById( id ); 154 | var context = canvas.getContext("2d"); 155 | var imageData; 156 | 157 | try { 158 | try { 159 | imageData = context.getImageData( top_x, top_y, width, height ); 160 | } catch(e) { 161 | 162 | // NOTE: this part is supposedly only needed if you want to work with local files 163 | // so it might be okay to remove the whole try/catch block and just use 164 | // imageData = context.getImageData( top_x, top_y, width, height ); 165 | try { 166 | netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); 167 | imageData = context.getImageData( top_x, top_y, width, height ); 168 | } catch(e) { 169 | alert("Cannot access local image"); 170 | throw new Error("unable to access local image data: " + e); 171 | return; 172 | } 173 | } 174 | } catch(e) { 175 | alert("Cannot access image"); 176 | throw new Error("unable to access image data: " + e); 177 | } 178 | 179 | renderCompundBlurRGB( imageData, radiusData, width, height, minRadius, increaseFactor, blurLevels ); 180 | context.putImageData( imageData, top_x, top_y ); 181 | } 182 | 183 | function compundBlurCanvasRGBA( id, top_x, top_y, width, height, radiusData, minRadius, increaseFactor, blurLevels ) 184 | { 185 | if ( isNaN(minRadius) || minRadius <= 0 || isNaN(increaseFactor) || increaseFactor == 0 ) return; 186 | 187 | var canvas = document.getElementById( id ); 188 | var context = canvas.getContext("2d"); 189 | var imageData; 190 | 191 | try { 192 | try { 193 | imageData = context.getImageData( top_x, top_y, width, height ); 194 | } catch(e) { 195 | 196 | // NOTE: this part is supposedly only needed if you want to work with local files 197 | // so it might be okay to remove the whole try/catch block and just use 198 | // imageData = context.getImageData( top_x, top_y, width, height ); 199 | try { 200 | netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); 201 | imageData = context.getImageData( top_x, top_y, width, height ); 202 | } catch(e) { 203 | alert("Cannot access local image"); 204 | throw new Error("unable to access local image data: " + e); 205 | return; 206 | } 207 | } 208 | } catch(e) { 209 | alert("Cannot access image"); 210 | throw new Error("unable to access image data: " + e); 211 | } 212 | 213 | renderCompundBlurRGBA( imageData, radiusData, width, height, minRadius, increaseFactor, blurLevels ); 214 | context.putImageData( imageData, top_x, top_y ); 215 | } 216 | 217 | function renderCompundBlurRGB( imageData, radiusData, width, height, radius, increaseFactor, blurLevels ) 218 | { 219 | var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, 220 | r_out_sum, g_out_sum, b_out_sum, 221 | r_in_sum, g_in_sum, b_in_sum, 222 | pr, pg, pb, rbs; 223 | 224 | var imagePixels = imageData.data; 225 | var radiusPixels = radiusData.data; 226 | 227 | var wh = width * height; 228 | var wh4 = wh << 2; 229 | var pixels = []; 230 | 231 | for ( var i = 0; i < wh4; i++ ) 232 | { 233 | pixels[i] = imagePixels[i]; 234 | } 235 | 236 | var currentIndex = 0; 237 | var steps = blurLevels; 238 | blurLevels -= 1; 239 | 240 | while ( steps-- >= 0 ) 241 | { 242 | var iradius = ( radius + 0.5 ) | 0; 243 | if ( iradius == 0 ) continue; 244 | if ( iradius > 256 ) iradius = 256; 245 | 246 | var div = iradius + iradius + 1; 247 | var w4 = width << 2; 248 | var widthMinus1 = width - 1; 249 | var heightMinus1 = height - 1; 250 | var radiusPlus1 = iradius + 1; 251 | var sumFactor = radiusPlus1 * ( radiusPlus1 + 1 ) / 2; 252 | 253 | var stackStart = new BlurStack(); 254 | var stack = stackStart; 255 | for ( i = 1; i < div; i++ ) 256 | { 257 | stack = stack.next = new BlurStack(); 258 | if ( i == radiusPlus1 ) var stackEnd = stack; 259 | } 260 | stack.next = stackStart; 261 | var stackIn = null; 262 | var stackOut = null; 263 | 264 | yw = yi = 0; 265 | 266 | var mul_sum = mul_table[iradius]; 267 | var shg_sum = shg_table[iradius]; 268 | 269 | for ( y = 0; y < height; y++ ) 270 | { 271 | r_in_sum = g_in_sum = b_in_sum = r_sum = g_sum = b_sum = 0; 272 | 273 | r_out_sum = radiusPlus1 * ( pr = pixels[yi] ); 274 | g_out_sum = radiusPlus1 * ( pg = pixels[yi+1] ); 275 | b_out_sum = radiusPlus1 * ( pb = pixels[yi+2] ); 276 | 277 | r_sum += sumFactor * pr; 278 | g_sum += sumFactor * pg; 279 | b_sum += sumFactor * pb; 280 | 281 | stack = stackStart; 282 | 283 | for( i = 0; i < radiusPlus1; i++ ) 284 | { 285 | stack.r = pr; 286 | stack.g = pg; 287 | stack.b = pb; 288 | stack = stack.next; 289 | } 290 | 291 | for( i = 1; i < radiusPlus1; i++ ) 292 | { 293 | p = yi + (( widthMinus1 < i ? widthMinus1 : i ) << 2 ); 294 | r_sum += ( stack.r = ( pr = pixels[p])) * ( rbs = radiusPlus1 - i ); 295 | g_sum += ( stack.g = ( pg = pixels[p+1])) * rbs; 296 | b_sum += ( stack.b = ( pb = pixels[p+2])) * rbs; 297 | 298 | r_in_sum += pr; 299 | g_in_sum += pg; 300 | b_in_sum += pb; 301 | 302 | stack = stack.next; 303 | } 304 | 305 | 306 | stackIn = stackStart; 307 | stackOut = stackEnd; 308 | for ( x = 0; x < width; x++ ) 309 | { 310 | pixels[yi] = (r_sum * mul_sum) >> shg_sum; 311 | pixels[yi+1] = (g_sum * mul_sum) >> shg_sum; 312 | pixels[yi+2] = (b_sum * mul_sum) >> shg_sum; 313 | 314 | r_sum -= r_out_sum; 315 | g_sum -= g_out_sum; 316 | b_sum -= b_out_sum; 317 | 318 | r_out_sum -= stackIn.r; 319 | g_out_sum -= stackIn.g; 320 | b_out_sum -= stackIn.b; 321 | 322 | p = ( yw + ( ( p = x + radiusPlus1 ) < widthMinus1 ? p : widthMinus1 ) ) << 2; 323 | 324 | r_in_sum += ( stackIn.r = pixels[p]); 325 | g_in_sum += ( stackIn.g = pixels[p+1]); 326 | b_in_sum += ( stackIn.b = pixels[p+2]); 327 | 328 | r_sum += r_in_sum; 329 | g_sum += g_in_sum; 330 | b_sum += b_in_sum; 331 | 332 | stackIn = stackIn.next; 333 | 334 | r_out_sum += ( pr = stackOut.r ); 335 | g_out_sum += ( pg = stackOut.g ); 336 | b_out_sum += ( pb = stackOut.b ); 337 | 338 | r_in_sum -= pr; 339 | g_in_sum -= pg; 340 | b_in_sum -= pb; 341 | 342 | stackOut = stackOut.next; 343 | 344 | yi += 4; 345 | } 346 | yw += width; 347 | } 348 | 349 | 350 | for ( x = 0; x < width; x++ ) 351 | { 352 | g_in_sum = b_in_sum = r_in_sum = g_sum = b_sum = r_sum = 0; 353 | 354 | yi = x << 2; 355 | r_out_sum = radiusPlus1 * ( pr = pixels[yi]); 356 | g_out_sum = radiusPlus1 * ( pg = pixels[yi+1]); 357 | b_out_sum = radiusPlus1 * ( pb = pixels[yi+2]); 358 | 359 | r_sum += sumFactor * pr; 360 | g_sum += sumFactor * pg; 361 | b_sum += sumFactor * pb; 362 | 363 | stack = stackStart; 364 | 365 | for( i = 0; i < radiusPlus1; i++ ) 366 | { 367 | stack.r = pr; 368 | stack.g = pg; 369 | stack.b = pb; 370 | stack = stack.next; 371 | } 372 | 373 | yp = width; 374 | 375 | for( i = 1; i < radiusPlus1; i++ ) 376 | { 377 | yi = ( yp + x ) << 2; 378 | 379 | r_sum += ( stack.r = ( pr = pixels[yi])) * ( rbs = radiusPlus1 - i ); 380 | g_sum += ( stack.g = ( pg = pixels[yi+1])) * rbs; 381 | b_sum += ( stack.b = ( pb = pixels[yi+2])) * rbs; 382 | 383 | r_in_sum += pr; 384 | g_in_sum += pg; 385 | b_in_sum += pb; 386 | 387 | stack = stack.next; 388 | 389 | if( i < heightMinus1 ) 390 | { 391 | yp += width; 392 | } 393 | } 394 | 395 | yi = x; 396 | stackIn = stackStart; 397 | stackOut = stackEnd; 398 | for ( y = 0; y < height; y++ ) 399 | { 400 | p = yi << 2; 401 | pixels[p] = (r_sum * mul_sum) >> shg_sum; 402 | pixels[p+1] = (g_sum * mul_sum) >> shg_sum; 403 | pixels[p+2] = (b_sum * mul_sum) >> shg_sum; 404 | 405 | r_sum -= r_out_sum; 406 | g_sum -= g_out_sum; 407 | b_sum -= b_out_sum; 408 | 409 | r_out_sum -= stackIn.r; 410 | g_out_sum -= stackIn.g; 411 | b_out_sum -= stackIn.b; 412 | 413 | p = ( x + (( ( p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1 ) * width )) << 2; 414 | 415 | r_sum += ( r_in_sum += ( stackIn.r = pixels[p])); 416 | g_sum += ( g_in_sum += ( stackIn.g = pixels[p+1])); 417 | b_sum += ( b_in_sum += ( stackIn.b = pixels[p+2])); 418 | 419 | stackIn = stackIn.next; 420 | 421 | r_out_sum += ( pr = stackOut.r ); 422 | g_out_sum += ( pg = stackOut.g ); 423 | b_out_sum += ( pb = stackOut.b ); 424 | 425 | r_in_sum -= pr; 426 | g_in_sum -= pg; 427 | b_in_sum -= pb; 428 | 429 | stackOut = stackOut.next; 430 | 431 | yi += width; 432 | } 433 | } 434 | 435 | radius *= increaseFactor; 436 | 437 | for ( i = wh; --i > -1 ; ) 438 | { 439 | var idx = i << 2; 440 | var lookupValue = (radiusPixels[idx+2] & 0xff) / 255.0 * blurLevels; 441 | var index = lookupValue | 0; 442 | 443 | if ( index == currentIndex ) 444 | { 445 | var blend = 256.0 * ( lookupValue - (lookupValue | 0 )); 446 | var iblend = 256 - blend; 447 | 448 | imagePixels[idx] = ( imagePixels[idx] * iblend + pixels[idx] * blend ) >> 8; 449 | imagePixels[idx+1] = ( imagePixels[idx+1] * iblend + pixels[idx+1] * blend) >> 8; 450 | imagePixels[idx+2] = ( imagePixels[idx+2] * iblend + pixels[idx+2] * blend) >> 8; 451 | 452 | } else if ( index == currentIndex + 1 ) 453 | { 454 | imagePixels[idx] = pixels[idx]; 455 | imagePixels[idx+1] = pixels[idx+1]; 456 | imagePixels[idx+2] = pixels[idx+2]; 457 | 458 | } 459 | } 460 | currentIndex++; 461 | } 462 | } 463 | 464 | function renderCompundBlurRGBA( imageData, radiusData, width, height, radius, increaseFactor, blurLevels ) 465 | { 466 | var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, a_sum, 467 | r_out_sum, g_out_sum, b_out_sum, a_out_sum, 468 | r_in_sum, g_in_sum, b_in_sum, a_in_sum, 469 | pa, pr, pg, pb, rbs; 470 | 471 | var imagePixels = imageData.data; 472 | var radiusPixels = radiusData.data; 473 | 474 | var wh = width * height; 475 | var wh4 = wh << 2; 476 | var pixels = []; 477 | 478 | for ( var i = 0; i < wh4; i++ ) 479 | { 480 | pixels[i] = imagePixels[i]; 481 | } 482 | 483 | var currentIndex = 0; 484 | var steps = blurLevels; 485 | blurLevels -= 1; 486 | 487 | while ( steps-- >= 0 ) 488 | { 489 | var iradius = ( radius + 0.5 ) | 0; 490 | if ( iradius == 0 ) continue; 491 | if ( iradius > 256 ) iradius = 256; 492 | 493 | var div = iradius + iradius + 1; 494 | var w4 = width << 2; 495 | var widthMinus1 = width - 1; 496 | var heightMinus1 = height - 1; 497 | var radiusPlus1 = iradius + 1; 498 | var sumFactor = radiusPlus1 * ( radiusPlus1 + 1 ) / 2; 499 | 500 | var stackStart = new BlurStack(); 501 | var stack = stackStart; 502 | for ( i = 1; i < div; i++ ) 503 | { 504 | stack = stack.next = new BlurStack(); 505 | if ( i == radiusPlus1 ) var stackEnd = stack; 506 | } 507 | stack.next = stackStart; 508 | var stackIn = null; 509 | var stackOut = null; 510 | 511 | yw = yi = 0; 512 | 513 | var mul_sum = mul_table[iradius]; 514 | var shg_sum = shg_table[iradius]; 515 | 516 | for ( y = 0; y < height; y++ ) 517 | { 518 | r_in_sum = g_in_sum = b_in_sum = a_in_sum = r_sum = g_sum = b_sum = a_sum = 0; 519 | 520 | r_out_sum = radiusPlus1 * ( pr = pixels[yi] ); 521 | g_out_sum = radiusPlus1 * ( pg = pixels[yi+1] ); 522 | b_out_sum = radiusPlus1 * ( pb = pixels[yi+2] ); 523 | a_out_sum = radiusPlus1 * ( pa = pixels[yi+3] ); 524 | 525 | r_sum += sumFactor * pr; 526 | g_sum += sumFactor * pg; 527 | b_sum += sumFactor * pb; 528 | a_sum += sumFactor * pa; 529 | 530 | stack = stackStart; 531 | 532 | for( i = 0; i < radiusPlus1; i++ ) 533 | { 534 | stack.r = pr; 535 | stack.g = pg; 536 | stack.b = pb; 537 | stack.a = pa; 538 | stack = stack.next; 539 | } 540 | 541 | for( i = 1; i < radiusPlus1; i++ ) 542 | { 543 | p = yi + (( widthMinus1 < i ? widthMinus1 : i ) << 2 ); 544 | r_sum += ( stack.r = ( pr = pixels[p])) * ( rbs = radiusPlus1 - i ); 545 | g_sum += ( stack.g = ( pg = pixels[p+1])) * rbs; 546 | b_sum += ( stack.b = ( pb = pixels[p+2])) * rbs; 547 | a_sum += ( stack.a = ( pa = pixels[p+3])) * rbs; 548 | 549 | r_in_sum += pr; 550 | g_in_sum += pg; 551 | b_in_sum += pb; 552 | a_in_sum += pa; 553 | 554 | stack = stack.next; 555 | } 556 | 557 | 558 | stackIn = stackStart; 559 | stackOut = stackEnd; 560 | for ( x = 0; x < width; x++ ) 561 | { 562 | pixels[yi+3] = pa = (a_sum * mul_sum) >> shg_sum; 563 | if ( pa != 0 ) 564 | { 565 | pa = 255 / pa; 566 | pixels[yi] = ((r_sum * mul_sum) >> shg_sum) * pa; 567 | pixels[yi+1] = ((g_sum * mul_sum) >> shg_sum) * pa; 568 | pixels[yi+2] = ((b_sum * mul_sum) >> shg_sum) * pa; 569 | } else { 570 | pixels[yi] = pixels[yi+1] = pixels[yi+2] = 0; 571 | } 572 | 573 | r_sum -= r_out_sum; 574 | g_sum -= g_out_sum; 575 | b_sum -= b_out_sum; 576 | a_sum -= a_out_sum; 577 | 578 | r_out_sum -= stackIn.r; 579 | g_out_sum -= stackIn.g; 580 | b_out_sum -= stackIn.b; 581 | a_out_sum -= stackIn.a; 582 | 583 | p = ( yw + ( ( p = x + radiusPlus1 ) < widthMinus1 ? p : widthMinus1 ) ) << 2; 584 | 585 | r_in_sum += ( stackIn.r = pixels[p]); 586 | g_in_sum += ( stackIn.g = pixels[p+1]); 587 | b_in_sum += ( stackIn.b = pixels[p+2]); 588 | a_in_sum += ( stackIn.a = pixels[p+3]); 589 | 590 | r_sum += r_in_sum; 591 | g_sum += g_in_sum; 592 | b_sum += b_in_sum; 593 | a_sum += a_in_sum; 594 | 595 | stackIn = stackIn.next; 596 | 597 | r_out_sum += ( pr = stackOut.r ); 598 | g_out_sum += ( pg = stackOut.g ); 599 | b_out_sum += ( pb = stackOut.b ); 600 | a_out_sum += ( pa = stackOut.a ); 601 | 602 | r_in_sum -= pr; 603 | g_in_sum -= pg; 604 | b_in_sum -= pb; 605 | a_in_sum -= pa; 606 | 607 | stackOut = stackOut.next; 608 | 609 | yi += 4; 610 | } 611 | yw += width; 612 | } 613 | 614 | 615 | for ( x = 0; x < width; x++ ) 616 | { 617 | g_in_sum = b_in_sum = a_in_sum = r_in_sum = g_sum = b_sum = a_sum = r_sum = 0; 618 | 619 | yi = x << 2; 620 | r_out_sum = radiusPlus1 * ( pr = pixels[yi]); 621 | g_out_sum = radiusPlus1 * ( pg = pixels[yi+1]); 622 | b_out_sum = radiusPlus1 * ( pb = pixels[yi+2]); 623 | a_out_sum = radiusPlus1 * ( pa = pixels[yi+3]); 624 | 625 | r_sum += sumFactor * pr; 626 | g_sum += sumFactor * pg; 627 | b_sum += sumFactor * pb; 628 | a_sum += sumFactor * pa; 629 | 630 | stack = stackStart; 631 | 632 | for( i = 0; i < radiusPlus1; i++ ) 633 | { 634 | stack.r = pr; 635 | stack.g = pg; 636 | stack.b = pb; 637 | stack.a = pa; 638 | stack = stack.next; 639 | } 640 | 641 | yp = width; 642 | 643 | for( i = 1; i < radiusPlus1; i++ ) 644 | { 645 | yi = ( yp + x ) << 2; 646 | 647 | r_sum += ( stack.r = ( pr = pixels[yi])) * ( rbs = radiusPlus1 - i ); 648 | g_sum += ( stack.g = ( pg = pixels[yi+1])) * rbs; 649 | b_sum += ( stack.b = ( pb = pixels[yi+2])) * rbs; 650 | a_sum += ( stack.a = ( pa = pixels[yi+3])) * rbs; 651 | 652 | r_in_sum += pr; 653 | g_in_sum += pg; 654 | b_in_sum += pb; 655 | a_in_sum += pa; 656 | 657 | stack = stack.next; 658 | 659 | if( i < heightMinus1 ) 660 | { 661 | yp += width; 662 | } 663 | } 664 | 665 | yi = x; 666 | stackIn = stackStart; 667 | stackOut = stackEnd; 668 | for ( y = 0; y < height; y++ ) 669 | { 670 | p = yi << 2; 671 | pixels[p+3] = pa = (a_sum * mul_sum) >> shg_sum; 672 | if ( pa > 0 ) 673 | { 674 | pa = 255 / pa; 675 | pixels[p] = ((r_sum * mul_sum) >> shg_sum ) * pa; 676 | pixels[p+1] = ((g_sum * mul_sum) >> shg_sum ) * pa; 677 | pixels[p+2] = ((b_sum * mul_sum) >> shg_sum ) * pa; 678 | } else { 679 | pixels[p] = pixels[p+1] = pixels[p+2] = 0; 680 | } 681 | 682 | r_sum -= r_out_sum; 683 | g_sum -= g_out_sum; 684 | b_sum -= b_out_sum; 685 | a_sum -= a_out_sum; 686 | 687 | r_out_sum -= stackIn.r; 688 | g_out_sum -= stackIn.g; 689 | b_out_sum -= stackIn.b; 690 | a_out_sum -= stackIn.a; 691 | 692 | p = ( x + (( ( p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1 ) * width )) << 2; 693 | 694 | r_sum += ( r_in_sum += ( stackIn.r = pixels[p])); 695 | g_sum += ( g_in_sum += ( stackIn.g = pixels[p+1])); 696 | b_sum += ( b_in_sum += ( stackIn.b = pixels[p+2])); 697 | a_sum += ( a_in_sum += ( stackIn.a = pixels[p+3])); 698 | 699 | stackIn = stackIn.next; 700 | 701 | r_out_sum += ( pr = stackOut.r ); 702 | g_out_sum += ( pg = stackOut.g ); 703 | b_out_sum += ( pb = stackOut.b ); 704 | a_out_sum += ( pa = stackOut.a ); 705 | 706 | r_in_sum -= pr; 707 | g_in_sum -= pg; 708 | b_in_sum -= pb; 709 | a_in_sum -= pa; 710 | 711 | stackOut = stackOut.next; 712 | 713 | yi += width; 714 | } 715 | } 716 | 717 | radius *= increaseFactor; 718 | 719 | for ( i = wh; --i > -1 ; ) 720 | { 721 | var idx = i << 2; 722 | var lookupValue = (radiusPixels[idx+2] & 0xff) / 255.0 * blurLevels; 723 | var index = lookupValue | 0; 724 | 725 | if ( index == currentIndex ) 726 | { 727 | var blend = 256.0 * ( lookupValue - (lookupValue | 0 )); 728 | var iblend = 256 - blend; 729 | 730 | imagePixels[idx] = (imagePixels[idx] * iblend + pixels[idx] * blend) >> 8; 731 | imagePixels[idx+1] = (imagePixels[idx+1] * iblend + pixels[idx+1] * blend) >> 8; 732 | imagePixels[idx+2] = (imagePixels[idx+2] * iblend + pixels[idx+2] * blend) >> 8; 733 | imagePixels[idx+3] = (imagePixels[idx+3] * iblend + pixels[idx+3] * blend) >> 8; 734 | 735 | } else if ( index == currentIndex + 1 ) 736 | { 737 | imagePixels[idx] = pixels[idx]; 738 | imagePixels[idx+1] = pixels[idx+1]; 739 | imagePixels[idx+2] = pixels[idx+2]; 740 | imagePixels[idx+3] = pixels[idx+3]; 741 | } 742 | } 743 | currentIndex++; 744 | } 745 | } 746 | 747 | function BlurStack() 748 | { 749 | this.r = 0; 750 | this.g = 0; 751 | this.b = 0; 752 | this.a = 0; 753 | this.next = null; 754 | } -------------------------------------------------------------------------------- /zorrosvg/img/example3.svg: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------