Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/modama/jaiutil/codecs/SimpleRenderedImage.java


1   /*
2    * @(#)SimpleRenderedImage.java  13.2 02/05/08
3    *
4    * Copyright (c) 2002 Sun Microsystems, Inc. All Rights Reserved.
5    *
6    * Redistribution and use in source and binary forms, with or without
7    * modification, are permitted provided that the following conditions are met:
8    *
9    * -Redistributions of source code must retain the above copyright notice, this
10   * list of conditions and the following disclaimer.
11   *
12   * -Redistribution in binary form must reproduct the above copyright notice,
13   * this list of conditions and the following disclaimer in the documentation
14   * and/or other materials provided with the distribution.
15   *
16   * Neither the name of Sun Microsystems, Inc. or the names of contributors may
17   * be used to endorse or promote products derived from this software without
18   * specific prior written permission.
19   *
20   * This software is provided "AS IS," without a warranty of any kind. ALL
21   * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
22   * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
23   * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
24   * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
25   * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
26   * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
27   * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
28   * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
29   * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGES.
31   *
32   * You acknowledge that Software is not designed,licensed or intended for use in
33   * the design, construction, operation or maintenance of any nuclear facility.
34   */
35  
36  package org.modama.jaiutil.codecs;
37  
38  import java.awt.Point;
39  import java.awt.Rectangle;
40  import java.awt.image.Raster;
41  import java.awt.image.WritableRaster;
42  import java.awt.image.RenderedImage;
43  import java.awt.image.ColorModel;
44  import java.awt.image.SampleModel;
45  import java.util.Enumeration;
46  import java.util.Hashtable;
47  import java.util.Iterator;
48  import java.util.Vector;
49  
50  /**
51   * A simple class implemented the <code>RenderedImage</code>
52   * interface.  Only the <code>getTile()</code> method needs to be
53   * implemented by subclasses.  The instance variables must also be
54   * filled in properly.
55   *
56   * <p> Normally in JAI <code>PlanarImage</code> is used for this
57   * purpose, but in the interest of modularity the
58   * use of <code>PlanarImage</code> has been avoided.
59   */
60  
61  public abstract class SimpleRenderedImage implements RenderedImage
62  {
63  
64      /** The X coordinate of the image's upper-left pixel. */
65      protected int minX;
66  
67      /** The Y coordinate of the image's upper-left pixel. */
68      protected int minY;
69  
70      /** The image's width in pixels. */
71      protected int width;
72  
73      /** The image's height in pixels. */
74      protected int height;
75  
76      /** The width of a tile. */
77      protected int tileWidth;
78  
79      /** The height of a tile. */
80      protected int tileHeight;
81  
82      /** The X coordinate of the upper-left pixel of tile (0, 0). */
83      protected int tileGridXOffset = 0;
84  
85      /** The Y coordinate of the upper-left pixel of tile (0, 0). */
86      protected int tileGridYOffset = 0;
87  
88      /** The image's SampleModel. */
89      protected SampleModel sampleModel = null;
90  
91      /** The image's ColorModel. */
92      protected ColorModel colorModel = null;
93  
94      /** The image's sources, stored in a Vector. */
95      protected Vector sources = new Vector();
96  
97      /** A Hashtable containing the image properties. */
98      protected Hashtable properties = new Hashtable();
99  
100     public SimpleRenderedImage()
101     {
102     }
103 
104     /** Returns the X coordinate of the leftmost column of the image. */
105     public int getMinX()
106     {
107         return minX;
108     }
109 
110     /**
111      * Returns the X coordinate of the column immediatetely to the
112      * right of the rightmost column of the image.  getMaxX() is
113      * implemented in terms of getMinX() and getWidth() and so does
114      * not need to be implemented by subclasses.
115      */
116     public final int getMaxX()
117     {
118         return getMinX() + getWidth();
119     }
120 
121     /** Returns the X coordinate of the uppermost row of the image. */
122     public int getMinY()
123     {
124         return minY;
125     }
126 
127     /**
128      * Returns the Y coordinate of the row immediately below the
129      * bottom row of the image.  getMaxY() is implemented in terms of
130      * getMinY() and getHeight() and so does not need to be
131      * implemented by subclasses.
132      */
133     public final int getMaxY()
134     {
135         return getMinY() + getHeight();
136     }
137 
138     /** Returns the width of the image. */
139     public int getWidth()
140     {
141         return width;
142     }
143 
144     /** Returns the height of the image. */
145     public int getHeight()
146     {
147         return height;
148     }
149 
150     /** Returns a Rectangle indicating the image bounds. */
151     public Rectangle getBounds()
152     {
153         return new Rectangle( getMinX(), getMinY(),
154                               getWidth(), getHeight() );
155     }
156 
157     /** Returns the width of a tile. */
158     public int getTileWidth()
159     {
160         return tileWidth;
161     }
162 
163     /** Returns the height of a tile. */
164     public int getTileHeight()
165     {
166         return tileHeight;
167     }
168 
169     /**
170      * Returns the X coordinate of the upper-left pixel of tile (0, 0).
171      */
172     public int getTileGridXOffset()
173     {
174         return tileGridXOffset;
175     }
176 
177     /**
178      * Returns the Y coordinate of the upper-left pixel of tile (0, 0).
179      */
180     public int getTileGridYOffset()
181     {
182         return tileGridYOffset;
183     }
184 
185     /**
186      * Returns the horizontal index of the leftmost column of tiles.
187      * getMinTileX() is implemented in terms of getMinX()
188      * and so does not need to be implemented by subclasses.
189      */
190     public int getMinTileX()
191     {
192         return XToTileX( getMinX() );
193     }
194 
195     /**
196      * Returns the horizontal index of the rightmost column of tiles.
197      * getMaxTileX() is implemented in terms of getMaxX()
198      * and so does not need to be implemented by subclasses.
199      */
200     public int getMaxTileX()
201     {
202         return XToTileX( getMaxX() - 1 );
203     }
204 
205     /**
206      * Returns the number of tiles along the tile grid in the
207      * horizontal direction.  getNumXTiles() is implemented in terms
208      * of getMinTileX() and getMaxTileX() and so does not need to be
209      * implemented by subclasses.
210      */
211     public int getNumXTiles()
212     {
213         return getMaxTileX() - getMinTileX() + 1;
214     }
215 
216     /**
217      * Returns the vertical index of the uppermost row of tiles.  getMinTileY()
218      * is implemented in terms of getMinY() and so does not need to be
219      * implemented by subclasses.
220      */
221     public int getMinTileY()
222     {
223         return YToTileY( getMinY() );
224     }
225 
226     /**
227      * Returns the vertical index of the bottom row of tiles.  getMaxTileY()
228      * is implemented in terms of getMaxY() and so does not need to
229      * be implemented by subclasses.
230      */
231     public int getMaxTileY()
232     {
233         return YToTileY( getMaxY() - 1 );
234     }
235 
236     /**
237      * Returns the number of tiles along the tile grid in the vertical
238      * direction.  getNumYTiles() is implemented in terms
239      * of getMinTileY() and getMaxTileY() and so does not need to be
240      * implemented by subclasses.
241      */
242     public int getNumYTiles()
243     {
244         return getMaxTileY() - getMinTileY() + 1;
245     }
246 
247     /** Returns the SampleModel of the image. */
248     public SampleModel getSampleModel()
249     {
250         return sampleModel;
251     }
252 
253     /** Returns the ColorModel of the image. */
254     public ColorModel getColorModel()
255     {
256         return colorModel;
257     }
258 
259     /**
260      * Gets a property from the property set of this image.  If the
261      * property name is not recognized,
262      * <code>java.awt.Image.UndefinedProperty</code> will be returned.
263      *
264      * @param name the name of the property to get, as a
265      * <code>String</code>.  @return a reference to the property
266      * <code>Object</code>, or the value
267      * <code>java.awt.Image.UndefinedProperty.</code>
268      */
269     public Object getProperty( String name )
270     {
271         name = name.toLowerCase();
272         return properties.get( name );
273     }
274 
275     /**
276      * Returns a list of the properties recognized by this image.  If
277      * no properties are available, <code>null</code> will be
278      * returned.
279      *
280      * @return an array of <code>String</code>s representing valid
281      *         property names.
282      */
283     public String[] getPropertyNames()
284     {
285         String[] names = new String[properties.size()];
286         int index = 0;
287 
288         Enumeration e = properties.keys();
289         while( e.hasMoreElements() )
290         {
291             String name = ( String )e.nextElement();
292             names[index++] = name;
293         }
294 
295         return names;
296     }
297 
298     /**
299      * Returns an array of <code>String</code>s recognized as names by
300      * this property source that begin with the supplied prefix.  If
301      * no property names match, <code>null</code> will be returned.
302      * The comparison is done in a case-independent manner.
303      *
304      * <p> The default implementation calls
305      * <code>getPropertyNames()</code> and searches the list of names
306      * for matches.
307      *
308      * @return an array of <code>String</code>s giving the valid
309      * property names.
310      */
311     public String[] getPropertyNames( String prefix )
312     {
313         String propertyNames[] = getPropertyNames();
314         if( propertyNames == null )
315         {
316             return null;
317         }
318 
319         prefix = prefix.toLowerCase();
320 
321         Vector names = new Vector();
322         for( int i = 0; i < propertyNames.length; i++ )
323         {
324             if( propertyNames[i].startsWith( prefix ) )
325             {
326                 names.addElement( propertyNames[i] );
327             }
328         }
329 
330         if( names.size() == 0 )
331         {
332             return null;
333         }
334 
335         // Copy the strings from the Vector over to a String array.
336         String prefixNames[] = new String[names.size()];
337         int count = 0;
338         for( Iterator it = names.iterator(); it.hasNext(); )
339         {
340             prefixNames[count++] = ( String )it.next();
341         }
342 
343         return prefixNames;
344     }
345 
346     // Utility methods.
347 
348     /**
349      * Converts a pixel's X coordinate into a horizontal tile index
350      * relative to a given tile grid layout specified by its X offset
351      * and tile width.
352      */
353     public static int XToTileX( int x, int tileGridXOffset, int tileWidth )
354     {
355         x -= tileGridXOffset;
356         if( x < 0 )
357         {
358             x += 1 - tileWidth; // Force round to -infinity
359         }
360         return x / tileWidth;
361     }
362 
363     /**
364      * Converts a pixel's Y coordinate into a vertical tile index
365      * relative to a given tile grid layout specified by its Y offset
366      * and tile height.
367      */
368     public static int YToTileY( int y, int tileGridYOffset, int tileHeight )
369     {
370         y -= tileGridYOffset;
371         if( y < 0 )
372         {
373             y += 1 - tileHeight; // Force round to -infinity
374         }
375         return y / tileHeight;
376     }
377 
378     /**
379      * Converts a pixel's X coordinate into a horizontal tile index.
380      * This is a convenience method.  No attempt is made to detect
381      * out-of-range coordinates.
382      *
383      * @param x the X coordinate of a pixel.
384      * @return the X index of the tile containing the pixel.
385      */
386     public int XToTileX( int x )
387     {
388         return XToTileX( x, getTileGridXOffset(), getTileWidth() );
389     }
390 
391     /**
392      * Converts a pixel's Y coordinate into a vertical tile index.
393      * This is a convenience method.  No attempt is made to detect
394      * out-of-range coordinates.
395      *
396      * @param y the Y coordinate of a pixel.
397      * @return the Y index of the tile containing the pixel.
398      */
399     public int YToTileY( int y )
400     {
401         return YToTileY( y, getTileGridYOffset(), getTileHeight() );
402     }
403 
404     /**
405      * Converts a horizontal tile index into the X coordinate of its
406      * upper left pixel relative to a given tile grid layout specified
407      * by its X offset and tile width.
408      */
409     public static int tileXToX( int tx, int tileGridXOffset, int tileWidth )
410     {
411         return tx * tileWidth + tileGridXOffset;
412     }
413 
414     /**
415      * Converts a vertical tile index into the Y coordinate of
416      * its upper left pixel relative to a given tile grid layout
417      * specified by its Y offset and tile height.
418      */
419     public static int tileYToY( int ty, int tileGridYOffset, int tileHeight )
420     {
421         return ty * tileHeight + tileGridYOffset;
422     }
423 
424     /**
425      * Converts a horizontal tile index into the X coordinate of its
426      * upper left pixel.  This is a convenience method.  No attempt is made
427      * to detect out-of-range indices.
428      *
429      * @param tx the horizontal index of a tile.
430      * @return the X coordinate of the tile's upper left pixel.
431      */
432     public int tileXToX( int tx )
433     {
434         return tx * tileWidth + tileGridXOffset;
435     }
436 
437     /**
438      * Converts a vertical tile index into the Y coordinate of its
439      * upper left pixel.  This is a convenience method.  No attempt is made
440      * to detect out-of-range indices.
441      *
442      * @param ty the vertical index of a tile.
443      * @return the Y coordinate of the tile's upper left pixel.
444      */
445     public int tileYToY( int ty )
446     {
447         return ty * tileHeight + tileGridYOffset;
448     }
449 
450     public Vector getSources()
451     {
452         return null;
453     }
454 
455     /**
456      * Returns the entire image in a single Raster.  For images with
457      * multiple tiles this will require making a copy.
458      *
459      * <p> The returned Raster is semantically a copy.  This means
460      * that updates to the source image will not be reflected in the
461      * returned Raster.  For non-writable (immutable) source images,
462      * the returned value may be a reference to the image's internal
463      * data.  The returned Raster should be considered non-writable;
464      * any attempt to alter its pixel data (such as by casting it to
465      * WritableRaster or obtaining and modifying its DataBuffer) may
466      * result in undefined behavior.  The copyData method should be
467      * used if the returned Raster is to be modified.
468      *
469      * @return a Raster containing a copy of this image's data.
470      */
471     public Raster getData()
472     {
473         Rectangle rect = new Rectangle( getMinX(), getMinY(),
474                                         getWidth(), getHeight() );
475         return getData( rect );
476     }
477 
478     /**
479      * Returns an arbitrary rectangular region of the RenderedImage
480      * in a Raster.  The rectangle of interest will be clipped against
481      * the image bounds.
482      *
483      * <p> The returned Raster is semantically a copy.  This means
484      * that updates to the source image will not be reflected in the
485      * returned Raster.  For non-writable (immutable) source images,
486      * the returned value may be a reference to the image's internal
487      * data.  The returned Raster should be considered non-writable;
488      * any attempt to alter its pixel data (such as by casting it to
489      * WritableRaster or obtaining and modifying its DataBuffer) may
490      * result in undefined behavior.  The copyData method should be
491      * used if the returned Raster is to be modified.
492      *
493      * @param bounds the region of the RenderedImage to be returned.
494      */
495     public Raster getData( Rectangle bounds )
496     {
497         int startX = XToTileX( bounds.x );
498         int startY = YToTileY( bounds.y );
499         int endX = XToTileX( bounds.x + bounds.width - 1 );
500         int endY = YToTileY( bounds.y + bounds.height - 1 );
501         Raster tile;
502 
503         if( ( startX == endX ) && ( startY == endY ) )
504         {
505             tile = getTile( startX, startY );
506             return tile.createChild( bounds.x, bounds.y,
507                                      bounds.width, bounds.height,
508                                      bounds.x, bounds.y, null );
509         } else
510         {
511             // Create a WritableRaster of the desired size
512             SampleModel sm =
513                     sampleModel.createCompatibleSampleModel( bounds.width,
514                                                              bounds.height );
515 
516             // Translate it
517             WritableRaster dest =
518                     Raster.createWritableRaster( sm, bounds.getLocation() );
519 
520             for( int j = startY; j <= endY; j++ )
521             {
522                 for( int i = startX; i <= endX; i++ )
523                 {
524                     tile = getTile( i, j );
525                     Rectangle tileRect = tile.getBounds();
526                     Rectangle intersectRect =
527                             bounds.intersection( tile.getBounds() );
528                     Raster liveRaster = tile.createChild( intersectRect.x,
529                                                           intersectRect.y,
530                                                           intersectRect.width,
531                                                           intersectRect.height,
532                                                           intersectRect.x,
533                                                           intersectRect.y,
534                                                           null );
535                     dest.setDataElements( 0, 0, liveRaster );
536                 }
537             }
538             return dest;
539         }
540     }
541 
542     /**
543      * Copies an arbitrary rectangular region of the RenderedImage
544      * into a caller-supplied WritableRaster.  The region to be
545      * computed is determined by clipping the bounds of the supplied
546      * WritableRaster against the bounds of the image.  The supplied
547      * WritableRaster must have a SampleModel that is compatible with
548      * that of the image.
549      *
550      * <p> If the raster argument is null, the entire image will
551      * be copied into a newly-created WritableRaster with a SampleModel
552      * that is compatible with that of the image.
553      *
554      * @param dest a WritableRaster to hold the returned portion of
555      *        the image.
556      * @return a reference to the supplied WritableRaster, or to a
557      *         new WritableRaster if the supplied one was null.
558      */
559     public WritableRaster copyData( WritableRaster dest )
560     {
561         Rectangle bounds;
562         Raster tile;
563 
564         if( dest == null )
565         {
566             bounds = getBounds();
567             Point p = new Point( minX, minY );
568             /* A SampleModel to hold the entire image. */
569             SampleModel sm = sampleModel.createCompatibleSampleModel(
570                     width, height );
571             dest = Raster.createWritableRaster( sm, p );
572         } else
573         {
574             bounds = dest.getBounds();
575         }
576 
577         int startX = XToTileX( bounds.x );
578         int startY = YToTileY( bounds.y );
579         int endX = XToTileX( bounds.x + bounds.width - 1 );
580         int endY = YToTileY( bounds.y + bounds.height - 1 );
581 
582         for( int j = startY; j <= endY; j++ )
583         {
584             for( int i = startX; i <= endX; i++ )
585             {
586                 tile = getTile( i, j );
587                 Rectangle tileRect = tile.getBounds();
588                 Rectangle intersectRect =
589                         bounds.intersection( tile.getBounds() );
590                 Raster liveRaster = tile.createChild( intersectRect.x,
591                                                       intersectRect.y,
592                                                       intersectRect.width,
593                                                       intersectRect.height,
594                                                       intersectRect.x,
595                                                       intersectRect.y,
596                                                       null );
597 
598                 /*
599                  * WritableRaster.setDataElements takes into account of
600                  * inRaster's minX and minY and add these to x and y. Since
601                  * liveRaster has the origin at the correct location, the
602                  * following call should not again give these coordinates in
603                  * places of x and y.
604                  */
605                 dest.setDataElements( 0, 0, liveRaster );
606             }
607         }
608         return dest;
609     }
610 }