001/* 002 * $Id: GraphicsUtilities.java 4193 2012-06-27 19:42:05Z 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 035package org.jdesktop.swingx.util; 036 037import java.awt.AlphaComposite; 038import java.awt.Color; 039import java.awt.Graphics; 040import java.awt.Graphics2D; 041import java.awt.GraphicsConfiguration; 042import java.awt.GraphicsEnvironment; 043import java.awt.Image; 044import java.awt.Insets; 045import java.awt.RenderingHints; 046import java.awt.Transparency; 047import java.awt.image.BufferedImage; 048import java.awt.image.ColorModel; 049import java.awt.image.Raster; 050import java.awt.image.WritableRaster; 051import java.io.IOException; 052import java.io.InputStream; 053import java.net.URL; 054 055import javax.imageio.ImageIO; 056import javax.swing.JComponent; 057 058/** 059 * <p><code>GraphicsUtilities</code> contains a set of tools to perform 060 * common graphics operations easily. These operations are divided into 061 * several themes, listed below.</p> 062 * 063 * <h2>Compatible Images</h2> 064 * 065 * <p>Compatible images can, and should, be used to increase drawing 066 * performance. This class provides a number of methods to load compatible 067 * images directly from files or to convert existing images to compatibles 068 * images.</p> 069 * 070 * <h2>Creating Thumbnails</h2> 071 * 072 * <p>This class provides a number of methods to easily scale down images. 073 * Some of these methods offer a trade-off between speed and result quality and 074 * should be used all the time. They also offer the advantage of producing 075 * compatible images, thus automatically resulting into better runtime 076 * performance.</p> 077 * 078 * <p>All these methods are both faster than 079 * {@link java.awt.Image#getScaledInstance(int, int, int)} and produce 080 * better-looking results than the various <code>drawImage()</code> methods 081 * in {@link java.awt.Graphics}, which can be used for image scaling.</p> 082 * 083 * <h2>Image Manipulation</h2> 084 * 085 * <p>This class provides two methods to get and set pixels in a buffered image. 086 * These methods try to avoid unmanaging the image in order to keep good 087 * performance.</p> 088 * 089 * @author Romain Guy <romain.guy@mac.com> 090 * @author rbair 091 * @author Karl Schaefer 092 */ 093@SuppressWarnings("nls") 094public class GraphicsUtilities { 095 private GraphicsUtilities() { 096 } 097 098 // Returns the graphics configuration for the primary screen 099 private static GraphicsConfiguration getGraphicsConfiguration() { 100 return GraphicsEnvironment.getLocalGraphicsEnvironment(). 101 getDefaultScreenDevice().getDefaultConfiguration(); 102 } 103 104 private static boolean isHeadless() { 105 return GraphicsEnvironment.isHeadless(); 106 } 107 108 /** 109 * Converts the specified image into a compatible buffered image. 110 * 111 * @param img 112 * the image to convert 113 * @return a compatible buffered image of the input 114 */ 115 public static BufferedImage convertToBufferedImage(Image img) { 116 BufferedImage buff = createCompatibleTranslucentImage( 117 img.getWidth(null), img.getHeight(null)); 118 Graphics2D g2 = buff.createGraphics(); 119 120 try { 121 g2.drawImage(img, 0, 0, null); 122 } finally { 123 g2.dispose(); 124 } 125 126 return buff; 127 } 128 129 /** 130 * <p>Returns a new <code>BufferedImage</code> using the same color model 131 * as the image passed as a parameter. The returned image is only compatible 132 * with the image passed as a parameter. This does not mean the returned 133 * image is compatible with the hardware.</p> 134 * 135 * @param image the reference image from which the color model of the new 136 * image is obtained 137 * @return a new <code>BufferedImage</code>, compatible with the color model 138 * of <code>image</code> 139 */ 140 public static BufferedImage createColorModelCompatibleImage(BufferedImage image) { 141 ColorModel cm = image.getColorModel(); 142 return new BufferedImage(cm, 143 cm.createCompatibleWritableRaster(image.getWidth(), 144 image.getHeight()), 145 cm.isAlphaPremultiplied(), null); 146 } 147 148 /** 149 * <p>Returns a new compatible image with the same width, height and 150 * transparency as the image specified as a parameter. That is, the 151 * returned BufferedImage will be compatible with the graphics hardware. 152 * If this method is called in a headless environment, then 153 * the returned BufferedImage will be compatible with the source 154 * image.</p> 155 * 156 * @see java.awt.Transparency 157 * @see #createCompatibleImage(int, int) 158 * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int) 159 * @see #createCompatibleTranslucentImage(int, int) 160 * @see #loadCompatibleImage(java.net.URL) 161 * @see #toCompatibleImage(java.awt.image.BufferedImage) 162 * @param image the reference image from which the dimension and the 163 * transparency of the new image are obtained 164 * @return a new compatible <code>BufferedImage</code> with the same 165 * dimension and transparency as <code>image</code> 166 */ 167 public static BufferedImage createCompatibleImage(BufferedImage image) { 168 return createCompatibleImage(image, image.getWidth(), image.getHeight()); 169 } 170 171 /** 172 * <p>Returns a new compatible image of the specified width and height, and 173 * the same transparency setting as the image specified as a parameter. 174 * That is, the returned <code>BufferedImage</code> is compatible with 175 * the graphics hardware. If the method is called in a headless 176 * environment, then the returned BufferedImage will be compatible with 177 * the source image.</p> 178 * 179 * @see java.awt.Transparency 180 * @see #createCompatibleImage(java.awt.image.BufferedImage) 181 * @see #createCompatibleImage(int, int) 182 * @see #createCompatibleTranslucentImage(int, int) 183 * @see #loadCompatibleImage(java.net.URL) 184 * @see #toCompatibleImage(java.awt.image.BufferedImage) 185 * @param width the width of the new image 186 * @param height the height of the new image 187 * @param image the reference image from which the transparency of the new 188 * image is obtained 189 * @return a new compatible <code>BufferedImage</code> with the same 190 * transparency as <code>image</code> and the specified dimension 191 */ 192 public static BufferedImage createCompatibleImage(BufferedImage image, 193 int width, int height) { 194 return isHeadless() ? 195 new BufferedImage(width, height, image.getType()) : 196 getGraphicsConfiguration().createCompatibleImage(width, height, 197 image.getTransparency()); 198 } 199 200 /** 201 * <p>Returns a new opaque compatible image of the specified width and 202 * height. That is, the returned <code>BufferedImage</code> is compatible with 203 * the graphics hardware. If the method is called in a headless 204 * environment, then the returned BufferedImage will be compatible with 205 * the source image.</p> 206 * 207 * @see #createCompatibleImage(java.awt.image.BufferedImage) 208 * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int) 209 * @see #createCompatibleTranslucentImage(int, int) 210 * @see #loadCompatibleImage(java.net.URL) 211 * @see #toCompatibleImage(java.awt.image.BufferedImage) 212 * @param width the width of the new image 213 * @param height the height of the new image 214 * @return a new opaque compatible <code>BufferedImage</code> of the 215 * specified width and height 216 */ 217 public static BufferedImage createCompatibleImage(int width, int height) { 218 return isHeadless() ? 219 new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB) : 220 getGraphicsConfiguration().createCompatibleImage(width, height); 221 } 222 223 /** 224 * <p>Returns a new translucent compatible image of the specified width and 225 * height. That is, the returned <code>BufferedImage</code> is compatible with 226 * the graphics hardware. If the method is called in a headless 227 * environment, then the returned BufferedImage will be compatible with 228 * the source image.</p> 229 * 230 * @see #createCompatibleImage(java.awt.image.BufferedImage) 231 * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int) 232 * @see #createCompatibleImage(int, int) 233 * @see #loadCompatibleImage(java.net.URL) 234 * @see #toCompatibleImage(java.awt.image.BufferedImage) 235 * @param width the width of the new image 236 * @param height the height of the new image 237 * @return a new translucent compatible <code>BufferedImage</code> of the 238 * specified width and height 239 */ 240 public static BufferedImage createCompatibleTranslucentImage(int width, 241 int height) { 242 return isHeadless() ? 243 new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) : 244 getGraphicsConfiguration().createCompatibleImage(width, height, 245 Transparency.TRANSLUCENT); 246 } 247 248 /** 249 * <p> 250 * Returns a new compatible image from a stream. The image is loaded from 251 * the specified stream and then turned, if necessary into a compatible 252 * image. 253 * </p> 254 * 255 * @see #createCompatibleImage(java.awt.image.BufferedImage) 256 * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int) 257 * @see #createCompatibleImage(int, int) 258 * @see #createCompatibleTranslucentImage(int, int) 259 * @see #toCompatibleImage(java.awt.image.BufferedImage) 260 * @param in 261 * the stream of the picture to load as a compatible image 262 * @return a new translucent compatible <code>BufferedImage</code> of the 263 * specified width and height 264 * @throws java.io.IOException 265 * if the image cannot be read or loaded 266 */ 267 public static BufferedImage loadCompatibleImage(InputStream in) throws IOException { 268 BufferedImage image = ImageIO.read(in); 269 if(image == null) return null; 270 return toCompatibleImage(image); 271 } 272 273 /** 274 * <p>Returns a new compatible image from a URL. The image is loaded from the 275 * specified location and then turned, if necessary into a compatible 276 * image.</p> 277 * 278 * @see #createCompatibleImage(java.awt.image.BufferedImage) 279 * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int) 280 * @see #createCompatibleImage(int, int) 281 * @see #createCompatibleTranslucentImage(int, int) 282 * @see #toCompatibleImage(java.awt.image.BufferedImage) 283 * @param resource the URL of the picture to load as a compatible image 284 * @return a new translucent compatible <code>BufferedImage</code> of the 285 * specified width and height 286 * @throws java.io.IOException if the image cannot be read or loaded 287 */ 288 public static BufferedImage loadCompatibleImage(URL resource) 289 throws IOException { 290 BufferedImage image = ImageIO.read(resource); 291 return toCompatibleImage(image); 292 } 293 294 /** 295 * <p>Return a new compatible image that contains a copy of the specified 296 * image. This method ensures an image is compatible with the hardware, 297 * and therefore optimized for fast blitting operations.</p> 298 * 299 * <p>If the method is called in a headless environment, then the returned 300 * <code>BufferedImage</code> will be the source image.</p> 301 * 302 * @see #createCompatibleImage(java.awt.image.BufferedImage) 303 * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int) 304 * @see #createCompatibleImage(int, int) 305 * @see #createCompatibleTranslucentImage(int, int) 306 * @see #loadCompatibleImage(java.net.URL) 307 * @param image the image to copy into a new compatible image 308 * @return a new compatible copy, with the 309 * same width and height and transparency and content, of <code>image</code> 310 */ 311 public static BufferedImage toCompatibleImage(BufferedImage image) { 312 if (isHeadless()) { 313 return image; 314 } 315 316 if (image.getColorModel().equals( 317 getGraphicsConfiguration().getColorModel())) { 318 return image; 319 } 320 321 BufferedImage compatibleImage = 322 getGraphicsConfiguration().createCompatibleImage( 323 image.getWidth(), image.getHeight(), 324 image.getTransparency()); 325 Graphics g = compatibleImage.getGraphics(); 326 327 try { 328 g.drawImage(image, 0, 0, null); 329 } finally { 330 g.dispose(); 331 } 332 333 return compatibleImage; 334 } 335 336 /** 337 * <p>Returns a thumbnail of a source image. <code>newSize</code> defines 338 * the length of the longest dimension of the thumbnail. The other 339 * dimension is then computed according to the dimensions ratio of the 340 * original picture.</p> 341 * <p>This method favors speed over quality. When the new size is less than 342 * half the longest dimension of the source image, 343 * {@link #createThumbnail(BufferedImage, int)} or 344 * {@link #createThumbnail(BufferedImage, int, int)} should be used instead 345 * to ensure the quality of the result without sacrificing too much 346 * performance.</p> 347 * 348 * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int) 349 * @see #createThumbnail(java.awt.image.BufferedImage, int) 350 * @see #createThumbnail(java.awt.image.BufferedImage, int, int) 351 * @param image the source image 352 * @param newSize the length of the largest dimension of the thumbnail 353 * @return a new compatible <code>BufferedImage</code> containing a 354 * thumbnail of <code>image</code> 355 * @throws IllegalArgumentException if <code>newSize</code> is larger than 356 * the largest dimension of <code>image</code> or <= 0 357 */ 358 public static BufferedImage createThumbnailFast(BufferedImage image, 359 int newSize) { 360 float ratio; 361 int width = image.getWidth(); 362 int height = image.getHeight(); 363 364 if (width > height) { 365 if (newSize >= width) { 366 throw new IllegalArgumentException("newSize must be lower than" + 367 " the image width"); 368 } else if (newSize <= 0) { 369 throw new IllegalArgumentException("newSize must" + 370 " be greater than 0"); 371 } 372 373 ratio = (float) width / (float) height; 374 width = newSize; 375 height = (int) (newSize / ratio); 376 } else { 377 if (newSize >= height) { 378 throw new IllegalArgumentException("newSize must be lower than" + 379 " the image height"); 380 } else if (newSize <= 0) { 381 throw new IllegalArgumentException("newSize must" + 382 " be greater than 0"); 383 } 384 385 ratio = (float) height / (float) width; 386 height = newSize; 387 width = (int) (newSize / ratio); 388 } 389 390 BufferedImage temp = createCompatibleImage(image, width, height); 391 Graphics2D g2 = temp.createGraphics(); 392 393 try { 394 g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 395 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 396 g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null); 397 } finally { 398 g2.dispose(); 399 } 400 401 return temp; 402 } 403 404 /** 405 * <p>Returns a thumbnail of a source image.</p> 406 * <p>This method favors speed over quality. When the new size is less than 407 * half the longest dimension of the source image, 408 * {@link #createThumbnail(BufferedImage, int)} or 409 * {@link #createThumbnail(BufferedImage, int, int)} should be used instead 410 * to ensure the quality of the result without sacrificing too much 411 * performance.</p> 412 * 413 * @see #createThumbnailFast(java.awt.image.BufferedImage, int) 414 * @see #createThumbnail(java.awt.image.BufferedImage, int) 415 * @see #createThumbnail(java.awt.image.BufferedImage, int, int) 416 * @param image the source image 417 * @param newWidth the width of the thumbnail 418 * @param newHeight the height of the thumbnail 419 * @return a new compatible <code>BufferedImage</code> containing a 420 * thumbnail of <code>image</code> 421 * @throws IllegalArgumentException if <code>newWidth</code> is larger than 422 * the width of <code>image</code> or if code>newHeight</code> is larger 423 * than the height of <code>image</code> or if one of the dimensions 424 * is <= 0 425 */ 426 public static BufferedImage createThumbnailFast(BufferedImage image, 427 int newWidth, int newHeight) { 428 if (newWidth >= image.getWidth() || 429 newHeight >= image.getHeight()) { 430 throw new IllegalArgumentException("newWidth and newHeight cannot" + 431 " be greater than the image" + 432 " dimensions"); 433 } else if (newWidth <= 0 || newHeight <= 0) { 434 throw new IllegalArgumentException("newWidth and newHeight must" + 435 " be greater than 0"); 436 } 437 438 BufferedImage temp = createCompatibleImage(image, newWidth, newHeight); 439 Graphics2D g2 = temp.createGraphics(); 440 441 try { 442 g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 443 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 444 g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null); 445 } finally { 446 g2.dispose(); 447 } 448 449 return temp; 450 } 451 452 /** 453 * <p>Returns a thumbnail of a source image. <code>newSize</code> defines 454 * the length of the longest dimension of the thumbnail. The other 455 * dimension is then computed according to the dimensions ratio of the 456 * original picture.</p> 457 * <p>This method offers a good trade-off between speed and quality. 458 * The result looks better than 459 * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when 460 * the new size is less than half the longest dimension of the source 461 * image, yet the rendering speed is almost similar.</p> 462 * 463 * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int) 464 * @see #createThumbnailFast(java.awt.image.BufferedImage, int) 465 * @see #createThumbnail(java.awt.image.BufferedImage, int, int) 466 * @param image the source image 467 * @param newSize the length of the largest dimension of the thumbnail 468 * @return a new compatible <code>BufferedImage</code> containing a 469 * thumbnail of <code>image</code> 470 * @throws IllegalArgumentException if <code>newSize</code> is larger than 471 * the largest dimension of <code>image</code> or <= 0 472 */ 473 public static BufferedImage createThumbnail(BufferedImage image, 474 int newSize) { 475 int width = image.getWidth(); 476 int height = image.getHeight(); 477 478 boolean isTranslucent = image.getTransparency() != Transparency.OPAQUE; 479 boolean isWidthGreater = width > height; 480 481 if (isWidthGreater) { 482 if (newSize >= width) { 483 throw new IllegalArgumentException("newSize must be lower than" + 484 " the image width"); 485 } 486 } else if (newSize >= height) { 487 throw new IllegalArgumentException("newSize must be lower than" + 488 " the image height"); 489 } 490 491 if (newSize <= 0) { 492 throw new IllegalArgumentException("newSize must" + 493 " be greater than 0"); 494 } 495 496 float ratioWH = (float) width / (float) height; 497 float ratioHW = (float) height / (float) width; 498 499 BufferedImage thumb = image; 500 BufferedImage temp = null; 501 502 Graphics2D g2 = null; 503 504 try { 505 int previousWidth = width; 506 int previousHeight = height; 507 508 do { 509 if (isWidthGreater) { 510 width /= 2; 511 if (width < newSize) { 512 width = newSize; 513 } 514 height = (int) (width / ratioWH); 515 } else { 516 height /= 2; 517 if (height < newSize) { 518 height = newSize; 519 } 520 width = (int) (height / ratioHW); 521 } 522 523 if (temp == null || isTranslucent) { 524 if (g2 != null) { 525 //do not need to wrap with finally 526 //outer finally block will ensure 527 //that resources are properly reclaimed 528 g2.dispose(); 529 } 530 temp = createCompatibleImage(image, width, height); 531 g2 = temp.createGraphics(); 532 g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 533 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 534 } 535 g2.drawImage(thumb, 0, 0, width, height, 536 0, 0, previousWidth, previousHeight, null); 537 538 previousWidth = width; 539 previousHeight = height; 540 541 thumb = temp; 542 } while (newSize != (isWidthGreater ? width : height)); 543 } finally { 544 if (g2 != null) { 545 g2.dispose(); 546 } 547 } 548 549 if (width != thumb.getWidth() || height != thumb.getHeight()) { 550 temp = createCompatibleImage(image, width, height); 551 g2 = temp.createGraphics(); 552 553 try { 554 g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 555 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 556 g2.drawImage(thumb, 0, 0, width, height, 0, 0, width, height, null); 557 } finally { 558 g2.dispose(); 559 } 560 561 thumb = temp; 562 } 563 564 return thumb; 565 } 566 567 /** 568 * <p>Returns a thumbnail of a source image.</p> 569 * <p>This method offers a good trade-off between speed and quality. 570 * The result looks better than 571 * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when 572 * the new size is less than half the longest dimension of the source 573 * image, yet the rendering speed is almost similar.</p> 574 * 575 * @see #createThumbnailFast(java.awt.image.BufferedImage, int) 576 * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int) 577 * @see #createThumbnail(java.awt.image.BufferedImage, int) 578 * @param image the source image 579 * @param newWidth the width of the thumbnail 580 * @param newHeight the height of the thumbnail 581 * @return a new compatible <code>BufferedImage</code> containing a 582 * thumbnail of <code>image</code> 583 * @throws IllegalArgumentException if <code>newWidth</code> is larger than 584 * the width of <code>image</code> or if code>newHeight</code> is larger 585 * than the height of <code>image or if one the dimensions is not > 0</code> 586 */ 587 public static BufferedImage createThumbnail(BufferedImage image, 588 int newWidth, int newHeight) { 589 int width = image.getWidth(); 590 int height = image.getHeight(); 591 592 boolean isTranslucent = image.getTransparency() != Transparency.OPAQUE; 593 594 if (newWidth >= width || newHeight >= height) { 595 throw new IllegalArgumentException("newWidth and newHeight cannot" + 596 " be greater than the image" + 597 " dimensions"); 598 } else if (newWidth <= 0 || newHeight <= 0) { 599 throw new IllegalArgumentException("newWidth and newHeight must" + 600 " be greater than 0"); 601 } 602 603 BufferedImage thumb = image; 604 BufferedImage temp = null; 605 606 Graphics2D g2 = null; 607 608 try { 609 int previousWidth = width; 610 int previousHeight = height; 611 612 do { 613 if (width > newWidth) { 614 width /= 2; 615 if (width < newWidth) { 616 width = newWidth; 617 } 618 } 619 620 if (height > newHeight) { 621 height /= 2; 622 if (height < newHeight) { 623 height = newHeight; 624 } 625 } 626 627 if (temp == null || isTranslucent) { 628 if (g2 != null) { 629 //do not need to wrap with finally 630 //outer finally block will ensure 631 //that resources are properly reclaimed 632 g2.dispose(); 633 } 634 temp = createCompatibleImage(image, width, height); 635 g2 = temp.createGraphics(); 636 g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 637 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 638 } 639 g2.drawImage(thumb, 0, 0, width, height, 640 0, 0, previousWidth, previousHeight, null); 641 642 previousWidth = width; 643 previousHeight = height; 644 645 thumb = temp; 646 } while (width != newWidth || height != newHeight); 647 } finally { 648 if (g2 != null) { 649 g2.dispose(); 650 } 651 } 652 653 if (width != thumb.getWidth() || height != thumb.getHeight()) { 654 temp = createCompatibleImage(image, width, height); 655 g2 = temp.createGraphics(); 656 657 try { 658 g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 659 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 660 g2.drawImage(thumb, 0, 0, width, height, 0, 0, width, height, null); 661 } finally { 662 g2.dispose(); 663 } 664 665 thumb = temp; 666 } 667 668 return thumb; 669 } 670 671 /** 672 * <p>Returns an array of pixels, stored as integers, from a 673 * <code>BufferedImage</code>. The pixels are grabbed from a rectangular 674 * area defined by a location and two dimensions. Calling this method on 675 * an image of type different from <code>BufferedImage.TYPE_INT_ARGB</code> 676 * and <code>BufferedImage.TYPE_INT_RGB</code> will unmanage the image.</p> 677 * 678 * @param img the source image 679 * @param x the x location at which to start grabbing pixels 680 * @param y the y location at which to start grabbing pixels 681 * @param w the width of the rectangle of pixels to grab 682 * @param h the height of the rectangle of pixels to grab 683 * @param pixels a pre-allocated array of pixels of size w*h; can be null 684 * @return <code>pixels</code> if non-null, a new array of integers 685 * otherwise 686 * @throws IllegalArgumentException is <code>pixels</code> is non-null and 687 * of length < w*h 688 */ 689 public static int[] getPixels(BufferedImage img, 690 int x, int y, int w, int h, int[] pixels) { 691 if (w == 0 || h == 0) { 692 return new int[0]; 693 } 694 695 if (pixels == null) { 696 pixels = new int[w * h]; 697 } else if (pixels.length < w * h) { 698 throw new IllegalArgumentException("pixels array must have a length" + 699 " >= w*h"); 700 } 701 702 int imageType = img.getType(); 703 if (imageType == BufferedImage.TYPE_INT_ARGB || 704 imageType == BufferedImage.TYPE_INT_RGB) { 705 Raster raster = img.getRaster(); 706 return (int[]) raster.getDataElements(x, y, w, h, pixels); 707 } 708 709 // Unmanages the image 710 return img.getRGB(x, y, w, h, pixels, 0, w); 711 } 712 713 /** 714 * <p>Writes a rectangular area of pixels in the destination 715 * <code>BufferedImage</code>. Calling this method on 716 * an image of type different from <code>BufferedImage.TYPE_INT_ARGB</code> 717 * and <code>BufferedImage.TYPE_INT_RGB</code> will unmanage the image.</p> 718 * 719 * @param img the destination image 720 * @param x the x location at which to start storing pixels 721 * @param y the y location at which to start storing pixels 722 * @param w the width of the rectangle of pixels to store 723 * @param h the height of the rectangle of pixels to store 724 * @param pixels an array of pixels, stored as integers 725 * @throws IllegalArgumentException is <code>pixels</code> is non-null and 726 * of length < w*h 727 */ 728 public static void setPixels(BufferedImage img, 729 int x, int y, int w, int h, int[] pixels) { 730 if (pixels == null || w == 0 || h == 0) { 731 return; 732 } else if (pixels.length < w * h) { 733 throw new IllegalArgumentException("pixels array must have a length" + 734 " >= w*h"); 735 } 736 737 int imageType = img.getType(); 738 if (imageType == BufferedImage.TYPE_INT_ARGB || 739 imageType == BufferedImage.TYPE_INT_RGB) { 740 WritableRaster raster = img.getRaster(); 741 raster.setDataElements(x, y, w, h, pixels); 742 } else { 743 // Unmanages the image 744 img.setRGB(x, y, w, h, pixels, 0, w); 745 } 746 } 747 748 /** 749 * Clears the data from the image. 750 * 751 * @param img 752 * the image to erase 753 */ 754 public static void clear(Image img) { 755 Graphics g = img.getGraphics(); 756 757 try { 758 if (g instanceof Graphics2D) { 759 ((Graphics2D) g).setComposite(AlphaComposite.Clear); 760 } else { 761 g.setColor(new Color(0, 0, 0, 0)); 762 } 763 764 g.fillRect(0, 0, img.getWidth(null), img.getHeight(null)); 765 } finally { 766 g.dispose(); 767 } 768 } 769 770 /** 771 * Draws an image on top of a component by doing a 3x3 grid stretch of the image 772 * using the specified insets. 773 */ 774 public static void tileStretchPaint(Graphics g, 775 JComponent comp, 776 BufferedImage img, 777 Insets ins) { 778 779 int left = ins.left; 780 int right = ins.right; 781 int top = ins.top; 782 int bottom = ins.bottom; 783 784 // top 785 g.drawImage(img, 786 0,0,left,top, 787 0,0,left,top, 788 null); 789 g.drawImage(img, 790 left, 0, 791 comp.getWidth() - right, top, 792 left, 0, 793 img.getWidth() - right, top, 794 null); 795 g.drawImage(img, 796 comp.getWidth() - right, 0, 797 comp.getWidth(), top, 798 img.getWidth() - right, 0, 799 img.getWidth(), top, 800 null); 801 802 // middle 803 g.drawImage(img, 804 0, top, 805 left, comp.getHeight()-bottom, 806 0, top, 807 left, img.getHeight()-bottom, 808 null); 809 810 g.drawImage(img, 811 left, top, 812 comp.getWidth()-right, comp.getHeight()-bottom, 813 left, top, 814 img.getWidth()-right, img.getHeight()-bottom, 815 null); 816 817 g.drawImage(img, 818 comp.getWidth()-right, top, 819 comp.getWidth(), comp.getHeight()-bottom, 820 img.getWidth()-right, top, 821 img.getWidth(), img.getHeight()-bottom, 822 null); 823 824 // bottom 825 g.drawImage(img, 826 0,comp.getHeight()-bottom, 827 left, comp.getHeight(), 828 0,img.getHeight()-bottom, 829 left,img.getHeight(), 830 null); 831 g.drawImage(img, 832 left, comp.getHeight()-bottom, 833 comp.getWidth()-right, comp.getHeight(), 834 left, img.getHeight()-bottom, 835 img.getWidth()-right, img.getHeight(), 836 null); 837 g.drawImage(img, 838 comp.getWidth()-right, comp.getHeight()-bottom, 839 comp.getWidth(), comp.getHeight(), 840 img.getWidth()-right, img.getHeight()-bottom, 841 img.getWidth(), img.getHeight(), 842 null); 843 } 844}