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

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