KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > ext > awt > image > renderable > ConvolveMatrixRable8Bit


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.renderable;
19
20 import java.awt.Point JavaDoc;
21 import java.awt.Rectangle JavaDoc;
22 import java.awt.RenderingHints JavaDoc;
23 import java.awt.Shape JavaDoc;
24 import java.awt.color.ColorSpace JavaDoc;
25 import java.awt.geom.AffineTransform JavaDoc;
26 import java.awt.geom.Rectangle2D JavaDoc;
27 import java.awt.image.BufferedImage JavaDoc;
28 import java.awt.image.BufferedImageOp JavaDoc;
29 import java.awt.image.ColorModel JavaDoc;
30 import java.awt.image.ConvolveOp JavaDoc;
31 import java.awt.image.DataBuffer JavaDoc;
32 import java.awt.image.DataBufferInt JavaDoc;
33 import java.awt.image.DirectColorModel JavaDoc;
34 import java.awt.image.Kernel JavaDoc;
35 import java.awt.image.Raster JavaDoc;
36 import java.awt.image.RenderedImage JavaDoc;
37 import java.awt.image.SinglePixelPackedSampleModel JavaDoc;
38 import java.awt.image.WritableRaster JavaDoc;
39 import java.awt.image.renderable.RenderContext JavaDoc;
40
41 import org.apache.batik.ext.awt.image.GraphicsUtil;
42 import org.apache.batik.ext.awt.image.PadMode;
43 import org.apache.batik.ext.awt.image.rendered.AffineRed;
44 import org.apache.batik.ext.awt.image.rendered.BufferedImageCachableRed;
45 import org.apache.batik.ext.awt.image.rendered.CachableRed;
46 import org.apache.batik.ext.awt.image.rendered.PadRed;
47
48 /**
49  * Convolves an image with a convolution matrix.
50  *
51  * Known limitations:
52  * Does not support bias other than zero - pending 16bit pathway
53  * Does not support edgeMode="wrap" - pending Tile code.
54  *
55  * @author <a HREF="mailto:Thomas.DeWeeese@Kodak.com">Thomas DeWeese</a>
56  * @version $Id: ConvolveMatrixRable8Bit.java,v 1.10 2004/08/18 07:13:59 vhardy Exp $
57  */

