KickJava   Java API By Example, From Geeks To Geeks.

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


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.geom.Point2D JavaDoc;
24 import java.awt.geom.Rectangle2D JavaDoc;
25 import java.awt.image.BufferedImage JavaDoc;
26 import java.awt.image.BufferedImageOp JavaDoc;
27 import java.awt.image.ColorModel JavaDoc;
28 import java.awt.image.DataBuffer JavaDoc;
29 import java.awt.image.DataBufferInt JavaDoc;
30 import java.awt.image.DirectColorModel JavaDoc;
31 import java.awt.image.Raster JavaDoc;
32 import java.awt.image.RasterOp JavaDoc;
33 import java.awt.image.SampleModel JavaDoc;
34 import java.awt.image.SinglePixelPackedSampleModel JavaDoc;
35 import java.awt.image.WritableRaster JavaDoc;
36
37 import org.apache.batik.ext.awt.image.GraphicsUtil;
38
39 /**
40  * This class provides an implementation for the SVG
41  * feMorphology filter, as defined in Chapter 15, section 20
42  * of the SVG specification.
43  *
44  * @author <a HREF="mailto:sheng.pei@sun.com">Sheng Pei</a>
45  * @version $Id: MorphologyOp.java,v 1.6 2004/08/18 07:14:08 vhardy Exp $
46  */

