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