58 public class ConvolveMatrixRable8Bit
59     extends AbstractColorInterpolationRable
60     implements ConvolveMatrixRable {
61
62     Kernel JavaDoc kernel;
63     Point JavaDoc target;
64     float bias;
65     boolean kernelHasNegValues;
66     PadMode edgeMode;
67     float [] kernelUnitLength = new float[2];
68
69     boolean preserveAlpha = false;
70
71     public ConvolveMatrixRable8Bit(Filter source) {
72         super(source);
73     }
74
75     public Filter getSource() {
76         return (Filter)getSources().get(0);
77     }
78
79     public void setSource(Filter src) {
80         init(src);
81     }
82
83
84     /**
85      * Returns the Convolution Kernel in use
86      */

87     public Kernel JavaDoc getKernel() {
88         return kernel;
89     }
90
91     /**
92      * Sets the Convolution Kernel to use.
93      * @param k Kernel to use for convolution.
94      */

95     public void setKernel(Kernel JavaDoc k) {
96         touch();
97         this.kernel = k;
98         kernelHasNegValues = false;
99         float [] kv = k.getKernelData(null);
100         for (int i=0; i<kv.length; i++)
101             if (kv[i] < 0) {
102                 kernelHasNegValues = true;
103                 break;
104             }
105     }
106
107     public Point JavaDoc getTarget() {
108         return (Point JavaDoc)target.clone();
109     }
110
111     public void setTarget(Point JavaDoc pt) {
112         touch();
113         this.target = (Point JavaDoc)pt.clone();
114     }
115
116     /**
117      * Returns the shift value to apply to the result of convolution
118      */

119     public double getBias() {
120         return bias;
121     }
122
123     /**
124      * Returns the shift value to apply to the result of convolution
125      */

126     public void setBias(double bias) {
127         touch();
128         this.bias = (float)bias;
129     }
130
131     /**
132      * Returns the current edge handling mode.
133      */

134     public PadMode getEdgeMode() {
135         return edgeMode;
136     }
137
138     /**
139      * Sets the current edge handling mode.
140      */

141     public void setEdgeMode(PadMode edgeMode) {
142         touch();
143         this.edgeMode = edgeMode;
144     }
145
146     /**
147      * Returns the [x,y] distance in user space between kernel values
148      */

149     public double [] getKernelUnitLength() {
150         if (kernelUnitLength == null)
151             return null;
152
153         double [] ret = new double[2];
154         ret[0] = kernelUnitLength[0];
155         ret[1] = kernelUnitLength[1];
156         return ret;
157     }
158
159     /**
160      * Sets the [x,y] distance in user space between kernel values
161      * If set to zero then device space will be used.
162      */

163     public void setKernelUnitLength(double [] kernelUnitLength) {
164         touch();
165         if (kernelUnitLength == null) {
166             this.kernelUnitLength = null;
167             return;
168         }
169
170         if (this.kernelUnitLength == null)
171             this.kernelUnitLength = new float[2];
172
173         this.kernelUnitLength[0] = (float)kernelUnitLength[0];
174         this.kernelUnitLength[1] = (float)kernelUnitLength[1];
175     }
176
177     /**
178      * Returns false if the convolution should affect the Alpha channel
179      */

180     public boolean getPreserveAlpha() {
181         return preserveAlpha;
182     }
183
184     /**
185      * Sets Alpha channel handling.
186      * A value of False indicates that the convolution should apply to
187      * the Alpha Channel
188      */

189     public void setPreserveAlpha(boolean preserveAlpha) {
190         touch();
191         this.preserveAlpha = preserveAlpha;
192     }
193
194
195     public void fixAlpha(BufferedImage JavaDoc bi) {
196         if ((!bi.getColorModel().hasAlpha()) ||
197             (!bi.isAlphaPremultiplied()))
198             // No need to fix alpha if it isn't premultiplied...
199
return;
200         if (GraphicsUtil.is_INT_PACK_Data(bi.getSampleModel(), true))
201             fixAlpha_INT_PACK(bi.getRaster());
202         else
203             fixAlpha_FALLBACK(bi.getRaster());
204     }
205
206     public void fixAlpha_INT_PACK(WritableRaster JavaDoc wr) {
207         SinglePixelPackedSampleModel JavaDoc sppsm;
208         sppsm = (SinglePixelPackedSampleModel JavaDoc)wr.getSampleModel();
209
210         final int width = wr.getWidth();
211
212         final int scanStride = sppsm.getScanlineStride();
213         DataBufferInt JavaDoc db = (DataBufferInt JavaDoc)wr.getDataBuffer();
214         final int base
215             = (db.getOffset() +
216                sppsm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(),
217                                wr.getMinY()-wr.getSampleModelTranslateY()));
218         int pixel, a, v;
219         // Access the pixel data array
220
final int pixels[] = db.getBankData()[0];
221         for (int y=0; y<wr.getHeight(); y++) {
222             int sp = base + y*scanStride;
223             final int end = sp + width;
224             while (sp < end) {
225                 pixel = pixels[sp];
226                 a = pixel>>>24;
227                 v = (pixel>>16)&0xFF;
228                 if (a < v) a = v;
229                 v = (pixel>> 8)&0xFF;
230                 if (a < v) a = v;
231                 v = (pixel )&0xFF;
232                 if (a < v) a = v;
233                 pixels[sp] = (pixel&0x00FFFFFF) | (a << 24);
234                 sp++;
235             }
236         }
237     }
238
239     public void fixAlpha_FALLBACK(WritableRaster JavaDoc wr) {
240         int x0=wr.getMinX();
241         int w =wr.getWidth();
242         int y0=wr.getMinY();
243         int y1=y0 + wr.getHeight()-1;
244         int bands = wr.getNumBands();
245         int a, x, y, b, i;
246         int [] pixel = null;
247         for (y=y0; y<=y1; y++) {
248             pixel = wr.getPixels(x0, y, w, 1, pixel);
249             i=0;
250             for (x=0; x<w; x++) {
251                 a=pixel[i];
252                 for (b=1; b<bands; b++)
253                     if (pixel[i+b] > a) a = pixel[i+b];
254                 pixel[i+bands-1] = a;
255                 i+=bands;
256             }
257             wr.setPixels(x0, y, w, 1, pixel);
258         }
259     }
260
261     public RenderedImage JavaDoc createRendering(RenderContext JavaDoc rc) {
262         // Just copy over the rendering hints.
263
RenderingHints JavaDoc rh = rc.getRenderingHints();
264         if (rh == null) rh = new RenderingHints JavaDoc(null);
265
266         // update the current affine transform
267
AffineTransform JavaDoc at = rc.getTransform();
268
269
270         // This splits out the scale and applies it
271
// prior to the Gaussian. Then after appying the gaussian
272
// it applies the shear (rotation) and translation components.
273
double sx = at.getScaleX();
274         double sy = at.getScaleY();
275
276         double shx = at.getShearX();
277         double shy = at.getShearY();
278
279         double tx = at.getTranslateX();
280         double ty = at.getTranslateY();
281
282         // The Scale is the "hypotonose" of the matrix vectors. This
283
// represents the complete scaling value from user to an
284
// intermediate space that is scaled similarly to device
285
// space.
286
double scaleX = Math.sqrt(sx*sx + shy*shy);
287         double scaleY = Math.sqrt(sy*sy + shx*shx);
288
289         // These values represent the scale factor to the intermediate
290
// coordinate system where we will apply our convolution.
291
if (kernelUnitLength != null) {
292             if (kernelUnitLength[0] > 0.0)
293                 scaleX = 1/kernelUnitLength[0];
294
295             if (kernelUnitLength[1] > 0.0)
296                 scaleY = 1/kernelUnitLength[1];
297         }
298
299         Shape JavaDoc aoi = rc.getAreaOfInterest();
300         if(aoi == null)
301             aoi = getBounds2D();
302
303         Rectangle2D JavaDoc r = aoi.getBounds2D();
304
305         int kw = kernel.getWidth();
306         int kh = kernel.getHeight();
307         int kx = target.x;
308         int ky = target.y;
309
310         // Grow the region in usr space.
311
{
312             double rx0 = r.getX() -(kx/scaleX);
313             double ry0 = r.getY() -(ky/scaleY);
314             double rx1 = rx0 + r.getWidth() + (kw-1)/scaleX;
315             double ry1 = ry0 + r.getHeight() + (kh-1)/scaleY;
316             r = new Rectangle2D.Double JavaDoc(Math.floor(rx0),
317                                        Math.floor(ry0),
318                                        Math.ceil (rx1-Math.floor(rx0)),
319                                        Math.ceil (ry1-Math.floor(ry0)));
320         }
321         // This will be the affine transform between our usr space and
322
// an intermediate space which is scaled according to
323
// kernelUnitLength and is axially aligned with our user
324
// space.
325
AffineTransform JavaDoc srcAt
326             = AffineTransform.getScaleInstance(scaleX, scaleY);
327
328         // This is the affine transform between our intermediate
329
// coordinate space (where the convolution takes place) and
330
// the real device space, or null (if we don't need an
331
// intermediate space).
332

333         // The shear/rotation simply divides out the
334
// common scale factor in the matrix.
335
AffineTransform JavaDoc resAt = new AffineTransform JavaDoc(sx/scaleX, shy/scaleX,
336                                                     shx/scaleY, sy/scaleY,
337                                                     tx, ty);
338
339         RenderedImage JavaDoc ri;
340         ri = getSource().createRendering(new RenderContext JavaDoc(srcAt, r, rh));
341         if (ri == null)
342             return null;
343
344         // org.apache.batik.test.gvt.ImageDisplay.printImage
345
// ("Padded Image", ri,
346
// new Rectangle(ri.getMinX()+22,ri.getMinY()+38,5,5));
347

348         CachableRed cr = convertSourceCS(ri);
349
350         Shape JavaDoc devShape = srcAt.createTransformedShape(aoi);
351         Rectangle2D JavaDoc devRect = devShape.getBounds2D();
352         r = devRect;
353         r = new Rectangle2D.Double JavaDoc(Math.floor(r.getX()-kx),
354                                    Math.floor(r.getY()-ky),
355                                    Math.ceil (r.getX()+r.getWidth())-
356                                    Math.floor(r.getX())+(kw-1),
357                                    Math.ceil (r.getY()+r.getHeight())-
358                                    Math.floor(r.getY())+(kh-1));
359
360         if (!r.getBounds().equals(cr.getBounds())) {
361             if (edgeMode == PadMode.WRAP)
362                 throw new IllegalArgumentException JavaDoc
363                     ("edgeMode=\"wrap\" is not supported by ConvolveMatrix.");
364             cr = new PadRed(cr, r.getBounds(), edgeMode, rh);
365         }
366
367         // org.apache.batik.test.gvt.ImageDisplay.printImage
368
// ("Padded Image", cr,
369
// new Rectangle(cr.getMinX()+23,cr.getMinY()+39,5,5));
370

371         if (bias != 0.0)
372             throw new IllegalArgumentException JavaDoc
373                 ("Only bias equal to zero is supported in ConvolveMatrix.");
374
375         BufferedImageOp JavaDoc op = new ConvolveOp JavaDoc(kernel,
376                                             ConvolveOp.EDGE_NO_OP,
377                                             rh);
378
379         ColorModel JavaDoc cm = cr.getColorModel();
380
381         // OK this is a bit of a cheat. We Pull the DataBuffer out of
382
// The read-only raster that getData gives us. And use it to
383
// build a WritableRaster. This avoids a copy of the data.
384
Raster JavaDoc rr = cr.getData();
385         WritableRaster JavaDoc wr = GraphicsUtil.makeRasterWritable(rr, 0, 0);
386
387         // Here we update the translate to account for the phase shift
388
// (if any) introduced by setting targetX, targetY in SVG.
389
int phaseShiftX = target.x - kernel.getXOrigin();
390         int phaseShiftY = target.y - kernel.getYOrigin();
391         int destX = (int)(r.getX() + phaseShiftX);
392         int destY = (int)(r.getY() + phaseShiftY);
393
394         BufferedImage JavaDoc destBI;
395         if (!preserveAlpha) {
396             // Force the data to be premultiplied since often the JDK
397
// code doesn't properly premultiply the values...
398
cm = GraphicsUtil.coerceData(wr, cm, true);
399
400             BufferedImage JavaDoc srcBI;
401             srcBI = new BufferedImage JavaDoc(cm, wr, cm.isAlphaPremultiplied(), null);
402
403             // Easy case just apply the op...
404
destBI = op.filter(srcBI, null);
405
406             if (kernelHasNegValues) {
407                 // When the kernel has negative values it's possible
408
// for the resultant image to have alpha values less
409
// than the associated color values this will lead to
410
// problems later when we try to display the image so
411
// we fix this here.
412
fixAlpha(destBI);
413             }
414
415         } else {
416             BufferedImage JavaDoc srcBI;
417             srcBI = new BufferedImage JavaDoc(cm, wr, cm.isAlphaPremultiplied(), null);
418
419             // Construct a linear sRGB cm without alpha...
420
cm = new DirectColorModel JavaDoc(ColorSpace.getInstance
421                                       (ColorSpace.CS_LINEAR_RGB), 24,
422                                       0x00FF0000, 0x0000FF00,
423                                       0x000000FF, 0x0, false,
424                                       DataBuffer.TYPE_INT);
425
426
427
428             // Create an image with that color model
429
BufferedImage JavaDoc tmpSrcBI = new BufferedImage JavaDoc
430                 (cm, cm.createCompatibleWritableRaster(wr.getWidth(),
431                                                        wr.getHeight()),
432                  cm.isAlphaPremultiplied(), null);
433
434             // Copy the color data (no alpha) to that image
435
// (dividing out alpha if needed).
436
GraphicsUtil.copyData(srcBI, tmpSrcBI);
437
438             // org.apache.batik.test.gvt.ImageDisplay.showImage
439
// ("tmpSrcBI: ", tmpSrcBI);
440

441             // Get a linear sRGB Premult ColorModel
442
ColorModel JavaDoc dstCM = GraphicsUtil.Linear_sRGB_Unpre;
443             // Construct out output image around that ColorModel
444
destBI = new BufferedImage JavaDoc
445                 (dstCM, dstCM.createCompatibleWritableRaster(wr.getWidth(),
446                                                              wr.getHeight()),
447                  dstCM.isAlphaPremultiplied(), null);
448
449             // Construct another image on the same data buffer but without
450
// an alpha channel.
451

452             // Create the Raster (note we are using 'cm' again).
453
WritableRaster JavaDoc dstWR =
454                 Raster.createWritableRaster
455                 (cm.createCompatibleSampleModel(wr.getWidth(), wr.getHeight()),
456                  destBI.getRaster().getDataBuffer(),
457                  new Point JavaDoc(0,0));
458
459             // Create the BufferedImage.
460
BufferedImage JavaDoc tmpDstBI = new BufferedImage JavaDoc
461                 (cm, dstWR, cm.isAlphaPremultiplied(), null);
462
463             // Filter between the two image without alpha.
464
tmpDstBI = op.filter(tmpSrcBI, tmpDstBI);
465
466             // org.apache.batik.test.gvt.ImageDisplay.showImage
467
// ("tmpDstBI: ", tmpDstBI);
468

469             // Copy the alpha channel into the result (note the color
470
// channels are still unpremult.
471
Rectangle JavaDoc srcRect = wr.getBounds();
472             Rectangle JavaDoc dstRect = new Rectangle JavaDoc(srcRect.x-phaseShiftX,
473                                               srcRect.y-phaseShiftY,
474                                               srcRect.width, srcRect.height);
475             GraphicsUtil.copyBand(wr, srcRect, wr.getNumBands()-1,
476                                   destBI.getRaster(), dstRect,
477                                   destBI.getRaster().getNumBands()-1);
478         }
479
480         // Wrap it as a CachableRed
481
cr = new BufferedImageCachableRed(destBI, destX, destY);
482
483         // org.apache.batik.test.gvt.ImageDisplay.printImage
484
// ("Cropped Image", cr,
485
// new Rectangle(cr.getMinX()+22,cr.getMinY()+38,5,5));
486
// org.apache.batik.test.gvt.ImageDisplay.printImage
487
// ("Cropped sRGB", GraphicsUtil.convertTosRGB(cr),
488
// new Rectangle(cr.getMinX()+22,cr.getMinY()+38,5,5));
489

490         // Make sure to crop junk from edges.
491
cr = new PadRed(cr, devRect.getBounds(), PadMode.ZERO_PAD, rh);
492
493         // If we need to scale/rotate/translate the result do so now...
494
if (!resAt.isIdentity())
495             cr = new AffineRed(cr, resAt, null);
496
497         // return the result.
498
return cr;
499     }
500
501 }
502
Popular Tags