KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > awt > image > LookupOp


1 /*
2  * @(#)LookupOp.java 1.49 03/12/19
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8
9 package java.awt.image;
10
11 import java.awt.color.ColorSpace JavaDoc;
12 import java.awt.geom.Rectangle2D JavaDoc;
13 import java.awt.Rectangle JavaDoc;
14 import java.awt.RenderingHints JavaDoc;
15 import java.awt.geom.Point2D JavaDoc;
16 import sun.awt.image.ImagingLib;
17
18 /**
19  * This class implements a lookup operation from the source
20  * to the destination. The LookupTable object may contain a single array
21  * or multiple arrays, subject to the restrictions below.
22  * <p>
23  * For Rasters, the lookup operates on bands. The number of
24  * lookup arrays may be one, in which case the same array is
25  * applied to all bands, or it must equal the number of Source
26  * Raster bands.
27  * <p>
28  * For BufferedImages, the lookup operates on color and alpha components.
29  * The number of lookup arrays may be one, in which case the
30  * same array is applied to all color (but not alpha) components.
31  * Otherwise, the number of lookup arrays may
32  * equal the number of Source color components, in which case no
33  * lookup of the alpha component (if present) is performed.
34  * If neither of these cases apply, the number of lookup arrays
35  * must equal the number of Source color components plus alpha components,
36  * in which case lookup is performed for all color and alpha components.
37  * This allows non-uniform rescaling of multi-band BufferedImages.
38  * <p>
39  * BufferedImage sources with premultiplied alpha data are treated in the same
40  * manner as non-premultiplied images for purposes of the lookup. That is,
41  * the lookup is done per band on the raw data of the BufferedImage source
42  * without regard to whether the data is premultiplied. If a color conversion
43  * is required to the destination ColorModel, the premultiplied state of
44  * both source and destination will be taken into account for this step.
45  * <p>
46  * Images with an IndexColorModel cannot be used.
47  * <p>
48  * If a RenderingHints object is specified in the constructor, the
49  * color rendering hint and the dithering hint may be used when color
50  * conversion is required.
51  * <p>
52  * This class allows the Source to be the same as the Destination.
53  *
54  * @version 10 Feb 1997
55  * @see LookupTable
56  * @see java.awt.RenderingHints#KEY_COLOR_RENDERING
57  * @see java.awt.RenderingHints#KEY_DITHERING
58  */