47 public class MorphologyOp implements BufferedImageOp JavaDoc, RasterOp JavaDoc {
48     /**
49      * The radius of the operation on X axis
50      */

51     private int radiusX;
52     /**
53      * The radius of the operation on Y axis
54      */

55     private int radiusY;
56     /*
57      * Determine whether to do the dilation or erosion operation.
58      * Will do dilation when it's true and erosion when it's false.
59      */

60     private boolean doDilation;
61
62     /*
63      * rangeX is 2*radiusX+1, which is the width of the Kernel
64      */

65     private final int rangeX;
66
67     /*
68      * rangeY is 2*radiusY+1, which is the height of the Kernel
69      */

70     private final int rangeY;
71
72     /*
73      * sRGB ColorSpace instance used for compatibility checking
74      */

75     private final ColorSpace JavaDoc sRGB = ColorSpace.getInstance(ColorSpace.CS_sRGB);
76
77     /*
78      * Linear RGB ColorSpace instance used for compatibility checking
79      */

80     private final ColorSpace JavaDoc lRGB = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
81
82     /**
83      * @param radiusX defines the radius of filter operation on X-axis. Should not be negative.
84      * A value of zero will disable the effect of the operation on X-axis, as described
85      * in the SVG specification.
86      * @param radiusY defines the radius of filter operation on Y-axis. Should not be negative.
87      * A value of zero will disable the effect of the operation on Y-axis, as described
88      * in the SVG specification.
89      * @param doDilation defines whether to do dilation or erosion operation. Will do dilation
90      * when the value is true, erosion when false.
91      */

92     public MorphologyOp (int radiusX, int radiusY, boolean doDilation){
93         if (radiusX<=0 || radiusY<=0){
94             throw new IllegalArgumentException JavaDoc(new String JavaDoc("The radius of X-axis or Y-axis should not be Zero or Negatives."));
95         }
96         else {
97             this.radiusX = radiusX;
98             this.radiusY = radiusY;
99             this.doDilation = doDilation;
100             rangeX = 2*radiusX + 1;
101             rangeY = 2*radiusY + 1;
102         }
103     }
104
105     public Rectangle2D JavaDoc getBounds2D(Raster JavaDoc src){
106         checkCompatible(src.getSampleModel());
107         return new Rectangle JavaDoc(src.getMinX(), src.getMinY(), src.getWidth(), src.getHeight());
108     }
109
110     public Rectangle2D JavaDoc getBounds2D(BufferedImage JavaDoc src){
111         return new Rectangle JavaDoc(0, 0, src.getWidth(), src.getHeight());
112     }
113
114     public Point2D JavaDoc getPoint2D(Point2D JavaDoc srcPt, Point2D JavaDoc destPt){
115         // This operation does not affect pixel location
116
if(destPt==null)
117             destPt = new Point2D.Float JavaDoc();
118         destPt.setLocation(srcPt.getX(), srcPt.getY());
119         return destPt;
120     }
121
122     private void checkCompatible(ColorModel JavaDoc colorModel,
123                                  SampleModel JavaDoc sampleModel){
124         ColorSpace JavaDoc cs = colorModel.getColorSpace();
125
126         // Check that model is sRGB or linear RGB
127
if((!cs .equals (sRGB)) && (!cs .equals( lRGB)))
128             throw new IllegalArgumentException JavaDoc("Expected CS_sRGB or CS_LINEAR_RGB color model");
129
130         // Check ColorModel is of type DirectColorModel
131
if(!(colorModel instanceof DirectColorModel JavaDoc))
132             throw new IllegalArgumentException JavaDoc("colorModel should be an instance of DirectColorModel");
133
134         // Check transfer type
135
if(sampleModel.getDataType() != DataBuffer.TYPE_INT)
136             throw new IllegalArgumentException JavaDoc("colorModel's transferType should be DataBuffer.TYPE_INT");
137
138         // Check red, green, blue and alpha mask
139
DirectColorModel JavaDoc dcm = (DirectColorModel JavaDoc)colorModel;
140         if(dcm.getRedMask() != 0x00ff0000)
141             throw new IllegalArgumentException JavaDoc("red mask in source should be 0x00ff0000");
142         if(dcm.getGreenMask() != 0x0000ff00)
143             throw new IllegalArgumentException JavaDoc("green mask in source should be 0x0000ff00");
144         if(dcm.getBlueMask() != 0x000000ff)
145             throw new IllegalArgumentException JavaDoc("blue mask in source should be 0x000000ff");
146         if(dcm.getAlphaMask() != 0xff000000)
147             throw new IllegalArgumentException JavaDoc("alpha mask in source should be 0xff000000");
148     }
149
150     private boolean isCompatible(ColorModel JavaDoc colorModel,
151                  SampleModel JavaDoc sampleModel){
152         ColorSpace JavaDoc cs = colorModel.getColorSpace();
153         // Check that model is sRGB or linear RGB
154
if((cs != ColorSpace.getInstance(ColorSpace.CS_sRGB))
155            &&
156            (cs != ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB)))
157             return false;
158
159         // Check ColorModel is of type DirectColorModel
160
if(!(colorModel instanceof DirectColorModel JavaDoc))
161             return false;
162
163         // Check transfer type
164
if(sampleModel.getDataType() != DataBuffer.TYPE_INT)
165             return false;
166
167         // Check red, green, blue and alpha mask
168
DirectColorModel JavaDoc dcm = (DirectColorModel JavaDoc)colorModel;
169         if(dcm.getRedMask() != 0x00ff0000)
170             return false;
171         if(dcm.getGreenMask() != 0x0000ff00)
172             return false;
173         if(dcm.getBlueMask() != 0x000000ff)
174             return false;
175         if(dcm.getAlphaMask() != 0xff000000)
176             return false;
177         return true;
178     }
179
180     private void checkCompatible(SampleModel JavaDoc model){
181         // Check model is ok: should be SinglePixelPackedSampleModel
182
if(!(model instanceof SinglePixelPackedSampleModel JavaDoc))
183             throw new IllegalArgumentException JavaDoc
184                 ("MorphologyOp only works with Rasters " +
185                  "using SinglePixelPackedSampleModels");
186         // Check number of bands
187
int nBands = model.getNumBands();
188         if(nBands!=4)
189             throw new IllegalArgumentException JavaDoc
190                 ("MorphologyOp only words with Rasters having 4 bands");
191         // Check that integer packed.
192
if(model.getDataType()!=DataBuffer.TYPE_INT)
193             throw new IllegalArgumentException JavaDoc
194                 ("MorphologyOp only works with Rasters using DataBufferInt");
195
196         // Check bit masks
197
int bitOffsets[]=((SinglePixelPackedSampleModel JavaDoc)model).getBitOffsets();
198         for(int i=0; i<bitOffsets.length; i++){
199             if(bitOffsets[i]%8 != 0)
200                 throw new IllegalArgumentException JavaDoc
201                     ("MorphologyOp only works with Rasters using 8 bits " +
202                      "per band : " + i + " : " + bitOffsets[i]);
203         }
204     }
205
206     public RenderingHints JavaDoc getRenderingHints(){
207         return null;
208     }
209
210     public WritableRaster JavaDoc createCompatibleDestRaster(Raster JavaDoc src){
211         checkCompatible(src.getSampleModel());
212         // Src Raster is OK: create a similar Raster for destination.
213
return src.createCompatibleWritableRaster();
214     }
215
216     public BufferedImage JavaDoc createCompatibleDestImage(BufferedImage JavaDoc src,
217                                                    ColorModel JavaDoc destCM){
218         BufferedImage JavaDoc dest = null;
219         if(destCM==null)
220             destCM = src.getColorModel();
221
222         WritableRaster JavaDoc wr;
223         wr = destCM.createCompatibleWritableRaster(src.getWidth(),
224                                                    src.getHeight());
225         checkCompatible(destCM, wr.getSampleModel());
226
227         dest = new BufferedImage JavaDoc(destCM, wr,
228                                  destCM.isAlphaPremultiplied(), null);
229         return dest;
230     }
231
232     /*
233      * This method compares the two input variables according
234      * to the doDilation boolean variable.
235      */

