1 /*
   2  * Copyright (c) 1995, 2007, 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 void setICMpixels(int x, int y, int w, int h, int[] lut,
 337                                     byte[] pix, int off, int scansize,
 338                                     IntegerComponentRaster ict);
 339     private native int 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)) == 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                         setICMpixels(x, y, w, h, srcLUT, pix, off, scansize,
 474                                      iraster);
 475                         iraster.markDirty();
 476                     }
 477                     else {
 478                         int[] storage = new int[w*h];
 479                         int soff = 0;
 480                         // It is an IndexColorModel
 481                         for (int yoff=0; yoff < h; yoff++,
 482                                  lineOff += scansize) {
 483                             poff = lineOff;
 484                             for (int i=0; i < w; i++) {
 485                                 storage[soff++] = srcLUT[pix[poff++]&0xff];
 486                             }
 487                         }
 488                         iraster.setDataElements(x, y, w, h, storage);
 489                     }
 490                 }
 491                 else {
 492                     int[] storage = new int[w];
 493                     for (int yoff=y; yoff < y+h; yoff++, lineOff += scansize) {
 494                         poff = lineOff;
 495                         for (int i=0; i < w; i++) {
 496                             storage[i] = model.getRGB(pix[poff++]&0xff);
 497                         }
 498                         iraster.setDataElements(x, yoff, w, 1, storage);
 499                     }
 500                     availinfo |= ImageObserver.SOMEBITS;
 501                 }
 502             }
 503             else if ((cmodel == model) &&
 504                      (biRaster instanceof ByteComponentRaster) &&
 505                      (biRaster.getNumDataElements() == 1)){
 506                 ByteComponentRaster bt = (ByteComponentRaster) biRaster;
 507                 if (off == 0 && scansize == w) {
 508                     bt.putByteData(x, y, w, h, pix);
 509                 }
 510                 else {
 511                     byte[] bpix = new byte[w];
 512                     poff = off;
 513                     for (int yoff=y; yoff < y+h; yoff++) {
 514                         System.arraycopy(pix, poff, bpix, 0, w);
 515                         bt.putByteData(x, yoff, w, 1, bpix);
 516                         poff += scansize;
 517                     }
 518                 }
 519             }
 520             else {
 521                 for (int yoff=y; yoff < y+h; yoff++, lineOff += scansize) {
 522                     poff = lineOff;
 523                     for (int xoff=x; xoff < x+w; xoff++) {
 524                         bimage.setRGB(xoff, yoff,
 525                                       model.getRGB(pix[poff++]&0xff));
 526                     }
 527                 }
 528                 availinfo |= ImageObserver.SOMEBITS;
 529             }
 530         }
 531 
 532         if ((availinfo & ImageObserver.FRAMEBITS) == 0) {
 533             newInfo(image, ImageObserver.SOMEBITS, x, y, w, h);
 534         }
 535     }
 536 
 537 
 538     public void setPixels(int x, int y, int w, int h, ColorModel model,
 539                           int pix[], int off, int scansize)
 540     {
 541         int lineOff=off;
 542         int poff;
 543 
 544         if (src != null) {
 545             src.checkSecurity(null, false);
 546         }
 547 
 548         // REMIND: What if the model doesn't fit in default color model?
 549         synchronized (this) {
 550             if (bimage == null) {
 551                 if (cmodel == null) {
 552                     cmodel = model;
 553                 }
 554                 createBufferedImage();
 555             }
 556 
 557             int[] storage = new int[w];
 558             int yoff;
 559             int pixel;
 560 
 561             if (cmodel instanceof IndexColorModel) {
 562                 // REMIND: Right now we don't support writing back into ICM
 563                 // images.
 564                 convertToRGB();
 565             }
 566 
 567             if ((model == cmodel) &&
 568                 (biRaster instanceof IntegerComponentRaster)) {
 569                 IntegerComponentRaster iraster =
 570                                          (IntegerComponentRaster) biRaster;
 571 
 572                 if (off == 0 && scansize == w) {
 573                     iraster.setDataElements(x, y, w, h, pix);
 574                 }
 575                 else {
 576                     // Need to pack the data
 577                     for (yoff=y; yoff < y+h; yoff++, lineOff+=scansize) {
 578                         System.arraycopy(pix, lineOff, storage, 0, w);
 579                         iraster.setDataElements(x, yoff, w, 1, storage);
 580                     }
 581                 }
 582             }
 583             else {
 584                 if (model.getTransparency() != model.OPAQUE &&
 585                     cmodel.getTransparency() == cmodel.OPAQUE) {
 586                     convertToRGB();
 587                 }
 588 
 589                 if (isDefaultBI) {
 590                     IntegerComponentRaster iraster =
 591                                         (IntegerComponentRaster) biRaster;
 592                     int[] data = iraster.getDataStorage();
 593                     if (cmodel.equals(model)) {
 594                         int sstride = iraster.getScanlineStride();
 595                         int doff = y*sstride + x;
 596                         for (yoff=0; yoff < h; yoff++, lineOff += scansize) {
 597                             System.arraycopy(pix, lineOff, data, doff, w);
 598                             doff += sstride;
 599                         }
 600                         // Note: manual modification of pixels, mark the
 601                         // raster as changed
 602                         iraster.markDirty();
 603                     }
 604                     else {
 605                         for (yoff=y; yoff < y+h; yoff++, lineOff += scansize) {
 606                             poff = lineOff;
 607                             for (int i=0; i < w; i++) {
 608                                 storage[i]=model.getRGB(pix[poff++]);
 609                             }
 610                             iraster.setDataElements(x, yoff, w, 1, storage);
 611                         }
 612                     }
 613 
 614                     availinfo |= ImageObserver.SOMEBITS;
 615                 }
 616                 else {
 617                     Object tmp = null;
 618 
 619                     for (yoff=y; yoff < y+h; yoff++, lineOff += scansize) {
 620                         poff = lineOff;
 621                         for (int xoff=x; xoff < x+w; xoff++) {
 622                             pixel = model.getRGB(pix[poff++]);
 623                             tmp = cmodel.getDataElements(pixel,tmp);
 624                             biRaster.setDataElements(xoff, yoff,tmp);
 625                         }
 626                     }
 627                     availinfo |= ImageObserver.SOMEBITS;
 628                 }
 629             }
 630         }
 631 
 632         // Can't do this here since we might need to transform/clip
 633         // the region
 634         if (((availinfo & ImageObserver.FRAMEBITS) == 0)) {
 635             newInfo(image, ImageObserver.SOMEBITS, x, y, w, h);
 636         }
 637     }
 638 
 639     public BufferedImage getOpaqueRGBImage() {
 640         if (bimage.getType() == BufferedImage.TYPE_INT_ARGB) {
 641             int w = bimage.getWidth();
 642             int h = bimage.getHeight();
 643             int size = w * h;
 644 
 645             // Note that we steal the data array here, but only for reading...
 646             DataBufferInt db = (DataBufferInt)biRaster.getDataBuffer();
 647             int[] pixels = SunWritableRaster.stealData(db, 0);
 648 
 649             for (int i = 0; i < size; i++) {
 650                 if ((pixels[i] >>> 24) != 0xff) {
 651                     return bimage;
 652                 }
 653             }
 654 
 655             ColorModel opModel = new DirectColorModel(24,
 656                                                       0x00ff0000,
 657                                                       0x0000ff00,
 658                                                       0x000000ff);
 659 
 660             int bandmasks[] = {0x00ff0000, 0x0000ff00, 0x000000ff};
 661             WritableRaster opRaster = Raster.createPackedRaster(db, w, h, w,
 662                                                                 bandmasks,
 663                                                                 null);
 664 
 665             try {
 666                 BufferedImage opImage = createImage(opModel, opRaster,
 667                                                     false, null);
 668                 return opImage;
 669             } catch (Exception e) {
 670                 return bimage;
 671             }
 672         }
 673         return bimage;
 674     }
 675 
 676     private boolean consuming = false;
 677 
 678     public void imageComplete(int status) {
 679         if (src != null) {
 680             src.checkSecurity(null, false);
 681         }
 682         boolean done;
 683         int info;
 684         switch (status) {
 685         default:
 686         case ImageConsumer.IMAGEABORTED:
 687             done = true;
 688             info = ImageObserver.ABORT;
 689             break;
 690         case ImageConsumer.IMAGEERROR:
 691             image.addInfo(ImageObserver.ERROR);
 692             done = true;
 693             info = ImageObserver.ERROR;
 694             dispose();
 695             break;
 696         case ImageConsumer.STATICIMAGEDONE:
 697             done = true;
 698             info = ImageObserver.ALLBITS;
 699             break;
 700         case ImageConsumer.SINGLEFRAMEDONE:
 701             done = false;
 702             info = ImageObserver.FRAMEBITS;
 703             break;
 704         }
 705         synchronized (this) {
 706             if (done) {
 707                 image.getSource().removeConsumer(this);
 708                 consuming = false;
 709                 newbits = null;
 710 
 711                 if (bimage != null) {
 712                     bimage = getOpaqueRGBImage();
 713                 }
 714             }
 715             availinfo |= info;
 716             notifyAll();
 717         }
 718 
 719         newInfo(image, info, 0, 0, width, height);
 720 
 721         image.infoDone(status);
 722     }
 723 
 724     /*synchronized*/ void startProduction() {
 725         if (!consuming) {
 726             consuming = true;
 727             image.getSource().startProduction(this);
 728         }
 729     }
 730 
 731     private int numWaiters;
 732 
 733     private synchronized void checkConsumption() {
 734         if (isWatcherListEmpty() && numWaiters == 0 &&
 735             ((availinfo & ImageObserver.ALLBITS) == 0))
 736         {
 737             dispose();
 738         }
 739     }
 740 
 741     public synchronized void notifyWatcherListEmpty() {
 742         checkConsumption();
 743     }
 744 
 745     private synchronized void decrementWaiters() {
 746         --numWaiters;
 747         checkConsumption();
 748     }
 749 
 750     public boolean prepare(ImageObserver iw) {
 751         if (src != null) {
 752             src.checkSecurity(null, false);
 753         }
 754         if ((availinfo & ImageObserver.ERROR) != 0) {
 755             if (iw != null) {
 756                 iw.imageUpdate(image, ImageObserver.ERROR|ImageObserver.ABORT,
 757                                -1, -1, -1, -1);
 758             }
 759             return false;
 760         }
 761         boolean done = ((availinfo & ImageObserver.ALLBITS) != 0);
 762         if (!done) {
 763             addWatcher(iw);
 764             startProduction();
 765             // Some producers deliver image data synchronously
 766             done = ((availinfo & ImageObserver.ALLBITS) != 0);
 767         }
 768         return done;
 769     }
 770 
 771     public int check(ImageObserver iw) {
 772 
 773         if (src != null) {
 774             src.checkSecurity(null, false);
 775         }
 776         if ((availinfo & (ImageObserver.ERROR | ImageObserver.ALLBITS)) == 0) {
 777             addWatcher(iw);
 778         }
 779 
 780         return availinfo;
 781     }
 782 
 783     public boolean drawToBufImage(Graphics g, ToolkitImage img,
 784                                   int x, int y, Color bg,
 785                                   ImageObserver iw) {
 786 
 787         if (src != null) {
 788             src.checkSecurity(null, false);
 789         }
 790         if ((availinfo & ImageObserver.ERROR) != 0) {
 791             if (iw != null) {
 792                 iw.imageUpdate(image, ImageObserver.ERROR|ImageObserver.ABORT,
 793                                -1, -1, -1, -1);
 794             }
 795             return false;
 796         }
 797         boolean done  = ((availinfo & ImageObserver.ALLBITS) != 0);
 798         boolean abort = ((availinfo & ImageObserver.ABORT) != 0);
 799 
 800         if (!done && !abort) {
 801             addWatcher(iw);
 802             startProduction();
 803             // Some producers deliver image data synchronously
 804             done = ((availinfo & ImageObserver.ALLBITS) != 0);
 805         }
 806 
 807         if (done || (0 != (availinfo & ImageObserver.FRAMEBITS))) {
 808             g.drawImage (bimage, x, y, bg, null);
 809         }
 810 
 811         return done;
 812     }
 813 
 814     public boolean drawToBufImage(Graphics g, ToolkitImage img,
 815                                   int x, int y, int w, int h,
 816                                   Color bg, ImageObserver iw) {
 817 
 818         if (src != null) {
 819             src.checkSecurity(null, false);
 820         }
 821         if ((availinfo & ImageObserver.ERROR) != 0) {
 822             if (iw != null) {
 823                 iw.imageUpdate(image, ImageObserver.ERROR|ImageObserver.ABORT,
 824                                -1, -1, -1, -1);
 825             }
 826             return false;
 827         }
 828 
 829         boolean done  = ((availinfo & ImageObserver.ALLBITS) != 0);
 830         boolean abort = ((availinfo & ImageObserver.ABORT) != 0);
 831 
 832         if (!done && !abort) {
 833             addWatcher(iw);
 834             startProduction();
 835             // Some producers deliver image data synchronously
 836             done = ((availinfo & ImageObserver.ALLBITS) != 0);
 837         }
 838 
 839         if (done || (0 != (availinfo & ImageObserver.FRAMEBITS))) {
 840             g.drawImage (bimage, x, y, w, h, bg, null);
 841         }
 842 
 843         return done;
 844     }
 845 
 846     public boolean drawToBufImage(Graphics g, ToolkitImage img,
 847                                   int dx1, int dy1, int dx2, int dy2,
 848                                   int sx1, int sy1, int sx2, int sy2,
 849                                   Color bg, ImageObserver iw) {
 850 
 851         if (src != null) {
 852             src.checkSecurity(null, false);
 853         }
 854         if ((availinfo & ImageObserver.ERROR) != 0) {
 855             if (iw != null) {
 856                 iw.imageUpdate(image, ImageObserver.ERROR|ImageObserver.ABORT,
 857                                -1, -1, -1, -1);
 858             }
 859             return false;
 860         }
 861         boolean done  = ((availinfo & ImageObserver.ALLBITS) != 0);
 862         boolean abort = ((availinfo & ImageObserver.ABORT) != 0);
 863 
 864         if (!done && !abort) {
 865             addWatcher(iw);
 866             startProduction();
 867             // Some producers deliver image data synchronously
 868             done = ((availinfo & ImageObserver.ALLBITS) != 0);
 869         }
 870 
 871         if (done || (0 != (availinfo & ImageObserver.FRAMEBITS))) {
 872             g.drawImage (bimage,
 873                          dx1, dy1, dx2, dy2,
 874                          sx1, sy1, sx2, sy2,
 875                          bg, null);
 876         }
 877 
 878         return done;
 879     }
 880 
 881     public boolean drawToBufImage(Graphics g, ToolkitImage img,
 882                                   AffineTransform xform,
 883                                   ImageObserver iw)
 884     {
 885         Graphics2D g2 = (Graphics2D) g;
 886 
 887         if (src != null) {
 888             src.checkSecurity(null, false);
 889         }
 890         if ((availinfo & ImageObserver.ERROR) != 0) {
 891             if (iw != null) {
 892                 iw.imageUpdate(image, ImageObserver.ERROR|ImageObserver.ABORT,
 893                                -1, -1, -1, -1);
 894             }
 895             return false;
 896         }
 897         boolean done  = ((availinfo & ImageObserver.ALLBITS) != 0);
 898         boolean abort = ((availinfo & ImageObserver.ABORT) != 0);
 899 
 900         if (!done && !abort) {
 901             addWatcher(iw);
 902             startProduction();
 903             // Some producers deliver image data synchronously
 904             done = ((availinfo & ImageObserver.ALLBITS) != 0);
 905         }
 906 
 907         if (done || (0 != (availinfo & ImageObserver.FRAMEBITS))) {
 908             g2.drawImage (bimage, xform, null);
 909         }
 910 
 911         return done;
 912     }
 913 
 914     synchronized void abort() {
 915         image.getSource().removeConsumer(this);
 916         consuming = false;
 917         newbits = null;
 918         bimage = null;
 919         biRaster = null;
 920         cmodel = null;
 921         srcLUT = null;
 922         isDefaultBI = false;
 923         isSameCM = false;
 924 
 925         newInfo(image, ImageObserver.ABORT, -1, -1, -1, -1);
 926         availinfo &= ~(ImageObserver.SOMEBITS
 927                        | ImageObserver.FRAMEBITS
 928                        | ImageObserver.ALLBITS
 929                        | ImageObserver.ERROR);
 930     }
 931 
 932     synchronized void dispose() {
 933         image.getSource().removeConsumer(this);
 934         consuming = false;
 935         newbits = null;
 936         availinfo &= ~(ImageObserver.SOMEBITS
 937                        | ImageObserver.FRAMEBITS
 938                        | ImageObserver.ALLBITS);
 939     }
 940 
 941     public void setAccelerationPriority(float priority) {
 942         if (bimage != null) {
 943             bimage.setAccelerationPriority(priority);
 944         }
 945     }
 946 }