Source for gnu.java.awt.peer.gtk.GdkPixbufDecoder

   1: /* GdkPixbufDecoder.java -- Image data decoding object
   2:    Copyright (C) 2003, 2004, 2005, 2006  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package gnu.java.awt.peer.gtk;
  40: 
  41: import java.awt.image.BufferedImage;
  42: import java.awt.image.ColorModel;
  43: import java.awt.image.DirectColorModel;
  44: import java.awt.image.ImageConsumer;
  45: import java.awt.image.Raster;
  46: import java.awt.image.RenderedImage;
  47: import java.io.DataInput;
  48: import java.io.DataOutput;
  49: import java.io.IOException;
  50: import java.io.InputStream;
  51: import java.net.URL;
  52: import java.util.ArrayList;
  53: import java.util.Hashtable;
  54: import java.util.Iterator;
  55: import java.util.Locale;
  56: import java.util.Vector;
  57: 
  58: import javax.imageio.IIOImage;
  59: import javax.imageio.ImageReadParam;
  60: import javax.imageio.ImageReader;
  61: import javax.imageio.ImageTypeSpecifier;
  62: import javax.imageio.ImageWriteParam;
  63: import javax.imageio.ImageWriter;
  64: import javax.imageio.metadata.IIOMetadata;
  65: import javax.imageio.spi.IIORegistry;
  66: import javax.imageio.spi.ImageReaderSpi;
  67: import javax.imageio.spi.ImageWriterSpi;
  68: import javax.imageio.stream.ImageInputStream;
  69: import javax.imageio.stream.ImageOutputStream;
  70: 
  71: import gnu.classpath.Pointer;
  72: 
  73: public class GdkPixbufDecoder extends gnu.java.awt.image.ImageDecoder
  74: {
  75:   static 
  76:   {
  77:     System.loadLibrary("gtkpeer");
  78: 
  79:     initStaticState ();
  80:   }
  81:   
  82:   /**
  83:    * Lock that should be held for all gdkpixbuf operations. We don't use
  84:    * the global gdk_threads_enter/leave functions since gdkpixbuf
  85:    * operations can be done in parallel to drawing and manipulating gtk
  86:    * widgets.
  87:    */
  88:   static Object pixbufLock = new Object();
  89: 
  90:   static native void initStaticState();
  91:   private final int native_state = GtkGenericPeer.getUniqueInteger ();
  92: 
  93:   // initState() has been called, but pumpDone() has not yet been called.
  94:   private boolean needsClose = false;
  95: 
  96:   // the current set of ImageConsumers for this decoder
  97:   Vector curr;
  98: 
  99:   /**
 100:    * The pointer to the native pixbuf loader.
 101:    *
 102:    * This field is manipulated by native code. Don't change or remove
 103:    * without adjusting the native code.
 104:    */
 105:   private Pointer nativeDecoder;
 106: 
 107:   // interface to GdkPixbuf
 108:   // These native functions should be called with the pixbufLock held.
 109:   native void initState ();
 110:   native void pumpBytes (byte[] bytes, int len) throws IOException;
 111:   native void pumpDone () throws IOException;
 112:   native void finish (boolean needsClose);
 113: 
 114:   /**
 115:    * Converts given image to bytes.
 116:    * Will call the GdkPixbufWriter for each chunk.
 117:    */
 118:   static native void streamImage(int[] bytes, String format,
 119:                                  int width, int height,
 120:                                  boolean hasAlpha, GdkPixbufWriter writer);
 121: 
 122:   // gdk-pixbuf provids data in RGBA format
 123:   static final ColorModel cm = new DirectColorModel (32, 0xff000000, 
 124:                                                      0x00ff0000, 
 125:                                                      0x0000ff00, 
 126:                                                      0x000000ff);
 127:   public GdkPixbufDecoder (DataInput datainput)
 128:   {
 129:     super (datainput);
 130:   }
 131: 
 132:   public GdkPixbufDecoder (InputStream in)
 133:   {
 134:     super (in);
 135:   }
 136: 
 137:   public GdkPixbufDecoder (String filename)
 138:   {
 139:     super (filename);
 140:   }
 141:   
 142:   public GdkPixbufDecoder (URL url)
 143:   {
 144:     super (url);
 145:   }
 146: 
 147:   public GdkPixbufDecoder (byte[] imagedata, int imageoffset, int imagelength)
 148:   {
 149:     super (imagedata, imageoffset, imagelength);
 150:   }
 151: 
 152:   // called back by native side: area_prepared_cb
 153:   void areaPrepared (int width, int height)
 154:   {
 155: 
 156:     if (curr == null)
 157:       return;
 158: 
 159:     for (int i = 0; i < curr.size (); i++)
 160:       {
 161:         ImageConsumer ic = (ImageConsumer) curr.elementAt (i);
 162:         ic.setDimensions (width, height);
 163:         ic.setColorModel (cm);
 164:         ic.setHints (ImageConsumer.RANDOMPIXELORDER);
 165:       }
 166:   }
 167:   
 168:   // called back by native side: area_updated_cb
 169:   void areaUpdated (int x, int y, int width, int height, 
 170:                     int pixels[], int scansize)
 171:   {
 172:     if (curr == null)
 173:       return;
 174:     
 175:     for (int i = 0; i < curr.size (); i++)
 176:       {
 177:         ImageConsumer ic = (ImageConsumer) curr.elementAt (i);
 178:         ic.setPixels (x, y, width, height, cm, pixels, 0, scansize);
 179:       }
 180:   }
 181:   
 182:   // called from an async image loader of one sort or another, this method
 183:   // repeatedly reads bytes from the input stream and passes them through a
 184:   // GdkPixbufLoader using the native method pumpBytes. pumpBytes in turn
 185:   // decodes the image data and calls back areaPrepared and areaUpdated on
 186:   // this object, feeding back decoded pixel blocks, which we pass to each
 187:   // of the ImageConsumers in the provided Vector.
 188: 
 189:   public void produce (Vector v, InputStream is) throws IOException
 190:   {
 191:     curr = v;
 192: 
 193:     byte bytes[] = new byte[4096];
 194:     int len = 0;
 195:     synchronized(pixbufLock)
 196:       {
 197:         initState();
 198:       }
 199:     needsClose = true;
 200: 
 201:     // Note: We don't want the pixbufLock while reading from the InputStream.
 202:     while ((len = is.read (bytes)) != -1)
 203:       {
 204:         synchronized(pixbufLock)
 205:           {
 206:             pumpBytes (bytes, len);
 207:           }
 208:       }
 209: 
 210:     synchronized(pixbufLock)
 211:       {
 212:         pumpDone();
 213:       }
 214: 
 215:     needsClose = false;
 216:     
 217:     for (int i = 0; i < curr.size (); i++)
 218:       {
 219:         ImageConsumer ic = (ImageConsumer) curr.elementAt (i);
 220:         ic.imageComplete (ImageConsumer.STATICIMAGEDONE);
 221:       }
 222: 
 223:     curr = null;
 224:   }
 225: 
 226:   public void finalize()
 227:   {
 228:     synchronized(pixbufLock)
 229:       {
 230:         finish(needsClose);
 231:       }
 232:   }
 233: 
 234: 
 235:   public static class ImageFormatSpec
 236:   {
 237:     public String name;
 238:     public boolean writable = false;    
 239:     public ArrayList<String> mimeTypes = new ArrayList<String>();
 240:     public ArrayList<String> extensions = new ArrayList<String>();
 241: 
 242:     public ImageFormatSpec(String name, boolean writable)
 243:     {
 244:       this.name = name;
 245:       this.writable = writable;
 246:     }
 247: 
 248:     public synchronized void addMimeType(String m)
 249:     {
 250:       mimeTypes.add(m);
 251:     }
 252: 
 253:     public synchronized void addExtension(String e)
 254:     {
 255:       extensions.add(e);
 256:     }    
 257:   }
 258: 
 259:   static ArrayList<ImageFormatSpec> imageFormatSpecs;
 260: 
 261:   public static ImageFormatSpec registerFormat(String name, boolean writable) 
 262:   {
 263:     ImageFormatSpec ifs = new ImageFormatSpec(name, writable);
 264:     synchronized(GdkPixbufDecoder.class)
 265:       {
 266:         if (imageFormatSpecs == null)
 267:           imageFormatSpecs = new ArrayList<ImageFormatSpec>();
 268:         imageFormatSpecs.add(ifs);
 269:       }
 270:     return ifs;
 271:   }
 272: 
 273:   static String[] getFormatNames(boolean writable)
 274:   {
 275:     ArrayList<String> names = new ArrayList<String>();
 276:     synchronized (imageFormatSpecs) 
 277:       {
 278:         Iterator<ImageFormatSpec> i = imageFormatSpecs.iterator();
 279:         while (i.hasNext())
 280:           {
 281:             ImageFormatSpec ifs = i.next();
 282:             if (writable && !ifs.writable)
 283:               continue;
 284:             names.add(ifs.name);
 285: 
 286:             /* 
 287:              * In order to make the filtering code work, we need to register
 288:              * this type under every "format name" likely to be used as a synonym.
 289:              * This generally means "all the extensions people might use". 
 290:              */
 291: 
 292:             Iterator<String> j = ifs.extensions.iterator();
 293:             while (j.hasNext())
 294:               names.add(j.next());
 295:           }
 296:       }
 297:     return names.toArray(new String[names.size()]);
 298:   }
 299: 
 300:   static String[] getFormatExtensions(boolean writable)
 301:   {
 302:     ArrayList<String> extensions = new ArrayList<String>();
 303:     synchronized (imageFormatSpecs) 
 304:       {
 305:         Iterator<ImageFormatSpec> i = imageFormatSpecs.iterator();
 306:         while (i.hasNext())
 307:           {
 308:             ImageFormatSpec ifs = i.next();
 309:             if (writable && !ifs.writable)
 310:               continue;
 311:             Iterator<String> j = ifs.extensions.iterator();
 312:             while (j.hasNext())
 313:               extensions.add(j.next());
 314:           }
 315:       }
 316:     return extensions.toArray(new String[extensions.size()]);
 317:   }
 318: 
 319:   static String[] getFormatMimeTypes(boolean writable)
 320:   {
 321:     ArrayList<String> mimeTypes = new ArrayList<String>();
 322:     synchronized (imageFormatSpecs) 
 323:       {
 324:         Iterator<ImageFormatSpec> i = imageFormatSpecs.iterator();
 325:         while (i.hasNext())
 326:           {
 327:             ImageFormatSpec ifs = i.next();
 328:             if (writable && !ifs.writable)
 329:               continue;
 330:             Iterator<String> j = ifs.mimeTypes.iterator();
 331:             while (j.hasNext())
 332:               mimeTypes.add(j.next());
 333:           }
 334:       }
 335:     return mimeTypes.toArray(new String[mimeTypes.size()]);
 336:   }
 337: 
 338:   
 339:   static String findFormatName(Object ext, boolean needWritable)
 340:   {
 341:     if (ext == null)
 342:       return null;
 343: 
 344:     if (!(ext instanceof String))
 345:       throw new IllegalArgumentException("extension is not a string");
 346: 
 347:     String str = (String) ext;
 348: 
 349:     Iterator<ImageFormatSpec> i = imageFormatSpecs.iterator();
 350:     while (i.hasNext())
 351:       {
 352:         ImageFormatSpec ifs = i.next();
 353: 
 354:         if (needWritable && !ifs.writable)
 355:           continue;
 356: 
 357:         if (ifs.name.equals(str))
 358:           return str;
 359: 
 360:         Iterator<String> j = ifs.extensions.iterator(); 
 361:         while (j.hasNext())
 362:           {
 363:             String extension = j.next();
 364:             if (extension.equals(str))
 365:               return ifs.name;
 366:           }
 367: 
 368:         j = ifs.mimeTypes.iterator(); 
 369:         while (j.hasNext())
 370:           {
 371:             String mimeType = j.next();
 372:             if (mimeType.equals(str))
 373:               return ifs.name;
 374:           }
 375:       }      
 376:     throw new IllegalArgumentException("unknown extension '" + str + "'");
 377:   }
 378: 
 379:   private static GdkPixbufReaderSpi readerSpi;
 380:   private static GdkPixbufWriterSpi writerSpi;
 381: 
 382:   public static synchronized GdkPixbufReaderSpi getReaderSpi()
 383:   {
 384:     if (readerSpi == null)
 385:       readerSpi = new GdkPixbufReaderSpi();
 386:     return readerSpi;
 387:   }
 388: 
 389:   public static synchronized GdkPixbufWriterSpi getWriterSpi()
 390:   {
 391:     if (writerSpi == null)
 392:       writerSpi = new GdkPixbufWriterSpi();
 393:     return writerSpi;
 394:   }
 395: 
 396:   public static void registerSpis(IIORegistry reg) 
 397:   {
 398:     reg.registerServiceProvider(getReaderSpi(), ImageReaderSpi.class);
 399:     reg.registerServiceProvider(getWriterSpi(), ImageWriterSpi.class);
 400:   }
 401: 
 402:   public static class GdkPixbufWriterSpi extends ImageWriterSpi
 403:   {
 404:     public GdkPixbufWriterSpi() 
 405:     {      
 406:       super("GdkPixbuf", "2.x",
 407:             GdkPixbufDecoder.getFormatNames(true), 
 408:             GdkPixbufDecoder.getFormatExtensions(true), 
 409:             GdkPixbufDecoder.getFormatMimeTypes(true),
 410:             "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufWriter",
 411:             new Class[] { ImageOutputStream.class },
 412:             new String[] { "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufReaderSpi" },
 413:             false, null, null, null, null,
 414:             false, null, null, null, null);
 415:     }
 416: 
 417:     public boolean canEncodeImage(ImageTypeSpecifier ts)
 418:     {
 419:       return true;
 420:     }
 421: 
 422:     public ImageWriter createWriterInstance(Object ext)
 423:     {
 424:       return new GdkPixbufWriter(this, ext);
 425:     }
 426: 
 427:     public String getDescription(java.util.Locale loc)
 428:     {
 429:       return "GdkPixbuf Writer SPI";
 430:     }
 431: 
 432:   }
 433: 
 434:   public static class GdkPixbufReaderSpi extends ImageReaderSpi
 435:   {
 436:     public GdkPixbufReaderSpi() 
 437:     { 
 438:       super("GdkPixbuf", "2.x",
 439:             GdkPixbufDecoder.getFormatNames(false), 
 440:             GdkPixbufDecoder.getFormatExtensions(false), 
 441:             GdkPixbufDecoder.getFormatMimeTypes(false),
 442:             "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufReader",
 443:             new Class[] { ImageInputStream.class },
 444:             new String[] { "gnu.java.awt.peer.gtk.GdkPixbufDecoder$GdkPixbufWriterSpi" },
 445:             false, null, null, null, null,
 446:             false, null, null, null, null);
 447:     }
 448: 
 449:     public boolean canDecodeInput(Object obj) 
 450:     { 
 451:       return true; 
 452:     }
 453: 
 454:     public ImageReader createReaderInstance(Object ext)
 455:     {
 456:       return new GdkPixbufReader(this, ext);
 457:     }
 458: 
 459:     public String getDescription(Locale loc)
 460:     {
 461:       return "GdkPixbuf Reader SPI";
 462:     }
 463:   }
 464: 
 465:   private static class GdkPixbufWriter
 466:     extends ImageWriter implements Runnable
 467:   {
 468:     String ext;
 469:     public GdkPixbufWriter(GdkPixbufWriterSpi ownerSpi, Object ext)
 470:     {
 471:       super(ownerSpi);
 472:       this.ext = findFormatName(ext, true);
 473:     }
 474: 
 475:     public IIOMetadata convertImageMetadata (IIOMetadata inData,
 476:                                              ImageTypeSpecifier imageType,
 477:                                              ImageWriteParam param)
 478:     {
 479:       return null;
 480:     }
 481: 
 482:     public IIOMetadata convertStreamMetadata (IIOMetadata inData,
 483:                                               ImageWriteParam param)
 484:     {
 485:       return null;
 486:     }
 487: 
 488:     public IIOMetadata getDefaultImageMetadata (ImageTypeSpecifier imageType, 
 489:                                                 ImageWriteParam param)
 490:     {
 491:       return null;
 492:     }
 493: 
 494:     public IIOMetadata getDefaultStreamMetadata (ImageWriteParam param)
 495:     {
 496:       return null;
 497:     }
 498: 
 499:   public void write (IIOMetadata streamMetadata, IIOImage i, ImageWriteParam param)
 500:     throws IOException
 501:     {
 502:       RenderedImage image = i.getRenderedImage();
 503:       Raster ras = image.getData();
 504:       int width = ras.getWidth();
 505:       int height = ras.getHeight();
 506:       ColorModel model = image.getColorModel();
 507:       int[] pixels = CairoGraphics2D.findSimpleIntegerArray (image.getColorModel(), ras);
 508:       
 509:       if (pixels == null)
 510:         {
 511:           BufferedImage img;
 512:           if(model != null && model.hasAlpha())
 513:             img = CairoSurface.getBufferedImage(width, height);
 514:           img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
 515:           int[] pix = new int[4];
 516:           for (int y = 0; y < height; ++y)
 517:             for (int x = 0; x < width; ++x)
 518:               img.setRGB(x, y, model.getRGB(ras.getPixel(x, y, pix)));
 519:           pixels = CairoGraphics2D.findSimpleIntegerArray (img.getColorModel(), 
 520:                                                          img.getRaster());
 521:           model = img.getColorModel();
 522:         }
 523: 
 524:       Thread workerThread = new Thread(this, "GdkPixbufWriter");
 525:       workerThread.start();
 526:       processImageStarted(1);
 527:       synchronized(pixbufLock)
 528:         {
 529:           streamImage(pixels, this.ext, width, height, model.hasAlpha(), 
 530:                       this);
 531:         }
 532:       synchronized(data)
 533:         {
 534:           data.add(DATADONE);
 535:           data.notifyAll();
 536:         }
 537: 
 538:       while (workerThread.isAlive())
 539:         {
 540:           try
 541:             {
 542:               workerThread.join();
 543:             }
 544:           catch (InterruptedException ioe)
 545:             {
 546:               // Ignored.
 547:             }
 548:         }
 549: 
 550:       if (exception != null)
 551:         throw exception;
 552: 
 553:       processImageComplete();
 554:     }    
 555: 
 556:     /**
 557:      * Object marking end of data from native streamImage code.
 558:      */
 559:     private static final Object DATADONE = new Object();
 560: 
 561:     /**
 562:      * Holds the data gotten from the native streamImage code.
 563:      * A worker thread will pull data out.
 564:      * Needs to be synchronized for access.
 565:      * The special object DATADONE is added when all data has been delivered.
 566:      */
 567:     private ArrayList<Object> data = new ArrayList<Object>();
 568: 
 569:     /**
 570:      * Holds any IOException thrown by the run method that needs
 571:      * to be rethrown by the write method.
 572:      */
 573:     private IOException exception;
 574: 
 575:     /** Callback for streamImage native code. **/
 576:     private void write(byte[] bs)
 577:     {
 578:       synchronized(data)
 579:         {
 580:           data.add(bs);
 581:           data.notifyAll();
 582:         }
 583:     }
 584: 
 585:     public void run()
 586:     {
 587:       boolean done = false;
 588:       while (!done)
 589:         {
 590:           synchronized(data)
 591:             {
 592:               while (data.isEmpty())
 593:                 {
 594:                   try
 595:                     {
 596:                       data.wait();
 597:                     }
 598:                   catch (InterruptedException ie)
 599:                     {
 600:                       /* ignore */
 601:                     }
 602:                 }
 603: 
 604:               Object o = data.remove(0);
 605:               if (o == DATADONE)
 606:                 done = true;
 607:               else
 608:                 {
 609:                   DataOutput out = (DataOutput) getOutput();
 610:                   try
 611:                     {
 612:                       out.write((byte[]) o);
 613:                     }
 614:                   catch (IOException ioe)
 615:                     {
 616:                       // We are only interested in the first exception.
 617:                       if (exception == null)
 618:                         exception = ioe;
 619:                     }
 620:                 }
 621:             }
 622:         }
 623:     }
 624:   }
 625: 
 626:   private static class GdkPixbufReader 
 627:     extends ImageReader
 628:     implements ImageConsumer
 629:   {
 630:     // ImageConsumer parts
 631:     GdkPixbufDecoder dec;
 632:     BufferedImage bufferedImage;
 633:     ColorModel defaultModel;
 634:     int width;
 635:     int height;
 636:     String ext;
 637:     
 638:     public GdkPixbufReader(GdkPixbufReaderSpi ownerSpi, Object ext)
 639:     {
 640:       super(ownerSpi);
 641:       this.ext = findFormatName(ext, false);
 642:     }
 643: 
 644:     public GdkPixbufReader(GdkPixbufReaderSpi ownerSpi, Object ext,
 645:                            GdkPixbufDecoder d)
 646:     {
 647:       this(ownerSpi, ext);
 648:       dec = d;
 649:     }
 650: 
 651:     public void setDimensions(int w, int h)
 652:     {
 653:       processImageStarted(1);
 654:       width = w;
 655:       height = h;
 656:     }
 657:     
 658:     public void setProperties(Hashtable props) {}
 659: 
 660:     public void setColorModel(ColorModel model) 
 661:     {
 662:       defaultModel = model;
 663:     }
 664: 
 665:     public void setHints(int flags) {}
 666: 
 667:     public void setPixels(int x, int y, int w, int h, 
 668:                           ColorModel model, byte[] pixels, 
 669:                           int offset, int scansize)
 670:     {
 671:     }      
 672: 
 673:     public void setPixels(int x, int y, int w, int h, 
 674:                           ColorModel model, int[] pixels, 
 675:                           int offset, int scansize)
 676:     {
 677:       if (model == null)
 678:         model = defaultModel;
 679:       
 680:       if (bufferedImage == null)
 681:         {
 682:           if(model != null && model.hasAlpha())
 683:             bufferedImage = new BufferedImage (width, height,
 684:                                                BufferedImage.TYPE_INT_ARGB);
 685:           else
 686:             bufferedImage = new BufferedImage (width, height,
 687:                                                BufferedImage.TYPE_INT_RGB);
 688:         }
 689: 
 690:       int pixels2[];
 691:       if (model != null)
 692:         {
 693:           pixels2 = new int[pixels.length];
 694:           for (int yy = 0; yy < h; yy++)
 695:             for (int xx = 0; xx < w; xx++)
 696:               {
 697:                 int i = yy * scansize + xx;
 698:                 pixels2[i] = model.getRGB (pixels[i]);
 699:               }
 700:         }
 701:       else
 702:         pixels2 = pixels;
 703: 
 704:       bufferedImage.setRGB (x, y, w, h, pixels2, offset, scansize);
 705:       processImageProgress(y / (height == 0 ? 1 : height));
 706:     }
 707: 
 708:     public void imageComplete(int status) 
 709:     {
 710:       processImageComplete();
 711:     }
 712: 
 713:     public BufferedImage getBufferedImage()
 714:     {
 715:       if (bufferedImage == null && dec != null)
 716:         dec.startProduction (this);
 717:       return bufferedImage;
 718:     }
 719: 
 720:     // ImageReader parts
 721: 
 722:     public int getNumImages(boolean allowSearch)
 723:       throws IOException
 724:     {
 725:       return 1;
 726:     }
 727: 
 728:     public IIOMetadata getImageMetadata(int i) 
 729:     {
 730:       return null;
 731:     }
 732: 
 733:     public IIOMetadata getStreamMetadata()
 734:       throws IOException
 735:     {
 736:       return null;
 737:     }
 738: 
 739:     public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex)
 740:       throws IOException
 741:     {
 742:       BufferedImage img = getBufferedImage();
 743:       Vector<ImageTypeSpecifier> vec = new Vector<ImageTypeSpecifier>();
 744:       vec.add(new ImageTypeSpecifier(img));
 745:       return vec.iterator();
 746:     }
 747:     
 748:     public int getHeight(int imageIndex)
 749:       throws IOException
 750:     {
 751:       return getBufferedImage().getHeight();
 752:     }
 753: 
 754:     public int getWidth(int imageIndex)
 755:       throws IOException
 756:     {
 757:       return getBufferedImage().getWidth();
 758:     }
 759: 
 760:     public void setInput(Object input,
 761:                          boolean seekForwardOnly,
 762:                          boolean ignoreMetadata)
 763:     {
 764:       super.setInput(input, seekForwardOnly, ignoreMetadata);
 765:       Object get = getInput();
 766:       if (get instanceof InputStream)
 767:         dec = new GdkPixbufDecoder((InputStream) get);
 768:       else if (get instanceof DataInput)
 769:         dec = new GdkPixbufDecoder((DataInput) get);
 770:       else
 771:         throw new IllegalArgumentException("input object not supported: "
 772:                                            + get);
 773:     }
 774: 
 775:     public BufferedImage read(int imageIndex, ImageReadParam param)
 776:       throws IOException
 777:     {
 778:       return getBufferedImage ();
 779:     }
 780:   }
 781: }