Source for java.awt.image.BufferedImage

   1: /* BufferedImage.java --
   2:    Copyright (C) 2000, 2002, 2003, 2004, 2005, 2006,  Free Software Foundation
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package java.awt.image;
  40: 
  41: import gnu.java.awt.Buffers;
  42: import gnu.java.awt.ClasspathGraphicsEnvironment;
  43: import gnu.java.awt.ComponentDataBlitOp;
  44: import java.awt.Graphics;
  45: import java.awt.Graphics2D;
  46: import java.awt.GraphicsEnvironment;
  47: import java.awt.Image;
  48: import java.awt.Point;
  49: import java.awt.Rectangle;
  50: import java.awt.Transparency;
  51: import java.awt.color.ColorSpace;
  52: import java.util.Hashtable;
  53: import java.util.Vector;
  54: 
  55: /**
  56:  * A buffered image always starts at coordinates (0, 0).
  57:  *
  58:  * The buffered image is not subdivided into multiple tiles. Instead,
  59:  * the image consists of one large tile (0,0) with the width and
  60:  * height of the image. This tile is always considered to be checked
  61:  * out.
  62:  * 
  63:  * @author Rolf W. Rasmussen (rolfwr@ii.uib.no)
  64:  */
  65: public class BufferedImage extends Image
  66:   implements WritableRenderedImage, Transparency
  67: {
  68:   public static final int TYPE_CUSTOM         =  0,
  69:                           TYPE_INT_RGB        =  1,
  70:                           TYPE_INT_ARGB       =  2,
  71:                           TYPE_INT_ARGB_PRE   =  3,
  72:                           TYPE_INT_BGR        =  4,
  73:                           TYPE_3BYTE_BGR      =  5,
  74:                           TYPE_4BYTE_ABGR     =  6,
  75:                           TYPE_4BYTE_ABGR_PRE =  7,
  76:                           TYPE_USHORT_565_RGB =  8,
  77:                           TYPE_USHORT_555_RGB =  9,
  78:                           TYPE_BYTE_GRAY      = 10,
  79:                           TYPE_USHORT_GRAY    = 11,
  80:                           TYPE_BYTE_BINARY    = 12,
  81:                           TYPE_BYTE_INDEXED   = 13;
  82:   
  83:   /**
  84:    * Vector of TileObservers (or null)
  85:    */
  86:   Vector<TileObserver> tileObservers;
  87:   
  88:   /**
  89:    * The image's WritableRaster
  90:    */
  91:   WritableRaster raster;
  92: 
  93:   /**
  94:    * The associated ColorModel
  95:    */
  96:   ColorModel colorModel;
  97: 
  98:   /**
  99:    * The image's properties (or null)
 100:    */
 101:   Hashtable properties;
 102: 
 103:   /**
 104:    * Whether alpha is premultiplied
 105:    */
 106:   boolean isPremultiplied;
 107: 
 108:   /**
 109:    * The predefined type, if any.
 110:    */
 111:   int type;
 112: 
 113:   /**
 114:    * Creates a new <code>BufferedImage</code> with the specified width, height
 115:    * and type.  Valid <code>type</code> values are:
 116:    * 
 117:    * <ul>
 118:    *   <li>{@link #TYPE_INT_RGB}</li>
 119:    *   <li>{@link #TYPE_INT_ARGB}</li>
 120:    *   <li>{@link #TYPE_INT_ARGB_PRE}</li>
 121:    *   <li>{@link #TYPE_INT_BGR}</li>
 122:    *   <li>{@link #TYPE_3BYTE_BGR}</li>
 123:    *   <li>{@link #TYPE_4BYTE_ABGR}</li>
 124:    *   <li>{@link #TYPE_4BYTE_ABGR_PRE}</li>
 125:    *   <li>{@link #TYPE_USHORT_565_RGB}</li>
 126:    *   <li>{@link #TYPE_USHORT_555_RGB}</li>
 127:    *   <li>{@link #TYPE_BYTE_GRAY}</li>
 128:    *   <li>{@link #TYPE_USHORT_GRAY}</li>
 129:    *   <li>{@link #TYPE_BYTE_BINARY}</li>
 130:    *   <li>{@link #TYPE_BYTE_INDEXED}</li>
 131:    * </ul>
 132:    * 
 133:    * @param width the width (must be > 0).
 134:    * @param height the height (must be > 0).
 135:    * @param type  the image type (see the list of valid types above).
 136:    * 
 137:    * @throws IllegalArgumentException if <code>width</code> or
 138:    *     <code>height</code> is less than or equal to zero.
 139:    * @throws IllegalArgumentException if <code>type</code> is not one of the
 140:    *     specified values.
 141:    */
 142:   public BufferedImage(int width, int height, int type)
 143:   {
 144:     SampleModel sm = null;
 145:     ColorModel cm = null;
 146:     boolean premultiplied = (type == BufferedImage.TYPE_INT_ARGB_PRE
 147:                             || type == BufferedImage.TYPE_4BYTE_ABGR_PRE);
 148: 
 149:     switch( type )
 150:       {
 151:       case BufferedImage.TYPE_INT_RGB:
 152:         sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT, 
 153:                                                width, height,
 154:                                                new int[]{ 0x00FF0000, 
 155:                                                           0x0000FF00, 
 156:                                                           0x000000FF } ) ;
 157:         cm = new DirectColorModel( 24, 0xff0000, 0xff00, 0xff );
 158:         break;
 159:     
 160:       case BufferedImage.TYPE_3BYTE_BGR:
 161:         sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_BYTE,
 162:                                               width, height,
 163:                                               3, width * 3, 
 164:                                               new int[]{ 2, 1, 0 } );
 165:         cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
 166:                                      false, false,
 167:                                      BufferedImage.OPAQUE,
 168:                                      DataBuffer.TYPE_BYTE);
 169:         break;
 170: 
 171:       case BufferedImage.TYPE_INT_ARGB:
 172:       case BufferedImage.TYPE_INT_ARGB_PRE:
 173:         sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT, 
 174:                                                width, height,
 175:                                                new int[]{ 0x00FF0000, 
 176:                                                           0x0000FF00, 
 177:                                                           0x000000FF, 
 178:                                                           0xFF000000 } );
 179:         if (premultiplied)
 180:           cm = new DirectColorModel( ColorSpace.getInstance(ColorSpace.CS_sRGB),
 181:                                      32, 0xff0000, 0xff00, 0xff, 0xff000000,
 182:                                      true,
 183:                                      Buffers.smallestAppropriateTransferType(32));
 184:         else
 185:           cm = new DirectColorModel( 32, 0xff0000, 0xff00, 0xff, 0xff000000 );
 186:         
 187:         break;
 188: 
 189:       case BufferedImage.TYPE_4BYTE_ABGR:
 190:       case BufferedImage.TYPE_4BYTE_ABGR_PRE:
 191:         sm = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, 
 192:                                              width, height,
 193:                                              4, 4*width,
 194:                                              new int[]{3, 2, 1, 0});
 195:         cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
 196:                                      true, premultiplied,
 197:                                      BufferedImage.TRANSLUCENT,
 198:                                      DataBuffer.TYPE_BYTE);
 199:         break;
 200: 
 201:       case BufferedImage.TYPE_INT_BGR:
 202:         sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT, 
 203:                                                width, height,
 204:                                                new int[]{ 0x000000FF, 
 205:                                                           0x0000FF00, 
 206:                                                           0x00FF0000 } ) ;
 207:         cm = new DirectColorModel( 24, 0xff, 0xff00, 0xff0000 );
 208:         break;
 209: 
 210:       case BufferedImage.TYPE_USHORT_565_RGB:
 211:         sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_USHORT,
 212:                                                width, height,
 213:                                                new int[]{ 0xF800, 
 214:                                                           0x7E0, 
 215:                                                           0x1F } ) ;
 216:         cm = new DirectColorModel( 16, 0xF800, 0x7E0, 0x1F );
 217:         break;
 218:         
 219:       case BufferedImage.TYPE_USHORT_555_RGB:
 220:         sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_USHORT,
 221:                                                width, height,
 222:                                                new int[]{ 0x7C00, 
 223:                                                           0x3E0, 
 224:                                                           0x1F } ) ;
 225:         cm = new DirectColorModel( 15, 0x7C00, 0x3E0, 0x1F );
 226:         break;
 227: 
 228:       case BufferedImage.TYPE_BYTE_INDEXED:
 229:         cm = createDefaultIndexedColorModel( false );
 230: 
 231:       case BufferedImage.TYPE_BYTE_GRAY:
 232:         sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_BYTE,
 233:                                               width, height,
 234:                                               1, width, new int[]{ 0 } );
 235:         break;
 236: 
 237:       case BufferedImage.TYPE_USHORT_GRAY:
 238:         sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_USHORT,
 239:                                               width, height,
 240:                                               1, width, new int[]{ 0 } );
 241:         break;
 242: 
 243:       case BufferedImage.TYPE_BYTE_BINARY:
 244:         cm = createDefaultIndexedColorModel( true );
 245:         sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, 
 246:                                              width, height, 1);
 247:         break;
 248: 
 249:       default:
 250:         sm = null;
 251:       }
 252: 
 253:     if( sm == null )
 254:       throw new IllegalArgumentException("Unknown predefined image type.");
 255:     
 256:     if( cm == null ) // only for the grayscale types 
 257:       {
 258:         int buftype;
 259:         int[] bits = new int[1];
 260:         if( type == BufferedImage.TYPE_BYTE_GRAY )
 261:           {
 262:             buftype = DataBuffer.TYPE_BYTE;
 263:             bits[0] = 8;
 264:           }
 265:         else
 266:           {
 267:             buftype = DataBuffer.TYPE_USHORT;
 268:             bits[0] = 16;
 269:           }
 270:         ColorSpace graySpace = ColorSpace.getInstance( ColorSpace.CS_GRAY );
 271:         
 272:         cm = new ComponentColorModel( graySpace, bits, false, false, 
 273:                                       Transparency.OPAQUE, buftype );
 274:       }
 275: 
 276:     WritableRaster rst = null;
 277:     
 278:     // Attempt to create an accelerated backend for this image
 279:     GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
 280:     if (env instanceof ClasspathGraphicsEnvironment)
 281:       rst = ((ClasspathGraphicsEnvironment)env).createRaster(cm, sm);
 282:     
 283:     // Default to a standard Java raster & databuffer if needed
 284:     if (rst == null)
 285:       rst = Raster.createWritableRaster(sm, new Point( 0, 0 ) );
 286:     
 287:     init(cm, rst, premultiplied,
 288:          null, // no properties
 289:          type );
 290:   }
 291: 
 292:   public BufferedImage(int w, int h, int type, IndexColorModel indexcolormodel)
 293:   {
 294:     if ((type != TYPE_BYTE_BINARY) && (type != TYPE_BYTE_INDEXED))
 295:       throw new IllegalArgumentException("Type must be TYPE_BYTE_BINARY or TYPE_BYTE_INDEXED");
 296:     if( indexcolormodel.getMapSize() > 16 && type == TYPE_BYTE_BINARY )
 297:       throw new IllegalArgumentException("Type TYPE_BYTE_BINARY cannot have a larger than 16-color palette.");
 298:     if( indexcolormodel.getMapSize() > 256 )
 299:       throw new IllegalArgumentException("Byte type cannot have a larger than 256-color palette.");
 300: 
 301:     init( indexcolormodel,
 302:           indexcolormodel.createCompatibleWritableRaster(w, h),
 303:           indexcolormodel.isAlphaPremultiplied(),
 304:           null, // no properties
 305:           type );
 306:   }
 307: 
 308:   public BufferedImage(ColorModel colormodel, WritableRaster writableraster,
 309:                boolean premultiplied, Hashtable<?,?> properties)
 310:   {
 311:     init(colormodel, writableraster, premultiplied, properties, TYPE_CUSTOM);
 312:   }
 313:  
 314: 
 315:   private void init(ColorModel cm, WritableRaster writableraster, 
 316:                     boolean premultiplied, Hashtable properties, int type)
 317:   {
 318:     raster = writableraster;
 319:     colorModel = cm;
 320:     this.properties = properties;
 321:     isPremultiplied = premultiplied;
 322:     this.type = type;
 323:   }
 324: 
 325:   /**
 326:    * Creates the default palettes for the predefined indexed color types
 327:    * (256-color or black-and-white)
 328:    *
 329:    * @param binary - If <code>true</code>, a black and white palette,
 330:    * otherwise a default 256-color palette is returned.
 331:    */    
 332:   private IndexColorModel createDefaultIndexedColorModel( boolean binary )
 333:   {
 334:     if( binary )
 335:       {
 336:         byte[] t = new byte[]{ 0, (byte)255 };
 337:         return new IndexColorModel( 1, 2, t, t, t );
 338:       }
 339: 
 340:     byte[] r = new byte[256];
 341:     byte[] g = new byte[256];
 342:     byte[] b = new byte[256];
 343:     
 344:     int index = 0;
 345:     for( int i = 0; i < 6; i++ )
 346:       for( int j = 0; j < 6; j++ )
 347:         for( int k = 0; k < 6; k++ )
 348:           {
 349:             r[ index ] = (byte)(i * 51);
 350:             g[ index ] = (byte)(j * 51);
 351:             b[ index ] = (byte)(k * 51);
 352:             index++;
 353:           }
 354:     
 355:     while( index < 256 )
 356:       {
 357:         r[ index ] = g[ index ] = b[ index ] = 
 358:           (byte)(18 + (index - 216) * 6);
 359:         index++;
 360:       }
 361:     
 362:     return new IndexColorModel( 8, 256, r, g, b );
 363:   }
 364:   
 365:   public void coerceData(boolean premultiplied)
 366:   {
 367:     colorModel = colorModel.coerceData(raster, premultiplied);
 368:     isPremultiplied = premultiplied;
 369:   }
 370: 
 371:   public WritableRaster copyData(WritableRaster dest)
 372:   {
 373:     if (dest == null)
 374:       dest = raster.createCompatibleWritableRaster(getMinX(), getMinY(),
 375:                                                    getWidth(),getHeight());
 376: 
 377:     int x = dest.getMinX();
 378:     int y = dest.getMinY();
 379:     int w = dest.getWidth();
 380:     int h = dest.getHeight();
 381:     
 382:     // create a src child that has the right bounds...
 383:     WritableRaster src =
 384:       raster.createWritableChild(x, y, w, h, x, y,
 385:                                  null);  // same bands
 386:     
 387:     if (src.getSampleModel () instanceof ComponentSampleModel
 388:         && dest.getSampleModel () instanceof ComponentSampleModel)
 389:       // Refer to ComponentDataBlitOp for optimized data blitting:
 390:       ComponentDataBlitOp.INSTANCE.filter(src, dest);
 391:     
 392:     else
 393:       {
 394:         // slower path
 395:         int samples[] = src.getPixels (x, y, w, h, (int [])null);
 396:         dest.setPixels (x, y, w, h, samples);
 397:       }
 398:     return dest;
 399:   }
 400: 
 401:   public Graphics2D createGraphics()
 402:   {
 403:     GraphicsEnvironment env;
 404:     env = GraphicsEnvironment.getLocalGraphicsEnvironment ();
 405:     return env.createGraphics (this);
 406:   }
 407: 
 408:   public void flush()
 409:   {
 410:   }
 411:   
 412:   public WritableRaster getAlphaRaster()
 413:   {
 414:     return colorModel.getAlphaRaster(raster);
 415:   }
 416:   
 417:   public ColorModel getColorModel()
 418:   {
 419:     return colorModel;
 420:   }
 421:   
 422:   public Raster getData()
 423:   {
 424:     return copyData(null);
 425:     /* TODO: this might be optimized by returning the same
 426:        raster (not writable) as long as image data doesn't change. */
 427:   }
 428: 
 429:   public Raster getData(Rectangle rectangle)
 430:   {
 431:     WritableRaster dest =
 432:       raster.createCompatibleWritableRaster(rectangle);
 433:     return copyData(dest);
 434:   }
 435:   
 436:   public Graphics getGraphics()
 437:   {
 438:     return createGraphics();
 439:   }
 440: 
 441:   public int getHeight()
 442:   {
 443:     return raster.getHeight();
 444:   }
 445:   
 446:   public int getHeight(ImageObserver imageobserver)
 447:   {
 448:     return getHeight();
 449:   }
 450:     
 451:   public int getMinTileX()
 452:   {
 453:     return 0;
 454:   }
 455:   
 456:   public int getMinTileY()
 457:   {
 458:     return 0;
 459:   }
 460: 
 461:   public int getMinX()
 462:   {
 463:     return 0; 
 464:   }
 465: 
 466:   public int getMinY() 
 467:   {
 468:     return 0;
 469:   }
 470:   
 471:   public int getNumXTiles()
 472:   {
 473:     return 1;
 474:   }
 475: 
 476:   public int getNumYTiles()
 477:   {
 478:     return 1;
 479:   }
 480: 
 481:   /**
 482:    * Returns the value of the specified property, or 
 483:    * {@link Image#UndefinedProperty} if the property is not defined.
 484:    * 
 485:    * @param string  the property key (<code>null</code> not permitted).
 486:    * 
 487:    * @return The property value.
 488:    * 
 489:    * @throws NullPointerException if <code>string</code> is <code>null</code>.
 490:    */
 491:   public Object getProperty(String string)
 492:   {
 493:     if (string == null)
 494:       throw new NullPointerException("The property name cannot be null.");
 495:     Object result = Image.UndefinedProperty;
 496:     if (properties != null)
 497:       {
 498:         Object v = properties.get(string);
 499:         if (v != null)
 500:           result = v;
 501:       }
 502:     return result;
 503:   }
 504: 
 505:   public Object getProperty(String string, ImageObserver imageobserver)
 506:   {
 507:     return getProperty(string);
 508:   }
 509: 
 510:   /**
 511:    * Returns <code>null</code> always.
 512:    * 
 513:    * @return <code>null</code> always.
 514:    */
 515:   public String[] getPropertyNames()
 516:   {
 517:     // This method should always return null, see:
 518:     // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4640609
 519:     return null;
 520:   }
 521: 
 522:   public int getRGB(int x, int y)
 523:   {
 524:     Object rgbElem = raster.getDataElements(x, y, null);
 525:     return colorModel.getRGB(rgbElem);
 526:   }
 527:     
 528:   public int[] getRGB(int startX, int startY, int w, int h, int[] rgbArray,
 529:                       int offset, int scanlineStride)
 530:   {
 531:     if (rgbArray == null)
 532:       {
 533:         /*
 534:           000000000000000000
 535:           00000[#######-----   [ = start
 536:           -----########-----   ] = end
 537:           -----#######]00000
 538:           000000000000000000 
 539:         */
 540:         int size = (h-1)*scanlineStride + w;
 541:         rgbArray = new int[size];
 542:     }
 543:     
 544:     int endX = startX + w;
 545:     int endY = startY + h;
 546:     
 547:     /* *TODO*:
 548:        Opportunity for optimization by examining color models...
 549:        
 550:        Perhaps wrap the rgbArray up in a WritableRaster with packed
 551:        sRGB color model and perform optimized rendering into the
 552:        array. */
 553: 
 554:     Object rgbElem = null;
 555:     for (int y=startY; y<endY; y++)
 556:       {
 557:         int xoffset = offset;
 558:         for (int x=startX; x<endX; x++)
 559:           {
 560:             int rgb;
 561:             rgbElem = raster.getDataElements(x, y, rgbElem);
 562:             rgb = colorModel.getRGB(rgbElem);
 563:             rgbArray[xoffset++] = rgb;
 564:           }
 565:         offset += scanlineStride;
 566:       }
 567:     return rgbArray;
 568:   }
 569: 
 570:   public WritableRaster getRaster()
 571:   {
 572:     return raster;
 573:   }
 574:   
 575:   public SampleModel getSampleModel()
 576:   {
 577:     return raster.getSampleModel();
 578:   }
 579:     
 580:   public ImageProducer getSource()
 581:   {
 582:     return new ImageProducer()
 583:       {
 584:         Vector<ImageConsumer> consumers = new Vector<ImageConsumer>();
 585: 
 586:         public void addConsumer(ImageConsumer ic)
 587:         {
 588:           if(!consumers.contains(ic))
 589:             consumers.add(ic);
 590:         }
 591: 
 592:         public boolean isConsumer(ImageConsumer ic)
 593:         {
 594:           return consumers.contains(ic);
 595:         }
 596: 
 597:         public void removeConsumer(ImageConsumer ic)
 598:         {
 599:           consumers.remove(ic);
 600:         }
 601: 
 602:         public void startProduction(ImageConsumer ic)
 603:         {
 604:           int x = 0;
 605:           int y = 0;
 606:           int width = getWidth();
 607:           int height = getHeight();
 608:           int stride = width;
 609:           int offset = 0;
 610:           int[] pixels = getRGB(x, y, 
 611:                                 width, height, 
 612:                                 (int[])null, offset, stride);
 613:           // We already convert the color to RGB in the getRGB call, so
 614:           // we pass a simple RGB color model to the consumers.
 615:           ColorModel model = new DirectColorModel(32, 0xff0000, 0xff00, 0xff,
 616:                                                   0xff000000);
 617: 
 618:           consumers.add(ic);
 619: 
 620:           for(int i = 0; i < consumers.size(); i++)
 621:             {
 622:               ImageConsumer c = consumers.elementAt(i);
 623:               c.setHints(ImageConsumer.SINGLEPASS);
 624:               c.setDimensions(getWidth(), getHeight());
 625:               c.setPixels(x, y, width, height, model, pixels, offset, stride);
 626:               c.imageComplete(ImageConsumer.STATICIMAGEDONE);
 627:             }
 628:         }
 629: 
 630:         public void requestTopDownLeftRightResend(ImageConsumer ic)
 631:         {
 632:           startProduction(ic);
 633:         }
 634: 
 635:       };
 636:   }
 637:   
 638:   public Vector<RenderedImage> getSources()
 639:   {
 640:     return null;
 641:   }
 642:   
 643:   public BufferedImage getSubimage(int x, int y, int w, int h)
 644:   {
 645:     WritableRaster subRaster = 
 646:       getRaster().createWritableChild(x, y, w, h, 0, 0, null);
 647:     
 648:     return new BufferedImage(getColorModel(), subRaster, isPremultiplied,
 649:                              properties);
 650:   }
 651: 
 652:   public Raster getTile(int tileX, int tileY)
 653:   {
 654:     return getWritableTile(tileX, tileY);
 655:   }
 656:     
 657:   public int getTileGridXOffset()
 658:   {
 659:     return 0; // according to javadocs
 660:   }
 661: 
 662:   public int getTileGridYOffset()
 663:   {
 664:     return 0; // according to javadocs
 665:   }
 666: 
 667:   public int getTileHeight()
 668:   {
 669:     return getHeight(); // image is one big tile
 670:   }
 671: 
 672:   public int getTileWidth()
 673:   {
 674:     return getWidth(); // image is one big tile
 675:   }
 676: 
 677:   public int getType()
 678:   {
 679:     return type;
 680:   }
 681: 
 682:   public int getWidth()
 683:   {
 684:     return raster.getWidth();
 685:   }
 686: 
 687:   public int getWidth(ImageObserver imageobserver)
 688:   {
 689:     return getWidth();
 690:   }
 691: 
 692:   public WritableRaster getWritableTile(int tileX, int tileY)
 693:   {
 694:     isTileWritable(tileX, tileY);  // for exception
 695:     return raster;
 696:   }
 697: 
 698:   private static final Point[] tileIndices = { new Point() };
 699:     
 700:   public Point[] getWritableTileIndices()
 701:   {
 702:     return tileIndices;
 703:   }
 704: 
 705:   public boolean hasTileWriters()
 706:   {
 707:     return true;
 708:   }
 709:   
 710:   public boolean isAlphaPremultiplied()
 711:   {
 712:     return isPremultiplied;
 713:   }
 714: 
 715:   public boolean isTileWritable(int tileX, int tileY)
 716:   {
 717:     if ((tileX != 0) || (tileY != 0))
 718:       throw new ArrayIndexOutOfBoundsException("only tile is (0,0)");
 719:     return true;
 720:   }
 721: 
 722:   public void releaseWritableTile(int tileX, int tileY)
 723:   {
 724:     isTileWritable(tileX, tileY);  // for exception
 725:   }
 726: 
 727:   //public void removeTileObserver(TileObserver tileobserver) {}
 728: 
 729:   public void setData(Raster src)
 730:   {
 731:     int x = src.getMinX();
 732:     int y = src.getMinY();
 733:     int w = src.getWidth();
 734:     int h = src.getHeight();
 735:     
 736:     // create a dest child that has the right bounds...
 737:     WritableRaster dest =
 738:       raster.createWritableChild(x, y, w, h, x, y, null);
 739: 
 740:     if (src.getSampleModel () instanceof ComponentSampleModel
 741:         && dest.getSampleModel () instanceof ComponentSampleModel)
 742: 
 743:       // Refer to ComponentDataBlitOp for optimized data blitting:
 744:       ComponentDataBlitOp.INSTANCE.filter(src, dest);
 745:     else
 746:       {
 747:         // slower path
 748:         int samples[] = src.getPixels (x, y, w, h, (int [])null);
 749:         dest.setPixels (x, y, w, h, samples);
 750:       }
 751:   }
 752: 
 753:   public void setRGB(int x, int y, int argb)
 754:   {
 755:     Object rgbElem = colorModel.getDataElements(argb, null);
 756:     raster.setDataElements(x, y, rgbElem);
 757:   }
 758:   
 759:   public void setRGB(int startX, int startY, int w, int h,
 760:              int[] argbArray, int offset, int scanlineStride)
 761:   {
 762:     int endX = startX + w;
 763:     int endY = startY + h;
 764:     
 765:     Object rgbElem = null;
 766:     for (int y=startY; y<endY; y++)
 767:       {
 768:         int xoffset = offset;
 769:         for (int x=startX; x<endX; x++)
 770:           {
 771:             int argb = argbArray[xoffset++];
 772:             rgbElem = colorModel.getDataElements(argb, rgbElem);
 773:             raster.setDataElements(x, y, rgbElem);
 774:           }
 775:         offset += scanlineStride;    
 776:       }
 777:   }
 778:     
 779:   public String toString()
 780:   {
 781:     StringBuffer buf;
 782: 
 783:     buf = new StringBuffer(/* estimated length */ 120);
 784:     buf.append("BufferedImage@");
 785:     buf.append(Integer.toHexString(hashCode()));
 786:     buf.append(": type=");
 787:     buf.append(type);
 788:     buf.append(' ');
 789:     buf.append(colorModel);
 790:     buf.append(' ');
 791:     buf.append(raster);
 792: 
 793:     return buf.toString();
 794:   }
 795: 
 796: 
 797:   /**
 798:    * Adds a tile observer. If the observer is already present, it receives
 799:    * multiple notifications.
 800:    *
 801:    * @param to The TileObserver to add.
 802:    */
 803:   public void addTileObserver (TileObserver to)
 804:   {
 805:     if (tileObservers == null)
 806:       tileObservers = new Vector<TileObserver>();
 807:     
 808:     tileObservers.add (to);
 809:   }
 810:     
 811:   /**
 812:    * Removes a tile observer. If the observer was not registered,
 813:    * nothing happens. If the observer was registered for multiple
 814:    * notifications, it is now registered for one fewer notification.
 815:    *
 816:    * @param to The TileObserver to remove.
 817:    */
 818:   public void removeTileObserver (TileObserver to)
 819:   {
 820:     if (tileObservers == null)
 821:       return;
 822:     
 823:     tileObservers.remove (to);
 824:   }
 825: 
 826:   /**
 827:    * Return the transparency type.
 828:    *
 829:    * @return One of {@link #OPAQUE}, {@link #BITMASK}, or {@link #TRANSLUCENT}.
 830:    * @see Transparency#getTransparency()
 831:    * @since 1.5
 832:    */
 833:   public int getTransparency()
 834:   {
 835:     return colorModel.getTransparency();
 836:   }
 837: }