1 /*
   2  * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.awt.image;
  27 
  28 import java.awt.Color;
  29 import java.awt.Graphics;
  30 import java.awt.Transparency;
  31 import java.awt.AWTException;
  32 import java.awt.Rectangle;
  33 import java.awt.image.BufferedImage;
  34 import java.awt.image.ColorModel;
  35 import java.awt.image.DirectColorModel;
  36 import java.awt.image.IndexColorModel;
  37 import java.awt.image.ImageConsumer;
  38 import java.awt.image.ImageObserver;
  39 import sun.awt.image.ByteComponentRaster;
  40 import sun.awt.image.IntegerComponentRaster;
  41 import java.awt.image.Raster;
  42 import java.awt.image.WritableRaster;
  43 import java.awt.image.DataBuffer;
  44 import java.awt.image.DataBufferInt;
  45 import java.awt.Graphics2D;
  46 import java.awt.geom.AffineTransform;
  47 import sun.awt.image.ImageWatched;
  48 import java.util.Hashtable;
  49 
  50 public class ImageRepresentation extends ImageWatched implements ImageConsumer
  51 {
  52     InputStreamImageSource src;
  53     ToolkitImage image;
  54     int tag;
  55 
  56     long pData; // used by windows native code only -- internal state REMIND ATTN @@
  57 
  58     int width = -1;
  59     int height = -1;
  60     int hints;
  61 
  62     int availinfo;
  63 
  64     Rectangle newbits;
  65 
  66     BufferedImage bimage;
  67     WritableRaster biRaster;
  68     protected ColorModel cmodel;
  69     ColorModel srcModel = null;
  70     int[] srcLUT = null;
  71     int srcLUTtransIndex = -1;
  72     int numSrcLUT = 0;
  73     boolean forceCMhint;
  74     int sstride;
  75     boolean isDefaultBI = false;
  76     boolean isSameCM = false;
  77 
  78     private native static void initIDs();
  79 
  80     static {
  81         /* ensure that the necessary native libraries are loaded */
  82         NativeLibLoader.loadLibraries();
  83         initIDs();
  84     }
  85 
  86     /**
  87      * Create an ImageRepresentation for the given Image.  The
  88      * width and height are unknown at this point.  The color
  89      * model is a hint as to the color model to use when creating
  90      * the buffered image.  If null, the src color model will
  91      * be used.
  92      */
  93     public ImageRepresentation(ToolkitImage im, ColorModel cmodel, boolean
  94                                forceCMhint) {
  95         image = im;
  96 
  97         if (image.getSource() instanceof InputStreamImageSource) {
  98             src = (InputStreamImageSource) image.getSource();
  99         }
 100 
 101         setColorModel(cmodel);
 102 
 103         this.forceCMhint = forceCMhint;
 104     }
 105 
 106     /* REMIND: Only used for Frame.setIcon - should use ImageWatcher instead */
 107     public synchronized void reconstruct(int flags) {
 108         if (src != null) {
 109             src.checkSecurity(null, false);
 110         }
 111         int missinginfo = flags & ~availinfo;
 112         if ((availinfo & ImageObserver.ERROR) == 0 && missinginfo != 0) {
 113             numWaiters++;
 114             try {
 115                 startProduction();
 116                 missinginfo = flags & ~availinfo;
 117                 while ((availinfo & ImageObserver.ERROR) == 0 &&
 118                        missinginfo != 0)
 119                 {
 120                     try {
 121                         wait();
 122                     } catch (InterruptedException e) {
 123                         Thread.currentThread().interrupt();
 124                         return;
 125                     }
 126                     missinginfo = flags & ~availinfo;
 127                 }
 128             } finally {
 129                 decrementWaiters();
 130             }
 131         }
 132     }
 133 
 134     public void setDimensions(int w, int h) {
 135         if (src != null) {
 136             src.checkSecurity(null, false);
 137         }
 138 
 139         image.setDimensions(w, h);
 140 
 141         newInfo(image, (ImageObserver.WIDTH | ImageObserver.HEIGHT),
 142                 0, 0, w, h);
 143 
 144         if (w <= 0 || h <= 0) {
 145             imageComplete(ImageConsumer.IMAGEERROR);
 146             return;
 147         }
 148 
 149         if (width != w || height != h) {
 150             // dimension mismatch => trigger recreation of the buffer
 151             bimage = null;
 152         }
 153 
 154         width = w;
 155         height = h;
 156 
 157         availinfo |= ImageObserver.WIDTH | ImageObserver.HEIGHT;
 158     }
 159 
 160     public int getWidth() {
 161         return width;
 162     }
 163 
 164     public int getHeight() {
 165         return height;
 166     }
 167 
 168     ColorModel getColorModel() {
 169         return cmodel;
 170     }
 171 
 172     BufferedImage getBufferedImage() {
 173         return bimage;
 174     }
 175 
 176     /**
 177      * Returns the BufferedImage that will be used as the representation of
 178      * the pixel data.  Subclasses can override this method to return
 179      * platform specific subclasses of BufferedImage that may or may not be
 180      * accelerated.
 181      *
 182      * It is subclass' responsibility to propagate acceleration priority
 183      * to the newly created image.
 184      */
 185     protected BufferedImage createImage(ColorModel cm,
 186                                         WritableRaster raster,
 187                                         boolean isRasterPremultiplied,
 188                                         Hashtable properties)
 189     {
 190         BufferedImage bi =
 191             new BufferedImage(cm, raster, isRasterPremultiplied, null);
 192         bi.setAccelerationPriority(image.getAccelerationPriority());
 193         return bi;
 194     }
 195 
 196     public void setProperties(Hashtable<?,?> props) {
 197         if (src != null) {
 198             src.checkSecurity(null, false);
 199         }
 200         image.setProperties(props);
 201         newInfo(image, ImageObserver.PROPERTIES, 0, 0, 0, 0);
 202     }
 203 
 204     public void setColorModel(ColorModel model) {
 205         if (src != null) {
 206             src.checkSecurity(null, false);
 207         }
 208         srcModel = model;
 209 
 210         // Check to see if model is INT_RGB
 211         if (model instanceof IndexColorModel) {
 212             if (model.getTransparency() == model.TRANSLUCENT) {
 213                 // REMIND:
 214                 // Probably need to composite anyway so force ARGB
 215                 cmodel = ColorModel.getRGBdefault();
 216                 srcLUT = null;
 217             }
 218             else {
 219                 IndexColorModel icm = (IndexColorModel) model;
 220                 numSrcLUT = icm.getMapSize();
 221                 srcLUT = new int[Math.max(numSrcLUT, 256)];
 222                 icm.getRGBs(srcLUT);
 223                 srcLUTtransIndex = icm.getTransparentPixel();
 224                 cmodel = model;
 225             }
 226         }
 227         else {
 228             if (cmodel == null) {
 229                 cmodel = model;
 230                 srcLUT   = null;
 231             }
 232             else if (model instanceof DirectColorModel) {
 233                 // If it is INT_RGB or INT_ARGB, use the model
 234                 DirectColorModel dcm = (DirectColorModel) model;
 235                 if ((dcm.getRedMask() == 0xff0000) &&
 236                     (dcm.getGreenMask() == 0xff00) &&
 237                     (dcm.getBlueMask()  == 0x00ff)) {
 238                     cmodel   = model;
 239                     srcLUT   = null;
 240                 }
 241             }
 242         }
 243 
 244         isSameCM = (cmodel == model);
 245     }
 246 
 247     void createBufferedImage() {
 248         // REMIND:  Be careful!  Is this called everytime there is a
 249         // startProduction?  We only want to call it if it is new or
 250         // there is an error
 251         isDefaultBI = false;
 252         try {
 253             biRaster = cmodel.createCompatibleWritableRaster(width, height);
 254             bimage = createImage(cmodel, biRaster,
 255                                  cmodel.isAlphaPremultiplied(), null);
 256         } catch (Exception e) {
 257             // Create a default image
 258             cmodel = ColorModel.getRGBdefault();
 259             biRaster = cmodel.createCompatibleWritableRaster(width, height);
 260             bimage = createImage(cmodel, biRaster, false, null);
 261         }
 262         int type = bimage.getType();
 263 
 264         if ((cmodel == ColorModel.getRGBdefault()) ||
 265                (type == BufferedImage.TYPE_INT_RGB) ||
 266                (type == BufferedImage.TYPE_INT_ARGB_PRE)) {
 267             isDefaultBI = true;
 268         }
 269         else if (cmodel instanceof DirectColorModel) {
 270             DirectColorModel dcm = (DirectColorModel) cmodel;
 271             if (dcm.getRedMask() == 0xff0000 &&
 272                 dcm.getGreenMask() == 0xff00 &&
 273                 dcm.getBlueMask()  == 0xff) {
 274                 isDefaultBI = true;
 275             }
 276         }
 277     }
 278 
 279     private void convertToRGB() {
 280         int w = bimage.getWidth();
 281         int h = bimage.getHeight();
 282         int size = w*h;
 283 
 284         DataBufferInt dbi = new DataBufferInt(size);
 285         // Note that stealData() requires a markDirty() afterwards
 286         // since we modify the data in it.
 287         int newpixels[] = SunWritableRaster.stealData(dbi, 0);
 288         if (cmodel instanceof IndexColorModel &&
 289             biRaster instanceof ByteComponentRaster &&
 290             biRaster.getNumDataElements() == 1)
 291         {
 292             ByteComponentRaster bct = (ByteComponentRaster) biRaster;
 293             byte[] data = bct.getDataStorage();
 294             int coff = bct.getDataOffset(0);
 295             for (int i=0; i < size; i++) {
 296                 newpixels[i] = srcLUT[data[coff+i]&0xff];
 297             }
 298         }
 299         else {
 300             Object srcpixels = null;
 301             int off=0;
 302             for (int y=0; y < h; y++) {
 303                 for (int x=0; x < w; x++) {
 304                     srcpixels=biRaster.getDataElements(x, y, srcpixels);
 305                     newpixels[off++] = cmodel.getRGB(srcpixels);
 306                 }
 307             }
 308         }
 309         // We modified the data array directly above so mark it as dirty now...
 310         SunWritableRaster.markDirty(dbi);
 311 
 312         isSameCM = false;
 313         cmodel = ColorModel.getRGBdefault();
 314 
 315         int bandMasks[] = {0x00ff0000,
 316                            0x0000ff00,
 317                            0x000000ff,
 318                            0xff000000};
 319 
 320         biRaster = Raster.createPackedRaster(dbi,w,h,w,
 321                                              bandMasks,null);
 322 
 323         bimage = createImage(cmodel, biRaster,
 324                              cmodel.isAlphaPremultiplied(), null);
 325         srcLUT = null;
 326         isDefaultBI = true;
 327     }
 328 
 329     public void setHints(int h) {
 330         if (src != null) {
 331             src.checkSecurity(null, false);
 332         }
 333         hints = h;
 334     }
 335 
 336     private native boolean setICMpixels(int x, int y, int w, int h, int[] lut,
 337                                     byte[] pix, int off, int scansize,
 338                                     IntegerComponentRaster ict);
 339     private native boolean setDiffICM(int x, int y, int w, int h, int[] lut,
 340                                  int transPix, int numLut, IndexColorModel icm,
 341                                  byte[] pix, int off, int scansize,
 342                                  ByteComponentRaster bct, int chanOff);
 343     static boolean s_useNative = true;
 344 
 345     public void setPixels(int x, int y, int w, int h,
 346                           ColorModel model,
 347                           byte pix[], int off, int scansize) {
 348         int lineOff=off;
 349         int poff;
 350         int[] newLUT=null;
 351 
 352         if (src != null) {
 353             src.checkSecurity(null, false);
 354         }
 355 
 356         // REMIND: What if the model doesn't fit in default color model?
 357         synchronized (this) {
 358             if (bimage == null) {
 359                 if (cmodel == null) {
 360                     cmodel = model;
 361                 }
 362                 createBufferedImage();
 363             }
 364 
 365             if (w <= 0 || h <= 0) {
 366                 return;
 367             }
 368 
 369             int biWidth = biRaster.getWidth();
 370             int biHeight = biRaster.getHeight();
 371 
 372             int x1 = x+w;  // Overflow protection below
 373             int y1 = y+h;  // Overflow protection below
 374             if (x < 0) {
 375                 off -= x;
 376                 x = 0;
 377             } else if (x1 < 0) {
 378                 x1 = biWidth;  // Must be overflow
 379             }
 380             if (y < 0) {
 381                 off -= y*scansize;
 382                 y = 0;
 383             } else if (y1 < 0) {
 384                 y1 = biHeight;  // Must be overflow
 385             }
 386             if (x1 > biWidth) {
 387                 x1 = biWidth;
 388             }
 389             if (y1 > biHeight) {
 390                 y1 = biHeight;
 391             }
 392             if (x >= x1 || y >= y1) {
 393                 return;
 394             }
 395             // x,y,x1,y1 are all >= 0, so w,h must be >= 0
 396             w = x1-x;
 397             h = y1-y;
 398             // off is first pixel read so it must be in bounds
 399             if (off < 0 || off >= pix.length) {
 400                 // They overflowed their own array
 401                 throw new ArrayIndexOutOfBoundsException("Data offset out of bounds.");
 402             }
 403             // pix.length and off are >= 0 so remainder >= 0
 404             int remainder = pix.length - off;
 405             if (remainder < w) {
 406                 // They overflowed their own array
 407                 throw new ArrayIndexOutOfBoundsException("Data array is too short.");
 408             }
 409             int num;
 410             if (scansize < 0) {
 411                 num = (off / -scansize) + 1;
 412             } else if (scansize > 0) {
 413                 num = ((remainder-w) / scansize) + 1;
 414             } else {
 415                 num = h;
 416             }
 417             if (h > num) {
 418                 // They overflowed their own array.
 419                 throw new ArrayIndexOutOfBoundsException("Data array is too short.");
 420             }
 421 
 422             if (isSameCM && (cmodel != model) && (srcLUT != null) &&
 423                 (model instanceof IndexColorModel) &&
 424                 (biRaster instanceof ByteComponentRaster))
 425             {
 426                 IndexColorModel icm = (IndexColorModel) model;
 427                 ByteComponentRaster bct = (ByteComponentRaster) biRaster;
 428                 int numlut = numSrcLUT;
 429                 if (!setDiffICM(x, y, w, h, srcLUT, srcLUTtransIndex,
 430                                numSrcLUT, icm,
 431                                pix, off, scansize, bct,
 432                                bct.getDataOffset(0))) {
 433                     convertToRGB();
 434                 }
 435                 else {
 436                     // Note that setDiffICM modified the raster directly
 437                     // so we must mark it as changed
 438                     bct.markDirty();
 439                     if (numlut != numSrcLUT) {
 440                         boolean hasAlpha = icm.hasAlpha();
 441                         if (srcLUTtransIndex != -1) {
 442                             hasAlpha = true;
 443                         }
 444                         int nbits = icm.getPixelSize();
 445                         icm = new IndexColorModel(nbits,
 446                                                   numSrcLUT, srcLUT,
 447                                                   0, hasAlpha,
 448                                                   srcLUTtransIndex,
 449                                                   (nbits > 8
 450                                                    ? DataBuffer.TYPE_USHORT
 451                                                    : DataBuffer.TYPE_BYTE));
 452                         cmodel = icm;
 453                         bimage = createImage(icm, bct, false, null);
 454                     }
 455                     return;
 456                 }
 457             }
 458 
 459             if (isDefaultBI) {
 460                 int pixel;
 461                 IntegerComponentRaster iraster =
 462                                           (IntegerComponentRaster) biRaster;
 463                 if (srcLUT != null && model instanceof IndexColorModel) {
 464                     if (model != srcModel) {
 465                         // Fill in the new lut
 466                         ((IndexColorModel)model).getRGBs(srcLUT);
 467                         srcModel = model;
 468                     }
 469 
 470                     if (s_useNative) {
 471                         // Note that setICMpixels modifies the raster directly
 472                         // so we must mark it as changed afterwards
 473                         if (setICMpixels(x, y, w, h, srcLUT, pix, off, scansize,
 474                                      iraster))
 475                         {
 476                             iraster.markDirty();
 477                         } else {
 478                             abort();
 479                             return;
 480                         }
 481                     }
 482                     else {
 483                         int[] storage = new int[w*h];
 484                         int soff = 0;
 485                         // It is an IndexColorModel
 486                         for (int yoff=0; yoff < h; yoff++,
 487                                  lineOff += scansize) {
 488                             poff = lineOff;
 489                             for (int i=0; i < w; i++) {
 490                                 storage[soff++] = srcLUT[pix[poff++]&0xff];
 491                             }
 492                         }
 493                         iraster.setDataElements(x, y, w, h, storage);
 494                     }
 495                 }
 496                 else {
 497                     int[] storage = new int[w];
 498                     for (int yoff=y; yoff < y+h; yoff++, lineOff += scansize) {
 499                         poff = lineOff;
 500                         for (int i=0; i < w; i++) {
 501                             storage[i] = model.getRGB(pix[poff++]&0xff);
 502                         }
 503                         iraster.setDataElements(x, yoff, w, 1, storage);
 504                     }
 505                     availinfo |= ImageObserver.SOMEBITS;
 506                 }
 507             }
 508             else if ((cmodel == model) &&
 509                      (biRaster instanceof ByteComponentRaster) &&
 510                      (biRaster.getNumDataElements() == 1)){
 511                 ByteComponentRaster bt = (ByteComponentRaster) biRaster;
 512                 if (off == 0 && scansize == w) {
 513                     bt.putByteData(x, y, w, h, pix);
 514                 }
 515                 else {
 516                     byte[] bpix = new byte[w];
 517                     poff = off;
 518                     for (int yoff=y; yoff < y+h; yoff++) {
 519                         System.arraycopy(pix, poff, bpix, 0, w);
 520                         bt.putByteData(x, yoff, w, 1, bpix);
 521                         poff += scansize;
 522                     }
 523                 }
 524             }
 525             else {
 526                 for (int yoff=y; yoff < y+h; yoff++, lineOff += scansize) {
 527                     poff = lineOff;
 528                     for (int xoff=x; xoff < x+w; xoff++) {
 529                         bimage.setRGB(xoff, yoff,
 530                                       model.getRGB(pix[poff++]&0xff));
 531                     }
 532                 }
 533                 availinfo |= ImageObserver.SOMEBITS;
 534             }
 535         }
 536 
 537         if ((availinfo & ImageObserver.FRAMEBITS) == 0) {
 538             newInfo(image, ImageObserver.SOMEBITS, x, y, w, h);
 539         }
 540     }
 541 
 542 
 543     public void setPixels(int x, int y, int w, int h, ColorModel model,
 544                           int pix[], int off, int scansize)
 545     {
 546         int lineOff=off;
 547         int poff;
 548 
 549         if (src != null) {
 550             src.checkSecurity(null, false);
 551         }
 552 
 553         // REMIND: What if the model doesn't fit in default color model?
 554         synchronized (this) {
 555             if (bimage == null) {
 556                 if (cmodel == null) {
 557                     cmodel = model;
 558                 }
 559                 createBufferedImage();
 560             }
 561 
 562             int[] storage = new int[w];
 563             int yoff;
 564             int pixel;
 565 
 566             if (cmodel instanceof IndexColorModel) {
 567                 // REMIND: Right now we don't support writing back into ICM
 568                 // images.
 569                 convertToRGB();
 570             }
 571 
 572             if ((model == cmodel) &&
 573                 (biRaster instanceof IntegerComponentRaster)) {
 574                 IntegerComponentRaster iraster =
 575                                          (IntegerComponentRaster) biRaster;
 576 
 577                 if (off == 0 && scansize == w) {
 578                     iraster.setDataElements(x, y, w, h, pix);
 579                 }
 580                 else {
 581                     // Need to pack the data
 582                     for (yoff=y; yoff < y+h; yoff++, lineOff+=scansize) {
 583                         System.arraycopy(pix, lineOff, storage, 0, w);
 584                         iraster.setDataElements(x, yoff, w, 1, storage);
 585                     }
 586                 }
 587             }
 588             else {
 589                 if (model.getTransparency() != model.OPAQUE &&
 590                     cmodel.getTransparency() == cmodel.OPAQUE) {
 591                     convertToRGB();
 592                 }
 593 
 594                 if (isDefaultBI) {
 595                     IntegerComponentRaster iraster =
 596                                         (IntegerComponentRaster) biRaster;
 597                     int[] data = iraster.getDataStorage();
 598                     if (cmodel.equals(model)) {
 599                         int sstride = iraster.getScanlineStride();
 600                         int doff = y*sstride + x;
 601                         for (yoff=0; yoff < h; yoff++, lineOff += scansize) {
 602                             System.arraycopy(pix, lineOff, data, doff, w);
 603                             doff += sstride;
 604                         }
 605                         // Note: manual modification of pixels, mark the
 606                         // raster as changed
 607                         iraster.markDirty();
 608                     }
 609                     else {
 610                         for (yoff=y; yoff < y+h; yoff++, lineOff += scansize) {
 611                             poff = lineOff;
 612                             for (int i=0; i < w; i++) {
 613                                 storage[i]=model.getRGB(pix[poff++]);
 614                             }
 615                             iraster.setDataElements(x, yoff, w, 1, storage);
 616                         }
 617                     }
 618 
 619                     availinfo |= ImageObserver.SOMEBITS;
 620                 }
 621                 else {
 622                     Object tmp = null;
 623 
 624                     for (yoff=y; yoff < y+h; yoff++, lineOff += scansize) {
 625                         poff = lineOff;
 626                         for (int xoff=x; xoff < x+w; xoff++) {
 627                             pixel = model.getRGB(pix[poff++]);
 628                             tmp = cmodel.getDataElements(pixel,tmp);
 629                             biRaster.setDataElements(xoff, yoff,tmp);
 630                         }
 631                     }
 632                     availinfo |= ImageObserver.SOMEBITS;
 633                 }
 634             }
 635         }
 636 
 637         // Can't do this here since we might need to transform/clip
 638         // the region
 639         if (((availinfo & ImageObserver.FRAMEBITS) == 0)) {
 640             newInfo(image, ImageObserver.SOMEBITS, x, y, w, h);
 641         }
 642     }
 643 
 644     public BufferedImage getOpaqueRGBImage() {
 645         if (bimage.getType() == BufferedImage.TYPE_INT_ARGB) {
 646             int w = bimage.getWidth();
 647             int h = bimage.getHeight();
 648             int size = w * h;
 649 
 650             // Note that we steal the data array here, but only for reading...
 651             DataBufferInt db = (DataBufferInt)biRaster.getDataBuffer();
 652             int[] pixels = SunWritableRaster.stealData(db, 0);
 653 
 654             for (int i = 0; i < size; i++) {
 655                 if ((pixels[i] >>> 24) != 0xff) {
 656                     return bimage;
 657                 }
 658             }
 659 
 660             ColorModel opModel = new DirectColorModel(24,
 661                                                       0x00ff0000,
 662                                                       0x0000ff00,
 663                                                       0x000000ff);
 664 
 665             int bandmasks[] = {0x00ff0000, 0x0000ff00, 0x000000ff};
 666             WritableRaster opRaster = Raster.createPackedRaster(db, w, h, w,
 667                                                                 bandmasks,
 668                                                                 null);
 669 
 670             try {
 671                 BufferedImage opImage = createImage(opModel, opRaster,
 672                                                     false, null);
 673                 return opImage;
 674             } catch (Exception e) {
 675                 return bimage;
 676             }
 677         }
 678         return bimage;
 679     }
 680 
 681     private boolean consuming = false;
 682 
 683     public void imageComplete(int status) {
 684         if (src != null) {
 685             src.checkSecurity(null, false);
 686         }
 687         boolean done;
 688         int info;
 689         switch (status) {
 690         default:
 691         case ImageConsumer.IMAGEABORTED:
 692             done = true;
 693             info = ImageObserver.ABORT;
 694             break;
 695         case ImageConsumer.IMAGEERROR:
 696             image.addInfo(ImageObserver.ERROR);
 697             done = true;
 698             info = ImageObserver.ERROR;
 699             dispose();
 700             break;
 701         case ImageConsumer.STATICIMAGEDONE:
 702             done = true;
 703             info = ImageObserver.ALLBITS;
 704             break;
 705         case ImageConsumer.SINGLEFRAMEDONE:
 706             done = false;
 707             info = ImageObserver.FRAMEBITS;
 708             break;
 709         }
 710         synchronized (this) {
 711             if (done) {
 712                 image.getSource().removeConsumer(this);
 713                 consuming = false;
 714                 newbits = null;
 715 
 716                 if (bimage != null) {
 717                     bimage = getOpaqueRGBImage();
 718                 }
 719             }
 720             availinfo |= info;
 721             notifyAll();
 722         }
 723 
 724         newInfo(image, info, 0, 0, width, height);
 725 
 726         image.infoDone(status);
 727     }
 728 
 729     /*synchronized*/ void startProduction() {
 730         if (!consuming) {
 731             consuming = true;
 732             image.getSource().startProduction(this);
 733         }
 734     }
 735 
 736     private int numWaiters;
 737 
 738     private synchronized void checkConsumption() {
 739         if (isWatcherListEmpty() && numWaiters == 0 &&
 740             ((availinfo & ImageObserver.ALLBITS) == 0))
 741         {
 742             dispose();
 743         }
 744     }
 745 
 746     public synchronized void notifyWatcherListEmpty() {
 747         checkConsumption();
 748     }
 749 
 750     private synchronized void decrementWaiters() {
 751         --numWaiters;
 752         checkConsumption();
 753     }
 754 
 755     public boolean prepare(ImageObserver iw) {
 756         if (src != null) {
 757             src.checkSecurity(null, false);
 758         }
 759         if ((availinfo & ImageObserver.ERROR) != 0) {
 760             if (iw != null) {
 761                 iw.imageUpdate(image, ImageObserver.ERROR|ImageObserver.ABORT,
 762                                -1, -1, -1, -1);
 763             }
 764             return false;
 765         }
 766         boolean done = ((availinfo & ImageObserver.ALLBITS) != 0);
 767         if (!done) {
 768             addWatcher(iw);
 769             startProduction();
 770             // Some producers deliver image data synchronously
 771             done = ((availinfo & ImageObserver.ALLBITS) != 0);
 772         }
 773         return done;
 774     }
 775 
 776     public int check(ImageObserver iw) {
 777 
 778         if (src != null) {
 779             src.checkSecurity(null, false);
 780         }
 781         if ((availinfo & (ImageObserver.ERROR | ImageObserver.ALLBITS)) == 0) {
 782             addWatcher(iw);
 783         }
 784 
 785         return availinfo;
 786     }
 787 
 788     public boolean drawToBufImage(Graphics g, ToolkitImage img,
 789                                   int x, int y, Color bg,
 790                                   ImageObserver iw) {
 791 
 792         if (src != null) {
 793             src.checkSecurity(null, false);
 794         }
 795         if ((availinfo & ImageObserver.ERROR) != 0) {
 796             if (iw != null) {
 797                 iw.imageUpdate(image, ImageObserver.ERROR|ImageObserver.ABORT,
 798                                -1, -1, -1, -1);
 799             }
 800             return false;
 801         }
 802         boolean done  = ((availinfo & ImageObserver.ALLBITS) != 0);
 803         boolean abort = ((availinfo & ImageObserver.ABORT) != 0);
 804 
 805         if (!done && !abort) {
 806             addWatcher(iw);
 807             startProduction();
 808             // Some producers deliver image data synchronously
 809             done = ((availinfo & ImageObserver.ALLBITS) != 0);
 810         }
 811 
 812         if (done || (0 != (availinfo & ImageObserver.FRAMEBITS))) {
 813             g.drawImage (bimage, x, y, bg, null);
 814         }
 815 
 816         return done;
 817     }
 818 
 819     public boolean drawToBufImage(Graphics g, ToolkitImage img,
 820                                   int x, int y, int w, int h,
 821                                   Color bg, ImageObserver iw) {
 822 
 823         if (src != null) {
 824             src.checkSecurity(null, false);
 825         }
 826         if ((availinfo & ImageObserver.ERROR) != 0) {
 827             if (iw != null) {
 828                 iw.imageUpdate(image, ImageObserver.ERROR|ImageObserver.ABORT,
 829                                -1, -1, -1, -1);
 830             }
 831             return false;
 832         }
 833 
 834         boolean done  = ((availinfo & ImageObserver.ALLBITS) != 0);
 835         boolean abort = ((availinfo & ImageObserver.ABORT) != 0);
 836 
 837         if (!done && !abort) {
 838             addWatcher(iw);
 839             startProduction();
 840             // Some producers deliver image data synchronously
 841             done = ((availinfo & ImageObserver.ALLBITS) != 0);
 842         }
 843 
 844         if (done || (0 != (availinfo & ImageObserver.FRAMEBITS))) {
 845             g.drawImage (bimage, x, y, w, h, bg, null);
 846         }
 847 
 848         return done;
 849     }
 850 
 851     public boolean drawToBufImage(Graphics g, ToolkitImage img,
 852                                   int dx1, int dy1, int dx2, int dy2,
 853                                   int sx1, int sy1, int sx2, int sy2,
 854                                   Color bg, ImageObserver iw) {
 855 
 856         if (src != null) {
 857             src.checkSecurity(null, false);
 858         }
 859         if ((availinfo & ImageObserver.ERROR) != 0) {
 860             if (iw != null) {
 861                 iw.imageUpdate(image, ImageObserver.ERROR|ImageObserver.ABORT,
 862                                -1, -1, -1, -1);
 863             }
 864             return false;
 865         }
 866         boolean done  = ((availinfo & ImageObserver.ALLBITS) != 0);
 867         boolean abort = ((availinfo & ImageObserver.ABORT) != 0);
 868 
 869         if (!done && !abort) {
 870             addWatcher(iw);
 871             startProduction();
 872             // Some producers deliver image data synchronously
 873             done = ((availinfo & ImageObserver.ALLBITS) != 0);
 874         }
 875 
 876         if (done || (0 != (availinfo & ImageObserver.FRAMEBITS))) {
 877             g.drawImage (bimage,
 878                          dx1, dy1, dx2, dy2,
 879                          sx1, sy1, sx2, sy2,
 880                          bg, null);
 881         }
 882 
 883         return done;
 884     }
 885 
 886     public boolean drawToBufImage(Graphics g, ToolkitImage img,
 887                                   AffineTransform xform,
 888                                   ImageObserver iw)
 889     {
 890         Graphics2D g2 = (Graphics2D) g;
 891 
 892         if (src != null) {
 893             src.checkSecurity(null, false);
 894         }
 895         if ((availinfo & ImageObserver.ERROR) != 0) {
 896             if (iw != null) {
 897                 iw.imageUpdate(image, ImageObserver.ERROR|ImageObserver.ABORT,
 898                                -1, -1, -1, -1);
 899             }
 900             return false;
 901         }
 902         boolean done  = ((availinfo & ImageObserver.ALLBITS) != 0);
 903         boolean abort = ((availinfo & ImageObserver.ABORT) != 0);
 904 
 905         if (!done && !abort) {
 906             addWatcher(iw);
 907             startProduction();
 908             // Some producers deliver image data synchronously
 909             done = ((availinfo & ImageObserver.ALLBITS) != 0);
 910         }
 911 
 912         if (done || (0 != (availinfo & ImageObserver.FRAMEBITS))) {
 913             g2.drawImage (bimage, xform, null);
 914         }
 915 
 916         return done;
 917     }
 918 
 919     synchronized void abort() {
 920         image.getSource().removeConsumer(this);
 921         consuming = false;
 922         newbits = null;
 923         bimage = null;
 924         biRaster = null;
 925         cmodel = null;
 926         srcLUT = null;
 927         isDefaultBI = false;
 928         isSameCM = false;
 929 
 930         newInfo(image, ImageObserver.ABORT, -1, -1, -1, -1);
 931         availinfo &= ~(ImageObserver.SOMEBITS
 932                        | ImageObserver.FRAMEBITS
 933                        | ImageObserver.ALLBITS
 934                        | ImageObserver.ERROR);
 935     }
 936 
 937     synchronized void dispose() {
 938         image.getSource().removeConsumer(this);
 939         consuming = false;
 940         newbits = null;
 941         availinfo &= ~(ImageObserver.SOMEBITS
 942                        | ImageObserver.FRAMEBITS
 943                        | ImageObserver.ALLBITS);
 944     }
 945 
 946     public void setAccelerationPriority(float priority) {
 947         if (bimage != null) {
 948             bimage.setAccelerationPriority(priority);
 949         }
 950     }
 951 }