KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > ext > awt > image > rendered > GaussianBlurRed8Bit


1 /*
2
3    Copyright 2001-2003 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17  */

18 package org.apache.batik.ext.awt.image.rendered;
19
20 import java.awt.Rectangle JavaDoc;
21 import java.awt.RenderingHints JavaDoc;
22 import java.awt.color.ColorSpace JavaDoc;
23 import java.awt.image.ColorModel JavaDoc;
24 import java.awt.image.ConvolveOp JavaDoc;
25 import java.awt.image.DataBuffer JavaDoc;
26 import java.awt.image.DataBufferInt JavaDoc;
27 import java.awt.image.DirectColorModel JavaDoc;
28 import java.awt.image.Kernel JavaDoc;
29 import java.awt.image.Raster JavaDoc;
30 import java.awt.image.SampleModel JavaDoc;
31 import java.awt.image.SinglePixelPackedSampleModel JavaDoc;
32 import java.awt.image.WritableRaster JavaDoc;
33
34 import org.apache.batik.ext.awt.image.GraphicsUtil;
35
36 /**
37  * This implementation of RenderableImage will render its input
38  * GraphicsNode on demand for tiles.
39  *
40  * @author <a HREF="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
41  * @version $Id: GaussianBlurRed8Bit.java,v 1.8 2004/08/18 07:14:08 vhardy Exp $
42  */

