001 /* 002 * $Id: GraphicsUtilities.java 3289 2009-03-10 14:43:54Z kschaefe $ 003 * 004 * Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy). 005 * 006 * Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle, 007 * Santa Clara, California 95054, U.S.A. All rights reserved. 008 * 009 * Copyright (c) 2006 Romain Guy <romain.guy@mac.com> 010 * All rights reserved. 011 * 012 * Redistribution and use in source and binary forms, with or without 013 * modification, are permitted provided that the following conditions 014 * are met: 015 * 1. Redistributions of source code must retain the above copyright 016 * notice, this list of conditions and the following disclaimer. 017 * 2. Redistributions in binary form must reproduce the above copyright 018 * notice, this list of conditions and the following disclaimer in the 019 * documentation and/or other materials provided with the distribution. 020 * 3. The name of the author may not be used to endorse or promote products 021 * derived from this software without specific prior written permission. 022 * 023 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 024 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 025 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 026 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 027 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 028 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 029 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 030 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 031 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 032 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 033 */ 034 035 package org.jdesktop.swingx.graphics; 036 037 import java.awt.geom.Area; 038 import java.awt.image.BufferedImage; 039 import java.awt.image.ColorModel; 040 import java.awt.image.Raster; 041 import java.awt.image.WritableRaster; 042 import java.awt.GraphicsConfiguration; 043 import java.awt.Image; 044 import java.awt.Shape; 045 import java.awt.Transparency; 046 import java.awt.Graphics; 047 import java.awt.GraphicsEnvironment; 048 import java.awt.Graphics2D; 049 import java.awt.RenderingHints; 050 import java.io.IOException; 051 import java.io.InputStream; 052 import java.net.URL; 053 import javax.imageio.ImageIO; 054 055 /** 056 * <p><code>GraphicsUtilities</code> contains a set of tools to perform 057 * common graphics operations easily. These operations are divided into 058 * several themes, listed below.</p> 059 * 060 * <h2>Compatible Images</h2> 061 * 062 * <p>Compatible images can, and should, be used to increase drawing 063 * performance. This class provides a number of methods to load compatible 064 * images directly from files or to convert existing images to compatibles 065 * images.</p> 066 * 067 * <h2>Creating Thumbnails</h2> 068 * 069 * <p>This class provides a number of methods to easily scale down images. 070 * Some of these methods offer a trade-off between speed and result quality and 071 * shouuld be used all the time. They also offer the advantage of producing 072 * compatible images, thus automatically resulting into better runtime 073 * performance.</p> 074 * 075 * <p>All these methodes are both faster than 076 * {@link java.awt.Image#getScaledInstance(int, int, int)} and produce 077 * better-looking results than the various <code>drawImage()</code> methods 078 * in {@link java.awt.Graphics}, which can be used for image scaling.</p> 079 * <h2>Image Manipulation</h2> 080 * 081 * <p>This class provides two methods to get and set pixels in a buffered image. 082 * These methods try to avoid unmanaging the image in order to keep good 083 * performance.</p> 084 * 085 * @author Romain Guy <romain.guy@mac.com> 086 * @author rbair 087 */ 088 public class GraphicsUtilities { 089 private GraphicsUtilities() { 090 } 091 092 // Returns the graphics configuration for the primary screen 093 private static GraphicsConfiguration getGraphicsConfiguration() { 094 return GraphicsEnvironment.getLocalGraphicsEnvironment(). 095 getDefaultScreenDevice().getDefaultConfiguration(); 096 } 097 098 private static boolean isHeadless() { 099 return GraphicsEnvironment.isHeadless(); 100 } 101 102 /** 103 * Converts the specified image into a compatible buffered image. 104 * 105 * @param img 106 * the image to convert 107 * @return a compatible buffered image of the input 108 */ 109 public static BufferedImage convertToBufferedImage(Image img) { 110 BufferedImage buff = createCompatibleTranslucentImage( 111 img.getWidth(null), img.getHeight(null)); 112 Graphics2D g2 = buff.createGraphics(); 113 114 try { 115 g2.drawImage(img, 0, 0, null); 116 } finally { 117 g2.dispose(); 118 } 119 120 return buff; 121 } 122 123 /** 124 * <p>Returns a new <code>BufferedImage</code> using the same color model 125 * as the image passed as a parameter. The returned image is only compatible 126 * with the image passed as a parameter. This does not mean the returned 127 * image is compatible with the hardware.</p> 128 * 129 * @param image the reference image from which the color model of the new 130 * image is obtained 131 * @return a new <code>BufferedImage</code>, compatible with the color model 132 * of <code>image</code> 133 */ 134 public static BufferedImage createColorModelCompatibleImage(BufferedImage image) { 135 ColorModel cm = image.getColorModel(); 136 return new BufferedImage(cm, 137 cm.createCompatibleWritableRaster(image.getWidth(), 138 image.getHeight()), 139 cm.isAlphaPremultiplied(), null); 140 } 141 142 /** 143 * <p>Returns a new compatible image with the same width, height and 144 * transparency as the image specified as a parameter. That is, the 145 * returned BufferedImage will be compatible with the graphics hardware. 146 * If this method is called in a headless environment, then 147 * the returned BufferedImage will be compatible with the source 148 * image.</p> 149 * 150 * @see java.awt.Transparency 151 * @see #createCompatibleImage(int, int) 152 * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int) 153 * @see #createCompatibleTranslucentImage(int, int) 154 * @see #loadCompatibleImage(java.net.URL) 155 * @see #toCompatibleImage(java.awt.image.BufferedImage) 156 * @param image the reference image from which the dimension and the 157 * transparency of the new image are obtained 158 * @return a new compatible <code>BufferedImage</code> with the same 159 * dimension and transparency as <code>image</code> 160 */ 161 public static BufferedImage createCompatibleImage(BufferedImage image) { 162 return createCompatibleImage(image, image.getWidth(), image.getHeight()); 163 } 164 165 /** 166 * <p>Returns a new compatible image of the specified width and height, and 167 * the same transparency setting as the image specified as a parameter. 168 * That is, the returned <code>BufferedImage</code> is compatible with 169 * the graphics hardware. If the method is called in a headless 170 * environment, then the returned BufferedImage will be compatible with 171 * the source image.</p> 172 * 173 * @see java.awt.Transparency 174 * @see #createCompatibleImage(java.awt.image.BufferedImage) 175 * @see #createCompatibleImage(int, int) 176 * @see #createCompatibleTranslucentImage(int, int) 177 * @see #loadCompatibleImage(java.net.URL) 178 * @see #toCompatibleImage(java.awt.image.BufferedImage) 179 * @param width the width of the new image 180 * @param height the height of the new image 181 * @param image the reference image from which the transparency of the new 182 * image is obtained 183 * @return a new compatible <code>BufferedImage</code> with the same 184 * transparency as <code>image</code> and the specified dimension 185 */ 186 public static BufferedImage createCompatibleImage(BufferedImage image, 187 int width, int height) { 188 return isHeadless() ? 189 new BufferedImage(width, height, image.getType()) : 190 getGraphicsConfiguration().createCompatibleImage(width, height, 191 image.getTransparency()); 192 } 193 194 /** 195 * <p>Returns a new opaque compatible image of the specified width and 196 * height. That is, the returned <code>BufferedImage</code> is compatible with 197 * the graphics hardware. If the method is called in a headless 198 * environment, then the returned BufferedImage will be compatible with 199 * the source image.</p> 200 * 201 * @see #createCompatibleImage(java.awt.image.BufferedImage) 202 * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int) 203 * @see #createCompatibleTranslucentImage(int, int) 204 * @see #loadCompatibleImage(java.net.URL) 205 * @see #toCompatibleImage(java.awt.image.BufferedImage) 206 * @param width the width of the new image 207 * @param height the height of the new image 208 * @return a new opaque compatible <code>BufferedImage</code> of the 209 * specified width and height 210 */ 211 public static BufferedImage createCompatibleImage(int width, int height) { 212 return isHeadless() ? 213 new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB) : 214 getGraphicsConfiguration().createCompatibleImage(width, height); 215 } 216 217 /** 218 * <p>Returns a new translucent compatible image of the specified width and 219 * height. That is, the returned <code>BufferedImage</code> is compatible with 220 * the graphics hardware. If the method is called in a headless 221 * environment, then the returned BufferedImage will be compatible with 222 * the source image.</p> 223 * 224 * @see #createCompatibleImage(java.awt.image.BufferedImage) 225 * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int) 226 * @see #createCompatibleImage(int, int) 227 * @see #loadCompatibleImage(java.net.URL) 228 * @see #toCompatibleImage(java.awt.image.BufferedImage) 229 * @param width the width of the new image 230 * @param height the height of the new image 231 * @return a new translucent compatible <code>BufferedImage</code> of the 232 * specified width and height 233 */ 234 public static BufferedImage createCompatibleTranslucentImage(int width, 235 int height) { 236 return isHeadless() ? 237 new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) : 238 getGraphicsConfiguration().createCompatibleImage(width, height, 239 Transparency.TRANSLUCENT); 240 } 241 242 /** 243 * <p> 244 * Returns a new compatible image from a stream. The image is loaded from 245 * the specified stream and then turned, if necessary into a compatible 246 * image. 247 * </p> 248 * 249 * @see #createCompatibleImage(java.awt.image.BufferedImage) 250 * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int) 251 * @see #createCompatibleImage(int, int) 252 * @see #createCompatibleTranslucentImage(int, int) 253 * @see #toCompatibleImage(java.awt.image.BufferedImage) 254 * @param in 255 * the stream of the picture to load as a compatible image 256 * @return a new translucent compatible <code>BufferedImage</code> of the 257 * specified width and height 258 * @throws java.io.IOException 259 * if the image cannot be read or loaded 260 */ 261 public static BufferedImage loadCompatibleImage(InputStream in) throws IOException { 262 BufferedImage image = ImageIO.read(in); 263 if(image == null) return null; 264 return toCompatibleImage(image); 265 } 266 267 /** 268 * <p>Returns a new compatible image from a URL. The image is loaded from the 269 * specified location and then turned, if necessary into a compatible 270 * image.</p> 271 * 272 * @see #createCompatibleImage(java.awt.image.BufferedImage) 273 * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int) 274 * @see #createCompatibleImage(int, int) 275 * @see #createCompatibleTranslucentImage(int, int) 276 * @see #toCompatibleImage(java.awt.image.BufferedImage) 277 * @param resource the URL of the picture to load as a compatible image 278 * @return a new translucent compatible <code>BufferedImage</code> of the 279 * specified width and height 280 * @throws java.io.IOException if the image cannot be read or loaded 281 */ 282 public static BufferedImage loadCompatibleImage(URL resource) 283 throws IOException { 284 BufferedImage image = ImageIO.read(resource); 285 return toCompatibleImage(image); 286 } 287 288 /** 289 * <p>Return a new compatible image that contains a copy of the specified 290 * image. This method ensures an image is compatible with the hardware, 291 * and therefore optimized for fast blitting operations.</p> 292 * 293 * <p>If the method is called in a headless environment, then the returned 294 * <code>BufferedImage</code> will be the source image.</p> 295 * 296 * @see #createCompatibleImage(java.awt.image.BufferedImage) 297 * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int) 298 * @see #createCompatibleImage(int, int) 299 * @see #createCompatibleTranslucentImage(int, int) 300 * @see #loadCompatibleImage(java.net.URL) 301 * @param image the image to copy into a new compatible image 302 * @return a new compatible copy, with the 303 * same width and height and transparency and content, of <code>image</code> 304 */ 305 public static BufferedImage toCompatibleImage(BufferedImage image) { 306 if (isHeadless()) { 307 return image; 308 } 309 310 if (image.getColorModel().equals( 311 getGraphicsConfiguration().getColorModel())) { 312 return image; 313 } 314 315 BufferedImage compatibleImage = 316 getGraphicsConfiguration().createCompatibleImage( 317 image.getWidth(), image.getHeight(), 318 image.getTransparency()); 319 Graphics g = compatibleImage.getGraphics(); 320 321 try { 322 g.drawImage(image, 0, 0, null); 323 } finally { 324 g.dispose(); 325 } 326 327 return compatibleImage; 328 } 329 330 /** 331 * <p>Returns a thumbnail of a source image. <code>newSize</code> defines 332 * the length of the longest dimension of the thumbnail. The other 333 * dimension is then computed according to the dimensions ratio of the 334 * original picture.</p> 335 * <p>This method favors speed over quality. When the new size is less than 336 * half the longest dimension of the source image, 337 * {@link #createThumbnail(BufferedImage, int)} or 338 * {@link #createThumbnail(BufferedImage, int, int)} should be used instead 339 * to ensure the quality of the result without sacrificing too much 340 * performance.</p> 341 * 342 * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int) 343 * @see #createThumbnail(java.awt.image.BufferedImage, int) 344 * @see #createThumbnail(java.awt.image.BufferedImage, int, int) 345 * @param image the source image 346 * @param newSize the length of the largest dimension of the thumbnail 347 * @return a new compatible <code>BufferedImage</code> containing a 348 * thumbnail of <code>image</code> 349 * @throws IllegalArgumentException if <code>newSize</code> is larger than 350 * the largest dimension of <code>image</code> or <= 0 351 */ 352 public static BufferedImage createThumbnailFast(BufferedImage image, 353 int newSize) { 354 float ratio; 355 int width = image.getWidth(); 356 int height = image.getHeight(); 357 358 if (width > height) { 359 if (newSize >= width) { 360 throw new IllegalArgumentException("newSize must be lower than" + 361 " the image width"); 362 } else if (newSize <= 0) { 363 throw new IllegalArgumentException("newSize must" + 364 " be greater than 0"); 365 } 366 367 ratio = (float) width / (float) height; 368 width = newSize; 369 height = (int) (newSize / ratio); 370 } else { 371 if (newSize >= height) { 372 throw new IllegalArgumentException("newSize must be lower than" + 373 " the image height"); 374 } else if (newSize <= 0) { 375 throw new IllegalArgumentException("newSize must" + 376 " be greater than 0"); 377 } 378 379 ratio = (float) height / (float) width; 380 height = newSize; 381 width = (int) (newSize / ratio); 382 } 383 384 BufferedImage temp = createCompatibleImage(image, width, height); 385 Graphics2D g2 = temp.createGraphics(); 386 387 try { 388 g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 389 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 390 g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null); 391 } finally { 392 g2.dispose(); 393 } 394 395 return temp; 396 } 397 398 /** 399 * <p>Returns a thumbnail of a source image.</p> 400 * <p>This method favors speed over quality. When the new size is less than 401 * half the longest dimension of the source image, 402 * {@link #createThumbnail(BufferedImage, int)} or 403 * {@link #createThumbnail(BufferedImage, int, int)} should be used instead 404 * to ensure the quality of the result without sacrificing too much 405 * performance.</p> 406 * 407 * @see #createThumbnailFast(java.awt.image.BufferedImage, int) 408 * @see #createThumbnail(java.awt.image.BufferedImage, int) 409 * @see #createThumbnail(java.awt.image.BufferedImage, int, int) 410 * @param image the source image 411 * @param newWidth the width of the thumbnail 412 * @param newHeight the height of the thumbnail 413 * @return a new compatible <code>BufferedImage</code> containing a 414 * thumbnail of <code>image</code> 415 * @throws IllegalArgumentException if <code>newWidth</code> is larger than 416 * the width of <code>image</code> or if code>newHeight</code> is larger 417 * than the height of <code>image</code> or if one of the dimensions 418 * is <= 0 419 */ 420 public static BufferedImage createThumbnailFast(BufferedImage image, 421 int newWidth, int newHeight) { 422 if (newWidth >= image.getWidth() || 423 newHeight >= image.getHeight()) { 424 throw new IllegalArgumentException("newWidth and newHeight cannot" + 425 " be greater than the image" + 426 " dimensions"); 427 } else if (newWidth <= 0 || newHeight <= 0) { 428 throw new IllegalArgumentException("newWidth and newHeight must" + 429 " be greater than 0"); 430 } 431 432 BufferedImage temp = createCompatibleImage(image, newWidth, newHeight); 433 Graphics2D g2 = temp.createGraphics(); 434 435 try { 436 g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 437 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 438 g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null); 439 } finally { 440 g2.dispose(); 441 } 442 443 return temp; 444 } 445 446 /** 447 * <p>Returns a thumbnail of a source image. <code>newSize</code> defines 448 * the length of the longest dimension of the thumbnail. The other 449 * dimension is then computed according to the dimensions ratio of the 450 * original picture.</p> 451 * <p>This method offers a good trade-off between speed and quality. 452 * The result looks better than 453 * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when 454 * the new size is less than half the longest dimension of the source 455 * image, yet the rendering speed is almost similar.</p> 456 * 457 * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int) 458 * @see #createThumbnailFast(java.awt.image.BufferedImage, int) 459 * @see #createThumbnail(java.awt.image.BufferedImage, int, int) 460 * @param image the source image 461 * @param newSize the length of the largest dimension of the thumbnail 462 * @return a new compatible <code>BufferedImage</code> containing a 463 * thumbnail of <code>image</code> 464 * @throws IllegalArgumentException if <code>newSize</code> is larger than 465 * the largest dimension of <code>image</code> or <= 0 466 */ 467 public static BufferedImage createThumbnail(BufferedImage image, 468 int newSize) { 469 int width = image.getWidth(); 470 int height = image.getHeight(); 471 472 boolean isTranslucent = image.getTransparency() != Transparency.OPAQUE; 473 boolean isWidthGreater = width > height; 474 475 if (isWidthGreater) { 476 if (newSize >= width) { 477 throw new IllegalArgumentException("newSize must be lower than" + 478 " the image width"); 479 } 480 } else if (newSize >= height) { 481 throw new IllegalArgumentException("newSize must be lower than" + 482 " the image height"); 483 } 484 485 if (newSize <= 0) { 486 throw new IllegalArgumentException("newSize must" + 487 " be greater than 0"); 488 } 489 490 float ratioWH = (float) width / (float) height; 491 float ratioHW = (float) height / (float) width; 492 493 BufferedImage thumb = image; 494 BufferedImage temp = null; 495 496 Graphics2D g2 = null; 497 498 try { 499 int previousWidth = width; 500 int previousHeight = height; 501 502 do { 503 if (isWidthGreater) { 504 width /= 2; 505 if (width < newSize) { 506 width = newSize; 507 } 508 height = (int) (width / ratioWH); 509 } else { 510 height /= 2; 511 if (height < newSize) { 512 height = newSize; 513 } 514 width = (int) (height / ratioHW); 515 } 516 517 if (temp == null || isTranslucent) { 518 if (g2 != null) { 519 //do not need to wrap with finally 520 //outer finally block will ensure 521 //that resources are properly reclaimed 522 g2.dispose(); 523 } 524 temp = createCompatibleImage(image, width, height); 525 g2 = temp.createGraphics(); 526 g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 527 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 528 } 529 g2.drawImage(thumb, 0, 0, width, height, 530 0, 0, previousWidth, previousHeight, null); 531 532 previousWidth = width; 533 previousHeight = height; 534 535 thumb = temp; 536 } while (newSize != (isWidthGreater ? width : height)); 537 } finally { 538 g2.dispose(); 539 } 540 541 if (width != thumb.getWidth() || height != thumb.getHeight()) { 542 temp = createCompatibleImage(image, width, height); 543 g2 = temp.createGraphics(); 544 545 try { 546 g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 547 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 548 g2.drawImage(thumb, 0, 0, width, height, 0, 0, width, height, null); 549 } finally { 550 g2.dispose(); 551 } 552 553 thumb = temp; 554 } 555 556 return thumb; 557 } 558 559 /** 560 * <p>Returns a thumbnail of a source image.</p> 561 * <p>This method offers a good trade-off between speed and quality. 562 * The result looks better than 563 * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when 564 * the new size is less than half the longest dimension of the source 565 * image, yet the rendering speed is almost similar.</p> 566 * 567 * @see #createThumbnailFast(java.awt.image.BufferedImage, int) 568 * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int) 569 * @see #createThumbnail(java.awt.image.BufferedImage, int) 570 * @param image the source image 571 * @param newWidth the width of the thumbnail 572 * @param newHeight the height of the thumbnail 573 * @return a new compatible <code>BufferedImage</code> containing a 574 * thumbnail of <code>image</code> 575 * @throws IllegalArgumentException if <code>newWidth</code> is larger than 576 * the width of <code>image</code> or if code>newHeight</code> is larger 577 * than the height of <code>image or if one the dimensions is not > 0</code> 578 */ 579 public static BufferedImage createThumbnail(BufferedImage image, 580 int newWidth, int newHeight) { 581 int width = image.getWidth(); 582 int height = image.getHeight(); 583 584 boolean isTranslucent = image.getTransparency() != Transparency.OPAQUE; 585 586 if (newWidth >= width || newHeight >= height) { 587 throw new IllegalArgumentException("newWidth and newHeight cannot" + 588 " be greater than the image" + 589 " dimensions"); 590 } else if (newWidth <= 0 || newHeight <= 0) { 591 throw new IllegalArgumentException("newWidth and newHeight must" + 592 " be greater than 0"); 593 } 594 595 BufferedImage thumb = image; 596 BufferedImage temp = null; 597 598 Graphics2D g2 = null; 599 600 try { 601 int previousWidth = width; 602 int previousHeight = height; 603 604 do { 605 if (width > newWidth) { 606 width /= 2; 607 if (width < newWidth) { 608 width = newWidth; 609 } 610 } 611 612 if (height > newHeight) { 613 height /= 2; 614 if (height < newHeight) { 615 height = newHeight; 616 } 617 } 618 619 if (temp == null || isTranslucent) { 620 if (g2 != null) { 621 //do not need to wrap with finally 622 //outer finally block will ensure 623 //that resources are properly reclaimed 624 g2.dispose(); 625 } 626 temp = createCompatibleImage(image, width, height); 627 g2 = temp.createGraphics(); 628 g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 629 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 630 } 631 g2.drawImage(thumb, 0, 0, width, height, 632 0, 0, previousWidth, previousHeight, null); 633 634 previousWidth = width; 635 previousHeight = height; 636 637 thumb = temp; 638 } while (width != newWidth || height != newHeight); 639 } finally { 640 g2.dispose(); 641 } 642 643 if (width != thumb.getWidth() || height != thumb.getHeight()) { 644 temp = createCompatibleImage(image, width, height); 645 g2 = temp.createGraphics(); 646 647 try { 648 g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 649 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 650 g2.drawImage(thumb, 0, 0, width, height, 0, 0, width, height, null); 651 } finally { 652 g2.dispose(); 653 } 654 655 thumb = temp; 656 } 657 658 return thumb; 659 } 660 661 /** 662 * <p>Returns an array of pixels, stored as integers, from a 663 * <code>BufferedImage</code>. The pixels are grabbed from a rectangular 664 * area defined by a location and two dimensions. Calling this method on 665 * an image of type different from <code>BufferedImage.TYPE_INT_ARGB</code> 666 * and <code>BufferedImage.TYPE_INT_RGB</code> will unmanage the image.</p> 667 * 668 * @param img the source image 669 * @param x the x location at which to start grabbing pixels 670 * @param y the y location at which to start grabbing pixels 671 * @param w the width of the rectangle of pixels to grab 672 * @param h the height of the rectangle of pixels to grab 673 * @param pixels a pre-allocated array of pixels of size w*h; can be null 674 * @return <code>pixels</code> if non-null, a new array of integers 675 * otherwise 676 * @throws IllegalArgumentException is <code>pixels</code> is non-null and 677 * of length < w*h 678 */ 679 public static int[] getPixels(BufferedImage img, 680 int x, int y, int w, int h, int[] pixels) { 681 if (w == 0 || h == 0) { 682 return new int[0]; 683 } 684 685 if (pixels == null) { 686 pixels = new int[w * h]; 687 } else if (pixels.length < w * h) { 688 throw new IllegalArgumentException("pixels array must have a length" + 689 " >= w*h"); 690 } 691 692 int imageType = img.getType(); 693 if (imageType == BufferedImage.TYPE_INT_ARGB || 694 imageType == BufferedImage.TYPE_INT_RGB) { 695 Raster raster = img.getRaster(); 696 return (int[]) raster.getDataElements(x, y, w, h, pixels); 697 } 698 699 // Unmanages the image 700 return img.getRGB(x, y, w, h, pixels, 0, w); 701 } 702 703 /** 704 * <p>Writes a rectangular area of pixels in the destination 705 * <code>BufferedImage</code>. Calling this method on 706 * an image of type different from <code>BufferedImage.TYPE_INT_ARGB</code> 707 * and <code>BufferedImage.TYPE_INT_RGB</code> will unmanage the image.</p> 708 * 709 * @param img the destination image 710 * @param x the x location at which to start storing pixels 711 * @param y the y location at which to start storing pixels 712 * @param w the width of the rectangle of pixels to store 713 * @param h the height of the rectangle of pixels to store 714 * @param pixels an array of pixels, stored as integers 715 * @throws IllegalArgumentException is <code>pixels</code> is non-null and 716 * of length < w*h 717 */ 718 public static void setPixels(BufferedImage img, 719 int x, int y, int w, int h, int[] pixels) { 720 if (pixels == null || w == 0 || h == 0) { 721 return; 722 } else if (pixels.length < w * h) { 723 throw new IllegalArgumentException("pixels array must have a length" + 724 " >= w*h"); 725 } 726 727 int imageType = img.getType(); 728 if (imageType == BufferedImage.TYPE_INT_ARGB || 729 imageType == BufferedImage.TYPE_INT_RGB) { 730 WritableRaster raster = img.getRaster(); 731 raster.setDataElements(x, y, w, h, pixels); 732 } else { 733 // Unmanages the image 734 img.setRGB(x, y, w, h, pixels, 0, w); 735 } 736 } 737 738 /** 739 * Sets the clip on a graphics object by merging a supplied clip with the 740 * existing one. The new clip will be an intersection of the old clip and 741 * the supplied clip. The old clip shape will be returned. This is useful 742 * for resetting the old clip after an operation is performed. 743 * 744 * @param g 745 * the graphics object to update 746 * @param clip 747 * a new clipping region to add to the graphics clip. This may 748 * return {@code null} if the current clip is {@code null}. 749 * @return the current clipping region of the supplied graphics object 750 * @throws NullPointerException 751 * if any parameter is {@code null} 752 */ 753 public static Shape mergeClip(Graphics g, Shape clip) { 754 Shape oldClip = g.getClip(); 755 if(oldClip == null) { 756 g.setClip(clip); 757 return null; 758 } 759 Area area = new Area(oldClip); 760 area.intersect(new Area(clip));//new Rectangle(0,0,width,height))); 761 g.setClip(area); 762 return oldClip; 763 } 764 }