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