source: other-projects/nz-flag-design/trunk/main-form/lib/canvg/StackBlur.js@ 29567

Last change on this file since 29567 was 29567, checked in by davidb, 9 years ago

Addition of further library (to support exporting SVG to PNG) on the iterative-design page

File size: 16.7 KB
Line 
1/*
2
3StackBlur - a fast almost Gaussian Blur For Canvas
4
5Version: 0.5
6Author: Mario Klingemann
7Contact: [email protected]
8Website: http://www.quasimondo.com/StackBlurForCanvas
9Twitter: @quasimondo
10
11In case you find this class useful - especially in commercial projects -
12I am not totally unhappy for a small donation to my PayPal account
13[email protected]
14
15Or support me on flattr:
16https://flattr.com/thing/72791/StackBlur-a-fast-almost-Gaussian-Blur-Effect-for-CanvasJavascript
17
18Copyright (c) 2010 Mario Klingemann
19
20Permission is hereby granted, free of charge, to any person
21obtaining a copy of this software and associated documentation
22files (the "Software"), to deal in the Software without
23restriction, including without limitation the rights to use,
24copy, modify, merge, publish, distribute, sublicense, and/or sell
25copies of the Software, and to permit persons to whom the
26Software is furnished to do so, subject to the following
27conditions:
28
29The above copyright notice and this permission notice shall be
30included in all copies or substantial portions of the Software.
31
32THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
33EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
34OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
35NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
36HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
37WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
38FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
39OTHER DEALINGS IN THE SOFTWARE.
40*/
41
42var 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
61var 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
79function stackBlurImage( imageID, canvasID, radius, blurAlphaChannel )
80{
81
82 var img = document.getElementById( imageID );
83 var w = img.naturalWidth;
84 var h = img.naturalHeight;
85
86 var canvas = document.getElementById( canvasID );
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( canvasID, 0, 0, w, h, radius );
101 else
102 stackBlurCanvasRGB( canvasID, 0, 0, w, h, radius );
103}
104
105
106function stackBlurCanvasRGBA( id, top_x, top_y, width, height, radius )
107{
108 if ( isNaN(radius) || radius < 1 ) return;
109 radius |= 0;
110
111 var canvas = document.getElementById( id );
112 var context = canvas.getContext("2d");
113 var imageData;
114
115 try {
116 try {
117 imageData = context.getImageData( top_x, top_y, width, height );
118 } catch(e) {
119
120 // NOTE: this part is supposedly only needed if you want to work with local files
121 // so it might be okay to remove the whole try/catch block and just use
122 // imageData = context.getImageData( top_x, top_y, width, height );
123 try {
124 netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
125 imageData = context.getImageData( top_x, top_y, width, height );
126 } catch(e) {
127 alert("Cannot access local image");
128 throw new Error("unable to access local image data: " + e);
129 return;
130 }
131 }
132 } catch(e) {
133 alert("Cannot access image");
134 throw new Error("unable to access image data: " + e);
135 }
136
137 var pixels = imageData.data;
138
139 var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, a_sum,
140 r_out_sum, g_out_sum, b_out_sum, a_out_sum,
141 r_in_sum, g_in_sum, b_in_sum, a_in_sum,
142 pr, pg, pb, pa, rbs;
143
144 var div = radius + radius + 1;
145 var w4 = width << 2;
146 var widthMinus1 = width - 1;
147 var heightMinus1 = height - 1;
148 var radiusPlus1 = radius + 1;
149 var sumFactor = radiusPlus1 * ( radiusPlus1 + 1 ) / 2;
150
151 var stackStart = new BlurStack();
152 var stack = stackStart;
153 for ( i = 1; i < div; i++ )
154 {
155 stack = stack.next = new BlurStack();
156 if ( i == radiusPlus1 ) var stackEnd = stack;
157 }
158 stack.next = stackStart;
159 var stackIn = null;
160 var stackOut = null;
161
162 yw = yi = 0;
163
164 var mul_sum = mul_table[radius];
165 var shg_sum = shg_table[radius];
166
167 for ( y = 0; y < height; y++ )
168 {
169 r_in_sum = g_in_sum = b_in_sum = a_in_sum = r_sum = g_sum = b_sum = a_sum = 0;
170
171 r_out_sum = radiusPlus1 * ( pr = pixels[yi] );
172 g_out_sum = radiusPlus1 * ( pg = pixels[yi+1] );
173 b_out_sum = radiusPlus1 * ( pb = pixels[yi+2] );
174 a_out_sum = radiusPlus1 * ( pa = pixels[yi+3] );
175
176 r_sum += sumFactor * pr;
177 g_sum += sumFactor * pg;
178 b_sum += sumFactor * pb;
179 a_sum += sumFactor * pa;
180
181 stack = stackStart;
182
183 for( i = 0; i < radiusPlus1; i++ )
184 {
185 stack.r = pr;
186 stack.g = pg;
187 stack.b = pb;
188 stack.a = pa;
189 stack = stack.next;
190 }
191
192 for( i = 1; i < radiusPlus1; i++ )
193 {
194 p = yi + (( widthMinus1 < i ? widthMinus1 : i ) << 2 );
195 r_sum += ( stack.r = ( pr = pixels[p])) * ( rbs = radiusPlus1 - i );
196 g_sum += ( stack.g = ( pg = pixels[p+1])) * rbs;
197 b_sum += ( stack.b = ( pb = pixels[p+2])) * rbs;
198 a_sum += ( stack.a = ( pa = pixels[p+3])) * rbs;
199
200 r_in_sum += pr;
201 g_in_sum += pg;
202 b_in_sum += pb;
203 a_in_sum += pa;
204
205 stack = stack.next;
206 }
207
208
209 stackIn = stackStart;
210 stackOut = stackEnd;
211 for ( x = 0; x < width; x++ )
212 {
213 pixels[yi+3] = pa = (a_sum * mul_sum) >> shg_sum;
214 if ( pa != 0 )
215 {
216 pa = 255 / pa;
217 pixels[yi] = ((r_sum * mul_sum) >> shg_sum) * pa;
218 pixels[yi+1] = ((g_sum * mul_sum) >> shg_sum) * pa;
219 pixels[yi+2] = ((b_sum * mul_sum) >> shg_sum) * pa;
220 } else {
221 pixels[yi] = pixels[yi+1] = pixels[yi+2] = 0;
222 }
223
224 r_sum -= r_out_sum;
225 g_sum -= g_out_sum;
226 b_sum -= b_out_sum;
227 a_sum -= a_out_sum;
228
229 r_out_sum -= stackIn.r;
230 g_out_sum -= stackIn.g;
231 b_out_sum -= stackIn.b;
232 a_out_sum -= stackIn.a;
233
234 p = ( yw + ( ( p = x + radius + 1 ) < widthMinus1 ? p : widthMinus1 ) ) << 2;
235
236 r_in_sum += ( stackIn.r = pixels[p]);
237 g_in_sum += ( stackIn.g = pixels[p+1]);
238 b_in_sum += ( stackIn.b = pixels[p+2]);
239 a_in_sum += ( stackIn.a = pixels[p+3]);
240
241 r_sum += r_in_sum;
242 g_sum += g_in_sum;
243 b_sum += b_in_sum;
244 a_sum += a_in_sum;
245
246 stackIn = stackIn.next;
247
248 r_out_sum += ( pr = stackOut.r );
249 g_out_sum += ( pg = stackOut.g );
250 b_out_sum += ( pb = stackOut.b );
251 a_out_sum += ( pa = stackOut.a );
252
253 r_in_sum -= pr;
254 g_in_sum -= pg;
255 b_in_sum -= pb;
256 a_in_sum -= pa;
257
258 stackOut = stackOut.next;
259
260 yi += 4;
261 }
262 yw += width;
263 }
264
265
266 for ( x = 0; x < width; x++ )
267 {
268 g_in_sum = b_in_sum = a_in_sum = r_in_sum = g_sum = b_sum = a_sum = r_sum = 0;
269
270 yi = x << 2;
271 r_out_sum = radiusPlus1 * ( pr = pixels[yi]);
272 g_out_sum = radiusPlus1 * ( pg = pixels[yi+1]);
273 b_out_sum = radiusPlus1 * ( pb = pixels[yi+2]);
274 a_out_sum = radiusPlus1 * ( pa = pixels[yi+3]);
275
276 r_sum += sumFactor * pr;
277 g_sum += sumFactor * pg;
278 b_sum += sumFactor * pb;
279 a_sum += sumFactor * pa;
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.a = pa;
289 stack = stack.next;
290 }
291
292 yp = width;
293
294 for( i = 1; i <= radius; i++ )
295 {
296 yi = ( yp + x ) << 2;
297
298 r_sum += ( stack.r = ( pr = pixels[yi])) * ( rbs = radiusPlus1 - i );
299 g_sum += ( stack.g = ( pg = pixels[yi+1])) * rbs;
300 b_sum += ( stack.b = ( pb = pixels[yi+2])) * rbs;
301 a_sum += ( stack.a = ( pa = pixels[yi+3])) * rbs;
302
303 r_in_sum += pr;
304 g_in_sum += pg;
305 b_in_sum += pb;
306 a_in_sum += pa;
307
308 stack = stack.next;
309
310 if( i < heightMinus1 )
311 {
312 yp += width;
313 }
314 }
315
316 yi = x;
317 stackIn = stackStart;
318 stackOut = stackEnd;
319 for ( y = 0; y < height; y++ )
320 {
321 p = yi << 2;
322 pixels[p+3] = pa = (a_sum * mul_sum) >> shg_sum;
323 if ( pa > 0 )
324 {
325 pa = 255 / pa;
326 pixels[p] = ((r_sum * mul_sum) >> shg_sum ) * pa;
327 pixels[p+1] = ((g_sum * mul_sum) >> shg_sum ) * pa;
328 pixels[p+2] = ((b_sum * mul_sum) >> shg_sum ) * pa;
329 } else {
330 pixels[p] = pixels[p+1] = pixels[p+2] = 0;
331 }
332
333 r_sum -= r_out_sum;
334 g_sum -= g_out_sum;
335 b_sum -= b_out_sum;
336 a_sum -= a_out_sum;
337
338 r_out_sum -= stackIn.r;
339 g_out_sum -= stackIn.g;
340 b_out_sum -= stackIn.b;
341 a_out_sum -= stackIn.a;
342
343 p = ( x + (( ( p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1 ) * width )) << 2;
344
345 r_sum += ( r_in_sum += ( stackIn.r = pixels[p]));
346 g_sum += ( g_in_sum += ( stackIn.g = pixels[p+1]));
347 b_sum += ( b_in_sum += ( stackIn.b = pixels[p+2]));
348 a_sum += ( a_in_sum += ( stackIn.a = pixels[p+3]));
349
350 stackIn = stackIn.next;
351
352 r_out_sum += ( pr = stackOut.r );
353 g_out_sum += ( pg = stackOut.g );
354 b_out_sum += ( pb = stackOut.b );
355 a_out_sum += ( pa = stackOut.a );
356
357 r_in_sum -= pr;
358 g_in_sum -= pg;
359 b_in_sum -= pb;
360 a_in_sum -= pa;
361
362 stackOut = stackOut.next;
363
364 yi += width;
365 }
366 }
367
368 context.putImageData( imageData, top_x, top_y );
369
370}
371
372
373function stackBlurCanvasRGB( id, top_x, top_y, width, height, radius )
374{
375 if ( isNaN(radius) || radius < 1 ) return;
376 radius |= 0;
377
378 var canvas = document.getElementById( id );
379 var context = canvas.getContext("2d");
380 var imageData;
381
382 try {
383 try {
384 imageData = context.getImageData( top_x, top_y, width, height );
385 } catch(e) {
386
387 // NOTE: this part is supposedly only needed if you want to work with local files
388 // so it might be okay to remove the whole try/catch block and just use
389 // imageData = context.getImageData( top_x, top_y, width, height );
390 try {
391 netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
392 imageData = context.getImageData( top_x, top_y, width, height );
393 } catch(e) {
394 alert("Cannot access local image");
395 throw new Error("unable to access local image data: " + e);
396 return;
397 }
398 }
399 } catch(e) {
400 alert("Cannot access image");
401 throw new Error("unable to access image data: " + e);
402 }
403
404 var pixels = imageData.data;
405
406 var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum,
407 r_out_sum, g_out_sum, b_out_sum,
408 r_in_sum, g_in_sum, b_in_sum,
409 pr, pg, pb, rbs;
410
411 var div = radius + radius + 1;
412 var w4 = width << 2;
413 var widthMinus1 = width - 1;
414 var heightMinus1 = height - 1;
415 var radiusPlus1 = radius + 1;
416 var sumFactor = radiusPlus1 * ( radiusPlus1 + 1 ) / 2;
417
418 var stackStart = new BlurStack();
419 var stack = stackStart;
420 for ( i = 1; i < div; i++ )
421 {
422 stack = stack.next = new BlurStack();
423 if ( i == radiusPlus1 ) var stackEnd = stack;
424 }
425 stack.next = stackStart;
426 var stackIn = null;
427 var stackOut = null;
428
429 yw = yi = 0;
430
431 var mul_sum = mul_table[radius];
432 var shg_sum = shg_table[radius];
433
434 for ( y = 0; y < height; y++ )
435 {
436 r_in_sum = g_in_sum = b_in_sum = r_sum = g_sum = b_sum = 0;
437
438 r_out_sum = radiusPlus1 * ( pr = pixels[yi] );
439 g_out_sum = radiusPlus1 * ( pg = pixels[yi+1] );
440 b_out_sum = radiusPlus1 * ( pb = pixels[yi+2] );
441
442 r_sum += sumFactor * pr;
443 g_sum += sumFactor * pg;
444 b_sum += sumFactor * pb;
445
446 stack = stackStart;
447
448 for( i = 0; i < radiusPlus1; i++ )
449 {
450 stack.r = pr;
451 stack.g = pg;
452 stack.b = pb;
453 stack = stack.next;
454 }
455
456 for( i = 1; i < radiusPlus1; i++ )
457 {
458 p = yi + (( widthMinus1 < i ? widthMinus1 : i ) << 2 );
459 r_sum += ( stack.r = ( pr = pixels[p])) * ( rbs = radiusPlus1 - i );
460 g_sum += ( stack.g = ( pg = pixels[p+1])) * rbs;
461 b_sum += ( stack.b = ( pb = pixels[p+2])) * rbs;
462
463 r_in_sum += pr;
464 g_in_sum += pg;
465 b_in_sum += pb;
466
467 stack = stack.next;
468 }
469
470
471 stackIn = stackStart;
472 stackOut = stackEnd;
473 for ( x = 0; x < width; x++ )
474 {
475 pixels[yi] = (r_sum * mul_sum) >> shg_sum;
476 pixels[yi+1] = (g_sum * mul_sum) >> shg_sum;
477 pixels[yi+2] = (b_sum * mul_sum) >> shg_sum;
478
479 r_sum -= r_out_sum;
480 g_sum -= g_out_sum;
481 b_sum -= b_out_sum;
482
483 r_out_sum -= stackIn.r;
484 g_out_sum -= stackIn.g;
485 b_out_sum -= stackIn.b;
486
487 p = ( yw + ( ( p = x + radius + 1 ) < widthMinus1 ? p : widthMinus1 ) ) << 2;
488
489 r_in_sum += ( stackIn.r = pixels[p]);
490 g_in_sum += ( stackIn.g = pixels[p+1]);
491 b_in_sum += ( stackIn.b = pixels[p+2]);
492
493 r_sum += r_in_sum;
494 g_sum += g_in_sum;
495 b_sum += b_in_sum;
496
497 stackIn = stackIn.next;
498
499 r_out_sum += ( pr = stackOut.r );
500 g_out_sum += ( pg = stackOut.g );
501 b_out_sum += ( pb = stackOut.b );
502
503 r_in_sum -= pr;
504 g_in_sum -= pg;
505 b_in_sum -= pb;
506
507 stackOut = stackOut.next;
508
509 yi += 4;
510 }
511 yw += width;
512 }
513
514
515 for ( x = 0; x < width; x++ )
516 {
517 g_in_sum = b_in_sum = r_in_sum = g_sum = b_sum = r_sum = 0;
518
519 yi = x << 2;
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
524 r_sum += sumFactor * pr;
525 g_sum += sumFactor * pg;
526 b_sum += sumFactor * pb;
527
528 stack = stackStart;
529
530 for( i = 0; i < radiusPlus1; i++ )
531 {
532 stack.r = pr;
533 stack.g = pg;
534 stack.b = pb;
535 stack = stack.next;
536 }
537
538 yp = width;
539
540 for( i = 1; i <= radius; i++ )
541 {
542 yi = ( yp + x ) << 2;
543
544 r_sum += ( stack.r = ( pr = pixels[yi])) * ( rbs = radiusPlus1 - i );
545 g_sum += ( stack.g = ( pg = pixels[yi+1])) * rbs;
546 b_sum += ( stack.b = ( pb = pixels[yi+2])) * rbs;
547
548 r_in_sum += pr;
549 g_in_sum += pg;
550 b_in_sum += pb;
551
552 stack = stack.next;
553
554 if( i < heightMinus1 )
555 {
556 yp += width;
557 }
558 }
559
560 yi = x;
561 stackIn = stackStart;
562 stackOut = stackEnd;
563 for ( y = 0; y < height; y++ )
564 {
565 p = yi << 2;
566 pixels[p] = (r_sum * mul_sum) >> shg_sum;
567 pixels[p+1] = (g_sum * mul_sum) >> shg_sum;
568 pixels[p+2] = (b_sum * mul_sum) >> shg_sum;
569
570 r_sum -= r_out_sum;
571 g_sum -= g_out_sum;
572 b_sum -= b_out_sum;
573
574 r_out_sum -= stackIn.r;
575 g_out_sum -= stackIn.g;
576 b_out_sum -= stackIn.b;
577
578 p = ( x + (( ( p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1 ) * width )) << 2;
579
580 r_sum += ( r_in_sum += ( stackIn.r = pixels[p]));
581 g_sum += ( g_in_sum += ( stackIn.g = pixels[p+1]));
582 b_sum += ( b_in_sum += ( stackIn.b = pixels[p+2]));
583
584 stackIn = stackIn.next;
585
586 r_out_sum += ( pr = stackOut.r );
587 g_out_sum += ( pg = stackOut.g );
588 b_out_sum += ( pb = stackOut.b );
589
590 r_in_sum -= pr;
591 g_in_sum -= pg;
592 b_in_sum -= pb;
593
594 stackOut = stackOut.next;
595
596 yi += width;
597 }
598 }
599
600 context.putImageData( imageData, top_x, top_y );
601
602}
603
604function BlurStack()
605{
606 this.r = 0;
607 this.g = 0;
608 this.b = 0;
609 this.a = 0;
610 this.next = null;
611}
Note: See TracBrowser for help on using the repository browser.