59
60 public class LookupOp implements BufferedImageOp JavaDoc, RasterOp JavaDoc {
61     private LookupTable JavaDoc ltable;
62     private int numComponents;
63     RenderingHints JavaDoc hints;
64     
65     /**
66      * Constructs a <code>LookupOp</code> object given the lookup
67      * table and a <code>RenderingHints</code> object, which might
68      * be <code>null</code>.
69      * @param lookup the specified <code>LookupTable</code>
70      * @param hints the specified <code>RenderingHints</code>,
71      * or <code>null</code>
72      */

73     public LookupOp(LookupTable JavaDoc lookup, RenderingHints JavaDoc hints) {
74         this.ltable = lookup;
75         this.hints = hints;
76         numComponents = ltable.getNumComponents();
77     }
78
79     /**
80      * Returns the <code>LookupTable</code>.
81      * @return the <code>LookupTable</code> of this
82      * <code>LookupOp</code>.
83      */

84     public final LookupTable JavaDoc getTable() {
85         return ltable;
86     }
87
88     /**
89      * Performs a lookup operation on a <code>BufferedImage</code>.
90      * If the color model in the source image is not the same as that
91      * in the destination image, the pixels will be converted
92      * in the destination. If the destination image is <code>null</code>,
93      * a <code>BufferedImage</code> will be created with an appropriate
94      * <code>ColorModel</code>. An <code>IllegalArgumentException</code>
95      * might be thrown if the number of arrays in the
96      * <code>LookupTable</code> does not meet the restrictions
97      * stated in the class comment above, or if the source image
98      * has an <code>IndexColorModel</code>.
99      * @param src the <code>BufferedImage</code> to be filtered
100      * @param dst the <code>BufferedImage</code> in which to
101      * store the results of the filter operation
102      * @return the filtered <code>BufferedImage</code>.
103      * @throws IllegalArgumentException if the number of arrays in the
104      * <code>LookupTable</code> does not meet the restrictions
105      * described in the class comments, or if the source image
106      * has an <code>IndexColorModel</code>.
107      */

108     public final BufferedImage JavaDoc filter(BufferedImage JavaDoc src, BufferedImage JavaDoc dst) {
109         ColorModel JavaDoc srcCM = src.getColorModel();
110         int numBands = srcCM.getNumColorComponents();
111         ColorModel JavaDoc dstCM;
112         if (srcCM instanceof IndexColorModel JavaDoc) {
113             throw new
114                 IllegalArgumentException JavaDoc("LookupOp cannot be "+
115                                          "performed on an indexed image");
116         }
117         int numComponents = ltable.getNumComponents();
118         if (numComponents != 1 &&
119             numComponents != srcCM.getNumComponents() &&
120             numComponents != srcCM.getNumColorComponents())
121         {
122             throw new IllegalArgumentException JavaDoc("Number of arrays in the "+
123                                                " lookup table ("+
124                                                numComponents+
125                                                " is not compatible with the "+
126                                                " src image: "+src);
127         }
128
129         
130         boolean needToConvert = false;
131         
132         int width = src.getWidth();
133         int height = src.getHeight();
134
135         if (dst == null) {
136             dst = createCompatibleDestImage(src, null);
137             dstCM = srcCM;
138         }
139         else {
140             if (width != dst.getWidth()) {
141                 throw new
142                     IllegalArgumentException JavaDoc("Src width ("+width+
143                                              ") not equal to dst width ("+
144                                              dst.getWidth()+")");
145             }
146             if (height != dst.getHeight()) {
147                 throw new
148                     IllegalArgumentException JavaDoc("Src height ("+height+
149                                              ") not equal to dst height ("+
150                                              dst.getHeight()+")");
151             }
152
153             dstCM = dst.getColorModel();
154             if (srcCM.getColorSpace().getType() !=
155                 dstCM.getColorSpace().getType())
156             {
157                 needToConvert = true;
158                 dst = createCompatibleDestImage(src, null);
159             }
160
161         }
162         
163         BufferedImage JavaDoc origDst = dst;
164
165         if (ImagingLib.filter(this, src, dst) == null) {
166             // Do it the slow way
167
WritableRaster JavaDoc srcRaster = src.getRaster();
168             WritableRaster JavaDoc dstRaster = dst.getRaster();
169
170             if (srcCM.hasAlpha()) {
171                 if (numBands-1 == numComponents || numComponents == 1) {
172                     int minx = srcRaster.getMinX();
173                     int miny = srcRaster.getMinY();
174                     int[] bands = new int[numBands-1];
175                     for (int i=0; i < numBands-1; i++) {
176                         bands[i] = i;
177                     }
178                     srcRaster =
179                         srcRaster.createWritableChild(minx, miny,
180                                                       srcRaster.getWidth(),
181                                                       srcRaster.getHeight(),
182                                                       minx, miny,
183                                                       bands);
184                 }
185             }
186             if (dstCM.hasAlpha()) {
187                 int dstNumBands = dstRaster.getNumBands();
188                 if (dstNumBands-1 == numComponents || numComponents == 1) {
189                     int minx = dstRaster.getMinX();
190                     int miny = dstRaster.getMinY();
191                     int[] bands = new int[numBands-1];
192                     for (int i=0; i < numBands-1; i++) {
193                         bands[i] = i;
194                     }
195                     dstRaster =
196                         dstRaster.createWritableChild(minx, miny,
197                                                       dstRaster.getWidth(),
198                                                       dstRaster.getHeight(),
199                                                       minx, miny,
200                                                       bands);
201                 }
202             }
203
204             filter(srcRaster, dstRaster);
205         }
206         
207         if (needToConvert) {
208             // ColorModels are not the same
209
ColorConvertOp JavaDoc ccop = new ColorConvertOp JavaDoc(hints);
210             ccop.filter(dst, origDst);
211         }
212
213         return origDst;
214     }
215
216     /**
217      * Performs a lookup operation on a <code>Raster</code>.
218      * If the destination <code>Raster</code> is <code>null</code>,
219      * a new <code>Raster</code> will be created.
220      * The <code>IllegalArgumentException</code> might be thrown
221      * if the source <code>Raster</code> and the destination
222      * <code>Raster</code> do not have the same
223      * number of bands or if the number of arrays in the
224      * <code>LookupTable</code> does not meet the
225      * restrictions stated in the class comment above.
226      * @param src the source <code>Raster</code> to filter
227      * @param dst the destination <code>WritableRaster</code> for the
228      * filtered <code>src</code>
229      * @return the filtered <code>WritableRaster</code>.
230      * @throws IllegalArgumentException if the source and destinations
231      * rasters do not have the same number of bands, or the
232      * number of arrays in the <code>LookupTable</code> does
233      * not meet the restrictions described in the class comments.
234      *
235      */

236     public final WritableRaster JavaDoc filter (Raster JavaDoc src, WritableRaster JavaDoc dst) {
237         int numBands = src.getNumBands();
238         int dstLength = dst.getNumBands();
239         int height = src.getHeight();
240         int width = src.getWidth();
241         int srcPix[] = new int[numBands];
242         
243         // Create a new destination Raster, if needed
244

245         if (dst == null) {
246             dst = createCompatibleDestRaster(src);
247         }
248         else if (height != dst.getHeight() || width != dst.getWidth()) {
249             throw new
250                 IllegalArgumentException JavaDoc ("Width or height of Rasters do not "+
251                                           "match");
252         }
253         dstLength = dst.getNumBands();
254         
255         if (numBands != dstLength) {
256             throw new
257                 IllegalArgumentException JavaDoc ("Number of channels in the src ("
258                                           + numBands +
259                                           ") does not match number of channels"
260                                           + " in the destination ("
261                                           + dstLength + ")");
262         }
263         int numComponents = ltable.getNumComponents();
264         if (numComponents != 1 && numComponents != src.getNumBands()) {
265             throw new IllegalArgumentException JavaDoc("Number of arrays in the "+
266                                                " lookup table ("+
267                                                numComponents+
268                                                " is not compatible with the "+
269                                                " src Raster: "+src);
270         }
271
272         
273         if (ImagingLib.filter(this, src, dst) != null) {
274             return dst;
275         }
276
277         // Optimize for cases we know about
278
if (ltable instanceof ByteLookupTable JavaDoc) {
279             byteFilter ((ByteLookupTable JavaDoc) ltable, src, dst,
280                         width, height, numBands);
281         }
282         else if (ltable instanceof ShortLookupTable JavaDoc) {
283             shortFilter ((ShortLookupTable JavaDoc) ltable, src, dst, width,
284                          height, numBands);
285         }
286         else {
287             // Not one we recognize so do it slowly
288
int sminX = src.getMinX();
289             int sY = src.getMinY();
290             int dminX = dst.getMinX();
291             int dY = dst.getMinY();
292             for (int y=0; y < height; y++, sY++, dY++) {
293                 int sX = sminX;
294                 int dX = dminX;
295                 for (int x=0; x < width; x++, sX++, dX++) {
296                     // Find data for all bands at this x,y position
297
src.getPixel(sX, sY, srcPix);
298
299                     // Lookup the data for all bands at this x,y position
300
ltable.lookupPixel(srcPix, srcPix);
301
302                     // Put it back for all bands
303
dst.setPixel(dX, dY, srcPix);
304                 }
305             }
306         }
307
308         return dst;
309     }
310
311     /**
312      * Returns the bounding box of the filtered destination image. Since
313      * this is not a geometric operation, the bounding box does not
314      * change.
315      * @param src the <code>BufferedImage</code> to be filtered
316      * @return the bounds of the filtered definition image.
317      */

318     public final Rectangle2D JavaDoc getBounds2D (BufferedImage JavaDoc src) {
319         return getBounds2D(src.getRaster());
320     }
321
322     /**
323      * Returns the bounding box of the filtered destination Raster. Since
324      * this is not a geometric operation, the bounding box does not
325      * change.
326      * @param src the <code>Raster</code> to be filtered
327      * @return the bounds of the filtered definition <code>Raster</code>.
328      */

329     public final Rectangle2D JavaDoc getBounds2D (Raster JavaDoc src) {
330     return src.getBounds();
331                   
332     }
333
334     /**
335      * Creates a zeroed destination image with the correct size and number of
336      * bands. If destCM is <code>null</code>, an appropriate
337      * <code>ColorModel</code> will be used.
338      * @param src Source image for the filter operation.
339      * @param destCM the destination's <code>ColorModel</code>, which
340      * can be <code>null</code>.
341      * @return a filtered destination <code>BufferedImage</code>.
342      */

343     public BufferedImage JavaDoc createCompatibleDestImage (BufferedImage JavaDoc src,
344                                                     ColorModel JavaDoc destCM) {
345         BufferedImage JavaDoc image;
346         int w = src.getWidth();
347         int h = src.getHeight();
348         int transferType = DataBuffer.TYPE_BYTE;
349         if (destCM == null) {
350             ColorModel JavaDoc cm = src.getColorModel();
351             Raster JavaDoc raster = src.getRaster();
352             if (cm instanceof ComponentColorModel JavaDoc) {
353                 DataBuffer JavaDoc db = raster.getDataBuffer();
354                 boolean hasAlpha = cm.hasAlpha();
355                 boolean isPre = cm.isAlphaPremultiplied();
356                 int trans = cm.getTransparency();
357                 int[] nbits = null;
358                 if (ltable instanceof ByteLookupTable JavaDoc) {
359                     if (db.getDataType() == db.TYPE_USHORT) {
360                         // Dst raster should be of type byte
361
if (hasAlpha) {
362                             nbits = new int[2];
363                             if (trans == cm.BITMASK) {
364                                 nbits[1] = 1;
365                             }
366                             else {
367                                 nbits[1] = 8;
368                             }
369                         }
370                         else {
371                             nbits = new int[1];
372                         }
373                         nbits[0] = 8;
374                     }
375                     // For byte, no need to change the cm
376
}
377                 else if (ltable instanceof ShortLookupTable JavaDoc) {
378                     transferType = DataBuffer.TYPE_USHORT;
379                     if (db.getDataType() == db.TYPE_BYTE) {
380                         if (hasAlpha) {
381                             nbits = new int[2];
382                             if (trans == cm.BITMASK) {
383                                 nbits[1] = 1;
384                             }
385                             else {
386                                 nbits[1] = 16;
387                             }
388                         }
389                         else {
390                             nbits = new int[1];
391                         }
392                         nbits[0] = 16;
393                     }
394                 }
395                 if (nbits != null) {
396                     cm = new ComponentColorModel JavaDoc(cm.getColorSpace(),
397                                                  nbits, hasAlpha, isPre,
398                                                  trans, transferType);
399                 }
400             }
401             image = new BufferedImage JavaDoc(cm,
402                                       cm.createCompatibleWritableRaster(w, h),
403                                       cm.isAlphaPremultiplied(),
404                                       null);
405         }
406         else {
407             image = new BufferedImage JavaDoc(destCM,
408                                       destCM.createCompatibleWritableRaster(w,
409                                                                             h),
410                                       destCM.isAlphaPremultiplied(),
411                                       null);
412         }
413
414         return image;
415     }
416     
417     /**
418      * Creates a zeroed-destination <code>Raster</code> with the
419      * correct size and number of bands, given this source.
420      * @param src the <code>Raster</code> to be transformed
421      * @return the zeroed-destination <code>Raster</code>.
422      */

423     public WritableRaster JavaDoc createCompatibleDestRaster (Raster JavaDoc src) {
424         return src.createCompatibleWritableRaster();
425     }
426     
427     /**
428      * Returns the location of the destination point given a
429      * point in the source. If <code>dstPt</code> is not
430      * <code>null</code>, it will be used to hold the return value.
431      * Since this is not a geometric operation, the <code>srcPt</code>
432      * will equal the <code>dstPt</code>.
433      * @param srcPt a <code>Point2D</code> that represents a point
434      * in the source image
435      * @param dstPt a <code>Point2D</code>that represents the location
436      * in the destination
437      * @return the <code>Point2D</code> in the destination that
438      * corresponds to the specified point in the source.
439      */

440     public final Point2D JavaDoc getPoint2D (Point2D JavaDoc srcPt, Point2D JavaDoc dstPt) {
441         if (dstPt == null) {
442             dstPt = new Point2D.Float JavaDoc();
443         }
444     dstPt.setLocation(srcPt.getX(), srcPt.getY());
445
446         return dstPt;
447     }
448     
449     /**
450      * Returns the rendering hints for this op.
451      * @return the <code>RenderingHints</code> object associated
452      * with this op.
453      */

454     public final RenderingHints JavaDoc getRenderingHints() {
455         return hints;
456     }
457
458     private final void byteFilter(ByteLookupTable JavaDoc lookup, Raster JavaDoc src,
459                                   WritableRaster JavaDoc dst,
460                                   int width, int height, int numBands) {
461         int[] srcPix = null;
462
463         // Find the ref to the table and the offset
464
byte[][] table = lookup.getTable();
465         int offset = lookup.getOffset();
466         int tidx;
467         int step=1;
468
469         // Check if it is one lookup applied to all bands
470
if (table.length == 1) {
471             step=0;
472         }
473
474         int x;
475         int y;
476         int band;
477         int len = table[0].length;
478         
479         // Loop through the data
480
for ( y=0; y < height; y++) {
481             tidx = 0;
482             for ( band=0; band < numBands; band++, tidx+=step) {
483                 // Find data for this band, scanline
484
srcPix = src.getSamples(0, y, width, 1, band, srcPix);
485
486                 for ( x=0; x < width; x++) {
487                     int index = srcPix[x]-offset;
488                     if (index < 0 || index > len) {
489                         throw new
490                             IllegalArgumentException JavaDoc("index ("+index+
491                                                      "(out of range: "+
492                                                      " srcPix["+x+
493                                                      "]="+ srcPix[x]+
494                                                      " offset="+ offset);
495                     }
496                     // Do the lookup
497
srcPix[x] = table[tidx][index];
498                 }
499                 // Put it back
500
dst.setSamples(0, y, width, 1, band, srcPix);
501             }
502         }
503     }
504     
505     private final void shortFilter(ShortLookupTable JavaDoc lookup, Raster JavaDoc src,
506                                    WritableRaster JavaDoc dst,
507                                    int width, int height, int numBands) {
508         int band;
509         int[] srcPix = null;
510
511         // Find the ref to the table and the offset
512
short[][] table = lookup.getTable();
513         int offset = lookup.getOffset();
514         int tidx;
515         int step=1;
516         
517         // Check if it is one lookup applied to all bands
518
if (table.length == 1) {
519             step=0;
520         }
521
522         int x = 0;
523         int y = 0;
524         int index;
525         int maxShort = (1<<16)-1;
526         // Loop through the data
527
for (y=0; y < height; y++) {
528             tidx = 0;
529             for ( band=0; band < numBands; band++, tidx+=step) {
530                 // Find data for this band, scanline
531
srcPix = src.getSamples(0, y, width, 1, band, srcPix);
532
533                 for ( x=0; x < width; x++) {
534                     index = srcPix[x]-offset;
535                     if (index < 0 || index > maxShort) {
536                         throw new
537                             IllegalArgumentException JavaDoc("index out of range "+
538                                                      index+" x is "+x+
539                                                      "srcPix[x]="+srcPix[x]
540                                                      +" offset="+ offset);
541                     }
542                     // Do the lookup
543
srcPix[x] = table[tidx][index];
544                 }
545                 // Put it back
546
dst.setSamples(0, y, width, 1, band, srcPix);
547             }
548         }
549     }
550 }
551
552
553
Popular Tags