236     static final boolean isBetter (final int v1, final int v2, final boolean doDilation) {
237         if (v1 > v2)
238             return doDilation;
239         if (v1 < v2)
240             return !doDilation;
241         return true;
242     }
243
244     /*
245      * This method deals with the condition that the Kernel is wider than
246      * the Image
247      */

248     private void specialProcessRow(Raster JavaDoc src, WritableRaster JavaDoc dest){
249         final int w = src.getWidth();
250         final int h = src.getHeight();
251
252         // Access the integer buffer for each image.
253
DataBufferInt JavaDoc srcDB = (DataBufferInt JavaDoc)src.getDataBuffer();
254         DataBufferInt JavaDoc dstDB = (DataBufferInt JavaDoc)dest.getDataBuffer();
255
256         // Offset defines where in the stack the real data begin
257
SinglePixelPackedSampleModel JavaDoc sppsm;
258         sppsm = (SinglePixelPackedSampleModel JavaDoc)src.getSampleModel();
259
260         final int srcOff = srcDB.getOffset() +
261             sppsm.getOffset(src.getMinX() - src.getSampleModelTranslateX(),
262                             src.getMinY() - src.getSampleModelTranslateY());
263
264
265         sppsm = (SinglePixelPackedSampleModel JavaDoc)dest.getSampleModel();
266         final int dstOff = dstDB.getOffset() +
267             sppsm.getOffset(dest.getMinX() - dest.getSampleModelTranslateX(),
268                             dest.getMinY() - dest.getSampleModelTranslateY());
269
270         // Stride is the distance between two consecutive column elements,
271
// in the one-dimention dataBuffer
272
final int srcScanStride = ((SinglePixelPackedSampleModel JavaDoc)src.getSampleModel()).getScanlineStride();
273         final int dstScanStride = ((SinglePixelPackedSampleModel JavaDoc)dest.getSampleModel()).getScanlineStride();
274
275         // Access the pixel value array
276
final int srcPixels[] = srcDB.getBankData()[0];
277         final int destPixels[] = dstDB.getBankData()[0];
278
279         // The pointer of src and dest indicating where the pixel values are
280
int sp, dp;
281
282         // Declaration for the circular buffer's implementation
283
// These are the circular buffers' head pointer and
284
// the index pointers
285

286         // bufferHead points to the leftmost element in the circular buffer
287
int bufferHead;
288
289         int maxIndexA;
290         int maxIndexR;
291         int maxIndexG;
292         int maxIndexB;
293
294         // Temp variables
295
int pel, currentPixel, lastPixel;
296         int a,r,g,b;
297         int a1,r1,g1,b1;
298
299         // If image width is less than or equal to the radiusX,
300
// all the pixels share the same max/min value
301
if (w<=radiusX){
302             for (int i=0; i<h; i++){
303                 // pointing to the first pixels of each row
304
sp = srcOff + i*srcScanStride;
305                 dp = dstOff + i*dstScanStride;
306                 pel = srcPixels[sp++];
307                 a = pel>>>24;
308                 r = pel&0xff0000;
309                 g = pel&0xff00;
310                 b = pel&0xff;
311
312                 for (int k=1; k<w; k++){
313                     currentPixel = srcPixels[sp++];
314                     a1 = currentPixel>>>24;
315                     r1 = currentPixel&0xff0000;
316                     g1 = currentPixel&0xff00;
317                     b1 = currentPixel&0xff;
318                     
319                     if (isBetter(a1, a, doDilation)){
320                         a = a1;
321                     }
322                     if (isBetter(r1, r, doDilation)){
323                         r = r1;
324                     }
325                     if (isBetter(g1, g, doDilation)){
326                         g = g1;
327                     }
328                     if (isBetter(b1, b, doDilation)){
329                         b = b1;
330                     }
331                 }
332                 // all the element share the same max/min value
333
for (int k=0; k<w; k++){
334                     destPixels[dp++] = (a << 24) | r | g | b;
335                 }
336             }
337         }
338
339         // When radiusX < w <= 2*radiusX
340
else {
341
342             // The width of the circular buffer is w
343
final int [] bufferA = new int [w];
344             final int [] bufferR = new int [w];
345             final int [] bufferG = new int [w];
346             final int [] bufferB = new int [w];
347
348             for (int i=0; i<h; i++){
349                 // initialization of pointers, indice
350
// at the head of each row
351
sp = srcOff + i*srcScanStride;
352                 dp = dstOff + i*dstScanStride;
353
354                 bufferHead = 0;
355                 maxIndexA = 0;
356                 maxIndexR = 0;
357                 maxIndexG = 0;
358                 maxIndexB = 0;
359
360                 pel = srcPixels[sp++];
361                 a = pel>>>24;
362                 r = pel&0xff0000;
363                 g = pel&0xff00;
364                 b = pel&0xff;
365                 bufferA[0] = a;
366                 bufferR[0] = r;
367                 bufferG[0] = g;
368                 bufferB[0] = b;
369
370                 for (int k=1; k<=radiusX; k++){
371                     currentPixel = srcPixels[sp++];
372                     a1 = currentPixel>>>24;
373                     r1 = currentPixel&0xff0000;
374                     g1 = currentPixel&0xff00;
375                     b1 = currentPixel&0xff;
376                     bufferA[k] = a1;
377                     bufferR[k] = r1;
378                     bufferG[k] = g1;
379                     bufferB[k] = b1;
380
381                     if (isBetter(a1, a, doDilation)){
382                         a = a1;
383                         maxIndexA = k;
384                     }
385                     if (isBetter(r1, r, doDilation)){
386                         r = r1;
387                         maxIndexR = k;
388                     }
389                     if (isBetter(g1, g, doDilation)){
390                         g = g1;
391                         maxIndexG = k;
392                     }
393                     if (isBetter(b1, b, doDilation)){
394                         b = b1;
395                         maxIndexB = k;
396                     }
397                 }
398                 destPixels[dp++] = (a << 24) | r | g | b;
399
400                 //
401
// 1 <= j <= w-radiusX-1 : The left margin of each row.
402
//
403
for (int j=1; j<=w-radiusX-1; j++){
404                     lastPixel = srcPixels[sp++];
405
406                     // here is the Alpha channel
407

408                     // we retrieve the previous max/min value
409
a = bufferA[maxIndexA];
410                     a1 = lastPixel>>>24;
411                     bufferA[j+radiusX] = a1;
412                     if (isBetter(a1, a, doDilation)){
413                         a = a1;
414                         maxIndexA = j+radiusX;
415                     }
416
417                     // now we deal with the Red channel
418

419                     r = bufferR[maxIndexR];
420                     r1 = lastPixel&0xff0000;
421                     bufferR[j+radiusX] = r1;
422                     if (isBetter(r1, r, doDilation)){
423                         r = r1;
424                         maxIndexR = j+radiusX;
425                     }
426
427                     // now we deal with the Green channel
428

429                     g = bufferG[maxIndexG];
430                     g1 = lastPixel&0xff00;
431                     bufferG[j+radiusX] = g1;
432                     if (isBetter(g1, g, doDilation)){
433                         g = g1;
434                         maxIndexG = j+radiusX;
435                     }
436
437                     // now we deal with the Blue channel
438

439                     b = bufferB[maxIndexB];
440                     b1 = lastPixel&0xff;
441                     bufferB[j+radiusX] = b1;
442                     if (isBetter(b1, b, doDilation)){
443                         b = b1;
444                         maxIndexB = j+radiusX;
445                     }
446                     // now we have gone through the four channels and
447
// updated the index array. then we'll pack the
448
// new max/min value according to each channel's
449
// max/min vlue
450

451                     destPixels[dp++] = (a << 24) | r | g | b;
452                 }
453                 // Now is the inner body of the row:
454
// all elements in this segment share the same max/min value
455
for (int j = w-radiusX; j<= radiusX; j++){
456                     destPixels[dp] = destPixels[dp-1];
457                     dp++;
458                 }
459                 // Now the circular buffer is full
460
// Now is the right margin of the row when radiusX < w <= 2*radiusX
461
for (int j = radiusX+1; j<w; j++){
462
463                     if (maxIndexA == bufferHead){
464                         a = bufferA[bufferHead+1];
465                         maxIndexA = bufferHead+1;
466                         for (int m= bufferHead+2; m< w; m++){
467                             a1 = bufferA[m];
468                             if (isBetter(a1, a, doDilation)){
469                                 a = a1;
470                                 maxIndexA = m;
471                             }
472                         }
473                     }
474                     else {
475                         a = bufferA[maxIndexA];
476                     }
477                     if (maxIndexR == bufferHead){
478                         r = bufferR[bufferHead+1];
479                         maxIndexR = bufferHead+1;
480                         for (int m= bufferHead+2; m< w; m++){
481                             r1 = bufferR[m];
482                             if (isBetter(r1, r, doDilation)){
483                                 r = r1;
484                                 maxIndexR = m;
485                             }
486                         }
487                     }
488                     else {
489                         r = bufferR[maxIndexR];
490                     }
491
492                     if (maxIndexG == bufferHead){
493                         g = bufferG[bufferHead+1];
494                         maxIndexG = bufferHead+1;
495                         for (int m= bufferHead+2; m< w; m++){
496                             g1 = bufferG[m];
497                             if (isBetter(g1, g, doDilation)){
498                                 g = g1;
499                                 maxIndexG = m;
500                             }
501                         }
502                     }
503                     // we can reuse the previous max/min value
504
else {
505                         g = bufferG[maxIndexG];
506                     }
507
508                     if (maxIndexB == bufferHead){
509                         b = bufferB[bufferHead+1];
510                         maxIndexB = bufferHead+1;
511                         for (int m= bufferHead+2; m< w; m++){
512                             b1 = bufferB[m];
513                             if (isBetter(b1, b, doDilation)){
514                                 b = b1;
515                                 maxIndexB = m;
516                             }
517                         }
518                     }
519                     // we can reuse the previous max/min value
520
else {
521                         b = bufferB[maxIndexB];
522                     }
523
524                     // discard the leftmost element
525
bufferHead++;
526
527                     destPixels[dp++] = (a << 24) | r | g | b;
528                 }
529                 // return to the first pixel of the next row
530
}
531         }// When radiusX < w <=2*radiusX
532
}
533
534     /*
535      * This method deals with the condition when the Kernel is
536      * higher than the image.
537      */

