1 18 package org.apache.batik.ext.awt.image.rendered; 19 20 import java.awt.Rectangle ; 21 import java.awt.RenderingHints ; 22 import java.awt.color.ColorSpace ; 23 import java.awt.image.ColorModel ; 24 import java.awt.image.ConvolveOp ; 25 import java.awt.image.DataBuffer ; 26 import java.awt.image.DataBufferInt ; 27 import java.awt.image.DirectColorModel ; 28 import java.awt.image.Kernel ; 29 import java.awt.image.Raster ; 30 import java.awt.image.SampleModel ; 31 import java.awt.image.SinglePixelPackedSampleModel ; 32 import java.awt.image.WritableRaster ; 33 34 import org.apache.batik.ext.awt.image.GraphicsUtil; 35 36 43 public class GaussianBlurRed8Bit extends AbstractRed { 44 45 int xinset, yinset; 46 double stdDevX, stdDevY; 47 RenderingHints hints; 48 ConvolveOp [] convOp = new ConvolveOp [2]; 49 int dX, dY; 50 51 58 public GaussianBlurRed8Bit(CachableRed src, 59 double stdDev, 60 RenderingHints rh) { 61 this(src, stdDev, stdDev, rh); 62 } 63 64 72 public GaussianBlurRed8Bit(CachableRed src, 73 double stdDevX, double stdDevY, 74 RenderingHints rh) { 75 super(); 77 this.stdDevX = stdDevX; 78 this.stdDevY = stdDevY; 79 this.hints = rh; 80 81 xinset = surroundPixels(stdDevX, rh); 82 yinset = surroundPixels(stdDevY, rh); 83 84 Rectangle myBounds = src.getBounds(); 85 myBounds.x += xinset; 86 myBounds.y += yinset; 87 myBounds.width -= 2*xinset; 88 myBounds.height -= 2*yinset; 89 if ((myBounds.width <= 0) || 90 (myBounds.height <= 0)) { 91 myBounds.width=0; 92 myBounds.height=0; 93 } 94 95 ColorModel cm = fixColorModel(src); 96 SampleModel sm = src.getSampleModel(); 97 int tw = sm.getWidth(); 98 int th = sm.getHeight(); 99 if (tw > myBounds.width) tw = myBounds.width; 100 if (th > myBounds.height) th = myBounds.height; 101 sm = cm.createCompatibleSampleModel(tw, th); 102 103 init(src, myBounds, cm, sm, 104 src.getTileGridXOffset()+xinset, 105 src.getTileGridYOffset()+yinset, null); 106 107 boolean highQuality = ((hints != null) && 108 RenderingHints.VALUE_RENDER_QUALITY.equals 109 (hints.get(RenderingHints.KEY_RENDERING))); 110 111 if ((xinset != 0) && ((stdDevX < 2) || highQuality)) 113 convOp[0] = new ConvolveOp (makeQualityKernelX(xinset*2+1)); 114 else 115 dX = (int)Math.floor(DSQRT2PI*stdDevX+0.5f); 116 117 if ((yinset != 0) && ((stdDevY < 2) || highQuality)) 118 convOp[1] = new ConvolveOp (makeQualityKernelY(yinset*2+1)); 119 else 120 dY = (int)Math.floor(DSQRT2PI*stdDevY+0.5f); 121 } 122 123 126 static final float SQRT2PI = (float)Math.sqrt(2*Math.PI); 127 128 131 static final float DSQRT2PI = SQRT2PI*3f/4f; 132 133 136 static final float precision = 0.499f; 137 138 142 public static int surroundPixels(double stdDev) { 143 return surroundPixels(stdDev, null); 144 } 145 146 151 public static int surroundPixels(double stdDev, RenderingHints hints) { 152 boolean highQuality = ((hints != null) && 153 RenderingHints.VALUE_RENDER_QUALITY.equals 154 (hints.get(RenderingHints.KEY_RENDERING))); 155 156 if ((stdDev < 2) || highQuality) { 157 float areaSum = (float)(0.5/(stdDev*SQRT2PI)); 159 int i=0; 160 while (areaSum < precision) { 161 areaSum += (float)(Math.pow(Math.E, -i*i/(2*stdDev*stdDev)) / 162 (stdDev*SQRT2PI)); 163 i++; 164 } 165 166 return i; 167 } 168 169 int diam = (int)Math.floor(DSQRT2PI*stdDev+0.5f); 171 if (diam%2 == 0) 172 return diam-1 + diam/2; else 174 return diam-2 + diam/2; } 176 177 183 private float [] computeQualityKernelData(int len, double stdDev){ 184 final float kernelData[] = new float [len]; 185 186 int mid = len/2; 187 float sum = 0; for(int i=0; i<len; i++){ 189 kernelData[i] = (float)(Math.pow(Math.E, -(i-mid)*(i-mid)/ 190 (2*stdDev*stdDev)) / 191 (SQRT2PI*stdDev)); 192 sum += kernelData[i]; 193 } 194 195 for (int i=0; i<len; i++) 197 kernelData[i] /= sum; 198 199 return kernelData; 200 } 201 202 private Kernel makeQualityKernelX(int len) { 203 return new Kernel (len, 1, computeQualityKernelData(len, stdDevX)); 204 } 205 206 private Kernel makeQualityKernelY(int len) { 207 return new Kernel (1, len, computeQualityKernelData(len, stdDevY)); 208 } 209 210 public WritableRaster copyData(WritableRaster wr) { 211 CachableRed src = (CachableRed)getSources().get(0); 213 214 Rectangle r = wr.getBounds(); 215 r.x -= xinset; 216 r.y -= yinset; 217 r.width += 2*xinset; 218 r.height += 2*yinset; 219 220 223 ColorModel srcCM = src.getColorModel(); 224 225 WritableRaster tmpR1=null, tmpR2=null; 226 227 tmpR1 = srcCM.createCompatibleWritableRaster(r.width, r.height); 228 { 229 WritableRaster fill; 230 fill = tmpR1.createWritableTranslatedChild(r.x, r.y); 231 src.copyData(fill); 232 } 233 if (srcCM.hasAlpha() && !srcCM.isAlphaPremultiplied()) 234 GraphicsUtil.coerceData(tmpR1, srcCM, true); 235 236 240 int skipX; 243 if (xinset == 0) { 245 skipX = 0; 246 } else if (convOp[0] != null) { 247 tmpR2 = getColorModel().createCompatibleWritableRaster 248 (r.width, r.height); 249 tmpR2 = convOp[0].filter(tmpR1, tmpR2); 250 skipX = convOp[0].getKernel().getXOrigin(); 251 252 WritableRaster tmp = tmpR1; 254 tmpR1 = tmpR2; 255 tmpR2 = tmp; 256 } else { 257 if ((dX&0x01) == 0){ 258 tmpR1 = boxFilterH(tmpR1, tmpR1, 0, 0, dX, dX/2); 259 tmpR1 = boxFilterH(tmpR1, tmpR1, dX/2, 0, dX, dX/2-1); 260 tmpR1 = boxFilterH(tmpR1, tmpR1, dX-1, 0, dX+1, dX/2); 261 skipX = dX-1 + dX/2; 262 } else { 263 tmpR1 = boxFilterH(tmpR1, tmpR1, 0, 0, dX, dX/2); 264 tmpR1 = boxFilterH(tmpR1, tmpR1, dX/2, 0, dX, dX/2); 265 tmpR1 = boxFilterH(tmpR1, tmpR1, dX-2, 0, dX, dX/2); 266 skipX = dX-2 + dX/2; 267 } 268 } 269 270 if (yinset == 0) { 271 tmpR2 = tmpR1; 272 } else if (convOp[1] != null) { 273 if (tmpR2 == null) { 274 tmpR2 = getColorModel().createCompatibleWritableRaster 275 (r.width, r.height); 276 } 277 tmpR2 = convOp[1].filter(tmpR1, tmpR2); 278 } else { 279 if ((dY&0x01) == 0){ 280 tmpR1 = boxFilterV(tmpR1, tmpR1, skipX, 0, dY, dY/2); 281 tmpR1 = boxFilterV(tmpR1, tmpR1, skipX, dY/2, dY, dY/2-1); 282 tmpR1 = boxFilterV(tmpR1, tmpR1, skipX, dY-1, dY+1, dY/2); 283 } 284 else { 285 tmpR1 = boxFilterV(tmpR1, tmpR1, skipX, 0, dY, dY/2); 286 tmpR1 = boxFilterV(tmpR1, tmpR1, skipX, dY/2, dY, dY/2); 287 tmpR1 = boxFilterV(tmpR1, tmpR1, skipX, dY-2, dY, dY/2); 288 } 289 tmpR2 = tmpR1; 290 } 291 tmpR2 = tmpR2.createWritableTranslatedChild(r.x, r.y); 301 GraphicsUtil.copyData(tmpR2, wr); 302 303 return wr; 304 } 305 306 private WritableRaster boxFilterH(Raster src, WritableRaster dest, 307 int skipX, int skipY, 308 int boxSz, int loc) { 309 310 final int w = src.getWidth(); 311 final int h = src.getHeight(); 312 313 if (w < (2*skipX)+boxSz) return dest; 315 if (h < (2*skipY)) return dest; 316 317 final SinglePixelPackedSampleModel srcSPPSM = 318 (SinglePixelPackedSampleModel )src.getSampleModel(); 319 320 final SinglePixelPackedSampleModel dstSPPSM = 321 (SinglePixelPackedSampleModel )dest.getSampleModel(); 322 323 final int srcScanStride = srcSPPSM.getScanlineStride(); 326 final int dstScanStride = dstSPPSM.getScanlineStride(); 327 328 DataBufferInt srcDB = (DataBufferInt )src.getDataBuffer(); 330 DataBufferInt dstDB = (DataBufferInt )dest.getDataBuffer(); 331 332 final int srcOff 334 = (srcDB.getOffset() + 335 srcSPPSM.getOffset 336 (src.getMinX()-src.getSampleModelTranslateX(), 337 src.getMinY()-src.getSampleModelTranslateY())); 338 final int dstOff 339 = (dstDB.getOffset() + 340 dstSPPSM.getOffset 341 (dest.getMinX()-dest.getSampleModelTranslateX(), 342 dest.getMinY()-dest.getSampleModelTranslateY())); 343 344 final int srcPixels [] = srcDB.getBankData()[0]; 346 final int destPixels[] = dstDB.getBankData()[0]; 347 348 final int [] buffer = new int [boxSz]; 349 int curr, prev; 350 351 int scale = (1<<24)/boxSz; 353 354 363 364 for (int y=skipY; y<(h-skipY); y++) { 365 int sp = srcOff + y*srcScanStride; 366 int dp = dstOff + y*dstScanStride; 367 int rowEnd = sp + (w-skipX); 368 369 int k = 0; 370 int sumA = 0; 371 int sumR = 0; 372 int sumG = 0; 373 int sumB = 0; 374 375 sp += skipX; 376 int end = sp+boxSz; 377 378 while (sp < end) { 379 curr = buffer[k] = srcPixels[sp]; 380 sumA += (curr>>> 24); 381 sumR += (curr >> 16)&0xFF; 382 sumG += (curr >> 8)&0xFF; 383 sumB += (curr )&0xFF; 384 k++; 385 sp++; 386 } 387 388 dp += skipX + loc; 389 prev = destPixels[dp] = (( (sumA*scale)&0xFF000000) | 390 (((sumR*scale)&0xFF000000)>>>8) | 391 (((sumG*scale)&0xFF000000)>>>16) | 392 (((sumB*scale)&0xFF000000)>>>24)); 393 dp++; 394 k=0; 395 while (sp < rowEnd) { 396 curr = buffer[k]; 397 if (curr == srcPixels[sp]) { 398 destPixels[dp] = prev; 399 } else { 400 sumA -= (curr>>> 24); 401 sumR -= (curr >> 16)&0xFF; 402 sumG -= (curr >> 8)&0xFF; 403 sumB -= (curr )&0xFF; 404 405 curr = buffer[k] = srcPixels[sp]; 406 407 sumA += (curr>>> 24); 408 sumR += (curr >> 16)&0xFF; 409 sumG += (curr >> 8)&0xFF; 410 sumB += (curr )&0xFF; 411 prev = destPixels[dp] = (( (sumA*scale)&0xFF000000) | 412 (((sumR*scale)&0xFF000000)>>>8) | 413 (((sumG*scale)&0xFF000000)>>>16) | 414 (((sumB*scale)&0xFF000000)>>>24)); 415 } 416 k = (k+1)%boxSz; 417 sp++; 418 dp++; 419 } 420 } 421 return dest; 422 } 423 424 private WritableRaster boxFilterV(Raster src, WritableRaster dest, 425 int skipX, int skipY, 426 int boxSz, int loc) { 427 428 final int w = src.getWidth(); 429 final int h = src.getHeight(); 430 431 if (w < (2*skipX)) return dest; 433 if (h < (2*skipY)+boxSz) return dest; 434 435 final SinglePixelPackedSampleModel srcSPPSM = 436 (SinglePixelPackedSampleModel )src.getSampleModel(); 437 438 final SinglePixelPackedSampleModel dstSPPSM = 439 (SinglePixelPackedSampleModel )dest.getSampleModel(); 440 441 final int srcScanStride = srcSPPSM.getScanlineStride(); 444 final int dstScanStride = dstSPPSM.getScanlineStride(); 445 446 DataBufferInt srcDB = (DataBufferInt )src.getDataBuffer(); 448 DataBufferInt dstDB = (DataBufferInt )dest.getDataBuffer(); 449 450 final int srcOff 452 = (srcDB.getOffset() + 453 srcSPPSM.getOffset 454 (src.getMinX()-src.getSampleModelTranslateX(), 455 src.getMinY()-src.getSampleModelTranslateY())); 456 final int dstOff 457 = (dstDB.getOffset() + 458 dstSPPSM.getOffset 459 (dest.getMinX()-dest.getSampleModelTranslateX(), 460 dest.getMinY()-dest.getSampleModelTranslateY())); 461 462 463 final int srcPixels [] = srcDB.getBankData()[0]; 465 final int destPixels[] = dstDB.getBankData()[0]; 466 467 final int [] buffer = new int [boxSz]; 468 int curr, prev; 469 470 final int scale = (1<<24)/boxSz; 472 473 482 483 for (int x=skipX; x<(w-skipX); x++) { 484 int sp = srcOff + x; 485 int dp = dstOff + x; 486 int colEnd = sp + (h-skipY)*srcScanStride; 487 488 int k=0; 489 int sumA = 0; 490 int sumR = 0; 491 int sumG = 0; 492 int sumB = 0; 493 494 sp += skipY*srcScanStride; 495 int end = sp+(boxSz*srcScanStride); 496 497 while (sp < end) { 498 curr = buffer[k] = srcPixels[sp]; 499 sumA += (curr>>> 24); 500 sumR += (curr >> 16)&0xFF; 501 sumG += (curr >> 8)&0xFF; 502 sumB += (curr )&0xFF; 503 k++; 504 sp+=srcScanStride; 505 } 506 507 508 dp += (skipY + loc)*dstScanStride; 509 prev = destPixels[dp] = (( (sumA*scale)&0xFF000000) | 510 (((sumR*scale)&0xFF000000)>>>8) | 511 (((sumG*scale)&0xFF000000)>>>16) | 512 (((sumB*scale)&0xFF000000)>>>24)); 513 dp+=dstScanStride; 514 k=0; 515 while (sp < colEnd) { 516 curr = buffer[k]; 517 if (curr == srcPixels[sp]) { 518 destPixels[dp] = prev; 519 } else { 520 sumA -= (curr>>> 24); 521 sumR -= (curr >> 16)&0xFF; 522 sumG -= (curr >> 8)&0xFF; 523 sumB -= (curr )&0xFF; 524 525 curr = buffer[k] = srcPixels[sp]; 526 527 sumA += (curr>>> 24); 528 sumR += (curr >> 16)&0xFF; 529 sumG += (curr >> 8)&0xFF; 530 sumB += (curr )&0xFF; 531 prev = destPixels[dp] = (( (sumA*scale)&0xFF000000) | 532 (((sumR*scale)&0xFF000000)>>>8) | 533 (((sumG*scale)&0xFF000000)>>>16) | 534 (((sumB*scale)&0xFF000000)>>>24)); 535 } 536 k = (k+1)%boxSz; 537 sp+=srcScanStride; 538 dp+=dstScanStride; 539 } 540 } 541 return dest; 542 } 543 544 protected static ColorModel fixColorModel(CachableRed src) { 545 ColorModel cm = src.getColorModel(); 546 547 int b = src.getSampleModel().getNumBands(); 548 int [] masks = new int[4]; 549 switch (b) { 550 case 1: 551 masks[0] = 0xFF; 552 break; 553 case 2: 554 masks[0] = 0x00FF; 555 masks[3] = 0xFF00; 556 break; 557 case 3: 558 masks[0] = 0xFF0000; 559 masks[1] = 0x00FF00; 560 masks[2] = 0x0000FF; 561 break; 562 case 4: 563 masks[0] = 0x00FF0000; 564 masks[1] = 0x0000FF00; 565 masks[2] = 0x000000FF; 566 masks[3] = 0xFF000000; 567 break; 568 default: 569 throw new IllegalArgumentException 570 ("GaussianBlurRed8Bit only supports one to four band images"); 571 } 572 ColorSpace cs = cm.getColorSpace(); 573 return new DirectColorModel (cs, 8*b, masks[0], masks[1], 574 masks[2], masks[3], 575 true, DataBuffer.TYPE_INT); 576 } 577 } 578 | Popular Tags |