Source for gnu.java.awt.peer.qt.QtImage

   1: /* QtImage.java --
   2:    Copyright (C)  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: package gnu.java.awt.peer.qt;
  39: 
  40: import java.awt.Graphics;
  41: import java.awt.Color;
  42: import java.awt.Image;
  43: import java.awt.image.ColorModel;
  44: import java.awt.image.DirectColorModel;
  45: import java.awt.image.MemoryImageSource;
  46: import java.awt.image.ImageObserver;
  47: import java.awt.image.ImageProducer;
  48: import java.io.File;
  49: import java.io.IOException;
  50: import java.io.ByteArrayOutputStream;
  51: import java.io.BufferedInputStream;
  52: import java.net.URL;
  53: import java.util.Hashtable;
  54: import java.util.WeakHashMap;
  55: import java.util.Vector;
  56: 
  57: /**
  58:  * QtImage - wraps a QImage
  59:  *
  60:  */
  61: public class QtImage extends Image
  62: {
  63:   int width = -1, height = -1;
  64: 
  65:   /**
  66:    * Properties.
  67:    */
  68:   Hashtable props;
  69: 
  70:   /**
  71:    * Loaded or not flag, for asynchronous compatibility.
  72:    */
  73:   boolean isLoaded;
  74: 
  75:   /**
  76:    * Pointer to the QImage
  77:    */
  78:   long nativeObject;
  79: 
  80:   /**
  81:    * Observer queue.
  82:    */
  83:   Vector observers;
  84: 
  85:   /**
  86:    * Error flag for loading.
  87:    */
  88:   boolean errorLoading;
  89: 
  90:   /**
  91:    * Original source, if created from an ImageProducer.
  92:    */
  93:   ImageProducer source;
  94: 
  95:   /*
  96:    * The 32-bit AARRGGBB format the  uses.
  97:    */
  98:   static ColorModel nativeModel = new DirectColorModel(32,
  99:                                                        0x00FF0000,
 100:                                                        0x0000FF00,
 101:                                                        0x000000FF,
 102:                                                        0xFF000000);
 103:   /**
 104:    * HashMap of Graphics objects painting on this Image.
 105:    */
 106:   WeakHashMap painters;
 107: 
 108:   /**
 109:    * Flags if this image is to be destroyed.
 110:    */
 111:   boolean killFlag;
 112: 
 113:   /**
 114:    * Clears the image to RGBA 0
 115:    */
 116:   public native void clear();
 117: 
 118:   /**
 119:    * Returns a copy of the pixel data as a java array.
 120:    */
 121:   private native int[] getPixels();
 122: 
 123:   /**
 124:    * Sets the pixel data from a java array.
 125:    */
 126:   private native void setPixels(int[] pixels);
 127: 
 128:   /**
 129:    * Loads an image
 130:    */
 131:   private native boolean loadImage(String name);
 132: 
 133:   /**
 134:    * Loads an image from data.
 135:    */
 136:   private native boolean loadImageFromData(byte[] data);
 137: 
 138:   /**
 139:    * Allocates a QImage
 140:    */
 141:   private native void createImage();
 142: 
 143:   /**
 144:    * Frees the above.
 145:    */
 146:   private synchronized native void freeImage();
 147: 
 148:   /**
 149:    * Sets the image to scaled copy of src image. hints are rendering hints.
 150:    */
 151:   private native void createScaledImage(QtImage src, int hints);
 152: 
 153:   /**
 154:    * Draws the image optionally composited.
 155:    */
 156:   native void drawPixels (QtGraphics gc,
 157:                           int bg_red, int bg_green, int bg_blue,
 158:                           int x, int y,
 159:                           boolean composite);
 160:   /**
 161:    * Draws the image, optionally scaled and composited.
 162:    */
 163:   private native void drawPixelsScaled (QtGraphics gc,
 164:                                         int bg_red, int bg_green, int bg_blue,
 165:                                         int x, int y, int width, int height,
 166:                                         boolean composite);
 167: 
 168:   /**
 169:    * Draws the image transformed.
 170:    */
 171:   private native void drawPixelsTransformed (QtGraphics gc, QMatrix transform);
 172: 
 173:   /**
 174:    * Draws the image scaled flipped and optionally composited.
 175:    */
 176:   native void drawPixelsScaledFlipped (QtGraphics gc,
 177:                                        int bg_red, int bg_green,
 178:                                        int bg_blue,
 179:                                        boolean flipX, boolean flipY,
 180:                                        int srcX, int srcY,
 181:                                        int srcWidth, int srcHeight,
 182:                                        int dstX, int dstY,
 183:                                        int dstWidth, int dstHeight,
 184:                                        boolean composite);
 185: 
 186:   /**
 187:    * Creates the image from an ImageProducer. May result in an error image.
 188:    */
 189:   public QtImage (ImageProducer producer)
 190:   {
 191:     killFlag = false;
 192:     isLoaded = false;
 193:     observers = new Vector();
 194:     source = producer;
 195:     errorLoading = false;
 196:     if( producer != null )
 197:       source.startProduction(new QtImageConsumer(this, source));
 198:   }
 199: 
 200:   /**
 201:    * Creates the image from a URL. May result in an error image.
 202:    */
 203:   public QtImage (URL url)
 204:   {
 205:     killFlag = false;
 206:     isLoaded = false;
 207:     observers = new Vector();
 208:     errorLoading = false;
 209:     if( url == null)
 210:       return;
 211:     ByteArrayOutputStream baos = new ByteArrayOutputStream( 5000 );
 212:     try
 213:       {
 214:         BufferedInputStream bis = new BufferedInputStream(url.openStream());
 215: 
 216:         byte[] buf = new byte[5000];
 217:         int n = 0;
 218: 
 219:         while ( (n = bis.read( buf )) != -1 )
 220:           baos.write(buf, 0, n);
 221:         bis.close();
 222:       }
 223:     catch(IOException e)
 224:       {
 225:         throw new IllegalArgumentException("Couldn't load image.");
 226:       }
 227:     if ( loadImageFromData( baos.toByteArray() ) != true )
 228:       throw new IllegalArgumentException("Couldn't load image.");
 229: 
 230:     isLoaded = true;
 231:     observers = null;
 232:     props = new Hashtable();
 233:   }
 234: 
 235:   /**
 236:    * Constructs a QtImage by loading a given file.
 237:    *
 238:    * @throws IllegalArgumentException if the image could not be loaded.
 239:    */
 240:   public QtImage (String filename)
 241:   {
 242:     killFlag = false;
 243:     File f = new File(filename);
 244:     observers = null;
 245:     props = new Hashtable();
 246:     try
 247:       {
 248:         String fn = f.getCanonicalPath();
 249:         if (loadImage( fn ) != true)
 250:           {
 251:             errorLoading = true;
 252:             isLoaded = false;
 253:             return;
 254:           }
 255:       }
 256:     catch(IOException e)
 257:       {
 258:         errorLoading = true;
 259:         isLoaded = false;
 260:         return;
 261:       }
 262:     errorLoading = false;
 263:     isLoaded = true;
 264:   }
 265: 
 266:   /**
 267:    * Constructs a QtImage from a byte array of an image file.
 268:    *
 269:    * @throws IllegalArgumentException if the image could not be loaded.
 270:    */
 271:   public QtImage (byte[] data)
 272:   {
 273:     if (loadImageFromData(data) != true)
 274:       throw new IllegalArgumentException("Couldn't load image.");
 275: 
 276:     killFlag = false;
 277:     isLoaded = true;
 278:     observers = null;
 279:     errorLoading = false;
 280:     props = new Hashtable();
 281:   }
 282: 
 283:   /**
 284:    * Constructs an empty QtImage.
 285:    */
 286:   public QtImage (int width, int height)
 287:   {
 288:     this.width = width;
 289:     this.height = height;
 290:     props = new Hashtable();
 291:     isLoaded = true;
 292:     killFlag = false;
 293:     observers = null;
 294:     errorLoading = false;
 295:     createImage();
 296:     clear();
 297:   }
 298: 
 299:   /**
 300:    * Constructs a scaled version of the src bitmap, using Qt
 301:    */
 302:   private QtImage (QtImage src, int width, int height, int hints)
 303:   {
 304:     this.width = width;
 305:     this.height = height;
 306:     props = new Hashtable();
 307:     isLoaded = true;
 308:     killFlag = false;
 309:     observers = null;
 310:     errorLoading = false;
 311: 
 312:     createScaledImage(src, hints);
 313:   }
 314: 
 315:   /**
 316:    * Callback from the image consumer.
 317:    */
 318:   public void setImage(int width, int height,
 319:                        int[] pixels, Hashtable properties)
 320:   {
 321:     this.width = width;
 322:     this.height = height;
 323:     props = (properties != null) ? properties : new Hashtable();
 324: 
 325:     if (width <= 0 || height <= 0 || pixels == null)
 326:       {
 327:         errorLoading = true;
 328:         return;
 329:       }
 330: 
 331:     isLoaded = true;
 332:     deliver();
 333:     createImage();
 334:     setPixels(pixels);
 335:   }
 336: 
 337:   // java.awt.Image methods ////////////////////////////////////////////////
 338: 
 339:   public int getWidth (ImageObserver observer)
 340:   {
 341:     if (addObserver(observer))
 342:       return -1;
 343: 
 344:     return width;
 345:   }
 346: 
 347:   public int getHeight (ImageObserver observer)
 348:   {
 349:     if (addObserver(observer))
 350:       return -1;
 351: 
 352:     return height;
 353:   }
 354: 
 355:   public Object getProperty (String name, ImageObserver observer)
 356:   {
 357:     if (addObserver(observer))
 358:       return UndefinedProperty;
 359: 
 360:     Object value = props.get (name);
 361:     return (value == null) ? UndefinedProperty : value;
 362:   }
 363: 
 364:   /**
 365:    * Returns the source of this image.
 366:    */
 367:   public ImageProducer getSource ()
 368:   {
 369:     if (!isLoaded)
 370:       return null;
 371:     return new MemoryImageSource(width, height, nativeModel, getPixels(),
 372:                                  0, width);
 373:   }
 374: 
 375:   void putPainter(QtImageGraphics g)
 376:   {
 377:     if( painters == null )
 378:       painters = new WeakHashMap();
 379:     painters.put( g, "dummy" );
 380:   }
 381: 
 382:   void removePainter(QtImageGraphics g)
 383:   {
 384:     painters.remove( g );
 385:     if( killFlag && painters.isEmpty() )
 386:       freeImage();
 387:   }
 388: 
 389:   /**
 390:    * Creates a Graphics context for this image.
 391:    */
 392:   public Graphics getGraphics ()
 393:   {
 394:     if (!isLoaded || killFlag)
 395:       return null;
 396: 
 397:     return new QtImageGraphics(this);
 398:   }
 399: 
 400:   /**
 401:    * Creates a Graphics context for this image.
 402:    */
 403:   Graphics getDirectGraphics(QtComponentPeer peer)
 404:   {
 405:     if (!isLoaded)
 406:       return null;
 407: 
 408:     return new QtImageDirectGraphics(this, peer);
 409:   }
 410: 
 411:   /**
 412:    * Returns a scaled instance of this image.
 413:    */
 414:   public Image getScaledInstance(int width,
 415:                                  int height,
 416:                                  int hints)
 417:   {
 418:     if (width <= 0 || height <= 0)
 419:       throw new IllegalArgumentException("Width and height of scaled bitmap"+
 420:                                          "must be >= 0");
 421: 
 422:     return new QtImage(this, width, height, hints);
 423:   }
 424: 
 425:   /**
 426:    * If the image is loaded and comes from an ImageProducer,
 427:    * regenerate the image from there.
 428:    *
 429:    * I have no idea if this is ever actually used. Since QtImage can't be
 430:    * instantiated directly, how is the user to know if it was created from
 431:    * an ImageProducer or not?
 432:    */
 433:   public synchronized void flush ()
 434:   {
 435:     if (isLoaded && source != null)
 436:       {
 437:         observers = new Vector();
 438:         isLoaded = false;
 439:         freeImage();
 440:         source.startProduction(new QtImageConsumer(this, source));
 441:       }
 442:   }
 443: 
 444:   public void finalize()
 445:   {
 446:     dispose();
 447:   }
 448: 
 449:   public void dispose()
 450:   {
 451:     if (isLoaded)
 452:       {
 453:         if( painters == null || painters.isEmpty() )
 454:           freeImage();
 455:         else
 456:           killFlag = true; // can't destroy image yet.
 457:         // Do so when all painters are gone.
 458:       }
 459:   }
 460: 
 461:   /**
 462:    * Returns the image status, used by QtToolkit
 463:    */
 464:   public int checkImage (ImageObserver observer)
 465:   {
 466:     if (addObserver(observer))
 467:       {
 468:         if (errorLoading == true)
 469:           return ImageObserver.ERROR;
 470:         else
 471:           return 0;
 472:       }
 473: 
 474:     return ImageObserver.ALLBITS | ImageObserver.WIDTH | ImageObserver.HEIGHT;
 475:   }
 476: 
 477:   // Drawing methods ////////////////////////////////////////////////
 478: 
 479:   /**
 480:    * Draws an image with eventual scaling/transforming.
 481:    */
 482:   public boolean drawImage (QtGraphics g, QMatrix matrix,
 483:                             ImageObserver observer)
 484:   {
 485:     if (addObserver(observer))
 486:       return false;
 487: 
 488:     drawPixelsTransformed (g, matrix);
 489: 
 490:     return true;
 491:   }
 492: 
 493:   /**
 494:    * Draws an image to the QtGraphics context, at (x,y) with optional
 495:    * compositing with a background color.
 496:    */
 497:   public boolean drawImage (QtGraphics g, int x, int y,
 498:                             Color bgcolor, ImageObserver observer)
 499:   {
 500:     if (addObserver(observer))
 501:       return false;
 502: 
 503:     if(bgcolor != null)
 504:       drawPixels(g, bgcolor.getRed (), bgcolor.getGreen (),
 505:                        bgcolor.getBlue (), x, y, true);
 506:     else
 507:       drawPixels(g, 0, 0, 0, x, y, false);
 508: 
 509:     return true;
 510:   }
 511: 
 512:   /**
 513:    * Draws an image to the QtGraphics context, at (x,y) scaled to
 514:    * width and height, with optional compositing with a background color.
 515:    */
 516:   public boolean drawImage (QtGraphics g, int x, int y, int width, int height,
 517:                             Color bgcolor, ImageObserver observer)
 518:   {
 519:     if (addObserver(observer))
 520:       return false;
 521: 
 522:     if(bgcolor != null)
 523:       drawPixelsScaled(g, bgcolor.getRed (), bgcolor.getGreen (),
 524:                        bgcolor.getBlue (), x, y, width, height, true);
 525:     else
 526:       drawPixelsScaled(g, 0, 0, 0, x, y, width, height, false);
 527: 
 528:     return true;
 529:   }
 530: 
 531:   /**
 532:    * Draws an image with eventual scaling/transforming.
 533:    */
 534:   public boolean drawImage (QtGraphics g, int dx1, int dy1, int dx2, int dy2,
 535:                             int sx1, int sy1, int sx2, int sy2,
 536:                             Color bgcolor, ImageObserver observer)
 537:   {
 538:     if (addObserver(observer))
 539:       return false;
 540: 
 541:     boolean flipX = (dx1 > dx2)^(sx1 > sx2);
 542:     boolean flipY = (dy1 > dy2)^(sy1 > sy2);
 543:     int dstWidth = Math.abs (dx2 - dx1);
 544:     int dstHeight = Math.abs (dy2 - dy1);
 545:     int srcWidth = Math.abs (sx2 - sx1);
 546:     int srcHeight = Math.abs (sy2 - sy1);
 547:     int srcX = (sx1 < sx2) ? sx1 : sx2;
 548:     int srcY = (sy1 < sy2) ? sy1 : sy2;
 549:     int dstX = (dx1 < dx2) ? dx1 : dx2;
 550:     int dstY = (dy1 < dy2) ? dy1 : dy2;
 551: 
 552:     // Clipping. This requires the dst to be scaled as well,
 553:     if (srcWidth > width)
 554:       {
 555:         dstWidth = (int)((double)dstWidth*((double)width/(double)srcWidth));
 556:         srcWidth = width - srcX;
 557:       }
 558: 
 559:     if (srcHeight > height)
 560:       {
 561:         dstHeight = (int)((double)dstHeight*((double)height/(double)srcHeight));
 562:         srcHeight = height - srcY;
 563:       }
 564: 
 565:     if (srcWidth + srcX > width)
 566:       {
 567:         dstWidth = (int)((double)dstWidth * (double)(width - srcX)/(double)srcWidth);
 568:         srcWidth = width - srcX;
 569:       }
 570: 
 571:     if (srcHeight + srcY > height)
 572:       {
 573:         dstHeight = (int)((double)dstHeight * (double)(width - srcY)/(double)srcHeight);
 574:         srcHeight = height - srcY;
 575:       }
 576: 
 577:     if ( srcWidth <= 0 || srcHeight <= 0 || dstWidth <= 0 || dstHeight <= 0)
 578:       return true;
 579: 
 580:     if(bgcolor != null)
 581:       drawPixelsScaledFlipped (g, bgcolor.getRed (), bgcolor.getGreen (),
 582:                                bgcolor.getBlue (),
 583:                                flipX, flipY,
 584:                                srcX, srcY,
 585:                                srcWidth, srcHeight,
 586:                                dstX,  dstY,
 587:                                dstWidth, dstHeight,
 588:                                true);
 589:     else
 590:       drawPixelsScaledFlipped (g, 0, 0, 0, flipX, flipY,
 591:                                srcX, srcY, srcWidth, srcHeight,
 592:                                dstX,  dstY, dstWidth, dstHeight,
 593:                                false);
 594:     return true;
 595:   }
 596: 
 597:   public native void copyArea(int x, int y, int width, int height,
 598:                               int dx, int dy);
 599: 
 600:   // Private methods ////////////////////////////////////////////////
 601: 
 602:   /**
 603:    * Delivers notifications to all queued observers.
 604:    */
 605:   private void deliver()
 606:   {
 607:     int flags = ImageObserver.HEIGHT |
 608:       ImageObserver.WIDTH |
 609:       ImageObserver.PROPERTIES |
 610:       ImageObserver.ALLBITS;
 611: 
 612:     if (observers != null)
 613:       for(int i=0; i < observers.size(); i++)
 614:         ((ImageObserver)observers.elementAt(i)).
 615:           imageUpdate(this, flags, 0, 0, width, height);
 616: 
 617:     observers = null;
 618:   }
 619: 
 620:   /**
 621:    * Adds an observer, if we need to.
 622:    * @return true if an observer was added.
 623:    */
 624:   private boolean addObserver(ImageObserver observer)
 625:   {
 626:     if (!isLoaded)
 627:       {
 628:         if(observer != null)
 629:           if (!observers.contains (observer))
 630:             observers.addElement (observer);
 631:         return true;
 632:       }
 633:     return false;
 634:   }
 635: 
 636:   public String toString()
 637:   {
 638:     return "QtImage [isLoaded="+isLoaded+", width="+width+", height="+height
 639:       +"]";
 640:   }
 641: }