43 public class GaussianBlurRed8Bit extends AbstractRed {
44
45     int xinset, yinset;
46     double stdDevX, stdDevY;
47     RenderingHints JavaDoc hints;
48     ConvolveOp JavaDoc [] convOp = new ConvolveOp JavaDoc [2];
49     int dX, dY;
50
51     /**
52      * Construct a blurred version of <tt>src</tt>, by blurring with a
53      * gaussian kernel with standard Deviation of <tt>stdDev</tt> pixels.
54      * @param src The source image to blur
55      * @param stdDev The Standard Deviation of the Gaussian kernel.
56      * @param rh Rendering hints.
57      */

58     public GaussianBlurRed8Bit(CachableRed src,
59                                double stdDev,
60                                RenderingHints JavaDoc rh) {
61         this(src, stdDev, stdDev, rh);
62     }
63
64     /**
65      * Construct a blurred version of <tt>src</tt>, by blurring with a
66      * gaussian kernel with standard Deviation of <tt>stdDev</tt> pixels.
67      * @param src The source image to blur
68      * @param stdDevX The Standard Deviation of the Gaussian kernel in X
69      * @param stdDevY The Standard Deviation of the Gaussian kernel in Y
70      * @param rh Rendering hints.
71      */

72     public GaussianBlurRed8Bit(CachableRed src,
73                                double stdDevX, double stdDevY,
74                                RenderingHints JavaDoc rh) {
75         super(); // Remember to call super.init()
76

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 JavaDoc 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 JavaDoc cm = fixColorModel(src);
96         SampleModel JavaDoc 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         // System.out.println("StdDev: " + stdDevX + "x" + stdDevY);
112
if ((xinset != 0) && ((stdDevX < 2) || highQuality))
113             convOp[0] = new ConvolveOp JavaDoc(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 JavaDoc(makeQualityKernelY(yinset*2+1));
119         else
120             dY = (int)Math.floor(DSQRT2PI*stdDevY+0.5f);
121     }
122
123     /**
124      * Constant: sqrt(2*PI)
125      */

126     static final float SQRT2PI = (float)Math.sqrt(2*Math.PI);
127
128     /**
129      * Constant: 3*sqrt(2*PI)/4
130      */

131     static final float DSQRT2PI = SQRT2PI*3f/4f;
132
133     /**
134      * Constant: precision used in computation of the Kernel radius
135      */

136     static final float precision = 0.499f;
137
138     /**
139      * Calculate the number of surround pixels required for a given
140      * standard Deviation.
141      */

142     public static int surroundPixels(double stdDev) {
143         return surroundPixels(stdDev, null);
144     }
145
146     /**
147      * Calculate the number of surround pixels required for a given
148      * standard Deviation. Also takes into account rendering quality
149      * hint.
150      */

151     public static int surroundPixels(double stdDev, RenderingHints JavaDoc 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             // Start with 1/2 the zero box enery.
158
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         //compute d
170
int diam = (int)Math.floor(DSQRT2PI*stdDev+0.5f);
171         if (diam%2 == 0)
172             return diam-1 + diam/2; // even case
173
else
174             return diam-2 + diam/2; // Odd case
175
}
176
177     /*
178      * Here we compute the data for the one-dimensional kernel of
179      * length '2*(radius-1) + 1'
180      *
181      * @param radius stdDeviationX or stdDeviationY.
182      * @see #makeQualityKernels */

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; // Used to normalise the kernel
188
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         // Normalise: make elements sum to 1
196
for (int i=0; i<len; i++)
197             kernelData[i] /= sum;
198
199         return kernelData;
200     }
201
202     private Kernel JavaDoc makeQualityKernelX(int len) {
203         return new Kernel JavaDoc(len, 1, computeQualityKernelData(len, stdDevX));
204     }
205
206     private Kernel JavaDoc makeQualityKernelY(int len) {
207         return new Kernel JavaDoc(1, len, computeQualityKernelData(len, stdDevY));
208     }
209
210     public WritableRaster JavaDoc copyData(WritableRaster JavaDoc wr) {
211         // Get my source.
212
CachableRed src = (CachableRed)getSources().get(0);
213
214         Rectangle JavaDoc r = wr.getBounds();
215         r.x -= xinset;
216         r.y -= yinset;
217         r.width += 2*xinset;
218         r.height += 2*yinset;
219
220         // System.out.println("Gaussian GenR: " + wr);
221
// System.out.println("SrcReq: " + r);
222

223         ColorModel JavaDoc srcCM = src.getColorModel();
224
225         WritableRaster JavaDoc tmpR1=null, tmpR2=null;
226
227         tmpR1 = srcCM.createCompatibleWritableRaster(r.width, r.height);
228         {
229             WritableRaster JavaDoc 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         // For the blur box approx we can use dest as our intermediate
237
// otherwise we let it default to null which means we create a new
238
// one...
239

240         // this lets the Vertical conv know how much is junk, so it
241
// doesn't bother to convolve the top and bottom edges
242
int skipX;
243     // long t1 = System.currentTimeMillis();
244
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             // Swap them...
253
WritableRaster JavaDoc 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     // long t2 = System.currentTimeMillis();
292
// System.out.println("Time: " + (t2-t1) +
293
// (((convOp[0] != null) || (convOp[1] != null))?
294
// " ConvOp":""));
295
// System.out.println("Rasters WR :" + wr.getBounds());
296
// System.out.println(" tmp:" + tmpR2.getBounds());
297
// System.out.println(" bounds:" + getBounds());
298
// System.out.println(" skipX:" + skipX +
299
// " dx:" + dX + " Dy: " + dY);
300
tmpR2 = tmpR2.createWritableTranslatedChild(r.x, r.y);
301         GraphicsUtil.copyData(tmpR2, wr);
302
303         return wr;
304     }
305
306     private WritableRaster JavaDoc boxFilterH(Raster JavaDoc src, WritableRaster JavaDoc 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           // Check if the raster is wide enough to do _any_ work
314
if (w < (2*skipX)+boxSz) return dest;
315         if (h < (2*skipY)) return dest;
316
317         final SinglePixelPackedSampleModel JavaDoc srcSPPSM =
318             (SinglePixelPackedSampleModel JavaDoc)src.getSampleModel();
319
320         final SinglePixelPackedSampleModel JavaDoc dstSPPSM =
321             (SinglePixelPackedSampleModel JavaDoc)dest.getSampleModel();
322         
323         // Stride is the distance between two consecutive column elements,
324
// in the one-dimention dataBuffer
325
final int srcScanStride = srcSPPSM.getScanlineStride();
326         final int dstScanStride = dstSPPSM.getScanlineStride();
327
328         // Access the integer buffer for each image.
329
DataBufferInt JavaDoc srcDB = (DataBufferInt JavaDoc)src.getDataBuffer();
330         DataBufferInt JavaDoc dstDB = (DataBufferInt JavaDoc)dest.getDataBuffer();
331
332         // Offset defines where in the stack the real data begin
333
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         // Access the pixel value array
345
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           // Fixed point normalization factor (8.24)
352
int scale = (1<<24)/boxSz;
353         
354         /*
355          * System.out.println("Info: srcOff: " + srcOff +
356          * " x: " + skipX +
357          * " y: " + skipY +
358          * " w: " + w +
359          * " h: " + h +
360          * " boxSz " + boxSz +
361          * " srcStride: " + srcScanStride);
362          */

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 JavaDoc boxFilterV(Raster JavaDoc src, WritableRaster JavaDoc 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           // Check if the raster is wide enough to do _any_ work
432
if (w < (2*skipX)) return dest;
433         if (h < (2*skipY)+boxSz) return dest;
434
435         final SinglePixelPackedSampleModel JavaDoc srcSPPSM =
436             (SinglePixelPackedSampleModel JavaDoc)src.getSampleModel();
437
438         final SinglePixelPackedSampleModel JavaDoc dstSPPSM =
439             (SinglePixelPackedSampleModel JavaDoc)dest.getSampleModel();
440         
441         // Stride is the distance between two consecutive column elements,
442
// in the one-dimention dataBuffer
443
final int srcScanStride = srcSPPSM.getScanlineStride();
444         final int dstScanStride = dstSPPSM.getScanlineStride();
445
446         // Access the integer buffer for each image.
447
DataBufferInt JavaDoc srcDB = (DataBufferInt JavaDoc)src.getDataBuffer();
448         DataBufferInt JavaDoc dstDB = (DataBufferInt JavaDoc)dest.getDataBuffer();
449
450         // Offset defines where in the stack the real data begin
451
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         // Access the pixel value array
464
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           // Fixed point normalization factor (8.24)
471
final int scale = (1<<24)/boxSz;
472
473         /*
474          * System.out.println("Info: srcOff: " + srcOff +
475          * " x: " + skipX +
476          * " y: " + skipY +
477          * " w: " + w +
478          * " h: " + h +
479          * " boxSz " + boxSz +
480          * " srcStride: " + srcScanStride);
481          */

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 JavaDoc fixColorModel(CachableRed src) {
545         ColorModel JavaDoc 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 JavaDoc
570                 ("GaussianBlurRed8Bit only supports one to four band images");
571         }
572         ColorSpace JavaDoc cs = cm.getColorSpace();
573         return new DirectColorModel JavaDoc(cs, 8*b, masks[0], masks[1],
574                                     masks[2], masks[3],
575                                     true, DataBuffer.TYPE_INT);
576     }
577 }
578
Popular Tags