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}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.