538     private void specialProcessColumn(Raster JavaDoc src, WritableRaster JavaDoc dest){
539
540         final int w = src.getWidth();
541         final int h = src.getHeight();
542
543         // Access the integer buffer for each image.
544
DataBufferInt JavaDoc dstDB = (DataBufferInt JavaDoc)dest.getDataBuffer();
545
546         // Offset defines where in the stack the real data begin
547
final int dstOff = dstDB.getOffset();
548
549         // Stride is the distance between two consecutive column elements,
550
// in the one-dimention dataBuffer
551
final int dstScanStride = ((SinglePixelPackedSampleModel JavaDoc)dest.getSampleModel()).getScanlineStride();
552
553         // Access the pixel value array
554
final int destPixels[] = dstDB.getBankData()[0];
555
556         // The pointer of src and dest indicating where the pixel values are
557
int dp, cp;
558
559         // Declaration for the circular buffer's implementation
560
// These are the circular buffers' head pointer and
561
// the index pointers
562

563         // bufferHead points to the leftmost element in the circular buffer
564
int bufferHead;
565
566         int maxIndexA;
567         int maxIndexR;
568         int maxIndexG;
569         int maxIndexB;
570
571         // Temp variables
572
int pel, currentPixel, lastPixel;
573         int a,r,g,b;
574         int a1,r1,g1,b1;
575
576         // Here all the pixels share the same
577
// max/min value
578
if (h<=radiusY){
579             for (int j=0; j<w; j++){
580                 dp = dstOff + j;
581                 cp = dstOff + j;
582                 pel = destPixels[cp];
583                 cp += dstScanStride;
584                 a = pel>>>24;
585                 r = pel&0xff0000;
586                 g = pel&0xff00;
587                 b = pel&0xff;
588
589                 for (int k=1; k<h; k++){
590                     currentPixel = destPixels[cp];
591                     cp += dstScanStride;
592                     a1 = currentPixel>>>24;
593                     r1 = currentPixel&0xff0000;
594                     g1 = currentPixel&0xff00;
595                     b1 = currentPixel&0xff;
596
597                     if (isBetter(a1, a, doDilation)){
598                         a = a1;
599                     }
600                     if (isBetter(r1, r, doDilation)){
601                         r = r1;
602                     }
603                     if (isBetter(g1, g, doDilation)){
604                         g = g1;
605                     }
606                     if (isBetter(b1, b, doDilation)){
607                         b = b1;
608                     }
609                 }
610                 for (int k=0; k<h; k++){
611                     destPixels[dp] = (a << 24) | r | g | b;
612                     dp += dstScanStride;
613                 }
614                 // return to the first pixel of the next column
615
}
616         }
617
618         // When radiusY < h <= 2*radiusY
619
else {
620
621             // The height of the circular buffer is h
622
final int [] bufferA = new int [h];
623             final int [] bufferR = new int [h];
624             final int [] bufferG = new int [h];
625             final int [] bufferB = new int [h];
626
627             for (int j=0; j<w; j++){
628                 // initialization of pointers, indice
629
// at the head of each column
630
dp = dstOff + j;
631                 cp = dstOff + j;
632
633                 bufferHead = 0;
634                 maxIndexA = 0;
635                 maxIndexR = 0;
636                 maxIndexG = 0;
637                 maxIndexB = 0;
638
639                 pel = destPixels[cp];
640                 cp += dstScanStride;
641                 a = pel>>>24;
642                 r = pel&0xff0000;
643                 g = pel&0xff00;
644                 b = pel&0xff;
645                 bufferA[0] = a;
646                 bufferR[0] = r;
647                 bufferG[0] = g;
648                 bufferB[0] = b;
649
650                 for (int k=1; k<=radiusY; k++){
651                     currentPixel = destPixels[cp];
652                     cp += dstScanStride;
653                     a1 = currentPixel>>>24;
654                     r1 = currentPixel&0xff0000;
655                     g1 = currentPixel&0xff00;
656                     b1 = currentPixel&0xff;
657                     bufferA[k] = a1;
658                     bufferR[k] = r1;
659                     bufferG[k] = g1;
660                     bufferB[k] = b1;
661
662                     if (isBetter(a1, a, doDilation)){
663                         a = a1;
664                         maxIndexA = k;
665                     }
666                     if (isBetter(r1, r, doDilation)){
667                         r = r1;
668                         maxIndexR = k;
669                     }
670                     if (isBetter(g1, g, doDilation)){
671                         g = g1;
672                         maxIndexG = k;
673                     }
674                     if (isBetter(b1, b, doDilation)){
675                         b = b1;
676                         maxIndexB = k;
677                     }
678                 }
679                 // fill the first pixel of each column
680
destPixels[dp] = (a << 24) | r | g | b;
681                 dp += dstScanStride;
682
683                 //
684
// 1 <= i <= h-1-radiusY : The upper margin of each column.
685
//
686
for (int i=1; i<=h-radiusY-1; i++){
687                     lastPixel = destPixels[cp];
688                     cp += dstScanStride;
689
690                     // here is the Alpha channel
691

692                     a = bufferA[maxIndexA];
693                     a1 = lastPixel>>>24;
694                     bufferA[i+radiusY] = a1;
695                     if (isBetter(a1, a, doDilation)){
696                         a = a1;
697                         maxIndexA = i+radiusY;
698                     }
699
700                     // now we deal with the Red channel
701

702                     r = bufferR[maxIndexR];
703                     r1 = lastPixel&0xff0000;
704                     bufferR[i+radiusY] = r1;
705                     if (isBetter(r1, r, doDilation)){
706                         r = r1;