001 /** 002 * Copyright 2011 The Buzz Media, LLC 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package com.thebuzzmedia.imgscalr; 017 018 import java.awt.Graphics2D; 019 import java.awt.Image; 020 import java.awt.RenderingHints; 021 import java.awt.Transparency; 022 import java.awt.geom.AffineTransform; 023 import java.awt.geom.Rectangle2D; 024 import java.awt.image.AreaAveragingScaleFilter; 025 import java.awt.image.BufferedImage; 026 import java.awt.image.BufferedImageOp; 027 import java.awt.image.ColorModel; 028 import java.awt.image.ConvolveOp; 029 import java.awt.image.IndexColorModel; 030 import java.awt.image.Kernel; 031 032 import javax.imageio.ImageIO; 033 034 /** 035 * Class used to implement performant, good-quality and intelligent image 036 * scaling algorithms in native Java 2D. This class utilizes the Java2D 037 * "best practices" for image-scaling, ensuring that images are hardware 038 * accelerated at all times if provided by the platform and host-VM. 039 * <p/> 040 * Hardware acceleration also includes execution of optional caller-supplied 041 * {@link BufferedImageOp}s that are applied to the resultant images before 042 * returning them as well as any optional rotations specified. 043 * <h3>Image Proportions</h3> 044 * All scaling operations implemented by this class maintain the proportion of 045 * the original image. If image-cropping is desired the caller will need to 046 * perform those edits before calling one of the <code>resize</code> methods 047 * provided by this class. 048 * <p/> 049 * In order to maintain the proportionality of the original images, this class 050 * implements the following behavior: 051 * <ol> 052 * <li>If the image is LANDSCAPE-oriented or SQUARE, treat the 053 * <code>targetWidth</code> as the primary dimension and re-calculate the 054 * <code>targetHeight</code> regardless of what is passed in.</li> 055 * <li>If image is PORTRAIT-oriented, treat the <code>targetHeight</code> as the 056 * primary dimension and re-calculate the <code>targetWidth</code> regardless of 057 * what is passed in.</li> 058 * <li>If a {@link Mode} value of {@link Mode#FIT_TO_WIDTH} or 059 * {@link Mode#FIT_TO_HEIGHT} is passed in to the <code>resize</code> method, 060 * the image's orientation is ignored and the scaled image is fit to the 061 * dimension the user specified with the {@link Mode}.</li> 062 * </ol> 063 * Recalculation of the secondary dimensions is extremely cheap and this 064 * approach provides users with better expected-behavior from the library. 065 * <h3>Image Quality</h3> 066 * This class implements a few different methods for scaling an image, providing 067 * either the best-looking result, the fastest result or a balanced result 068 * between the two depending on the scaling hint provided (see {@link Method}). 069 * <p/> 070 * This class also implements the incremental scaling algorithm presented by 071 * Chris Campbell in his <a href="http://today.java 072 * .net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html">Perils of 073 * Image.getScaledInstance()</a> article in order to give the best-looking 074 * results to images scaled down below roughly 800px in size where using a 075 * single scaling operation (even with 076 * {@link RenderingHints#VALUE_INTERPOLATION_BICUBIC} interpolation) would 077 * produce a much worse-looking result. 078 * <p/> 079 * Only when scaling using the {@link Method#AUTOMATIC} method will this class 080 * look at the size of the image before selecting an approach to scaling the 081 * image. If {@link Method#QUALITY} is specified, the best-looking algorithm 082 * possible is always used. 083 * <p/> 084 * Minor modifications are made to Campbell's original implementation in the 085 * form of: 086 * <ol> 087 * <li>Instead of accepting a user-supplied interpolation method, 088 * {@link RenderingHints#VALUE_INTERPOLATION_BICUBIC} interpolation is always 089 * used. This was done after A/B comparison testing with large images 090 * down-scaled to thumbnail sizes showed noticeable "blurring" when BILINEAR 091 * interpolation was used. Given that Campbell's algorithm is only used in 092 * QUALITY mode when down-scaling, it was determined that the user's expectation 093 * of a much less blurry picture would require that BICUBIC be the default 094 * interpolation in order to meet the QUALITY expectation.</li> 095 * <li>After each iteration of the do-while loop that incrementally scales the 096 * source image down, an explicit effort is made to call 097 * {@link BufferedImage#flush()} on the interim temporary {@link BufferedImage} 098 * instances created by the algorithm in an attempt to ensure a more complete GC 099 * cycle by the VM when cleaning up the temporary instances (this is in addition 100 * to disposing of the temporary {@link Graphics2D} references as well).</li> 101 * <li>Extensive comments have been added to increase readability of the code.</li> 102 * <li>Variable names have been expanded to increase readability of the code.</li> 103 * </ol> 104 * <p/> 105 * <strong>NOTE</strong>: This class does not call {@link BufferedImage#flush()} 106 * on any of the <em>source images</em> passed in by calling code; it is up to 107 * the original caller to dispose of their source images when they are no longer 108 * needed so the VM can most efficiently GC them. 109 * <h3>Generated Image Types</h3> 110 * Java2D provides support for a number of different image types defined as 111 * <code>BufferedImage.TYPE_*</code> variables, unfortunately not all image 112 * types are supported equally in Java2D. Some more obscure image types either 113 * have poor or no support, leading to severely degraded quality when an attempt 114 * is made by imgscalr to create a scaled instance <em>of the same type</em> as 115 * the source image. 116 * <p/> 117 * To avoid imgscalr generating significantly worse-looking results than 118 * alternative scaling approaches (e.g. 119 * {@link Image#getScaledInstance(int, int, int)}), all resultant images 120 * generated by imgscalr are one of two types: 121 * <ol> 122 * <li>{@link BufferedImage#TYPE_INT_RGB}</li> 123 * <li>{@link BufferedImage#TYPE_INT_ARGB}</li> 124 * </ol> 125 * depending on if the source image utilizes transparency or not. 126 * <p/> 127 * This is a recommended approach by the Java2D team for dealing with poorly (or 128 * non) supported image types. More can be read about this issue <a href= 129 * "http://www.mail-archive.com/java2d-interest@capra.eng.sun.com/msg05621.html" 130 * >here</a>. 131 * <h3>Logging</h3> 132 * This class implements all its debug logging via the 133 * {@link #log(String, Object...)} method. At this time logging is done directly 134 * to <code>System.out</code> via the <code>printf</code> method. This allows 135 * the logging to be light weight and easy to capture while adding no 136 * dependencies to the library. 137 * <p/> 138 * Implementation of logging in this class is as efficient as possible; avoiding 139 * any calls to the logger or passing of arguments if logging is not enabled to 140 * avoid the (hidden) cost of constructing the Object[] argument for the varargs 141 * method call. 142 * <h3>GIF Transparency</h3> 143 * Unfortunately in Java 6 and earlier, support for GIF's 144 * {@link IndexColorModel} is sub-par, both in accurate color-selection and in 145 * maintaining transparency when moving to an image of type 146 * {@link BufferedImage#TYPE_INT_ARGB}; because of this issue when a GIF image 147 * is processed by imgscalr and the result saved as a GIF file, it is possible 148 * to lose the alpha channel of a transparent image or in the case of applying 149 * an optional {@link BufferedImageOp}, lose the entire picture all together in 150 * the result (long standing JDK bugs are filed for these). 151 * <p/> 152 * imgscalr currently does nothing to work around this manually because it is a 153 * defect in the native platform code itself. Fortunately it looks like the 154 * issues are half-fixed in Java 7 and any manual workarounds we could attempt 155 * internally are relatively expensive, in the form of hand-creating and setting 156 * RGB values pixel-by-pixel with a custom {@link ColorModel} in the scaled 157 * image. This would lead to a very measurable negative impact on performance 158 * without the caller understanding why. 159 * <p> 160 * <strong>Workaround</strong>: A workaround to this issue with all version of 161 * Java is to simply save a GIF as a PNG; no change to your code needs to be 162 * made except when the image is saved out, e.g. using {@link ImageIO}. When a 163 * file type of "PNG" is used, both the transparency and high color quality will 164 * be maintained as the PNG code path in Java2D is superior to the GIF 165 * implementation. 166 * <p> 167 * If the issue with optional {@link BufferedImageOp}s destroying GIF image 168 * content is ever fixed in the platform, saving out resulting images as GIFs 169 * should suddenly start working. 170 * <p> 171 * More can be read about the issue <a 172 * href="http://gman.eichberger.de/2007/07/transparent-gifs-in-java.html" 173 * >here</a> and <a 174 * href="http://ubuntuforums.org/archive/index.php/t-1060128.html">here</a>. 175 * 176 * @author Riyad Kalla (software@thebuzzmedia.com) 177 * @since 1.1 178 */ 179 public class Scalr { 180 /** 181 * Flag used to indicate if debugging output has been enabled by setting the 182 * "imgscalr.debug" system property to <code>true</code>. This value will be 183 * <code>false</code> if the "imgscalr.debug" system property is undefined 184 * or set to <code>false</code>. 185 * <p/> 186 * This system property can be set on startup with:<br/> 187 * <code> 188 * -Dimgscalr.debug=true 189 * </code> or by calling {@link System#setProperty(String, String)} before 190 * this class is loaded. 191 * <p/> 192 * Default value is <code>false</code>. 193 */ 194 public static final boolean DEBUG = Boolean.getBoolean("imgscalr.debug"); 195 196 /** 197 * Prefix to every log message this library logs. Using a well-defined 198 * prefix helps make it easier both visually and programmatically to scan 199 * log files for messages produced by this library. 200 * <p/> 201 * The value is "[imgscalr] " (including the space). 202 */ 203 public static final String LOG_PREFIX = "[imgscalr] "; 204 205 /** 206 * A {@link ConvolveOp} using a very light "blur" kernel that acts like an 207 * anti-aliasing filter (softens the image a bit) when applied to an image. 208 * <p/> 209 * A common request by users of the library was that they wished to "soften" 210 * resulting images when scaling them down drastically. After quite a bit of 211 * A/B testing, the kernel used by this Op was selected as the closest match 212 * for the target which was the softer results from the deprecated 213 * {@link AreaAveragingScaleFilter} (which is used internally by the 214 * deprecated {@link Image#getScaledInstance(int, int, int)} method in the 215 * JDK that imgscalr is meant to replace). 216 * <p/> 217 * This ConvolveOp uses a 3x3 kernel with the values: 218 * <table cellpadding="4" border="1"> 219 * <tr> 220 * <td>.0f</td> 221 * <td>.08f</td> 222 * <td>.0f</td> 223 * </tr> 224 * <tr> 225 * <td>.08f</td> 226 * <td>.68f</td> 227 * <td>.08f</td> 228 * </tr> 229 * <tr> 230 * <td>.0f</td> 231 * <td>.08f</td> 232 * <td>.0f</td> 233 * </tr> 234 * </table> 235 * <p/> 236 * For those that have worked with ConvolveOps before, this Op uses the 237 * {@link ConvolveOp#EDGE_NO_OP} instruction to not process the pixels along 238 * the very edge of the image (otherwise EDGE_ZERO_FILL would create a 239 * black-border around the image). If you have not worked with a ConvolveOp 240 * before, it just means this default OP will "do the right thing" and not 241 * give you garbage results. 242 * <p/> 243 * This ConvolveOp uses no {@link RenderingHints} values as internally the 244 * {@link ConvolveOp} class only uses hints when doing a color conversion 245 * between the source and destination {@link BufferedImage} targets. 246 * imgscalr allows the {@link ConvolveOp} to create its own destination 247 * image every time, so no color conversion is ever needed and thus no 248 * hints. 249 * <h3>Performance</h3> 250 * Use of this (and other) {@link ConvolveOp}s are hardware accelerated when 251 * possible. For more information on if your image op is hardware 252 * accelerated or not, check the source code of the underlying JDK class 253 * that actually executes the Op code, <a href= 254 * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html" 255 * >sun.awt.image.ImagingLib</a>. 256 * <h3>Known Issues</h3> 257 * In all versions of Java (tested up to Java 7 preview Build 131), running 258 * this op against a GIF with transparency and attempting to save the 259 * resulting image as a GIF results in a corrupted/empty file. The file must 260 * be saved out as a PNG to maintain the transparency. 261 */ 262 public static final ConvolveOp OP_ANTIALIAS = new ConvolveOp( 263 new Kernel(3, 3, new float[] { .0f, .08f, .0f, .08f, .68f, .08f, 264 .0f, .08f, .0f }), ConvolveOp.EDGE_NO_OP, null); 265 266 /** 267 * Static initializer used to prepare some of the variables used by this 268 * class. 269 */ 270 static { 271 log("Debug output ENABLED"); 272 } 273 274 /** 275 * Used to define the different scaling hints that the algorithm can use. 276 */ 277 public static enum Method { 278 /** 279 * Used to indicate that the scaling implementation should decide which 280 * method to use in order to get the best looking scaled image in the 281 * least amount of time. 282 * <p/> 283 * The scaling algorithm will use the 284 * {@link Scalr#THRESHOLD_QUALITY_BALANCED} or 285 * {@link Scalr#THRESHOLD_BALANCED_SPEED} thresholds as cut-offs to 286 * decide between selecting the <code>QUALITY</code>, 287 * <code>BALANCED</code> or <code>SPEED</code> scaling algorithms. 288 * <p/> 289 * By default the thresholds chosen will give nearly the best looking 290 * result in the fastest amount of time. We intend this method to work 291 * for 80% of people looking to scale an image quickly and get a good 292 * looking result. 293 */ 294 AUTOMATIC, 295 /** 296 * Used to indicate that the scaling implementation should scale as fast 297 * as possible and return a result. For smaller images (800px in size) 298 * this can result in noticeable aliasing but it can be a few magnitudes 299 * times faster than using the QUALITY method. 300 */ 301 SPEED, 302 /** 303 * Used to indicate that the scaling implementation should use a scaling 304 * operation balanced between SPEED and QUALITY. Sometimes SPEED looks 305 * too low quality to be useful (e.g. text can become unreadable when 306 * scaled using SPEED) but using QUALITY mode will increase the 307 * processing time too much. This mode provides a "better than SPEED" 308 * quality in a "less than QUALITY" amount of time. 309 */ 310 BALANCED, 311 /** 312 * Used to indicate that the scaling implementation should do everything 313 * it can to create as nice of a result as possible. This approach is 314 * most important for smaller pictures (800px or smaller) and less 315 * important for larger pictures as the difference between this method 316 * and the SPEED method become less and less noticeable as the 317 * source-image size increases. Using the AUTOMATIC method will 318 * automatically prefer the QUALITY method when scaling an image down 319 * below 800px in size. 320 */ 321 QUALITY; 322 } 323 324 /** 325 * Used to define the different modes of resizing that the algorithm can 326 * use. 327 */ 328 public static enum Mode { 329 /** 330 * Used to indicate that the scaling implementation should calculate 331 * dimensions for the resultant image by looking at the image's 332 * orientation and generating proportional dimensions that best fit into 333 * the target width and height given 334 * 335 * See "Image Proportions" in the {@link Scalr} class description for 336 * more detail. 337 */ 338 AUTOMATIC, 339 /** 340 * Used to indicate that the scaling implementation should calculate 341 * dimensions for the resultant image that best-fit within the given 342 * width, regardless of the orientation of the image. 343 */ 344 FIT_TO_WIDTH, 345 /** 346 * Used to indicate that the scaling implementation should calculate 347 * dimensions for the resultant image that best-fit within the given 348 * height, regardless of the orientation of the image. 349 */ 350 FIT_TO_HEIGHT; 351 } 352 353 /** 354 * Used to define the different types of rotations that can be applied to an 355 * image during a resize operation. 356 */ 357 public static enum Rotation { 358 /** 359 * No rotation should be applied to the image. 360 */ 361 NONE, 362 /** 363 * Rotate the image 90-degrees clockwise (to the right). This is 364 * equivalent to a quarter-turn of the image to the right. 365 */ 366 CLOCKWISE, 367 /** 368 * Rotate the image negative 90-degrees counter-clockwise (to the left). 369 * This is equivalent to a quarter-turn of the image to the left. This 370 * is also equivalent to a 270-degree rotation to the right. 371 */ 372 COUNTER_CLOCKWISE, 373 /** 374 * Flip the image. This is equivalent to rotating an image 180 degrees 375 * (right or left, it doesn't matter). 376 */ 377 FLIP; 378 } 379 380 /** 381 * Threshold (in pixels) at which point the scaling operation using the 382 * {@link Method#AUTOMATIC} method will decide if a {@link Method#BALANCED} 383 * method will be used (if smaller than or equal to threshold) or a 384 * {@link Method#SPEED} method will be used (if larger than threshold). 385 * <p/> 386 * The bigger the image is being scaled to, the less noticeable degradations 387 * in the image becomes and the faster algorithms can be selected. 388 * <p/> 389 * The value of this threshold (1600) was chosen after visual, by-hand, A/B 390 * testing between different types of images scaled with this library; both 391 * photographs and screenshots. It was determined that images below this 392 * size need to use a {@link Method#BALANCED} scale method to look decent in 393 * most all cases while using the faster {@link Method#SPEED} method for 394 * images bigger than this threshold showed no noticeable degradation over a 395 * <code>BALANCED</code> scale. 396 */ 397 public static final int THRESHOLD_BALANCED_SPEED = 1600; 398 399 /** 400 * Threshold (in pixels) at which point the scaling operation using the 401 * {@link Method#AUTOMATIC} method will decide if a {@link Method#QUALITY} 402 * method will be used (if smaller than or equal to threshold) or a 403 * {@link Method#BALANCED} method will be used (if larger than threshold). 404 * <p/> 405 * The bigger the image is being scaled to, the less noticeable degradations 406 * in the image becomes and the faster algorithms can be selected. 407 * <p/> 408 * The value of this threshold (800) was chosen after visual, by-hand, A/B 409 * testing between different types of images scaled with this library; both 410 * photographs and screenshots. It was determined that images below this 411 * size need to use a {@link Method#QUALITY} scale method to look decent in 412 * most all cases while using the faster {@link Method#BALANCED} method for 413 * images bigger than this threshold showed no noticeable degradation over a 414 * <code>QUALITY</code> scale. 415 */ 416 public static final int THRESHOLD_QUALITY_BALANCED = 800; 417 418 /** 419 * Resize a given image (maintaining its original proportion) to a width and 420 * height no bigger than <code>targetSize</code> and apply the given 421 * {@link BufferedImageOp}s (if any) to the result before returning it. 422 * <p/> 423 * A scaling method of {@link Method#AUTOMATIC}, mode of 424 * {@link Mode#AUTOMATIC} and rotation of {@link Rotation#NONE} are used. 425 * <p/> 426 * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are 427 * hardware accelerated operations, but many of the most popular (like 428 * {@link ConvolveOp}) are. For more information on if your image op is 429 * hardware accelerated or not, check the source code of the underlying JDK 430 * class that actually executes the Op code, <a href= 431 * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html" 432 * >sun.awt.image.ImagingLib</a>. 433 * 434 * @param src 435 * The image that will be scaled. 436 * @param targetSize 437 * The target width and height (square) that you wish the image 438 * to fit within. 439 * @param ops 440 * Zero or more optional image operations (e.g. sharpen, blur, 441 * etc.) that can be applied to the final result before returning 442 * the image. 443 * 444 * @return the proportionally scaled image with either a width or height of 445 * the given target size. 446 * 447 * @throws IllegalArgumentException 448 * if <code>src</code> is <code>null</code>. 449 * @throws IllegalArgumentException 450 * if <code>targetSize</code> is < 0. 451 * 452 * @see #OP_ANTIALIAS 453 */ 454 public static BufferedImage resize(BufferedImage src, int targetSize, 455 BufferedImageOp... ops) throws IllegalArgumentException { 456 return resize(src, Method.AUTOMATIC, Mode.AUTOMATIC, Rotation.NONE, 457 targetSize, targetSize, ops); 458 } 459 460 /** 461 * Resize a given image (maintaining its original proportion) to a width and 462 * height no bigger than <code>targetSize</code>, apply the given 463 * {@link BufferedImageOp}s (if any) and then apply the given rotation to 464 * the result before returning it. 465 * <p/> 466 * A scaling method of {@link Method#AUTOMATIC} and mode of 467 * {@link Mode#AUTOMATIC} are used. 468 * <p/> 469 * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are 470 * hardware accelerated operations, but many of the most popular (like 471 * {@link ConvolveOp}) are. For more information on if your image op is 472 * hardware accelerated or not, check the source code of the underlying JDK 473 * class that actually executes the Op code, <a href= 474 * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html" 475 * >sun.awt.image.ImagingLib</a>. 476 * 477 * @param src 478 * The image that will be scaled. 479 * @param rotation 480 * The rotation to be applied to the scaled image right before it 481 * is returned. 482 * @param targetSize 483 * The target width and height (square) that you wish the image 484 * to fit within. 485 * @param ops 486 * Zero or more optional image operations (e.g. sharpen, blur, 487 * etc.) that can be applied to the final result before returning 488 * the image. 489 * 490 * @return the proportionally scaled image with either a width or height of 491 * the given target size. 492 * 493 * @throws IllegalArgumentException 494 * if <code>src</code> is <code>null</code>. 495 * @throws IllegalArgumentException 496 * if <code>rotation</code> is <code>null</code>. 497 * @throws IllegalArgumentException 498 * if <code>targetSize</code> is < 0. 499 * 500 * @see Rotation 501 * @see #OP_ANTIALIAS 502 */ 503 public static BufferedImage resize(BufferedImage src, Rotation rotation, 504 int targetSize, BufferedImageOp... ops) 505 throws IllegalArgumentException { 506 return resize(src, Method.AUTOMATIC, Mode.AUTOMATIC, rotation, 507 targetSize, targetSize, ops); 508 } 509 510 /** 511 * Resize a given image (maintaining its original proportion) to a width and 512 * height no bigger than <code>targetSize</code> using the given scaling 513 * method and apply the given {@link BufferedImageOp}s (if any) to the 514 * result before returning it. 515 * <p/> 516 * A mode of {@link Mode#AUTOMATIC} and rotation of {@link Rotation#NONE} 517 * are used. 518 * <p/> 519 * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are 520 * hardware accelerated operations, but many of the most popular (like 521 * {@link ConvolveOp}) are. For more information on if your image op is 522 * hardware accelerated or not, check the source code of the underlying JDK 523 * class that actually executes the Op code, <a href= 524 * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html" 525 * >sun.awt.image.ImagingLib</a>. 526 * 527 * @param src 528 * The image that will be scaled. 529 * @param scalingMethod 530 * The method used for scaling the image; preferring speed to 531 * quality or a balance of both. 532 * @param targetSize 533 * The target width and height (square) that you wish the image 534 * to fit within. 535 * @param ops 536 * Zero or more optional image operations (e.g. sharpen, blur, 537 * etc.) that can be applied to the final result before returning 538 * the image. 539 * 540 * @return the proportionally scaled image with either a width or height of 541 * the given target size. 542 * 543 * @throws IllegalArgumentException 544 * if <code>src</code> is <code>null</code>. 545 * @throws IllegalArgumentException 546 * if <code>scalingMethod</code> is <code>null</code>. 547 * @throws IllegalArgumentException 548 * if <code>targetSize</code> is < 0. 549 * 550 * @see Method 551 * @see #OP_ANTIALIAS 552 */ 553 public static BufferedImage resize(BufferedImage src, Method scalingMethod, 554 int targetSize, BufferedImageOp... ops) 555 throws IllegalArgumentException { 556 return resize(src, scalingMethod, Mode.AUTOMATIC, Rotation.NONE, 557 targetSize, targetSize, ops); 558 } 559 560 /** 561 * Resize a given image (maintaining its original proportion) to a width and 562 * height no bigger than <code>targetSize</code> using the given scaling 563 * method, apply the given {@link BufferedImageOp}s (if any) and then apply 564 * the given rotation to the result before returning it. 565 * <p/> 566 * A mode of {@link Mode#AUTOMATIC} is used. 567 * <p/> 568 * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are 569 * hardware accelerated operations, but many of the most popular (like 570 * {@link ConvolveOp}) are. For more information on if your image op is 571 * hardware accelerated or not, check the source code of the underlying JDK 572 * class that actually executes the Op code, <a href= 573 * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html" 574 * >sun.awt.image.ImagingLib</a>. 575 * 576 * @param src 577 * The image that will be scaled. 578 * @param scalingMethod 579 * The method used for scaling the image; preferring speed to 580 * quality or a balance of both. 581 * @param rotation 582 * The rotation to be applied to the scaled image right before it 583 * is returned. 584 * @param targetSize 585 * The target width and height (square) that you wish the image 586 * to fit within. 587 * @param ops 588 * Zero or more optional image operations (e.g. sharpen, blur, 589 * etc.) that can be applied to the final result before returning 590 * the image. 591 * 592 * @return the proportionally scaled image with either a width or height of 593 * the given target size. 594 * 595 * @throws IllegalArgumentException 596 * if <code>src</code> is <code>null</code>. 597 * @throws IllegalArgumentException 598 * if <code>scalingMethod</code> is <code>null</code>. 599 * @throws IllegalArgumentException 600 * if <code>rotation</code> is <code>null</code>. 601 * @throws IllegalArgumentException 602 * if <code>targetSize</code> is < 0. 603 * 604 * @see Method 605 * @see Rotation 606 * @see #OP_ANTIALIAS 607 */ 608 public static BufferedImage resize(BufferedImage src, Method scalingMethod, 609 Rotation rotation, int targetSize, BufferedImageOp... ops) 610 throws IllegalArgumentException { 611 return resize(src, scalingMethod, Mode.AUTOMATIC, rotation, targetSize, 612 targetSize, ops); 613 } 614 615 /** 616 * Resize a given image (maintaining its original proportion) to a width and 617 * height no bigger than <code>targetSize</code> (or fitting the image to 618 * the given WIDTH or HEIGHT explicitly, depending on the {@link Mode} 619 * specified) and then apply the given {@link BufferedImageOp}s (if any) to 620 * the result before returning it. 621 * <p/> 622 * A scaling method of {@link Method#AUTOMATIC} and rotation of 623 * {@link Rotation#NONE} are used. 624 * <p/> 625 * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are 626 * hardware accelerated operations, but many of the most popular (like 627 * {@link ConvolveOp}) are. For more information on if your image op is 628 * hardware accelerated or not, check the source code of the underlying JDK 629 * class that actually executes the Op code, <a href= 630 * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html" 631 * >sun.awt.image.ImagingLib</a>. 632 * 633 * @param src 634 * The image that will be scaled. 635 * @param resizeMode 636 * Used to indicate how imgscalr should calculate the final 637 * target size for the image, either fitting the image to the 638 * given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image 639 * to the given height ({@link Mode#FIT_TO_HEIGHT}). If 640 * {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate 641 * proportional dimensions for the scaled image based on its 642 * orientation (landscape, square or portrait). Unless you have 643 * very specific size requirements, most of the time you just 644 * want to use {@link Mode#AUTOMATIC} to "do the right thing". 645 * @param targetSize 646 * The target width and height (square) that you wish the image 647 * to fit within. 648 * @param ops 649 * Zero or more optional image operations (e.g. sharpen, blur, 650 * etc.) that can be applied to the final result before returning 651 * the image. 652 * 653 * @return the proportionally scaled image with either a width or height of 654 * the given target size. 655 * 656 * @throws IllegalArgumentException 657 * if <code>src</code> is <code>null</code>. 658 * @throws IllegalArgumentException 659 * if <code>resizeMode</code> is <code>null</code>. 660 * @throws IllegalArgumentException 661 * if <code>targetSize</code> is < 0. 662 * 663 * @see Mode 664 * @see #OP_ANTIALIAS 665 */ 666 public static BufferedImage resize(BufferedImage src, Mode resizeMode, 667 int targetSize, BufferedImageOp... ops) 668 throws IllegalArgumentException { 669 return resize(src, Method.AUTOMATIC, resizeMode, Rotation.NONE, 670 targetSize, targetSize, ops); 671 } 672 673 /** 674 * Resize a given image (maintaining its original proportion) to a width and 675 * height no bigger than <code>targetSize</code> (or fitting the image to 676 * the given WIDTH or HEIGHT explicitly, depending on the {@link Mode} 677 * specified), apply the given {@link BufferedImageOp}s (if any) and then 678 * apply the given rotation to the result before returning it. 679 * <p/> 680 * A scaling method of {@link Method#AUTOMATIC} is used. 681 * <p/> 682 * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are 683 * hardware accelerated operations, but many of the most popular (like 684 * {@link ConvolveOp}) are. For more information on if your image op is 685 * hardware accelerated or not, check the source code of the underlying JDK 686 * class that actually executes the Op code, <a href= 687 * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html" 688 * >sun.awt.image.ImagingLib</a>. 689 * 690 * @param src 691 * The image that will be scaled. 692 * @param resizeMode 693 * Used to indicate how imgscalr should calculate the final 694 * target size for the image, either fitting the image to the 695 * given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image 696 * to the given height ({@link Mode#FIT_TO_HEIGHT}). If 697 * {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate 698 * proportional dimensions for the scaled image based on its 699 * orientation (landscape, square or portrait). Unless you have 700 * very specific size requirements, most of the time you just 701 * want to use {@link Mode#AUTOMATIC} to "do the right thing". 702 * @param rotation 703 * The rotation to be applied to the scaled image right before it 704 * is returned. 705 * @param targetSize 706 * The target width and height (square) that you wish the image 707 * to fit within. 708 * @param ops 709 * Zero or more optional image operations (e.g. sharpen, blur, 710 * etc.) that can be applied to the final result before returning 711 * the image. 712 * 713 * @return the proportionally scaled image with either a width or height of 714 * the given target size. 715 * 716 * @throws IllegalArgumentException 717 * if <code>src</code> is <code>null</code>. 718 * @throws IllegalArgumentException 719 * if <code>resizeMode</code> is <code>null</code>. 720 * @throws IllegalArgumentException 721 * if <code>rotation</code> is <code>null</code>. 722 * @throws IllegalArgumentException 723 * if <code>targetSize</code> is < 0. 724 * 725 * @see Mode 726 * @see Rotation 727 * @see #OP_ANTIALIAS 728 */ 729 public static BufferedImage resize(BufferedImage src, Mode resizeMode, 730 Rotation rotation, int targetSize, BufferedImageOp... ops) 731 throws IllegalArgumentException { 732 return resize(src, Method.AUTOMATIC, resizeMode, rotation, targetSize, 733 targetSize, ops); 734 } 735 736 /** 737 * Resize a given image (maintaining its original proportion) to a width and 738 * height no bigger than <code>targetSize</code> (or fitting the image to 739 * the given WIDTH or HEIGHT explicitly, depending on the {@link Mode} 740 * specified) using the given scaling method and apply the given 741 * {@link BufferedImageOp}s (if any) to the result before returning it. 742 * <p/> 743 * A rotation of {@link Rotation#NONE} is used. 744 * <p/> 745 * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are 746 * hardware accelerated operations, but many of the most popular (like 747 * {@link ConvolveOp}) are. For more information on if your image op is 748 * hardware accelerated or not, check the source code of the underlying JDK 749 * class that actually executes the Op code, <a href= 750 * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html" 751 * >sun.awt.image.ImagingLib</a>. 752 * 753 * @param src 754 * The image that will be scaled. 755 * @param scalingMethod 756 * The method used for scaling the image; preferring speed to 757 * quality or a balance of both. 758 * @param resizeMode 759 * Used to indicate how imgscalr should calculate the final 760 * target size for the image, either fitting the image to the 761 * given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image 762 * to the given height ({@link Mode#FIT_TO_HEIGHT}). If 763 * {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate 764 * proportional dimensions for the scaled image based on its 765 * orientation (landscape, square or portrait). Unless you have 766 * very specific size requirements, most of the time you just 767 * want to use {@link Mode#AUTOMATIC} to "do the right thing". 768 * @param targetSize 769 * The target width and height (square) that you wish the image 770 * to fit within. 771 * @param ops 772 * Zero or more optional image operations (e.g. sharpen, blur, 773 * etc.) that can be applied to the final result before returning 774 * the image. 775 * 776 * @return the proportionally scaled image with either a width or height of 777 * the given target size. 778 * 779 * @throws IllegalArgumentException 780 * if <code>src</code> is <code>null</code>. 781 * @throws IllegalArgumentException 782 * if <code>scalingMethod</code> is <code>null</code>. 783 * @throws IllegalArgumentException 784 * if <code>resizeMode</code> is <code>null</code>. 785 * @throw IllegalArgumentException if <code>targetSize</code> is < 0. 786 * 787 * @see Method 788 * @see Mode 789 * @see #OP_ANTIALIAS 790 */ 791 public static BufferedImage resize(BufferedImage src, Method scalingMethod, 792 Mode resizeMode, int targetSize, BufferedImageOp... ops) 793 throws IllegalArgumentException { 794 return resize(src, scalingMethod, resizeMode, Rotation.NONE, 795 targetSize, targetSize, ops); 796 } 797 798 /** 799 * Resize a given image (maintaining its original proportion) to a width and 800 * height no bigger than <code>targetSize</code> (or fitting the image to 801 * the given WIDTH or HEIGHT explicitly, depending on the {@link Mode} 802 * specified) using the given scaling method, apply the given 803 * {@link BufferedImageOp}s (if any) and apply the given rotation to the 804 * result before returning it. 805 * <p/> 806 * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are 807 * hardware accelerated operations, but many of the most popular (like 808 * {@link ConvolveOp}) are. For more information on if your image op is 809 * hardware accelerated or not, check the source code of the underlying JDK 810 * class that actually executes the Op code, <a href= 811 * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html" 812 * >sun.awt.image.ImagingLib</a>. 813 * 814 * @param src 815 * The image that will be scaled. 816 * @param scalingMethod 817 * The method used for scaling the image; preferring speed to 818 * quality or a balance of both. 819 * @param resizeMode 820 * Used to indicate how imgscalr should calculate the final 821 * target size for the image, either fitting the image to the 822 * given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image 823 * to the given height ({@link Mode#FIT_TO_HEIGHT}). If 824 * {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate 825 * proportional dimensions for the scaled image based on its 826 * orientation (landscape, square or portrait). Unless you have 827 * very specific size requirements, most of the time you just 828 * want to use {@link Mode#AUTOMATIC} to "do the right thing". 829 * @param rotation 830 * The rotation to be applied to the scaled image right before it 831 * is returned. 832 * @param targetSize 833 * The target width and height (square) that you wish the image 834 * to fit within. 835 * @param ops 836 * Zero or more optional image operations (e.g. sharpen, blur, 837 * etc.) that can be applied to the final result before returning 838 * the image. 839 * 840 * @return the proportionally scaled image with either a width or height of 841 * the given target size. 842 * 843 * @throws IllegalArgumentException 844 * if <code>src</code> is <code>null</code>. 845 * @throws IllegalArgumentException 846 * if <code>scalingMethod</code> is <code>null</code>. 847 * @throws IllegalArgumentException 848 * if <code>resizeMode</code> is <code>null</code>. 849 * @throws IllegalArgumentException 850 * if <code>rotation</code> is <code>null</code>. 851 * @throw IllegalArgumentException if <code>targetSize</code> is < 0. 852 * 853 * @see Method 854 * @see Mode 855 * @see Rotation 856 * @see #OP_ANTIALIAS 857 */ 858 public static BufferedImage resize(BufferedImage src, Method scalingMethod, 859 Mode resizeMode, Rotation rotation, int targetSize, 860 BufferedImageOp... ops) throws IllegalArgumentException { 861 return resize(src, scalingMethod, resizeMode, rotation, targetSize, 862 targetSize, ops); 863 } 864 865 /** 866 * Resize a given image (maintaining its original proportion) to the target 867 * width and height and apply the given {@link BufferedImageOp}s (if any) to 868 * the result before returning it. 869 * <p/> 870 * A scaling method of {@link Method#AUTOMATIC}, mode of 871 * {@link Mode#AUTOMATIC} and rotation of {@link Rotation#NONE} are used. 872 * <p/> 873 * <strong>TIP</strong>: See the class description to understand how this 874 * class handles recalculation of the <code>targetWidth</code> or 875 * <code>targetHeight</code> depending on the image's orientation in order 876 * to maintain the original proportion. 877 * <p/> 878 * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are 879 * hardware accelerated operations, but many of the most popular (like 880 * {@link ConvolveOp}) are. For more information on if your image op is 881 * hardware accelerated or not, check the source code of the underlying JDK 882 * class that actually executes the Op code, <a href= 883 * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html" 884 * >sun.awt.image.ImagingLib</a>. 885 * 886 * @param src 887 * The image that will be scaled. 888 * @param targetWidth 889 * The target width that you wish the image to have. 890 * @param targetHeight 891 * The target height that you wish the image to have. 892 * @param ops 893 * Zero or more optional image operations (e.g. sharpen, blur, 894 * etc.) that can be applied to the final result before returning 895 * the image. 896 * 897 * @return the proportionally scaled image with either a width or height of 898 * the given target size. 899 * 900 * @throws IllegalArgumentException 901 * if <code>src</code> is <code>null</code>. 902 * @throws IllegalArgumentException 903 * if <code>targetWidth</code> is < 0 or if 904 * <code>targetHeight</code> is < 0. 905 * 906 * @see #OP_ANTIALIAS 907 */ 908 public static BufferedImage resize(BufferedImage src, int targetWidth, 909 int targetHeight, BufferedImageOp... ops) 910 throws IllegalArgumentException { 911 return resize(src, Method.AUTOMATIC, Mode.AUTOMATIC, Rotation.NONE, 912 targetWidth, targetHeight, ops); 913 } 914 915 /** 916 * Resize a given image (maintaining its original proportion) to the target 917 * width and height, apply the given {@link BufferedImageOp}s (if any) and 918 * apply the given rotation to the result before returning it. 919 * <p/> 920 * A scaling method of {@link Method#AUTOMATIC} and mode of 921 * {@link Mode#AUTOMATIC} are used. 922 * <p/> 923 * <strong>TIP</strong>: See the class description to understand how this 924 * class handles recalculation of the <code>targetWidth</code> or 925 * <code>targetHeight</code> depending on the image's orientation in order 926 * to maintain the original proportion. 927 * <p/> 928 * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are 929 * hardware accelerated operations, but many of the most popular (like 930 * {@link ConvolveOp}) are. For more information on if your image op is 931 * hardware accelerated or not, check the source code of the underlying JDK 932 * class that actually executes the Op code, <a href= 933 * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html" 934 * >sun.awt.image.ImagingLib</a>. 935 * 936 * @param src 937 * The image that will be scaled. 938 * @param rotation 939 * The rotation to be applied to the scaled image right before it 940 * is returned. 941 * @param targetWidth 942 * The target width that you wish the image to have. 943 * @param targetHeight 944 * The target height that you wish the image to have. 945 * @param ops 946 * Zero or more optional image operations (e.g. sharpen, blur, 947 * etc.) that can be applied to the final result before returning 948 * the image. 949 * 950 * @return the proportionally scaled image with either a width or height of 951 * the given target size. 952 * 953 * @throws IllegalArgumentException 954 * if <code>src</code> is <code>null</code>. 955 * @throws IllegalArgumentException 956 * if <code>rotation</code> is <code>null</code>. 957 * @throws IllegalArgumentException 958 * if <code>targetWidth</code> is < 0 or if 959 * <code>targetHeight</code> is < 0. 960 * 961 * @see Rotation 962 * @see #OP_ANTIALIAS 963 */ 964 public static BufferedImage resize(BufferedImage src, Rotation rotation, 965 int targetWidth, int targetHeight, BufferedImageOp... ops) 966 throws IllegalArgumentException { 967 return resize(src, Method.AUTOMATIC, Mode.AUTOMATIC, rotation, 968 targetWidth, targetHeight, ops); 969 } 970 971 /** 972 * Resize a given image (maintaining its original proportion) to the target 973 * width and height using the given scaling method and apply the given 974 * {@link BufferedImageOp}s (if any) to the result before returning it. 975 * <p/> 976 * A mode of {@link Mode#AUTOMATIC} and rotation of {@link Rotation#NONE} 977 * are used. 978 * <p/> 979 * <strong>TIP</strong>: See the class description to understand how this 980 * class handles recalculation of the <code>targetWidth</code> or 981 * <code>targetHeight</code> depending on the image's orientation in order 982 * to maintain the original proportion. 983 * <p/> 984 * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are 985 * hardware accelerated operations, but many of the most popular (like 986 * {@link ConvolveOp}) are. For more information on if your image op is 987 * hardware accelerated or not, check the source code of the underlying JDK 988 * class that actually executes the Op code, <a href= 989 * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html" 990 * >sun.awt.image.ImagingLib</a>. 991 * 992 * @param src 993 * The image that will be scaled. 994 * @param scalingMethod 995 * The method used for scaling the image; preferring speed to 996 * quality or a balance of both. 997 * @param targetWidth 998 * The target width that you wish the image to have. 999 * @param targetHeight 1000 * The target height that you wish the image to have. 1001 * @param ops 1002 * Zero or more optional image operations (e.g. sharpen, blur, 1003 * etc.) that can be applied to the final result before returning 1004 * the image. 1005 * 1006 * @return the proportionally scaled image no bigger than the given width 1007 * and height. 1008 * 1009 * @throws IllegalArgumentException 1010 * if <code>src</code> is <code>null</code>. 1011 * @throws IllegalArgumentException 1012 * if <code>scalingMethod</code> is <code>null</code>. 1013 * @throws IllegalArgumentException 1014 * if <code>targetWidth</code> is < 0 or if 1015 * <code>targetHeight</code> is < 0. 1016 * 1017 * @see Method 1018 * @see #OP_ANTIALIAS 1019 */ 1020 public static BufferedImage resize(BufferedImage src, Method scalingMethod, 1021 int targetWidth, int targetHeight, BufferedImageOp... ops) { 1022 return resize(src, scalingMethod, Mode.AUTOMATIC, Rotation.NONE, 1023 targetWidth, targetHeight, ops); 1024 } 1025 1026 /** 1027 * Resize a given image (maintaining its original proportion) to the target 1028 * width and height using the given scaling method, apply the given 1029 * {@link BufferedImageOp}s (if any) and apply the given rotation to the 1030 * result before returning it. 1031 * <p/> 1032 * A mode of {@link Mode#AUTOMATIC} is used. 1033 * <p/> 1034 * <strong>TIP</strong>: See the class description to understand how this 1035 * class handles recalculation of the <code>targetWidth</code> or 1036 * <code>targetHeight</code> depending on the image's orientation in order 1037 * to maintain the original proportion. 1038 * <p/> 1039 * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are 1040 * hardware accelerated operations, but many of the most popular (like 1041 * {@link ConvolveOp}) are. For more information on if your image op is 1042 * hardware accelerated or not, check the source code of the underlying JDK 1043 * class that actually executes the Op code, <a href= 1044 * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html" 1045 * >sun.awt.image.ImagingLib</a>. 1046 * 1047 * @param src 1048 * The image that will be scaled. 1049 * @param scalingMethod 1050 * The method used for scaling the image; preferring speed to 1051 * quality or a balance of both. 1052 * @param rotation 1053 * The rotation to be applied to the scaled image right before it 1054 * is returned. 1055 * @param targetWidth 1056 * The target width that you wish the image to have. 1057 * @param targetHeight 1058 * The target height that you wish the image to have. 1059 * @param ops 1060 * Zero or more optional image operations (e.g. sharpen, blur, 1061 * etc.) that can be applied to the final result before returning 1062 * the image. 1063 * 1064 * @return the proportionally scaled image no bigger than the given width 1065 * and height. 1066 * 1067 * @throws IllegalArgumentException 1068 * if <code>src</code> is <code>null</code>. 1069 * @throws IllegalArgumentException 1070 * if <code>scalingMethod</code> is <code>null</code>. 1071 * @throws IllegalArgumentException 1072 * if <code>rotation</code> is <code>null</code>. 1073 * @throws IllegalArgumentException 1074 * if <code>targetWidth</code> is < 0 or if 1075 * <code>targetHeight</code> is < 0. 1076 * 1077 * @see Method 1078 * @see Rotation 1079 * @see #OP_ANTIALIAS 1080 */ 1081 public static BufferedImage resize(BufferedImage src, Method scalingMethod, 1082 Rotation rotation, int targetWidth, int targetHeight, 1083 BufferedImageOp... ops) { 1084 return resize(src, scalingMethod, Mode.AUTOMATIC, rotation, 1085 targetWidth, targetHeight, ops); 1086 } 1087 1088 /** 1089 * Resize a given image (maintaining its original proportion) to the target 1090 * width and height (or fitting the image to the given WIDTH or HEIGHT 1091 * explicitly, depending on the {@link Mode} specified) and then apply the 1092 * given {@link BufferedImageOp}s (if any) to the result before returning 1093 * it. 1094 * <p/> 1095 * A scaling method of {@link Method#AUTOMATIC} and rotation of 1096 * {@link Rotation#NONE} are used. 1097 * <p/> 1098 * <strong>TIP</strong>: See the class description to understand how this 1099 * class handles recalculation of the <code>targetWidth</code> or 1100 * <code>targetHeight</code> depending on the image's orientation in order 1101 * to maintain the original proportion. 1102 * <p/> 1103 * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are 1104 * hardware accelerated operations, but many of the most popular (like 1105 * {@link ConvolveOp}) are. For more information on if your image op is 1106 * hardware accelerated or not, check the source code of the underlying JDK 1107 * class that actually executes the Op code, <a href= 1108 * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html" 1109 * >sun.awt.image.ImagingLib</a>. 1110 * 1111 * @param src 1112 * The image that will be scaled. 1113 * @param resizeMode 1114 * Used to indicate how imgscalr should calculate the final 1115 * target size for the image, either fitting the image to the 1116 * given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image 1117 * to the given height ({@link Mode#FIT_TO_HEIGHT}). If 1118 * {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate 1119 * proportional dimensions for the scaled image based on its 1120 * orientation (landscape, square or portrait). Unless you have 1121 * very specific size requirements, most of the time you just 1122 * want to use {@link Mode#AUTOMATIC} to "do the right thing". 1123 * @param targetWidth 1124 * The target width that you wish the image to have. 1125 * @param targetHeight 1126 * The target height that you wish the image to have. 1127 * @param ops 1128 * Zero or more optional image operations (e.g. sharpen, blur, 1129 * etc.) that can be applied to the final result before returning 1130 * the image. 1131 * 1132 * @return the proportionally scaled image with either a width or height of 1133 * the given target size. 1134 * 1135 * @throws IllegalArgumentException 1136 * if <code>src</code> is <code>null</code>. 1137 * @throws IllegalArgumentException 1138 * if <code>resizeMode</code> is <code>null</code>. 1139 * @throws IllegalArgumentException 1140 * if <code>targetWidth</code> is < 0 or if 1141 * <code>targetHeight</code> is < 0. 1142 * 1143 * @see Mode 1144 * @see #OP_ANTIALIAS 1145 */ 1146 public static BufferedImage resize(BufferedImage src, Mode resizeMode, 1147 int targetWidth, int targetHeight, BufferedImageOp... ops) 1148 throws IllegalArgumentException { 1149 return resize(src, Method.AUTOMATIC, resizeMode, Rotation.NONE, 1150 targetWidth, targetHeight, ops); 1151 } 1152 1153 /** 1154 * Resize a given image (maintaining its original proportion) to the target 1155 * width and height (or fitting the image to the given WIDTH or HEIGHT 1156 * explicitly, depending on the {@link Mode} specified), apply the given 1157 * {@link BufferedImageOp}s (if any) and then apply the given rotation to 1158 * the result before returning it. 1159 * <p/> 1160 * A scaling method of {@link Method#AUTOMATIC} is used. 1161 * <p/> 1162 * <strong>TIP</strong>: See the class description to understand how this 1163 * class handles recalculation of the <code>targetWidth</code> or 1164 * <code>targetHeight</code> depending on the image's orientation in order 1165 * to maintain the original proportion. 1166 * <p/> 1167 * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are 1168 * hardware accelerated operations, but many of the most popular (like 1169 * {@link ConvolveOp}) are. For more information on if your image op is 1170 * hardware accelerated or not, check the source code of the underlying JDK 1171 * class that actually executes the Op code, <a href= 1172 * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html" 1173 * >sun.awt.image.ImagingLib</a>. 1174 * 1175 * @param src 1176 * The image that will be scaled. 1177 * @param resizeMode 1178 * Used to indicate how imgscalr should calculate the final 1179 * target size for the image, either fitting the image to the 1180 * given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image 1181 * to the given height ({@link Mode#FIT_TO_HEIGHT}). If 1182 * {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate 1183 * proportional dimensions for the scaled image based on its 1184 * orientation (landscape, square or portrait). Unless you have 1185 * very specific size requirements, most of the time you just 1186 * want to use {@link Mode#AUTOMATIC} to "do the right thing". 1187 * @param rotation 1188 * The rotation to be applied to the scaled image right before it 1189 * is returned. 1190 * @param targetWidth 1191 * The target width that you wish the image to have. 1192 * @param targetHeight 1193 * The target height that you wish the image to have. 1194 * @param ops 1195 * Zero or more optional image operations (e.g. sharpen, blur, 1196 * etc.) that can be applied to the final result before returning 1197 * the image. 1198 * 1199 * @return the proportionally scaled image with either a width or height of 1200 * the given target size. 1201 * 1202 * @throws IllegalArgumentException 1203 * if <code>src</code> is <code>null</code>. 1204 * @throws IllegalArgumentException 1205 * if <code>resizeMode</code> is <code>null</code>. 1206 * @throws IllegalArgumentException 1207 * if <code>rotation</code> is <code>null</code>. 1208 * @throws IllegalArgumentException 1209 * if <code>targetWidth</code> is < 0 or if 1210 * <code>targetHeight</code> is < 0. 1211 * 1212 * @see Mode 1213 * @see Rotation 1214 * @see #OP_ANTIALIAS 1215 */ 1216 public static BufferedImage resize(BufferedImage src, Mode resizeMode, 1217 Rotation rotation, int targetWidth, int targetHeight, 1218 BufferedImageOp... ops) throws IllegalArgumentException { 1219 return resize(src, Method.AUTOMATIC, resizeMode, rotation, targetWidth, 1220 targetHeight, ops); 1221 } 1222 1223 /** 1224 * Resize a given image (maintaining its original proportion) to the target 1225 * width and height (or fitting the image to the given WIDTH or HEIGHT 1226 * explicitly, depending on the {@link Mode} specified) using the given 1227 * scaling method and apply the given {@link BufferedImageOp}s (if any) to 1228 * the result before returning it. 1229 * <p/> 1230 * A rotation of {@link Rotation#NONE} is used. 1231 * <p/> 1232 * <strong>TIP</strong>: See the class description to understand how this 1233 * class handles recalculation of the <code>targetWidth</code> or 1234 * <code>targetHeight</code> depending on the image's orientation in order 1235 * to maintain the original proportion. 1236 * <p/> 1237 * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are 1238 * hardware accelerated operations, but many of the most popular (like 1239 * {@link ConvolveOp}) are. For more information on if your image op is 1240 * hardware accelerated or not, check the source code of the underlying JDK 1241 * class that actually executes the Op code, <a href= 1242 * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html" 1243 * >sun.awt.image.ImagingLib</a>. 1244 * 1245 * @param src 1246 * The image that will be scaled. 1247 * @param scalingMethod 1248 * The method used for scaling the image; preferring speed to 1249 * quality or a balance of both. 1250 * @param resizeMode 1251 * Used to indicate how imgscalr should calculate the final 1252 * target size for the image, either fitting the image to the 1253 * given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image 1254 * to the given height ({@link Mode#FIT_TO_HEIGHT}). If 1255 * {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate 1256 * proportional dimensions for the scaled image based on its 1257 * orientation (landscape, square or portrait). Unless you have 1258 * very specific size requirements, most of the time you just 1259 * want to use {@link Mode#AUTOMATIC} to "do the right thing". 1260 * @param targetWidth 1261 * The target width that you wish the image to have. 1262 * @param targetHeight 1263 * The target height that you wish the image to have. 1264 * @param ops 1265 * Zero or more optional image operations (e.g. sharpen, blur, 1266 * etc.) that can be applied to the final result before returning 1267 * the image. 1268 * 1269 * @return the proportionally scaled image no bigger than the given width 1270 * and height. 1271 * 1272 * @throws IllegalArgumentException 1273 * if <code>src</code> is <code>null</code>. 1274 * @throws IllegalArgumentException 1275 * if <code>scalingMethod</code> is <code>null</code>. 1276 * @throws IllegalArgumentException 1277 * if <code>resizeMode</code> is <code>null</code>. 1278 * @throws IllegalArgumentException 1279 * if <code>targetWidth</code> is < 0 or if 1280 * <code>targetHeight</code> is < 0. 1281 * 1282 * @see Method 1283 * @see Mode 1284 * @see #OP_ANTIALIAS 1285 */ 1286 public static BufferedImage resize(BufferedImage src, Method scalingMethod, 1287 Mode resizeMode, int targetWidth, int targetHeight, 1288 BufferedImageOp... ops) throws IllegalArgumentException { 1289 return resize(src, scalingMethod, resizeMode, Rotation.NONE, 1290 targetWidth, targetHeight, ops); 1291 } 1292 1293 /** 1294 * Resize a given image (maintaining its original proportion) to the target 1295 * width and height (or fitting the image to the given WIDTH or HEIGHT 1296 * explicitly, depending on the {@link Mode} specified) using the given 1297 * scaling method, apply the given {@link BufferedImageOp}s (if any) and 1298 * apply the given rotation to the result before returning it. 1299 * <p/> 1300 * <strong>TIP</strong>: See the class description to understand how this 1301 * class handles recalculation of the <code>targetWidth</code> or 1302 * <code>targetHeight</code> depending on the image's orientation in order 1303 * to maintain the original proportion. 1304 * <p/> 1305 * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are 1306 * hardware accelerated operations, but many of the most popular (like 1307 * {@link ConvolveOp}) are. For more information on if your image op is 1308 * hardware accelerated or not, check the source code of the underlying JDK 1309 * class that actually executes the Op code, <a href= 1310 * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html" 1311 * >sun.awt.image.ImagingLib</a>. 1312 * 1313 * @param src 1314 * The image that will be scaled. 1315 * @param scalingMethod 1316 * The method used for scaling the image; preferring speed to 1317 * quality or a balance of both. 1318 * @param resizeMode 1319 * Used to indicate how imgscalr should calculate the final 1320 * target size for the image, either fitting the image to the 1321 * given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image 1322 * to the given height ({@link Mode#FIT_TO_HEIGHT}). If 1323 * {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate 1324 * proportional dimensions for the scaled image based on its 1325 * orientation (landscape, square or portrait). Unless you have 1326 * very specific size requirements, most of the time you just 1327 * want to use {@link Mode#AUTOMATIC} to "do the right thing". 1328 * @param rotation 1329 * The rotation to be applied to the scaled image right before it 1330 * is returned. 1331 * @param targetWidth 1332 * The target width that you wish the image to have. 1333 * @param targetHeight 1334 * The target height that you wish the image to have. 1335 * @param ops 1336 * Zero or more optional image operations (e.g. sharpen, blur, 1337 * etc.) that can be applied to the final result before returning 1338 * the image. 1339 * 1340 * @return the proportionally scaled image no bigger than the given width 1341 * and height. 1342 * 1343 * @throws IllegalArgumentException 1344 * if <code>src</code> is <code>null</code>. 1345 * @throws IllegalArgumentException 1346 * if <code>scalingMethod</code> is <code>null</code>. 1347 * @throws IllegalArgumentException 1348 * if <code>resizeMode</code> is <code>null</code>. 1349 * @throws IllegalArgumentException 1350 * if <code>rotation</code> is <code>null</code>. 1351 * @throws IllegalArgumentException 1352 * if <code>targetWidth</code> is < 0 or if 1353 * <code>targetHeight</code> is < 0. 1354 * 1355 * @see Method 1356 * @see Mode 1357 * @see Rotation 1358 * @see #OP_ANTIALIAS 1359 */ 1360 public static BufferedImage resize(BufferedImage src, Method scalingMethod, 1361 Mode resizeMode, Rotation rotation, int targetWidth, 1362 int targetHeight, BufferedImageOp... ops) 1363 throws IllegalArgumentException { 1364 if (src == null) 1365 throw new IllegalArgumentException( 1366 "src cannot be null, a valid BufferedImage instance must be provided."); 1367 if (scalingMethod == null) 1368 throw new IllegalArgumentException( 1369 "scalingMethod cannot be null. A good default value is Method.AUTOMATIC."); 1370 if (resizeMode == null) 1371 throw new IllegalArgumentException( 1372 "resizeMode cannot be null. A good default value is Mode.AUTOMATIC."); 1373 if (rotation == null) 1374 throw new IllegalArgumentException( 1375 "rotation cannot be null. A good default value is Rotation.NONE."); 1376 if (targetWidth < 0) 1377 throw new IllegalArgumentException("targetWidth must be >= 0"); 1378 if (targetHeight < 0) 1379 throw new IllegalArgumentException("targetHeight must be >= 0"); 1380 1381 BufferedImage result = null; 1382 1383 long startTime = System.currentTimeMillis(); 1384 1385 // Clear the 'null' ops arg passed in from other API methods 1386 if (ops != null && ops.length == 1 && ops[0] == null) 1387 ops = null; 1388 1389 int currentWidth = src.getWidth(); 1390 int currentHeight = src.getHeight(); 1391 1392 // <= 1 is a square or landscape-oriented image, > 1 is a portrait. 1393 float ratio = ((float) currentHeight / (float) currentWidth); 1394 1395 if (DEBUG) 1396 log("START Resizing Source Image [size=%dx%d, mode=%s, orientation=%s, ratio(H/W)=%f] to [targetSize=%dx%d]", 1397 currentWidth, currentHeight, resizeMode, 1398 (ratio <= 1 ? "Landscape/Square" : "Portrait"), ratio, 1399 targetWidth, targetHeight); 1400 1401 /* 1402 * The proportion of the picture must be honored, the way that is done 1403 * is to figure out if the image is in a LANDSCAPE/SQUARE or PORTRAIT 1404 * orientation and depending on its orientation, use the primary 1405 * dimension (width for LANDSCAPE/SQUARE and height for PORTRAIT) to 1406 * recalculate the alternative (height and width respectively) value 1407 * that adheres to the existing ratio. This helps make life easier for 1408 * the caller as they don't need to pre-compute proportional dimensions 1409 * before calling the API, they can just specify the dimensions they 1410 * would like the image to roughly fit within and it will do the right 1411 * thing without mangling the result. 1412 */ 1413 if ((ratio <= 1 && resizeMode == Mode.AUTOMATIC) 1414 || (resizeMode == Mode.FIT_TO_WIDTH)) { 1415 // First make sure we need to do any work in the first place 1416 if (targetWidth == src.getWidth()) 1417 return src; 1418 1419 // Save for detailed logging (this is cheap). 1420 int originalTargetHeight = targetHeight; 1421 1422 /* 1423 * Landscape or Square Orientation: Ignore the given height and 1424 * re-calculate a proportionally correct value based on the 1425 * targetWidth. 1426 */ 1427 targetHeight = Math.round((float) targetWidth * ratio); 1428 1429 if (DEBUG && originalTargetHeight != targetHeight) 1430 log("Auto-Corrected targetHeight [from=%d to=%d] to honor image proportions", 1431 originalTargetHeight, targetHeight); 1432 } else { 1433 // First make sure we need to do any work in the first place 1434 if (targetHeight == src.getHeight()) 1435 return src; 1436 1437 // Save for detailed logging (this is cheap). 1438 int originalTargetWidth = targetWidth; 1439 1440 /* 1441 * Portrait Orientation: Ignore the given width and re-calculate a 1442 * proportionally correct value based on the targetHeight. 1443 */ 1444 targetWidth = Math.round((float) targetHeight / ratio); 1445 1446 if (DEBUG && originalTargetWidth != targetWidth) 1447 log("Auto-Corrected targetWidth [from=%d to=%d] to honor image proportions", 1448 originalTargetWidth, targetWidth); 1449 } 1450 1451 // If AUTOMATIC was specified, determine the real scaling method. 1452 if (scalingMethod == Scalr.Method.AUTOMATIC) 1453 scalingMethod = determineScalingMethod(targetWidth, targetHeight, 1454 ratio); 1455 1456 if (DEBUG) 1457 log("Scaling Image to [size=%dx%d] using the %s method...", 1458 targetWidth, targetHeight, scalingMethod); 1459 1460 // Now we scale the image 1461 if (scalingMethod == Scalr.Method.SPEED) { 1462 result = scaleImage(src, targetWidth, targetHeight, 1463 RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); 1464 } else if (scalingMethod == Scalr.Method.BALANCED) { 1465 result = scaleImage(src, targetWidth, targetHeight, 1466 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 1467 } else if (scalingMethod == Scalr.Method.QUALITY) { 1468 /* 1469 * If we are scaling up (in either width or height - since we know 1470 * the image will stay proportional we just check if either are 1471 * being scaled up), directly using a single BICUBIC will give us 1472 * better results then using Chris Campbell's incremental scaling 1473 * operation (and take a lot less time). If we are scaling down, we 1474 * must use the incremental scaling algorithm for the best result. 1475 */ 1476 if (targetWidth > currentWidth || targetHeight > currentHeight) { 1477 log("\tQUALITY Up-scale, single BICUBIC scaling will be used..."); 1478 1479 /* 1480 * BILINEAR and BICUBIC look similar the smaller the scale jump 1481 * upwards is, if the scale is larger BICUBIC looks sharper and 1482 * less fuzzy. But most importantly we have to use BICUBIC to 1483 * match the contract of the QUALITY rendering method. This note 1484 * is just here for anyone reading the code and wondering how 1485 * they can speed their own calls up. 1486 */ 1487 result = scaleImage(src, targetWidth, targetHeight, 1488 RenderingHints.VALUE_INTERPOLATION_BICUBIC); 1489 } else { 1490 log("\tQUALITY Down-scale, incremental scaling will be used..."); 1491 1492 /* 1493 * Originally we wanted to use BILINEAR interpolation here 1494 * because it takes 1/3rd the time that the BICUBIC 1495 * interpolation does, however, when scaling large images down 1496 * to most sizes bigger than a thumbnail we witnessed noticeable 1497 * "softening" in the resultant image with BILINEAR that would 1498 * be unexpectedly annoying to a user expecting a "QUALITY" 1499 * scale of their original image. Instead BICUBIC was chosen to 1500 * honor the contract of a QUALITY scale of the original image. 1501 */ 1502 result = scaleImageIncrementally(src, targetWidth, 1503 targetHeight, 1504 RenderingHints.VALUE_INTERPOLATION_BICUBIC); 1505 } 1506 } 1507 1508 // Apply the image ops if any were provided 1509 if (ops != null && ops.length > 0) { 1510 if (DEBUG) 1511 log("Applying %d Image Ops to Result", ops.length); 1512 1513 for (BufferedImageOp op : ops) { 1514 // In case a null op was passed in, skip it instead of dying 1515 if (op == null) 1516 continue; 1517 1518 long opStartTime = System.currentTimeMillis(); 1519 Rectangle2D dims = op.getBounds2D(result); 1520 1521 /* 1522 * We must manually create the target image; we cannot rely on 1523 * the null-dest filter() method to create a valid destination 1524 * for us thanks to this JDK bug that has been filed for almost 1525 * a decade: 1526 * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4965606 1527 */ 1528 BufferedImage dest = new BufferedImage((int) Math.round(dims 1529 .getWidth()), (int) Math.round(dims.getHeight()), 1530 result.getType()); 1531 1532 result = op.filter(result, dest); 1533 1534 if (DEBUG) 1535 log("\tOp Applied in %d ms, Resultant Image [width=%d, height=%d], Op: %s", 1536 (System.currentTimeMillis() - opStartTime), 1537 result.getWidth(), result.getHeight(), op); 1538 } 1539 } 1540 1541 // Perform the rotation if one was requested 1542 if (rotation != Rotation.NONE) { 1543 if (DEBUG) 1544 log("Applying %s rotation to image...", rotation); 1545 1546 long rotStartTime = System.currentTimeMillis(); 1547 1548 /* 1549 * A 90 or -90 degree rotation will cause the height and width to 1550 * flip-flop from the original image to the rotated one. 1551 * 1552 * Given that MOST rotations will typically be some form of a 1553 * 90-degree rotation (portrait to landscape, etc.) just assume that 1554 * here and correct it below in the switch statement if need be. 1555 */ 1556 int newWidth = result.getHeight(); 1557 int newHeight = result.getWidth(); 1558 1559 /* 1560 * We create a transform per operation request as (oddly enough) it 1561 * ends up being faster for the VM to create, use and destroy these 1562 * instances than it is to re-use a single AffineTransform 1563 * per-thread via the AffineTransform.setTo(...) methods which was 1564 * my first choice (less object creation). 1565 * 1566 * Unfortunately this introduces the need for ThreadLocal instances 1567 * of AffineTransforms to avoid race conditions where two or more 1568 * resize threads are manipulating the same transform before 1569 * applying it. 1570 * 1571 * ThreadLocals are one of the #1 reasons for memory leaks in server 1572 * applications and since we have no nice way to hook into the 1573 * init/destroy Servlet cycle or any other initialization cycle for 1574 * this library to automatically call ThreadLocal.remove() to avoid 1575 * the memory leak, it would have made using this library *safely* 1576 * on the server side much harder. 1577 * 1578 * So we opt for creating individual transforms per rotation op and 1579 * let the VM clean them up in a GC. 1580 */ 1581 AffineTransform tx = new AffineTransform(); 1582 1583 switch (rotation) { 1584 case CLOCKWISE: 1585 // Reminder: newWidth == result.getHeight() at this point 1586 tx.translate(newWidth, 0); 1587 tx.rotate(Math.toRadians(90)); 1588 1589 break; 1590 1591 case COUNTER_CLOCKWISE: 1592 // Reminder: newHeight == result.getWidth() at this point 1593 tx.translate(0, newHeight); 1594 tx.rotate(Math.toRadians(-90)); 1595 break; 1596 1597 case FLIP: 1598 /* 1599 * This is the one rotation case where the new width and height 1600 * will be the same as the original image, so reset the values 1601 * from the defaults we set above. 1602 */ 1603 newWidth = result.getWidth(); 1604 newHeight = result.getHeight(); 1605 1606 tx.translate(newWidth, newHeight); 1607 tx.rotate(Math.toRadians(180)); 1608 break; 1609 } 1610 1611 /* 1612 * Create our target image we will render the rotated result to. At 1613 * this point the resultant image has already been put into the best 1614 * image type so we can just copy that without trying to 1615 * re-determine the most effective image type like scaleImage(...) 1616 * has to do. 1617 */ 1618 BufferedImage rotatedImage = new BufferedImage(newWidth, newHeight, 1619 result.getType()); 1620 Graphics2D g2d = (Graphics2D) rotatedImage.createGraphics(); 1621 1622 /* 1623 * Render the resultant image to our new rotatedImage buffer, 1624 * applying the AffineTransform that we calculated above during 1625 * rendering so the pixels from the old position to the new 1626 * transposed positions are mapped correctly. 1627 */ 1628 g2d.drawImage(result, tx, null); 1629 g2d.dispose(); 1630 1631 /* 1632 * Before re-assigning the new result to be returned to our rotated 1633 * image, explicitly notify the VM that you are done with any 1634 * resources being used by the old resultant image that we don't 1635 * need anymore. 1636 */ 1637 result.flush(); 1638 1639 // Reassign the result to our rotated image before returning it. 1640 result = rotatedImage; 1641 1642 if (DEBUG) 1643 log("\t%s Rotation Applied in %d ms, Resultant Image [width=%d, height=%d]", 1644 rotation, (System.currentTimeMillis() - rotStartTime), 1645 result.getWidth(), result.getHeight()); 1646 } 1647 1648 if (DEBUG) { 1649 long elapsedTime = System.currentTimeMillis() - startTime; 1650 log("END Source Image Scaled from [%dx%d] to [%dx%d] and %d BufferedImageOp(s) Applied in %d ms", 1651 currentWidth, currentHeight, result.getWidth(), 1652 result.getHeight(), (ops == null ? 0 : ops.length), 1653 elapsedTime); 1654 } 1655 1656 return result; 1657 } 1658 1659 /** 1660 * Helper method used to ensure a message is loggable before it is logged 1661 * and then pre-pend a universal prefix to all log messages generated by 1662 * this library to make the log entries easy to parse visually or 1663 * programmatically. 1664 * <p/> 1665 * If a message cannot be logged (logging is disabled) then this method 1666 * returns immediately. 1667 * <p/> 1668 * <strong>NOTE</strong>: Because Java will auto-box primitive arguments 1669 * into Objects when building out the <code>params</code> array, care should 1670 * be taken not to call this method with primitive values unless 1671 * {@link #DEBUG} is <code>true</code>; otherwise the VM will be spending 1672 * time performing unnecessary auto-boxing calculations. 1673 * 1674 * @param message 1675 * The log message in <a href= 1676 * "http://download.oracle.com/javase/6/docs/api/java/util/Formatter.html#syntax" 1677 * >format string syntax</a> that will be logged. 1678 * @param params 1679 * The parameters that will be swapped into all the place holders 1680 * in the original messages before being logged. 1681 * 1682 * @see #LOG_PREFIX 1683 */ 1684 protected static void log(String message, Object... params) { 1685 if (DEBUG) 1686 System.out.printf(LOG_PREFIX + message + '\n', params); 1687 } 1688 1689 /** 1690 * Used to determine the scaling {@link Method} that is best suited for 1691 * scaling the image to the targeted dimensions. 1692 * <p/> 1693 * This method is intended to be used to select a specific scaling 1694 * {@link Method} when a {@link Method#AUTOMATIC} method is specified. This 1695 * method utilizes the {@link #THRESHOLD_QUALITY_BALANCED} and 1696 * {@link #THRESHOLD_BALANCED_SPEED} thresholds when selecting which method 1697 * should be used by comparing the primary dimension (width or height) 1698 * against the threshold and seeing where the image falls. The primary 1699 * dimension is determined by looking at the orientation of the image: 1700 * landscape or square images use their width and portrait-oriented images 1701 * use their height. 1702 * 1703 * @param targetWidth 1704 * The target width for the scaled image. 1705 * @param targetHeight 1706 * The target height for the scaled image. 1707 * @param ratio 1708 * A height/width ratio used to determine the orientation of the 1709 * image so the primary dimension (width or height) can be 1710 * selected to test if it is greater than or less than a 1711 * particular threshold. 1712 * 1713 * @return the fastest {@link Method} suited for scaling the image to the 1714 * specified dimensions while maintaining a good-looking result. 1715 */ 1716 protected static Method determineScalingMethod(int targetWidth, 1717 int targetHeight, float ratio) { 1718 // Get the primary dimension based on the orientation of the image 1719 int length = (ratio <= 1 ? targetWidth : targetHeight); 1720 1721 // Default to speed 1722 Method result = Method.SPEED; 1723 1724 // Figure out which method should be used 1725 if (length <= THRESHOLD_QUALITY_BALANCED) 1726 result = Method.QUALITY; 1727 else if (length <= THRESHOLD_BALANCED_SPEED) 1728 result = Method.BALANCED; 1729 1730 if (DEBUG) 1731 log("AUTOMATIC Scaling Method Selected [%s] for Image [size=%dx%d]", 1732 result.name(), targetWidth, targetHeight); 1733 1734 return result; 1735 } 1736 1737 /** 1738 * Used to implement a straight-forward image-scaling operation using Java 1739 * 2D. 1740 * <p/> 1741 * This method uses the Snoracle-encouraged method of 1742 * <code>Graphics2D.drawImage(...)</code> to scale the given image with the 1743 * given interpolation hint. 1744 * 1745 * @param src 1746 * The image that will be scaled. 1747 * @param targetWidth 1748 * The target width for the scaled image. 1749 * @param targetHeight 1750 * The target height for the scaled image. 1751 * @param interpolationHintValue 1752 * The {@link RenderingHints} interpolation value used to 1753 * indicate the method that {@link Graphics2D} should use when 1754 * scaling the image. 1755 * 1756 * @return the result of scaling the original <code>src</code> to the given 1757 * dimensions using the given interpolation method. 1758 */ 1759 protected static BufferedImage scaleImage(BufferedImage src, 1760 int targetWidth, int targetHeight, Object interpolationHintValue) { 1761 /* 1762 * Determine the RGB-based TYPE of image (plain RGB or RGB + Alpha) that 1763 * we want to render the scaled instance into. We force all rendering 1764 * results into one of these two types, avoiding the case where a source 1765 * image is of an unsupported (or poorly supported) format by Java2D and 1766 * the written results, when attempting to re-create and write out that 1767 * format, is garbage. 1768 * 1769 * Originally reported by Magnus Kvalheim from Movellas when scaling 1770 * certain GIF and PNG images. 1771 * 1772 * More information about Java2D and poorly supported image types: 1773 * http:/ 1774 * /www.mail-archive.com/java2d-interest@capra.eng.sun.com/msg05621.html 1775 * 1776 * Thanks to Morten Nobel for the implementation hint: 1777 * http://code.google 1778 * .com/p/java-image-scaling/source/browse/trunk/src/main 1779 * /java/com/mortennobel/imagescaling/MultiStepRescaleOp.java 1780 */ 1781 int imageType = (src.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB 1782 : BufferedImage.TYPE_INT_ARGB); 1783 1784 // Setup the rendering resources to match the source image's 1785 BufferedImage result = new BufferedImage(targetWidth, targetHeight, 1786 imageType); 1787 Graphics2D resultGraphics = result.createGraphics(); 1788 1789 // Scale the image to the new buffer using the specified rendering hint. 1790 resultGraphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 1791 interpolationHintValue); 1792 resultGraphics.drawImage(src, 0, 0, targetWidth, targetHeight, null); 1793 1794 // Just to be clean, explicitly dispose our temporary graphics object 1795 resultGraphics.dispose(); 1796 1797 // Return the scaled image to the caller. 1798 return result; 1799 } 1800 1801 /** 1802 * Used to implement Chris Campbell's incremental-scaling algorithm: <a 1803 * href="http://today.java.net/pub/a/today/2007/04/03/perils 1804 * -of-image-getscaledinstance 1805 * .html">http://today.java.net/pub/a/today/2007/04/03/perils 1806 * -of-image-getscaledinstance.html</a>. 1807 * <p/> 1808 * Modifications to the original algorithm are variable names and comments 1809 * added for clarity and the hard-coding of using BICUBIC interpolation as 1810 * well as the explicit "flush()" operation on the interim BufferedImage 1811 * instances to avoid resource leaking. 1812 * 1813 * @param src 1814 * The image that will be scaled. 1815 * @param targetWidth 1816 * The target width for the scaled image. 1817 * @param targetHeight 1818 * The target height for the scaled image. 1819 * @param interpolationHintValue 1820 * The {@link RenderingHints} interpolation value used to 1821 * indicate the method that {@link Graphics2D} should use when 1822 * scaling the image. 1823 * 1824 * @return an image scaled to the given dimensions using the given rendering 1825 * hint. 1826 */ 1827 protected static BufferedImage scaleImageIncrementally(BufferedImage src, 1828 int targetWidth, int targetHeight, Object interpolationHintValue) { 1829 boolean hasReassignedSrc = false; 1830 int incrementCount = 0; 1831 int currentWidth = src.getWidth(); 1832 int currentHeight = src.getHeight(); 1833 1834 do { 1835 /* 1836 * If the current width is bigger than our target, cut it in half 1837 * and sample again. 1838 */ 1839 if (currentWidth > targetWidth) { 1840 currentWidth /= 2; 1841 1842 /* 1843 * If we cut the width too far it means we are on our last 1844 * iteration. Just set it to the target width and finish up. 1845 */ 1846 if (currentWidth < targetWidth) 1847 currentWidth = targetWidth; 1848 } 1849 1850 /* 1851 * If the current height is bigger than our target, cut it in half 1852 * and sample again. 1853 */ 1854 1855 if (currentHeight > targetHeight) { 1856 currentHeight /= 2; 1857 1858 /* 1859 * If we cut the height too far it means we are on our last 1860 * iteration. Just set it to the target height and finish up. 1861 */ 1862 1863 if (currentHeight < targetHeight) 1864 currentHeight = targetHeight; 1865 } 1866 1867 // Render the incremental scaled image. 1868 BufferedImage incrementalImage = scaleImage(src, currentWidth, 1869 currentHeight, interpolationHintValue); 1870 1871 /* 1872 * Before re-assigning our interim (partially scaled) 1873 * incrementalImage to be the new src image before we iterate around 1874 * again to process it down further, we want to flush() the previous 1875 * src image IF (and only IF) it was one of our own temporary 1876 * BufferedImages created during this incremental down-sampling 1877 * cycle. If it wasn't one of ours, then it was the original 1878 * caller-supplied BufferedImage in which case we don't want to 1879 * flush() it and just leave it alone. 1880 */ 1881 if (hasReassignedSrc) 1882 src.flush(); 1883 1884 /* 1885 * Now treat our incremental partially scaled image as the src image 1886 * and cycle through our loop again to do another incremental 1887 * scaling of it (if necessary). 1888 */ 1889 src = incrementalImage; 1890 1891 /* 1892 * Keep track of us re-assigning the original caller-supplied source 1893 * image with one of our interim BufferedImages so we know when to 1894 * explicitly flush the interim "src" on the next cycle through. 1895 */ 1896 if (!hasReassignedSrc) 1897 hasReassignedSrc = true; 1898 1899 // Track how many times we go through this cycle to scale the image. 1900 incrementCount++; 1901 } while (currentWidth != targetWidth || currentHeight != targetHeight); 1902 1903 if (DEBUG) 1904 log("\tScaled Image in %d steps", incrementCount); 1905 1906 /* 1907 * Once the loop has exited, the src image argument is now our scaled 1908 * result image that we want to return. 1909 */ 1910 return src; 1911 } 1912 }