View Javadoc
1   /*
2    * Copyright (c) 1995, 2013, 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 }