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 */ clear()116 public native void clear(); 117 118 /** 119 * Returns a copy of the pixel data as a java array. 120 */ getPixels()121 private native int[] getPixels(); 122 123 /** 124 * Sets the pixel data from a java array. 125 */ setPixels(int[] pixels)126 private native void setPixels(int[] pixels); 127 128 /** 129 * Loads an image 130 */ loadImage(String name)131 private native boolean loadImage(String name); 132 133 /** 134 * Loads an image from data. 135 */ loadImageFromData(byte[] data)136 private native boolean loadImageFromData(byte[] data); 137 138 /** 139 * Allocates a QImage 140 */ createImage()141 private native void createImage(); 142 143 /** 144 * Frees the above. 145 */ freeImage()146 private synchronized native void freeImage(); 147 148 /** 149 * Sets the image to scaled copy of src image. hints are rendering hints. 150 */ createScaledImage(QtImage src, int hints)151 private native void createScaledImage(QtImage src, int hints); 152 153 /** 154 * Draws the image optionally composited. 155 */ drawPixels(QtGraphics gc, int bg_red, int bg_green, int bg_blue, int x, int y, boolean composite)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 */ drawPixelsScaled(QtGraphics gc, int bg_red, int bg_green, int bg_blue, int x, int y, int width, int height, boolean composite)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 */ drawPixelsTransformed(QtGraphics gc, QMatrix transform)171 private native void drawPixelsTransformed (QtGraphics gc, QMatrix transform); 172 173 /** 174 * Draws the image scaled flipped and optionally composited. 175 */ drawPixelsScaledFlipped(QtGraphics gc, int bg_red, int bg_green, int bg_blue, boolean flipX, boolean flipY, int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY, int dstWidth, int dstHeight, boolean composite)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 */ QtImage(ImageProducer producer)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 */ QtImage(URL url)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 */ QtImage(String filename)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 */ QtImage(byte[] data)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 */ QtImage(int width, int height)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 */ QtImage(QtImage src, int width, int height, int hints)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 */ setImage(int width, int height, int[] pixels, Hashtable properties)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 getWidth(ImageObserver observer)339 public int getWidth (ImageObserver observer) 340 { 341 if (addObserver(observer)) 342 return -1; 343 344 return width; 345 } 346 getHeight(ImageObserver observer)347 public int getHeight (ImageObserver observer) 348 { 349 if (addObserver(observer)) 350 return -1; 351 352 return height; 353 } 354 getProperty(String name, ImageObserver observer)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 */ getSource()367 public ImageProducer getSource () 368 { 369 if (!isLoaded) 370 return null; 371 return new MemoryImageSource(width, height, nativeModel, getPixels(), 372 0, width); 373 } 374 putPainter(QtImageGraphics g)375 void putPainter(QtImageGraphics g) 376 { 377 if( painters == null ) 378 painters = new WeakHashMap(); 379 painters.put( g, "dummy" ); 380 } 381 removePainter(QtImageGraphics g)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 */ getGraphics()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 */ getDirectGraphics(QtComponentPeer peer)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 */ getScaledInstance(int width, int height, int hints)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 */ flush()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 finalize()444 public void finalize() 445 { 446 dispose(); 447 } 448 dispose()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 */ checkImage(ImageObserver observer)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 */ drawImage(QtGraphics g, QMatrix matrix, ImageObserver observer)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 */ drawImage(QtGraphics g, int x, int y, Color bgcolor, ImageObserver observer)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 */ drawImage(QtGraphics g, int x, int y, int width, int height, Color bgcolor, ImageObserver observer)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 */ drawImage(QtGraphics g, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer)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 copyArea(int x, int y, int width, int height, int dx, int dy)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 */ deliver()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 */ addObserver(ImageObserver observer)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 toString()636 public String toString() 637 { 638 return "QtImage [isLoaded="+isLoaded+", width="+width+", height="+height 639 +"]"; 640 } 641 } 642