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 org.imgscalr;
017    
018    import java.awt.Color;
019    import java.awt.Graphics;
020    import java.awt.Graphics2D;
021    import java.awt.Image;
022    import java.awt.RenderingHints;
023    import java.awt.Transparency;
024    import java.awt.color.ColorSpace;
025    import java.awt.geom.AffineTransform;
026    import java.awt.geom.Rectangle2D;
027    import java.awt.image.AreaAveragingScaleFilter;
028    import java.awt.image.BufferedImage;
029    import java.awt.image.BufferedImageOp;
030    import java.awt.image.ColorConvertOp;
031    import java.awt.image.ColorModel;
032    import java.awt.image.ConvolveOp;
033    import java.awt.image.ImagingOpException;
034    import java.awt.image.IndexColorModel;
035    import java.awt.image.Kernel;
036    import java.awt.image.RasterFormatException;
037    import java.awt.image.RescaleOp;
038    
039    import javax.imageio.ImageIO;
040    
041    /**
042     * Class used to implement performant, high-quality and intelligent image
043     * scaling and manipulation algorithms in native Java 2D.
044     * <p/>
045     * This class utilizes the Java2D "best practices" for image manipulation,
046     * ensuring that all operations (even most user-provided {@link BufferedImageOp}
047     * s) are hardware accelerated if provided by the platform and host-VM.
048     * <p/>
049     * <h3>Image Quality</h3>
050     * This class implements a few different methods for scaling an image, providing
051     * either the best-looking result, the fastest result or a balanced result
052     * between the two depending on the scaling hint provided (see {@link Method}).
053     * <p/>
054     * This class also implements an optimized version of the incremental scaling
055     * algorithm presented by Chris Campbell in his <a href="http://today.java
056     * .net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html">Perils of
057     * Image.getScaledInstance()</a> article in order to give the best-looking image
058     * resize results (e.g. generating thumbnails that aren't blurry or jagged).
059     * <p>
060     * The results generated by imgscalr using this method, as compared to a single
061     * {@link RenderingHints#VALUE_INTERPOLATION_BICUBIC} scale operation look much
062     * better, especially when using the {@link Method#ULTRA_QUALITY} method.
063     * <p/>
064     * Only when scaling using the {@link Method#AUTOMATIC} method will this class
065     * look at the size of the image before selecting an approach to scaling the
066     * image. If {@link Method#QUALITY} is specified, the best-looking algorithm
067     * possible is always used.
068     * <p/>
069     * Minor modifications are made to Campbell's original implementation in the
070     * form of:
071     * <ol>
072     * <li>Instead of accepting a user-supplied interpolation method,
073     * {@link RenderingHints#VALUE_INTERPOLATION_BICUBIC} interpolation is always
074     * used. This was done after A/B comparison testing with large images
075     * down-scaled to thumbnail sizes showed noticeable "blurring" when BILINEAR
076     * interpolation was used. Given that Campbell's algorithm is only used in
077     * QUALITY mode when down-scaling, it was determined that the user's expectation
078     * of a much less blurry picture would require that BICUBIC be the default
079     * interpolation in order to meet the QUALITY expectation.</li>
080     * <li>After each iteration of the do-while loop that incrementally scales the
081     * source image down, an explicit effort is made to call
082     * {@link BufferedImage#flush()} on the interim temporary {@link BufferedImage}
083     * instances created by the algorithm in an attempt to ensure a more complete GC
084     * cycle by the VM when cleaning up the temporary instances (this is in addition
085     * to disposing of the temporary {@link Graphics2D} references as well).</li>
086     * <li>Extensive comments have been added to increase readability of the code.</li>
087     * <li>Variable names have been expanded to increase readability of the code.</li>
088     * </ol>
089     * <p/>
090     * <strong>NOTE</strong>: This class does not call {@link BufferedImage#flush()}
091     * on any of the <em>source images</em> passed in by calling code; it is up to
092     * the original caller to dispose of their source images when they are no longer
093     * needed so the VM can most efficiently GC them.
094     * <h3>Image Proportions</h3>
095     * All scaling operations implemented by this class maintain the proportions of
096     * the original image unless a mode of {@link Mode#FIT_EXACT} is specified; in
097     * which case the orientation and proportion of the source image is ignored and
098     * the image is stretched (if necessary) to fit the exact dimensions given.
099     * <p/>
100     * When not using {@link Mode#FIT_EXACT}, in order to maintain the
101     * proportionality of the original images, this class implements the following
102     * behavior:
103     * <ol>
104     * <li>If the image is LANDSCAPE-oriented or SQUARE, treat the
105     * <code>targetWidth</code> as the primary dimension and re-calculate the
106     * <code>targetHeight</code> regardless of what is passed in.</li>
107     * <li>If image is PORTRAIT-oriented, treat the <code>targetHeight</code> as the
108     * primary dimension and re-calculate the <code>targetWidth</code> regardless of
109     * what is passed in.</li>
110     * <li>If a {@link Mode} value of {@link Mode#FIT_TO_WIDTH} or
111     * {@link Mode#FIT_TO_HEIGHT} is passed in to the <code>resize</code> method,
112     * the image's orientation is ignored and the scaled image is fit to the
113     * preferred dimension by using the value passed in by the user for that
114     * dimension and recalculating the other (regardless of image orientation). This
115     * is useful, for example, when working with PORTRAIT oriented images that you
116     * need to all be the same width or visa-versa (e.g. showing user profile
117     * pictures in a directory listing).</li>
118     * </ol>
119     * <h3>Optimized Image Handling</h3>
120     * Java2D provides support for a number of different image types defined as
121     * <code>BufferedImage.TYPE_*</code> variables, unfortunately not all image
122     * types are supported equally in the Java2D rendering pipeline.
123     * <p/>
124     * Some more obscure image types either have poor or no support, leading to
125     * severely degraded quality and processing performance when an attempt is made
126     * by imgscalr to create a scaled instance <em>of the same type</em> as the
127     * source image. In many cases, especially when applying {@link BufferedImageOp}
128     * s, using poorly supported image types can even lead to exceptions or total
129     * corruption of the image (e.g. solid black image).
130     * <p/>
131     * imgscalr specifically accounts for and automatically hands
132     * <strong>ALL</strong> of these pain points for you internally by shuffling all
133     * images into one of two types:
134     * <ol>
135     * <li>{@link BufferedImage#TYPE_INT_RGB}</li>
136     * <li>{@link BufferedImage#TYPE_INT_ARGB}</li>
137     * </ol>
138     * depending on if the source image utilizes transparency or not. This is a
139     * recommended approach by the Java2D team for dealing with poorly (or non)
140     * supported image types. More can be read about this issue <a href=
141     * "http://www.mail-archive.com/java2d-interest@capra.eng.sun.com/msg05621.html"
142     * >here</a>.
143     * <p/>
144     * This is also the reason we recommend using
145     * {@link #apply(BufferedImage, BufferedImageOp...)} to apply your own ops to
146     * images even if you aren't using imgscalr for anything else.
147     * <h3>GIF Transparency</h3>
148     * Unfortunately in Java 6 and earlier, support for GIF's
149     * {@link IndexColorModel} is sub-par, both in accurate color-selection and in
150     * maintaining transparency when moving to an image of type
151     * {@link BufferedImage#TYPE_INT_ARGB}; because of this issue when a GIF image
152     * is processed by imgscalr and the result saved as a GIF file (instead of PNG),
153     * it is possible to lose the alpha channel of a transparent image or in the
154     * case of applying an optional {@link BufferedImageOp}, lose the entire picture
155     * all together in the result (long standing JDK bugs are filed for all of these
156     * issues).
157     * <p/>
158     * imgscalr currently does nothing to work around this manually because it is a
159     * defect in the native platform code itself. Fortunately it looks like the
160     * issues are half-fixed in Java 7 and any manual workarounds we could attempt
161     * internally are relatively expensive, in the form of hand-creating and setting
162     * RGB values pixel-by-pixel with a custom {@link ColorModel} in the scaled
163     * image. This would lead to a very measurable negative impact on performance
164     * without the caller understanding why.
165     * <p>
166     * <strong>Workaround</strong>: A workaround to this issue with all version of
167     * Java is to simply save a GIF as a PNG; no change to your code needs to be
168     * made except when the image is saved out, e.g. using {@link ImageIO}.
169     * <p>
170     * When a file type of "PNG" is used, both the transparency and high color
171     * quality will be maintained as the PNG code path in Java2D is superior to the
172     * GIF implementation.
173     * <p>
174     * If the issue with optional {@link BufferedImageOp}s destroying GIF image
175     * content is ever fixed in the platform, saving out resulting images as GIFs
176     * should suddenly start working.
177     * <p>
178     * More can be read about the issue <a
179     * href="http://gman.eichberger.de/2007/07/transparent-gifs-in-java.html"
180     * >here</a> and <a
181     * href="http://ubuntuforums.org/archive/index.php/t-1060128.html">here</a>.
182     * <h3>Thread Safety</h3>
183     * The {@link Scalr} class is <strong>thread-safe</strong> (as all the methods
184     * are <code>static</code>); this class maintains no internal state while
185     * performing any of the provided operations and is safe to call simultaneously
186     * from multiple threads.
187     * <h3>Logging</h3>
188     * This class implements all its debug logging via the
189     * {@link #log(int, String, Object...)} method. At this time logging is done
190     * directly to <code>System.out</code> via the <code>printf</code> method. This
191     * allows the logging to be light weight and easy to capture (every imgscalr log
192     * message is prefixed with the {@link #LOG_PREFIX} string) while adding no
193     * dependencies to the library.
194     * <p/>
195     * Implementation of logging in this class is as efficient as possible; avoiding
196     * any calls to the logger method or passing of arguments if logging is not
197     * enabled to avoid the (hidden) cost of constructing the Object[] argument for
198     * the varargs-based method call.
199     * 
200     * @author Riyad Kalla (software@thebuzzmedia.com)
201     * @since 1.1
202     */
203    public class Scalr {
204            /**
205             * System property name used to define the debug boolean flag.
206             * <p/>
207             * Value is "<code>imgscalr.debug</code>".
208             */
209            public static final String DEBUG_PROPERTY_NAME = "imgscalr.debug";
210    
211            /**
212             * System property name used to define a custom log prefix.
213             * <p/>
214             * Value is "<code>imgscalr.logPrefix</code>".
215             */
216            public static final String LOG_PREFIX_PROPERTY_NAME = "imgscalr.logPrefix";
217    
218            /**
219             * Flag used to indicate if debugging output has been enabled by setting the
220             * "<code>imgscalr.debug</code>" system property to <code>true</code>. This
221             * value will be <code>false</code> if the "<code>imgscalr.debug</code>"
222             * system property is undefined or set to <code>false</code>.
223             * <p/>
224             * This property can be set on startup with:<br/>
225             * <code>
226             * -Dimgscalr.debug=true
227             * </code> or by calling {@link System#setProperty(String, String)} to set a
228             * new property value for {@link #DEBUG_PROPERTY_NAME} before this class is
229             * loaded.
230             * <p/>
231             * Default value is <code>false</code>.
232             */
233            public static final boolean DEBUG = Boolean.getBoolean(DEBUG_PROPERTY_NAME);
234    
235            /**
236             * Prefix to every log message this library logs. Using a well-defined
237             * prefix helps make it easier both visually and programmatically to scan
238             * log files for messages produced by this library.
239             * <p/>
240             * This property can be set on startup with:<br/>
241             * <code>
242             * -Dimgscalr.logPrefix=&lt;YOUR PREFIX HERE&gt;
243             * </code> or by calling {@link System#setProperty(String, String)} to set a
244             * new property value for {@link #LOG_PREFIX_PROPERTY_NAME} before this
245             * class is loaded.
246             * <p/>
247             * Default value is "<code>[imgscalr] </code>" (including the space).
248             */
249            public static final String LOG_PREFIX = System.getProperty(
250                            LOG_PREFIX_PROPERTY_NAME, "[imgscalr] ");
251    
252            /**
253             * A {@link ConvolveOp} using a very light "blur" kernel that acts like an
254             * anti-aliasing filter (softens the image a bit) when applied to an image.
255             * <p/>
256             * A common request by users of the library was that they wished to "soften"
257             * resulting images when scaling them down drastically. After quite a bit of
258             * A/B testing, the kernel used by this Op was selected as the closest match
259             * for the target which was the softer results from the deprecated
260             * {@link AreaAveragingScaleFilter} (which is used internally by the
261             * deprecated {@link Image#getScaledInstance(int, int, int)} method in the
262             * JDK that imgscalr is meant to replace).
263             * <p/>
264             * This ConvolveOp uses a 3x3 kernel with the values:
265             * <table cellpadding="4" border="1">
266             * <tr>
267             * <td>.0f</td>
268             * <td>.08f</td>
269             * <td>.0f</td>
270             * </tr>
271             * <tr>
272             * <td>.08f</td>
273             * <td>.68f</td>
274             * <td>.08f</td>
275             * </tr>
276             * <tr>
277             * <td>.0f</td>
278             * <td>.08f</td>
279             * <td>.0f</td>
280             * </tr>
281             * </table>
282             * <p/>
283             * For those that have worked with ConvolveOps before, this Op uses the
284             * {@link ConvolveOp#EDGE_NO_OP} instruction to not process the pixels along
285             * the very edge of the image (otherwise EDGE_ZERO_FILL would create a
286             * black-border around the image). If you have not worked with a ConvolveOp
287             * before, it just means this default OP will "do the right thing" and not
288             * give you garbage results.
289             * <p/>
290             * This ConvolveOp uses no {@link RenderingHints} values as internally the
291             * {@link ConvolveOp} class only uses hints when doing a color conversion
292             * between the source and destination {@link BufferedImage} targets.
293             * imgscalr allows the {@link ConvolveOp} to create its own destination
294             * image every time, so no color conversion is ever needed and thus no
295             * hints.
296             * <h3>Performance</h3>
297             * Use of this (and other) {@link ConvolveOp}s are hardware accelerated when
298             * possible. For more information on if your image op is hardware
299             * accelerated or not, check the source code of the underlying JDK class
300             * that actually executes the Op code, <a href=
301             * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html"
302             * >sun.awt.image.ImagingLib</a>.
303             * <h3>Known Issues</h3>
304             * In all versions of Java (tested up to Java 7 preview Build 131), running
305             * this op against a GIF with transparency and attempting to save the
306             * resulting image as a GIF results in a corrupted/empty file. The file must
307             * be saved out as a PNG to maintain the transparency.
308             * 
309             * @since 3.0
310             */
311            public static final ConvolveOp OP_ANTIALIAS = new ConvolveOp(
312                            new Kernel(3, 3, new float[] { .0f, .08f, .0f, .08f, .68f, .08f,
313                                            .0f, .08f, .0f }), ConvolveOp.EDGE_NO_OP, null);
314    
315            /**
316             * A {@link RescaleOp} used to make any input image 10% darker.
317             * <p/>
318             * This operation can be applied multiple times in a row if greater than 10%
319             * changes in brightness are desired.
320             * 
321             * @since 4.0
322             */
323            public static final RescaleOp OP_DARKER = new RescaleOp(0.9f, 0, null);
324    
325            /**
326             * A {@link RescaleOp} used to make any input image 10% brighter.
327             * <p/>
328             * This operation can be applied multiple times in a row if greater than 10%
329             * changes in brightness are desired.
330             * 
331             * @since 4.0
332             */
333            public static final RescaleOp OP_BRIGHTER = new RescaleOp(1.1f, 0, null);
334    
335            /**
336             * A {@link ColorConvertOp} used to convert any image to a grayscale color
337             * palette.
338             * <p/>
339             * Applying this op multiple times to the same image has no compounding
340             * effects.
341             * 
342             * @since 4.0
343             */
344            public static final ColorConvertOp OP_GRAYSCALE = new ColorConvertOp(
345                            ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
346    
347            /**
348             * Static initializer used to prepare some of the variables used by this
349             * class.
350             */
351            static {
352                    log(0, "Debug output ENABLED");
353            }
354    
355            /**
356             * Used to define the different scaling hints that the algorithm can use.
357             * 
358             * @author Riyad Kalla (software@thebuzzmedia.com)
359             * @since 1.1
360             */
361            public static enum Method {
362                    /**
363                     * Used to indicate that the scaling implementation should decide which
364                     * method to use in order to get the best looking scaled image in the
365                     * least amount of time.
366                     * <p/>
367                     * The scaling algorithm will use the
368                     * {@link Scalr#THRESHOLD_QUALITY_BALANCED} or
369                     * {@link Scalr#THRESHOLD_BALANCED_SPEED} thresholds as cut-offs to
370                     * decide between selecting the <code>QUALITY</code>,
371                     * <code>BALANCED</code> or <code>SPEED</code> scaling algorithms.
372                     * <p/>
373                     * By default the thresholds chosen will give nearly the best looking
374                     * result in the fastest amount of time. We intend this method to work
375                     * for 80% of people looking to scale an image quickly and get a good
376                     * looking result.
377                     */
378                    AUTOMATIC,
379                    /**
380                     * Used to indicate that the scaling implementation should scale as fast
381                     * as possible and return a result. For smaller images (800px in size)
382                     * this can result in noticeable aliasing but it can be a few magnitudes
383                     * times faster than using the QUALITY method.
384                     */
385                    SPEED,
386                    /**
387                     * Used to indicate that the scaling implementation should use a scaling
388                     * operation balanced between SPEED and QUALITY. Sometimes SPEED looks
389                     * too low quality to be useful (e.g. text can become unreadable when
390                     * scaled using SPEED) but using QUALITY mode will increase the
391                     * processing time too much. This mode provides a "better than SPEED"
392                     * quality in a "less than QUALITY" amount of time.
393                     */
394                    BALANCED,
395                    /**
396                     * Used to indicate that the scaling implementation should do everything
397                     * it can to create as nice of a result as possible. This approach is
398                     * most important for smaller pictures (800px or smaller) and less
399                     * important for larger pictures as the difference between this method
400                     * and the SPEED method become less and less noticeable as the
401                     * source-image size increases. Using the AUTOMATIC method will
402                     * automatically prefer the QUALITY method when scaling an image down
403                     * below 800px in size.
404                     */
405                    QUALITY,
406                    /**
407                     * Used to indicate that the scaling implementation should go above and
408                     * beyond the work done by {@link Method#QUALITY} to make the image look
409                     * exceptionally good at the cost of more processing time. This is
410                     * especially evident when generating thumbnails of images that look
411                     * jagged with some of the other {@link Method}s (even
412                     * {@link Method#QUALITY}).
413                     */
414                    ULTRA_QUALITY;
415            }
416    
417            /**
418             * Used to define the different modes of resizing that the algorithm can
419             * use.
420             * 
421             * @author Riyad Kalla (software@thebuzzmedia.com)
422             * @since 3.1
423             */
424            public static enum Mode {
425                    /**
426                     * Used to indicate that the scaling implementation should calculate
427                     * dimensions for the resultant image by looking at the image's
428                     * orientation and generating proportional dimensions that best fit into
429                     * the target width and height given
430                     * 
431                     * See "Image Proportions" in the {@link Scalr} class description for
432                     * more detail.
433                     */
434                    AUTOMATIC,
435                    /**
436                     * Used to fit the image to the exact dimensions given regardless of the
437                     * image's proportions. If the dimensions are not proportionally
438                     * correct, this will introduce vertical or horizontal stretching to the
439                     * image.
440                     * <p/>
441                     * It is recommended that you use one of the other <code>FIT_TO</code>
442                     * modes or {@link Mode#AUTOMATIC} if you want the image to look
443                     * correct, but if dimension-fitting is the #1 priority regardless of
444                     * how it makes the image look, that is what this mode is for.
445                     */
446                    FIT_EXACT,
447                    /**
448                     * Used to indicate that the scaling implementation should calculate
449                     * dimensions for the resultant image that best-fit within the given
450                     * width, regardless of the orientation of the image.
451                     */
452                    FIT_TO_WIDTH,
453                    /**
454                     * Used to indicate that the scaling implementation should calculate
455                     * dimensions for the resultant image that best-fit within the given
456                     * height, regardless of the orientation of the image.
457                     */
458                    FIT_TO_HEIGHT;
459            }
460    
461            /**
462             * Used to define the different types of rotations that can be applied to an
463             * image during a resize operation.
464             * 
465             * @author Riyad Kalla (software@thebuzzmedia.com)
466             * @since 3.2
467             */
468            public static enum Rotation {
469                    /**
470                     * 90-degree, clockwise rotation (to the right). This is equivalent to a
471                     * quarter-turn of the image to the right; moving the picture on to its
472                     * right side.
473                     */
474                    CW_90,
475                    /**
476                     * 180-degree, clockwise rotation (to the right). This is equivalent to
477                     * 1 half-turn of the image to the right; rotating the picture around
478                     * until it is upside down from the original position.
479                     */
480                    CW_180,
481                    /**
482                     * 270-degree, clockwise rotation (to the right). This is equivalent to
483                     * a quarter-turn of the image to the left; moving the picture on to its
484                     * left side.
485                     */
486                    CW_270,
487                    /**
488                     * Flip the image horizontally by reflecting it around the y axis.
489                     * <p/>
490                     * This is not a standard rotation around a center point, but instead
491                     * creates the mirrored reflection of the image horizontally.
492                     * <p/>
493                     * More specifically, the vertical orientation of the image stays the
494                     * same (the top stays on top, and the bottom on bottom), but the right
495                     * and left sides flip. This is different than a standard rotation where
496                     * the top and bottom would also have been flipped.
497                     */
498                    FLIP_HORZ,
499                    /**
500                     * Flip the image vertically by reflecting it around the x axis.
501                     * <p/>
502                     * This is not a standard rotation around a center point, but instead
503                     * creates the mirrored reflection of the image vertically.
504                     * <p/>
505                     * More specifically, the horizontal orientation of the image stays the
506                     * same (the left stays on the left and the right stays on the right),
507                     * but the top and bottom sides flip. This is different than a standard
508                     * rotation where the left and right would also have been flipped.
509                     */
510                    FLIP_VERT;
511            }
512    
513            /**
514             * Threshold (in pixels) at which point the scaling operation using the
515             * {@link Method#AUTOMATIC} method will decide if a {@link Method#BALANCED}
516             * method will be used (if smaller than or equal to threshold) or a
517             * {@link Method#SPEED} method will be used (if larger than threshold).
518             * <p/>
519             * The bigger the image is being scaled to, the less noticeable degradations
520             * in the image becomes and the faster algorithms can be selected.
521             * <p/>
522             * The value of this threshold (1600) was chosen after visual, by-hand, A/B
523             * testing between different types of images scaled with this library; both
524             * photographs and screenshots. It was determined that images below this
525             * size need to use a {@link Method#BALANCED} scale method to look decent in
526             * most all cases while using the faster {@link Method#SPEED} method for
527             * images bigger than this threshold showed no noticeable degradation over a
528             * <code>BALANCED</code> scale.
529             */
530            public static final int THRESHOLD_BALANCED_SPEED = 1600;
531    
532            /**
533             * Threshold (in pixels) at which point the scaling operation using the
534             * {@link Method#AUTOMATIC} method will decide if a {@link Method#QUALITY}
535             * method will be used (if smaller than or equal to threshold) or a
536             * {@link Method#BALANCED} method will be used (if larger than threshold).
537             * <p/>
538             * The bigger the image is being scaled to, the less noticeable degradations
539             * in the image becomes and the faster algorithms can be selected.
540             * <p/>
541             * The value of this threshold (800) was chosen after visual, by-hand, A/B
542             * testing between different types of images scaled with this library; both
543             * photographs and screenshots. It was determined that images below this
544             * size need to use a {@link Method#QUALITY} scale method to look decent in
545             * most all cases while using the faster {@link Method#BALANCED} method for
546             * images bigger than this threshold showed no noticeable degradation over a
547             * <code>QUALITY</code> scale.
548             */
549            public static final int THRESHOLD_QUALITY_BALANCED = 800;
550    
551            /**
552             * Used to apply, in the order given, 1 or more {@link BufferedImageOp}s to
553             * a given {@link BufferedImage} and return the result.
554             * <p/>
555             * <strong>Feature</strong>: This implementation works around <a
556             * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4965606">a
557             * decade-old JDK bug</a> that can cause a {@link RasterFormatException}
558             * when applying a perfectly valid {@link BufferedImageOp}s to images.
559             * <p/>
560             * <strong>Feature</strong>: This implementation also works around
561             * {@link BufferedImageOp}s failing to apply and throwing
562             * {@link ImagingOpException}s when run against a <code>src</code> image
563             * type that is poorly supported. Unfortunately using {@link ImageIO} and
564             * standard Java methods to load images provides no consistency in getting
565             * images in well-supported formats. This method automatically accounts and
566             * corrects for all those problems (if necessary).
567             * <p/>
568             * It is recommended you always use this method to apply any
569             * {@link BufferedImageOp}s instead of relying on directly using the
570             * {@link BufferedImageOp#filter(BufferedImage, BufferedImage)} method.
571             * <p/>
572             * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are
573             * hardware accelerated operations, but many of the most popular (like
574             * {@link ConvolveOp}) are. For more information on if your image op is
575             * hardware accelerated or not, check the source code of the underlying JDK
576             * class that actually executes the Op code, <a href=
577             * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html"
578             * >sun.awt.image.ImagingLib</a>.
579             * <p/>
580             * <strong>TIP</strong>: This operation leaves the original <code>src</code>
581             * image unmodified. If the caller is done with the <code>src</code> image
582             * after getting the result of this operation, remember to call
583             * {@link BufferedImage#flush()} on the <code>src</code> to free up native
584             * resources and make it easier for the GC to collect the unused image.
585             * 
586             * @param src
587             *            The image that will have the ops applied to it.
588             * @param ops
589             *            <code>1</code> or more ops to apply to the image.
590             * 
591             * @return a new {@link BufferedImage} that represents the <code>src</code>
592             *         with all the given operations applied to it.
593             * 
594             * @throws IllegalArgumentException
595             *             if <code>src</code> is <code>null</code>.
596             * @throws IllegalArgumentException
597             *             if <code>ops</code> is <code>null</code> or empty.
598             * @throws ImagingOpException
599             *             if one of the given {@link BufferedImageOp}s fails to apply.
600             *             These exceptions bubble up from the inside of most of the
601             *             {@link BufferedImageOp} implementations and are explicitly
602             *             defined on the imgscalr API to make it easier for callers to
603             *             catch the exception (if they are passing along optional ops
604             *             to be applied). imgscalr takes detailed steps to avoid the
605             *             most common pitfalls that will cause {@link BufferedImageOp}s
606             *             to fail, even when using straight forward JDK-image
607             *             operations.
608             */
609            public static BufferedImage apply(BufferedImage src, BufferedImageOp... ops)
610                            throws IllegalArgumentException, ImagingOpException {
611                    long t = System.currentTimeMillis();
612    
613                    if (src == null)
614                            throw new IllegalArgumentException("src cannot be null");
615                    if (ops == null || ops.length == 0)
616                            throw new IllegalArgumentException("ops cannot be null or empty");
617    
618                    int type = src.getType();
619    
620                    /*
621                     * Ensure the src image is in the best supported image type before we
622                     * continue, otherwise it is possible our calls below to getBounds2D and
623                     * certainly filter(...) may fail if not.
624                     * 
625                     * Java2D makes an attempt at applying most BufferedImageOps using
626                     * hardware acceleration via the ImagingLib internal library.
627                     * 
628                     * Unfortunately may of the BufferedImageOp are written to simply fail
629                     * with an ImagingOpException if the operation cannot be applied with no
630                     * additional information about what went wrong or attempts at
631                     * re-applying it in different ways.
632                     * 
633                     * This is assuming the failing BufferedImageOp even returns a null
634                     * image after failing to apply; some simply return a corrupted/black
635                     * image that result in no exception and it is up to the user to
636                     * discover this.
637                     * 
638                     * In internal testing, EVERY failure I've ever seen was the result of
639                     * the source image being in a poorly-supported BufferedImage Type like
640                     * BGR or ABGR (even though it was loaded with ImageIO).
641                     * 
642                     * To avoid this nasty/stupid surprise with BufferedImageOps, we always
643                     * ensure that the src image starts in an optimally supported format
644                     * before we try and apply the filter.
645                     */
646                    if (!(type == BufferedImage.TYPE_INT_RGB || type == BufferedImage.TYPE_INT_ARGB))
647                            src = copyToOptimalImage(src);
648    
649                    if (DEBUG)
650                            log(0, "Applying %d BufferedImageOps...", ops.length);
651    
652                    boolean hasReassignedSrc = false;
653    
654                    for (int i = 0; i < ops.length; i++) {
655                            long subT = System.currentTimeMillis();
656                            BufferedImageOp op = ops[i];
657    
658                            // Skip null ops instead of throwing an exception.
659                            if (op == null)
660                                    continue;
661    
662                            if (DEBUG)
663                                    log(1, "Applying BufferedImageOp [class=%s, toString=%s]...",
664                                                    op.getClass(), op.toString());
665    
666                            /*
667                             * Must use op.getBounds instead of src.getWidth and src.getHeight
668                             * because we are trying to create an image big enough to hold the
669                             * result of this operation (which may be to scale the image
670                             * smaller), in that case the bounds reported by this op and the
671                             * bounds reported by the source image will be different.
672                             */
673                            Rectangle2D resultBounds = op.getBounds2D(src);
674    
675                            // Watch out for flaky/misbehaving ops that fail to work right.
676                            if (resultBounds == null)
677                                    throw new ImagingOpException(
678                                                    "BufferedImageOp ["
679                                                                    + op.toString()
680                                                                    + "] getBounds2D(src) returned null bounds for the target image; this should not happen and indicates a problem with application of this type of op.");
681    
682                            /*
683                             * We must manually create the target image; we cannot rely on the
684                             * null-destination filter() method to create a valid destination
685                             * for us thanks to this JDK bug that has been filed for almost a
686                             * decade:
687                             * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4965606
688                             */
689                            BufferedImage dest = createOptimalImage(src,
690                                            (int) Math.round(resultBounds.getWidth()),
691                                            (int) Math.round(resultBounds.getHeight()));
692    
693                            // Perform the operation, update our result to return.
694                            BufferedImage result = op.filter(src, dest);
695    
696                            /*
697                             * Flush the 'src' image ONLY IF it is one of our interim temporary
698                             * images being used when applying 2 or more operations back to
699                             * back. We never want to flush the original image passed in.
700                             */
701                            if (hasReassignedSrc)
702                                    src.flush();
703    
704                            /*
705                             * Incase there are more operations to perform, update what we
706                             * consider the 'src' reference to our last result so on the next
707                             * iteration the next op is applied to this result and not back
708                             * against the original src passed in.
709                             */
710                            src = result;
711    
712                            /*
713                             * Keep track of when we re-assign 'src' to an interim temporary
714                             * image, so we know when we can explicitly flush it and clean up
715                             * references on future iterations.
716                             */
717                            hasReassignedSrc = true;
718    
719                            if (DEBUG)
720                                    log(1,
721                                                    "Applied BufferedImageOp in %d ms, result [width=%d, height=%d]",
722                                                    System.currentTimeMillis() - subT, result.getWidth(),
723                                                    result.getHeight());
724                    }
725    
726                    if (DEBUG)
727                            log(0, "All %d BufferedImageOps applied in %d ms", ops.length,
728                                            System.currentTimeMillis() - t);
729    
730                    return src;
731            }
732    
733            /**
734             * Used to crop the given <code>src</code> image from the top-left corner
735             * and applying any optional {@link BufferedImageOp}s to the result before
736             * returning it.
737             * <p/>
738             * <strong>TIP</strong>: This operation leaves the original <code>src</code>
739             * image unmodified. If the caller is done with the <code>src</code> image
740             * after getting the result of this operation, remember to call
741             * {@link BufferedImage#flush()} on the <code>src</code> to free up native
742             * resources and make it easier for the GC to collect the unused image.
743             * 
744             * @param src
745             *            The image to crop.
746             * @param width
747             *            The width of the bounding cropping box.
748             * @param height
749             *            The height of the bounding cropping box.
750             * @param ops
751             *            <code>0</code> or more ops to apply to the image. If
752             *            <code>null</code> or empty then <code>src</code> is return
753             *            unmodified.
754             * 
755             * @return a new {@link BufferedImage} representing the cropped region of
756             *         the <code>src</code> image with any optional operations applied
757             *         to it.
758             * 
759             * @throws IllegalArgumentException
760             *             if <code>src</code> is <code>null</code>.
761             * @throws IllegalArgumentException
762             *             if any coordinates of the bounding crop box is invalid within
763             *             the bounds of the <code>src</code> image (e.g. negative or
764             *             too big).
765             * @throws ImagingOpException
766             *             if one of the given {@link BufferedImageOp}s fails to apply.
767             *             These exceptions bubble up from the inside of most of the
768             *             {@link BufferedImageOp} implementations and are explicitly
769             *             defined on the imgscalr API to make it easier for callers to
770             *             catch the exception (if they are passing along optional ops
771             *             to be applied). imgscalr takes detailed steps to avoid the
772             *             most common pitfalls that will cause {@link BufferedImageOp}s
773             *             to fail, even when using straight forward JDK-image
774             *             operations.
775             */
776            public static BufferedImage crop(BufferedImage src, int width, int height,
777                            BufferedImageOp... ops) throws IllegalArgumentException,
778                            ImagingOpException {
779                    return crop(src, 0, 0, width, height, ops);
780            }
781    
782            /**
783             * Used to crop the given <code>src</code> image and apply any optional
784             * {@link BufferedImageOp}s to it before returning the result.
785             * <p/>
786             * <strong>TIP</strong>: This operation leaves the original <code>src</code>
787             * image unmodified. If the caller is done with the <code>src</code> image
788             * after getting the result of this operation, remember to call
789             * {@link BufferedImage#flush()} on the <code>src</code> to free up native
790             * resources and make it easier for the GC to collect the unused image.
791             * 
792             * @param src
793             *            The image to crop.
794             * @param x
795             *            The x-coordinate of the top-left corner of the bounding box
796             *            used for cropping.
797             * @param y
798             *            The y-coordinate of the top-left corner of the bounding box
799             *            used for cropping.
800             * @param width
801             *            The width of the bounding cropping box.
802             * @param height
803             *            The height of the bounding cropping box.
804             * @param ops
805             *            <code>0</code> or more ops to apply to the image. If
806             *            <code>null</code> or empty then <code>src</code> is return
807             *            unmodified.
808             * 
809             * @return a new {@link BufferedImage} representing the cropped region of
810             *         the <code>src</code> image with any optional operations applied
811             *         to it.
812             * 
813             * @throws IllegalArgumentException
814             *             if <code>src</code> is <code>null</code>.
815             * @throws IllegalArgumentException
816             *             if any coordinates of the bounding crop box is invalid within
817             *             the bounds of the <code>src</code> image (e.g. negative or
818             *             too big).
819             * @throws ImagingOpException
820             *             if one of the given {@link BufferedImageOp}s fails to apply.
821             *             These exceptions bubble up from the inside of most of the
822             *             {@link BufferedImageOp} implementations and are explicitly
823             *             defined on the imgscalr API to make it easier for callers to
824             *             catch the exception (if they are passing along optional ops
825             *             to be applied). imgscalr takes detailed steps to avoid the
826             *             most common pitfalls that will cause {@link BufferedImageOp}s
827             *             to fail, even when using straight forward JDK-image
828             *             operations.
829             */
830            public static BufferedImage crop(BufferedImage src, int x, int y,
831                            int width, int height, BufferedImageOp... ops)
832                            throws IllegalArgumentException, ImagingOpException {
833                    long t = System.currentTimeMillis();
834    
835                    if (src == null)
836                            throw new IllegalArgumentException("src cannot be null");
837                    if (x < 0 || y < 0 || width < 0 || height < 0)
838                            throw new IllegalArgumentException("Invalid crop bounds: x [" + x
839                                            + "], y [" + y + "], width [" + width + "] and height ["
840                                            + height + "] must all be >= 0");
841    
842                    int srcWidth = src.getWidth();
843                    int srcHeight = src.getHeight();
844    
845                    if ((x + width) > srcWidth)
846                            throw new IllegalArgumentException(
847                                            "Invalid crop bounds: x + width [" + (x + width)
848                                                            + "] must be <= src.getWidth() [" + srcWidth + "]");
849                    if ((y + height) > srcHeight)
850                            throw new IllegalArgumentException(
851                                            "Invalid crop bounds: y + height [" + (y + height)
852                                                            + "] must be <= src.getHeight() [" + srcHeight
853                                                            + "]");
854    
855                    if (DEBUG)
856                            log(0,
857                                            "Cropping Image [width=%d, height=%d] to [x=%d, y=%d, width=%d, height=%d]...",
858                                            srcWidth, srcHeight, x, y, width, height);
859    
860                    // Create a target image of an optimal type to render into.
861                    BufferedImage result = createOptimalImage(src, width, height);
862                    Graphics g = result.getGraphics();
863    
864                    /*
865                     * Render the region specified by our crop bounds from the src image
866                     * directly into our result image (which is the exact size of the crop
867                     * region).
868                     */
869                    g.drawImage(src, 0, 0, width, height, x, y, (x + width), (y + height),
870                                    null);
871                    g.dispose();
872    
873                    if (DEBUG)
874                            log(0, "Cropped Image in %d ms", System.currentTimeMillis() - t);
875    
876                    // Apply any optional operations (if specified).
877                    if (ops != null && ops.length > 0)
878                            result = apply(result, ops);
879    
880                    return result;
881            }
882    
883            /**
884             * Used to apply padding around the edges of an image using
885             * {@link Color#BLACK} to fill the extra padded space and then return the
886             * result.
887             * <p/>
888             * The amount of <code>padding</code> specified is applied to all sides;
889             * more specifically, a <code>padding</code> of <code>2</code> would add 2
890             * extra pixels of space (filled by the given <code>color</code>) on the
891             * top, bottom, left and right sides of the resulting image causing the
892             * result to be 4 pixels wider and 4 pixels taller than the <code>src</code>
893             * image.
894             * <p/>
895             * <strong>TIP</strong>: This operation leaves the original <code>src</code>
896             * image unmodified. If the caller is done with the <code>src</code> image
897             * after getting the result of this operation, remember to call
898             * {@link BufferedImage#flush()} on the <code>src</code> to free up native
899             * resources and make it easier for the GC to collect the unused image.
900             * 
901             * @param src
902             *            The image the padding will be added to.
903             * @param padding
904             *            The number of pixels of padding to add to each side in the
905             *            resulting image. If this value is <code>0</code> then
906             *            <code>src</code> is returned unmodified.
907             * @param ops
908             *            <code>0</code> or more ops to apply to the image. If
909             *            <code>null</code> or empty then <code>src</code> is return
910             *            unmodified.
911             * 
912             * @return a new {@link BufferedImage} representing <code>src</code> with
913             *         the given padding applied to it.
914             * 
915             * @throws IllegalArgumentException
916             *             if <code>src</code> is <code>null</code>.
917             * @throws IllegalArgumentException
918             *             if <code>padding</code> is &lt; <code>1</code>.
919             * @throws ImagingOpException
920             *             if one of the given {@link BufferedImageOp}s fails to apply.
921             *             These exceptions bubble up from the inside of most of the
922             *             {@link BufferedImageOp} implementations and are explicitly
923             *             defined on the imgscalr API to make it easier for callers to
924             *             catch the exception (if they are passing along optional ops
925             *             to be applied). imgscalr takes detailed steps to avoid the
926             *             most common pitfalls that will cause {@link BufferedImageOp}s
927             *             to fail, even when using straight forward JDK-image
928             *             operations.
929             */
930            public static BufferedImage pad(BufferedImage src, int padding,
931                            BufferedImageOp... ops) throws IllegalArgumentException,
932                            ImagingOpException {
933                    return pad(src, padding, Color.BLACK);
934            }
935    
936            /**
937             * Used to apply padding around the edges of an image using the given color
938             * to fill the extra padded space and then return the result. {@link Color}s
939             * using an alpha channel (i.e. transparency) are supported.
940             * <p/>
941             * The amount of <code>padding</code> specified is applied to all sides;
942             * more specifically, a <code>padding</code> of <code>2</code> would add 2
943             * extra pixels of space (filled by the given <code>color</code>) on the
944             * top, bottom, left and right sides of the resulting image causing the
945             * result to be 4 pixels wider and 4 pixels taller than the <code>src</code>
946             * image.
947             * <p/>
948             * <strong>TIP</strong>: This operation leaves the original <code>src</code>
949             * image unmodified. If the caller is done with the <code>src</code> image
950             * after getting the result of this operation, remember to call
951             * {@link BufferedImage#flush()} on the <code>src</code> to free up native
952             * resources and make it easier for the GC to collect the unused image.
953             * 
954             * @param src
955             *            The image the padding will be added to.
956             * @param padding
957             *            The number of pixels of padding to add to each side in the
958             *            resulting image. If this value is <code>0</code> then
959             *            <code>src</code> is returned unmodified.
960             * @param color
961             *            The color to fill the padded space with. {@link Color}s using
962             *            an alpha channel (i.e. transparency) are supported.
963             * @param ops
964             *            <code>0</code> or more ops to apply to the image. If
965             *            <code>null</code> or empty then <code>src</code> is return
966             *            unmodified.
967             * 
968             * @return a new {@link BufferedImage} representing <code>src</code> with
969             *         the given padding applied to it.
970             * 
971             * @throws IllegalArgumentException
972             *             if <code>src</code> is <code>null</code>.
973             * @throws IllegalArgumentException
974             *             if <code>padding</code> is &lt; <code>1</code>.
975             * @throws IllegalArgumentException
976             *             if <code>color</code> is <code>null</code>.
977             * @throws ImagingOpException
978             *             if one of the given {@link BufferedImageOp}s fails to apply.
979             *             These exceptions bubble up from the inside of most of the
980             *             {@link BufferedImageOp} implementations and are explicitly
981             *             defined on the imgscalr API to make it easier for callers to
982             *             catch the exception (if they are passing along optional ops
983             *             to be applied). imgscalr takes detailed steps to avoid the
984             *             most common pitfalls that will cause {@link BufferedImageOp}s
985             *             to fail, even when using straight forward JDK-image
986             *             operations.
987             */
988            public static BufferedImage pad(BufferedImage src, int padding,
989                            Color color, BufferedImageOp... ops)
990                            throws IllegalArgumentException, ImagingOpException {
991                    long t = System.currentTimeMillis();
992    
993                    if (src == null)
994                            throw new IllegalArgumentException("src cannot be null");
995                    if (padding < 1)
996                            throw new IllegalArgumentException("padding [" + padding
997                                            + "] must be > 0");
998                    if (color == null)
999                            throw new IllegalArgumentException("color cannot be null");
1000    
1001                    int srcWidth = src.getWidth();
1002                    int srcHeight = src.getHeight();
1003    
1004                    /*
1005                     * Double the padding to account for all sides of the image. More
1006                     * specifically, if padding is "1" we add 2 pixels to width and 2 to
1007                     * height, so we have 1 new pixel of padding all the way around our
1008                     * image.
1009                     */
1010                    int sizeDiff = (padding * 2);
1011                    int newWidth = srcWidth + sizeDiff;
1012                    int newHeight = srcHeight + sizeDiff;
1013    
1014                    if (DEBUG)
1015                            log(0,
1016                                            "Padding Image from [originalWidth=%d, originalHeight=%d, padding=%d] to [newWidth=%d, newHeight=%d]...",
1017                                            srcWidth, srcHeight, padding, newWidth, newHeight);
1018    
1019                    boolean colorHasAlpha = (color.getAlpha() != 255);
1020                    boolean imageHasAlpha = (src.getTransparency() != BufferedImage.OPAQUE);
1021    
1022                    BufferedImage result;
1023    
1024                    /*
1025                     * We need to make sure our resulting image that we render into contains
1026                     * alpha if either our original image OR the padding color we are using
1027                     * contain it.
1028                     */
1029                    if (colorHasAlpha || imageHasAlpha) {
1030                            if (DEBUG)
1031                                    log(1,
1032                                                    "Transparency FOUND in source image or color, using ARGB image type...");
1033    
1034                            result = new BufferedImage(newWidth, newHeight,
1035                                            BufferedImage.TYPE_INT_ARGB);
1036                    } else {
1037                            if (DEBUG)
1038                                    log(1,
1039                                                    "Transparency NOT FOUND in source image or color, using RGB image type...");
1040    
1041                            result = new BufferedImage(newWidth, newHeight,
1042                                            BufferedImage.TYPE_INT_RGB);
1043                    }
1044    
1045                    Graphics g = result.getGraphics();
1046    
1047                    // "Clear" the background of the new image with our padding color first.
1048                    g.setColor(color);
1049                    g.fillRect(0, 0, newWidth, newHeight);
1050    
1051                    // Draw the image into the center of the new padded image.
1052                    g.drawImage(src, padding, padding, null);
1053                    g.dispose();
1054    
1055                    if (DEBUG)
1056                            log(0, "Padding Applied in %d ms", System.currentTimeMillis() - t);
1057    
1058                    // Apply any optional operations (if specified).
1059                    if (ops != null && ops.length > 0)
1060                            result = apply(result, ops);
1061    
1062                    return result;
1063            }
1064    
1065            /**
1066             * Resize a given image (maintaining its original proportion) to a width and
1067             * height no bigger than <code>targetSize</code> and apply the given
1068             * {@link BufferedImageOp}s (if any) to the result before returning it.
1069             * <p/>
1070             * A scaling method of {@link Method#AUTOMATIC} and mode of
1071             * {@link Mode#AUTOMATIC} are used.
1072             * <p/>
1073             * <strong>TIP</strong>: This operation leaves the original <code>src</code>
1074             * image unmodified. If the caller is done with the <code>src</code> image
1075             * after getting the result of this operation, remember to call
1076             * {@link BufferedImage#flush()} on the <code>src</code> to free up native
1077             * resources and make it easier for the GC to collect the unused image.
1078             * 
1079             * @param src
1080             *            The image that will be scaled.
1081             * @param targetSize
1082             *            The target width and height (square) that you wish the image
1083             *            to fit within.
1084             * @param ops
1085             *            <code>0</code> or more optional image operations (e.g.
1086             *            sharpen, blur, etc.) that can be applied to the final result
1087             *            before returning the image.
1088             * 
1089             * @return a new {@link BufferedImage} representing the scaled
1090             *         <code>src</code> image.
1091             * 
1092             * @throws IllegalArgumentException
1093             *             if <code>src</code> is <code>null</code>.
1094             * @throws IllegalArgumentException
1095             *             if <code>targetSize</code> is &lt; 0.
1096             * @throws ImagingOpException
1097             *             if one of the given {@link BufferedImageOp}s fails to apply.
1098             *             These exceptions bubble up from the inside of most of the
1099             *             {@link BufferedImageOp} implementations and are explicitly
1100             *             defined on the imgscalr API to make it easier for callers to
1101             *             catch the exception (if they are passing along optional ops
1102             *             to be applied). imgscalr takes detailed steps to avoid the
1103             *             most common pitfalls that will cause {@link BufferedImageOp}s
1104             *             to fail, even when using straight forward JDK-image
1105             *             operations.
1106             */
1107            public static BufferedImage resize(BufferedImage src, int targetSize,
1108                            BufferedImageOp... ops) throws IllegalArgumentException,
1109                            ImagingOpException {
1110                    return resize(src, Method.AUTOMATIC, Mode.AUTOMATIC, targetSize,
1111                                    targetSize, ops);
1112            }
1113    
1114            /**
1115             * Resize a given image (maintaining its original proportion) to a width and
1116             * height no bigger than <code>targetSize</code> using the given scaling
1117             * method and apply the given {@link BufferedImageOp}s (if any) to the
1118             * result before returning it.
1119             * <p/>
1120             * A mode of {@link Mode#AUTOMATIC} is used.
1121             * <p/>
1122             * <strong>TIP</strong>: This operation leaves the original <code>src</code>
1123             * image unmodified. If the caller is done with the <code>src</code> image
1124             * after getting the result of this operation, remember to call
1125             * {@link BufferedImage#flush()} on the <code>src</code> to free up native
1126             * resources and make it easier for the GC to collect the unused image.
1127             * 
1128             * @param src
1129             *            The image that will be scaled.
1130             * @param scalingMethod
1131             *            The method used for scaling the image; preferring speed to
1132             *            quality or a balance of both.
1133             * @param targetSize
1134             *            The target width and height (square) that you wish the image
1135             *            to fit within.
1136             * @param ops
1137             *            <code>0</code> or more optional image operations (e.g.
1138             *            sharpen, blur, etc.) that can be applied to the final result
1139             *            before returning the image.
1140             * 
1141             * @return a new {@link BufferedImage} representing the scaled
1142             *         <code>src</code> image.
1143             * 
1144             * @throws IllegalArgumentException
1145             *             if <code>src</code> is <code>null</code>.
1146             * @throws IllegalArgumentException
1147             *             if <code>scalingMethod</code> is <code>null</code>.
1148             * @throws IllegalArgumentException
1149             *             if <code>targetSize</code> is &lt; 0.
1150             * @throws ImagingOpException
1151             *             if one of the given {@link BufferedImageOp}s fails to apply.
1152             *             These exceptions bubble up from the inside of most of the
1153             *             {@link BufferedImageOp} implementations and are explicitly
1154             *             defined on the imgscalr API to make it easier for callers to
1155             *             catch the exception (if they are passing along optional ops
1156             *             to be applied). imgscalr takes detailed steps to avoid the
1157             *             most common pitfalls that will cause {@link BufferedImageOp}s
1158             *             to fail, even when using straight forward JDK-image
1159             *             operations.
1160             * 
1161             * @see Method
1162             */
1163            public static BufferedImage resize(BufferedImage src, Method scalingMethod,
1164                            int targetSize, BufferedImageOp... ops)
1165                            throws IllegalArgumentException, ImagingOpException {
1166                    return resize(src, scalingMethod, Mode.AUTOMATIC, targetSize,
1167                                    targetSize, ops);
1168            }
1169    
1170            /**
1171             * Resize a given image (maintaining its original proportion) to a width and
1172             * height no bigger than <code>targetSize</code> (or fitting the image to
1173             * the given WIDTH or HEIGHT explicitly, depending on the {@link Mode}
1174             * specified) and apply the given {@link BufferedImageOp}s (if any) to the
1175             * result before returning it.
1176             * <p/>
1177             * A scaling method of {@link Method#AUTOMATIC} is used.
1178             * <p/>
1179             * <strong>TIP</strong>: This operation leaves the original <code>src</code>
1180             * image unmodified. If the caller is done with the <code>src</code> image
1181             * after getting the result of this operation, remember to call
1182             * {@link BufferedImage#flush()} on the <code>src</code> to free up native
1183             * resources and make it easier for the GC to collect the unused image.
1184             * 
1185             * @param src
1186             *            The image that will be scaled.
1187             * @param resizeMode
1188             *            Used to indicate how imgscalr should calculate the final
1189             *            target size for the image, either fitting the image to the
1190             *            given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image
1191             *            to the given height ({@link Mode#FIT_TO_HEIGHT}). If
1192             *            {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate
1193             *            proportional dimensions for the scaled image based on its
1194             *            orientation (landscape, square or portrait). Unless you have
1195             *            very specific size requirements, most of the time you just
1196             *            want to use {@link Mode#AUTOMATIC} to "do the right thing".
1197             * @param targetSize
1198             *            The target width and height (square) that you wish the image
1199             *            to fit within.
1200             * @param ops
1201             *            <code>0</code> or more optional image operations (e.g.
1202             *            sharpen, blur, etc.) that can be applied to the final result
1203             *            before returning the image.
1204             * 
1205             * @return a new {@link BufferedImage} representing the scaled
1206             *         <code>src</code> image.
1207             * 
1208             * @throws IllegalArgumentException
1209             *             if <code>src</code> is <code>null</code>.
1210             * @throws IllegalArgumentException
1211             *             if <code>resizeMode</code> is <code>null</code>.
1212             * @throws IllegalArgumentException
1213             *             if <code>targetSize</code> is &lt; 0.
1214             * @throws ImagingOpException
1215             *             if one of the given {@link BufferedImageOp}s fails to apply.
1216             *             These exceptions bubble up from the inside of most of the
1217             *             {@link BufferedImageOp} implementations and are explicitly
1218             *             defined on the imgscalr API to make it easier for callers to
1219             *             catch the exception (if they are passing along optional ops
1220             *             to be applied). imgscalr takes detailed steps to avoid the
1221             *             most common pitfalls that will cause {@link BufferedImageOp}s
1222             *             to fail, even when using straight forward JDK-image
1223             *             operations.
1224             * 
1225             * @see Mode
1226             */
1227            public static BufferedImage resize(BufferedImage src, Mode resizeMode,
1228                            int targetSize, BufferedImageOp... ops)
1229                            throws IllegalArgumentException, ImagingOpException {
1230                    return resize(src, Method.AUTOMATIC, resizeMode, targetSize,
1231                                    targetSize, ops);
1232            }
1233    
1234            /**
1235             * Resize a given image (maintaining its original proportion) to a width and
1236             * height no bigger than <code>targetSize</code> (or fitting the image to
1237             * the given WIDTH or HEIGHT explicitly, depending on the {@link Mode}
1238             * specified) using the given scaling method and apply the given
1239             * {@link BufferedImageOp}s (if any) to the result before returning it.
1240             * <p/>
1241             * <strong>TIP</strong>: This operation leaves the original <code>src</code>
1242             * image unmodified. If the caller is done with the <code>src</code> image
1243             * after getting the result of this operation, remember to call
1244             * {@link BufferedImage#flush()} on the <code>src</code> to free up native
1245             * resources and make it easier for the GC to collect the unused image.
1246             * 
1247             * @param src
1248             *            The image that will be scaled.
1249             * @param scalingMethod
1250             *            The method used for scaling the image; preferring speed to
1251             *            quality or a balance of both.
1252             * @param resizeMode
1253             *            Used to indicate how imgscalr should calculate the final
1254             *            target size for the image, either fitting the image to the
1255             *            given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image
1256             *            to the given height ({@link Mode#FIT_TO_HEIGHT}). If
1257             *            {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate
1258             *            proportional dimensions for the scaled image based on its
1259             *            orientation (landscape, square or portrait). Unless you have
1260             *            very specific size requirements, most of the time you just
1261             *            want to use {@link Mode#AUTOMATIC} to "do the right thing".
1262             * @param targetSize
1263             *            The target width and height (square) that you wish the image
1264             *            to fit within.
1265             * @param ops
1266             *            <code>0</code> or more optional image operations (e.g.
1267             *            sharpen, blur, etc.) that can be applied to the final result
1268             *            before returning the image.
1269             * 
1270             * @return a new {@link BufferedImage} representing the scaled
1271             *         <code>src</code> image.
1272             * 
1273             * @throws IllegalArgumentException
1274             *             if <code>src</code> is <code>null</code>.
1275             * @throws IllegalArgumentException
1276             *             if <code>scalingMethod</code> is <code>null</code>.
1277             * @throws IllegalArgumentException
1278             *             if <code>resizeMode</code> is <code>null</code>.
1279             * @throws IllegalArgumentException
1280             *             if <code>targetSize</code> is &lt; 0.
1281             * @throws ImagingOpException
1282             *             if one of the given {@link BufferedImageOp}s fails to apply.
1283             *             These exceptions bubble up from the inside of most of the
1284             *             {@link BufferedImageOp} implementations and are explicitly
1285             *             defined on the imgscalr API to make it easier for callers to
1286             *             catch the exception (if they are passing along optional ops
1287             *             to be applied). imgscalr takes detailed steps to avoid the
1288             *             most common pitfalls that will cause {@link BufferedImageOp}s
1289             *             to fail, even when using straight forward JDK-image
1290             *             operations.
1291             * 
1292             * @see Method
1293             * @see Mode
1294             */
1295            public static BufferedImage resize(BufferedImage src, Method scalingMethod,
1296                            Mode resizeMode, int targetSize, BufferedImageOp... ops)
1297                            throws IllegalArgumentException, ImagingOpException {
1298                    return resize(src, scalingMethod, resizeMode, targetSize, targetSize,
1299                                    ops);
1300            }
1301    
1302            /**
1303             * Resize a given image (maintaining its original proportion) to the target
1304             * width and height and apply the given {@link BufferedImageOp}s (if any) to
1305             * the result before returning it.
1306             * <p/>
1307             * A scaling method of {@link Method#AUTOMATIC} and mode of
1308             * {@link Mode#AUTOMATIC} are used.
1309             * <p/>
1310             * <strong>TIP</strong>: See the class description to understand how this
1311             * class handles recalculation of the <code>targetWidth</code> or
1312             * <code>targetHeight</code> depending on the image's orientation in order
1313             * to maintain the original proportion.
1314             * <p/>
1315             * <strong>TIP</strong>: This operation leaves the original <code>src</code>
1316             * image unmodified. If the caller is done with the <code>src</code> image
1317             * after getting the result of this operation, remember to call
1318             * {@link BufferedImage#flush()} on the <code>src</code> to free up native
1319             * resources and make it easier for the GC to collect the unused image.
1320             * 
1321             * @param src
1322             *            The image that will be scaled.
1323             * @param targetWidth
1324             *            The target width that you wish the image to have.
1325             * @param targetHeight
1326             *            The target height that you wish the image to have.
1327             * @param ops
1328             *            <code>0</code> or more optional image operations (e.g.
1329             *            sharpen, blur, etc.) that can be applied to the final result
1330             *            before returning the image.
1331             * 
1332             * @return a new {@link BufferedImage} representing the scaled
1333             *         <code>src</code> image.
1334             * 
1335             * @throws IllegalArgumentException
1336             *             if <code>src</code> is <code>null</code>.
1337             * @throws IllegalArgumentException
1338             *             if <code>targetWidth</code> is &lt; 0 or if
1339             *             <code>targetHeight</code> is &lt; 0.
1340             * @throws ImagingOpException
1341             *             if one of the given {@link BufferedImageOp}s fails to apply.
1342             *             These exceptions bubble up from the inside of most of the
1343             *             {@link BufferedImageOp} implementations and are explicitly
1344             *             defined on the imgscalr API to make it easier for callers to
1345             *             catch the exception (if they are passing along optional ops
1346             *             to be applied). imgscalr takes detailed steps to avoid the
1347             *             most common pitfalls that will cause {@link BufferedImageOp}s
1348             *             to fail, even when using straight forward JDK-image
1349             *             operations.
1350             */
1351            public static BufferedImage resize(BufferedImage src, int targetWidth,
1352                            int targetHeight, BufferedImageOp... ops)
1353                            throws IllegalArgumentException, ImagingOpException {
1354                    return resize(src, Method.AUTOMATIC, Mode.AUTOMATIC, targetWidth,
1355                                    targetHeight, ops);
1356            }
1357    
1358            /**
1359             * Resize a given image (maintaining its original proportion) to the target
1360             * width and height using the given scaling method and apply the given
1361             * {@link BufferedImageOp}s (if any) to the result before returning it.
1362             * <p/>
1363             * A mode of {@link Mode#AUTOMATIC} is used.
1364             * <p/>
1365             * <strong>TIP</strong>: See the class description to understand how this
1366             * class handles recalculation of the <code>targetWidth</code> or
1367             * <code>targetHeight</code> depending on the image's orientation in order
1368             * to maintain the original proportion.
1369             * <p/>
1370             * <strong>TIP</strong>: This operation leaves the original <code>src</code>
1371             * image unmodified. If the caller is done with the <code>src</code> image
1372             * after getting the result of this operation, remember to call
1373             * {@link BufferedImage#flush()} on the <code>src</code> to free up native
1374             * resources and make it easier for the GC to collect the unused image.
1375             * 
1376             * @param src
1377             *            The image that will be scaled.
1378             * @param scalingMethod
1379             *            The method used for scaling the image; preferring speed to
1380             *            quality or a balance of both.
1381             * @param targetWidth
1382             *            The target width that you wish the image to have.
1383             * @param targetHeight
1384             *            The target height that you wish the image to have.
1385             * @param ops
1386             *            <code>0</code> or more optional image operations (e.g.
1387             *            sharpen, blur, etc.) that can be applied to the final result
1388             *            before returning the image.
1389             * 
1390             * @return a new {@link BufferedImage} representing the scaled
1391             *         <code>src</code> image.
1392             * 
1393             * @throws IllegalArgumentException
1394             *             if <code>src</code> is <code>null</code>.
1395             * @throws IllegalArgumentException
1396             *             if <code>scalingMethod</code> is <code>null</code>.
1397             * @throws IllegalArgumentException
1398             *             if <code>targetWidth</code> is &lt; 0 or if
1399             *             <code>targetHeight</code> is &lt; 0.
1400             * @throws ImagingOpException
1401             *             if one of the given {@link BufferedImageOp}s fails to apply.
1402             *             These exceptions bubble up from the inside of most of the
1403             *             {@link BufferedImageOp} implementations and are explicitly
1404             *             defined on the imgscalr API to make it easier for callers to
1405             *             catch the exception (if they are passing along optional ops
1406             *             to be applied). imgscalr takes detailed steps to avoid the
1407             *             most common pitfalls that will cause {@link BufferedImageOp}s
1408             *             to fail, even when using straight forward JDK-image
1409             *             operations.
1410             * 
1411             * @see Method
1412             */
1413            public static BufferedImage resize(BufferedImage src, Method scalingMethod,
1414                            int targetWidth, int targetHeight, BufferedImageOp... ops) {
1415                    return resize(src, scalingMethod, Mode.AUTOMATIC, targetWidth,
1416                                    targetHeight, ops);
1417            }
1418    
1419            /**
1420             * Resize a given image (maintaining its original proportion) to the target
1421             * width and height (or fitting the image to the given WIDTH or HEIGHT
1422             * explicitly, depending on the {@link Mode} specified) and apply the given
1423             * {@link BufferedImageOp}s (if any) to the result before returning it.
1424             * <p/>
1425             * A scaling method of {@link Method#AUTOMATIC} is used.
1426             * <p/>
1427             * <strong>TIP</strong>: See the class description to understand how this
1428             * class handles recalculation of the <code>targetWidth</code> or
1429             * <code>targetHeight</code> depending on the image's orientation in order
1430             * to maintain the original proportion.
1431             * <p/>
1432             * <strong>TIP</strong>: This operation leaves the original <code>src</code>
1433             * image unmodified. If the caller is done with the <code>src</code> image
1434             * after getting the result of this operation, remember to call
1435             * {@link BufferedImage#flush()} on the <code>src</code> to free up native
1436             * resources and make it easier for the GC to collect the unused image.
1437             * 
1438             * @param src
1439             *            The image that will be scaled.
1440             * @param resizeMode
1441             *            Used to indicate how imgscalr should calculate the final
1442             *            target size for the image, either fitting the image to the
1443             *            given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image
1444             *            to the given height ({@link Mode#FIT_TO_HEIGHT}). If
1445             *            {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate
1446             *            proportional dimensions for the scaled image based on its
1447             *            orientation (landscape, square or portrait). Unless you have
1448             *            very specific size requirements, most of the time you just
1449             *            want to use {@link Mode#AUTOMATIC} to "do the right thing".
1450             * @param targetWidth
1451             *            The target width that you wish the image to have.
1452             * @param targetHeight
1453             *            The target height that you wish the image to have.
1454             * @param ops
1455             *            <code>0</code> or more optional image operations (e.g.
1456             *            sharpen, blur, etc.) that can be applied to the final result
1457             *            before returning the image.
1458             * 
1459             * @return a new {@link BufferedImage} representing the scaled
1460             *         <code>src</code> image.
1461             * 
1462             * @throws IllegalArgumentException
1463             *             if <code>src</code> is <code>null</code>.
1464             * @throws IllegalArgumentException
1465             *             if <code>resizeMode</code> is <code>null</code>.
1466             * @throws IllegalArgumentException
1467             *             if <code>targetWidth</code> is &lt; 0 or if
1468             *             <code>targetHeight</code> is &lt; 0.
1469             * @throws ImagingOpException
1470             *             if one of the given {@link BufferedImageOp}s fails to apply.
1471             *             These exceptions bubble up from the inside of most of the
1472             *             {@link BufferedImageOp} implementations and are explicitly
1473             *             defined on the imgscalr API to make it easier for callers to
1474             *             catch the exception (if they are passing along optional ops
1475             *             to be applied). imgscalr takes detailed steps to avoid the
1476             *             most common pitfalls that will cause {@link BufferedImageOp}s
1477             *             to fail, even when using straight forward JDK-image
1478             *             operations.
1479             * 
1480             * @see Mode
1481             */
1482            public static BufferedImage resize(BufferedImage src, Mode resizeMode,
1483                            int targetWidth, int targetHeight, BufferedImageOp... ops)
1484                            throws IllegalArgumentException, ImagingOpException {
1485                    return resize(src, Method.AUTOMATIC, resizeMode, targetWidth,
1486                                    targetHeight, ops);
1487            }
1488    
1489            /**
1490             * Resize a given image (maintaining its original proportion) to the target
1491             * width and height (or fitting the image to the given WIDTH or HEIGHT
1492             * explicitly, depending on the {@link Mode} specified) using the given
1493             * scaling method and apply the given {@link BufferedImageOp}s (if any) to
1494             * the result before returning it.
1495             * <p/>
1496             * <strong>TIP</strong>: See the class description to understand how this
1497             * class handles recalculation of the <code>targetWidth</code> or
1498             * <code>targetHeight</code> depending on the image's orientation in order
1499             * to maintain the original proportion.
1500             * <p/>
1501             * <strong>TIP</strong>: This operation leaves the original <code>src</code>
1502             * image unmodified. If the caller is done with the <code>src</code> image
1503             * after getting the result of this operation, remember to call
1504             * {@link BufferedImage#flush()} on the <code>src</code> to free up native
1505             * resources and make it easier for the GC to collect the unused image.
1506             * 
1507             * @param src
1508             *            The image that will be scaled.
1509             * @param scalingMethod
1510             *            The method used for scaling the image; preferring speed to
1511             *            quality or a balance of both.
1512             * @param resizeMode
1513             *            Used to indicate how imgscalr should calculate the final
1514             *            target size for the image, either fitting the image to the
1515             *            given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image
1516             *            to the given height ({@link Mode#FIT_TO_HEIGHT}). If
1517             *            {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate
1518             *            proportional dimensions for the scaled image based on its
1519             *            orientation (landscape, square or portrait). Unless you have
1520             *            very specific size requirements, most of the time you just
1521             *            want to use {@link Mode#AUTOMATIC} to "do the right thing".
1522             * @param targetWidth
1523             *            The target width that you wish the image to have.
1524             * @param targetHeight
1525             *            The target height that you wish the image to have.
1526             * @param ops
1527             *            <code>0</code> or more optional image operations (e.g.
1528             *            sharpen, blur, etc.) that can be applied to the final result
1529             *            before returning the image.
1530             * 
1531             * @return a new {@link BufferedImage} representing the scaled
1532             *         <code>src</code> image.
1533             * 
1534             * @throws IllegalArgumentException
1535             *             if <code>src</code> is <code>null</code>.
1536             * @throws IllegalArgumentException
1537             *             if <code>scalingMethod</code> is <code>null</code>.
1538             * @throws IllegalArgumentException
1539             *             if <code>resizeMode</code> is <code>null</code>.
1540             * @throws IllegalArgumentException
1541             *             if <code>targetWidth</code> is &lt; 0 or if
1542             *             <code>targetHeight</code> is &lt; 0.
1543             * @throws ImagingOpException
1544             *             if one of the given {@link BufferedImageOp}s fails to apply.
1545             *             These exceptions bubble up from the inside of most of the
1546             *             {@link BufferedImageOp} implementations and are explicitly
1547             *             defined on the imgscalr API to make it easier for callers to
1548             *             catch the exception (if they are passing along optional ops
1549             *             to be applied). imgscalr takes detailed steps to avoid the
1550             *             most common pitfalls that will cause {@link BufferedImageOp}s
1551             *             to fail, even when using straight forward JDK-image
1552             *             operations.
1553             * 
1554             * @see Method
1555             * @see Mode
1556             */
1557            public static BufferedImage resize(BufferedImage src, Method scalingMethod,
1558                            Mode resizeMode, int targetWidth, int targetHeight,
1559                            BufferedImageOp... ops) throws IllegalArgumentException,
1560                            ImagingOpException {
1561                    long t = System.currentTimeMillis();
1562    
1563                    if (src == null)
1564                            throw new IllegalArgumentException("src cannot be null");
1565                    if (targetWidth < 0)
1566                            throw new IllegalArgumentException("targetWidth must be >= 0");
1567                    if (targetHeight < 0)
1568                            throw new IllegalArgumentException("targetHeight must be >= 0");
1569                    if (scalingMethod == null)
1570                            throw new IllegalArgumentException(
1571                                            "scalingMethod cannot be null. A good default value is Method.AUTOMATIC.");
1572                    if (resizeMode == null)
1573                            throw new IllegalArgumentException(
1574                                            "resizeMode cannot be null. A good default value is Mode.AUTOMATIC.");
1575    
1576                    BufferedImage result = null;
1577    
1578                    int currentWidth = src.getWidth();
1579                    int currentHeight = src.getHeight();
1580    
1581                    // <= 1 is a square or landscape-oriented image, > 1 is a portrait.
1582                    float ratio = ((float) currentHeight / (float) currentWidth);
1583    
1584                    if (DEBUG)
1585                            log(0,
1586                                            "Resizing Image [size=%dx%d, resizeMode=%s, orientation=%s, ratio(H/W)=%f] to [targetSize=%dx%d]",
1587                                            currentWidth, currentHeight, resizeMode,
1588                                            (ratio <= 1 ? "Landscape/Square" : "Portrait"), ratio,
1589                                            targetWidth, targetHeight);
1590    
1591                    /*
1592                     * First determine if ANY size calculation needs to be done, in the case
1593                     * of FIT_EXACT, ignore image proportions and orientation and just use
1594                     * what the user sent in, otherwise the proportion of the picture must
1595                     * be honored.
1596                     * 
1597                     * The way that is done is to figure out if the image is in a
1598                     * LANDSCAPE/SQUARE or PORTRAIT orientation and depending on its
1599                     * orientation, use the primary dimension (width for LANDSCAPE/SQUARE
1600                     * and height for PORTRAIT) to recalculate the alternative (height and
1601                     * width respectively) value that adheres to the existing ratio.
1602                     * 
1603                     * This helps make life easier for the caller as they don't need to
1604                     * pre-compute proportional dimensions before calling the API, they can
1605                     * just specify the dimensions they would like the image to roughly fit
1606                     * within and it will do the right thing without mangling the result.
1607                     */
1608                    if (resizeMode != Mode.FIT_EXACT) {
1609                            if ((ratio <= 1 && resizeMode == Mode.AUTOMATIC)
1610                                            || (resizeMode == Mode.FIT_TO_WIDTH)) {
1611                                    // First make sure we need to do any work in the first place
1612                                    if (targetWidth == src.getWidth())
1613                                            return src;
1614    
1615                                    // Save for detailed logging (this is cheap).
1616                                    int originalTargetHeight = targetHeight;
1617    
1618                                    /*
1619                                     * Landscape or Square Orientation: Ignore the given height and
1620                                     * re-calculate a proportionally correct value based on the
1621                                     * targetWidth.
1622                                     */
1623                                    targetHeight = Math.round((float) targetWidth * ratio);
1624    
1625                                    if (DEBUG && originalTargetHeight != targetHeight)
1626                                            log(1,
1627                                                            "Auto-Corrected targetHeight [from=%d to=%d] to honor image proportions.",
1628                                                            originalTargetHeight, targetHeight);
1629                            } else {
1630                                    // First make sure we need to do any work in the first place
1631                                    if (targetHeight == src.getHeight())
1632                                            return src;
1633    
1634                                    // Save for detailed logging (this is cheap).
1635                                    int originalTargetWidth = targetWidth;
1636    
1637                                    /*
1638                                     * Portrait Orientation: Ignore the given width and re-calculate
1639                                     * a proportionally correct value based on the targetHeight.
1640                                     */
1641                                    targetWidth = Math.round((float) targetHeight / ratio);
1642    
1643                                    if (DEBUG && originalTargetWidth != targetWidth)
1644                                            log(1,
1645                                                            "Auto-Corrected targetWidth [from=%d to=%d] to honor image proportions.",
1646                                                            originalTargetWidth, targetWidth);
1647                            }
1648                    } else {
1649                            if (DEBUG)
1650                                    log(1,
1651                                                    "Resize Mode FIT_EXACT used, no width/height checking or re-calculation will be done.");
1652                    }
1653    
1654                    // If AUTOMATIC was specified, determine the real scaling method.
1655                    if (scalingMethod == Scalr.Method.AUTOMATIC)
1656                            scalingMethod = determineScalingMethod(targetWidth, targetHeight,
1657                                            ratio);
1658    
1659                    if (DEBUG)
1660                            log(1, "Using Scaling Method: %s", scalingMethod);
1661    
1662                    // Now we scale the image
1663                    if (scalingMethod == Scalr.Method.SPEED) {
1664                            result = scaleImage(src, targetWidth, targetHeight,
1665                                            RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
1666                    } else if (scalingMethod == Scalr.Method.BALANCED) {
1667                            result = scaleImage(src, targetWidth, targetHeight,
1668                                            RenderingHints.VALUE_INTERPOLATION_BILINEAR);
1669                    } else if (scalingMethod == Scalr.Method.QUALITY
1670                                    || scalingMethod == Scalr.Method.ULTRA_QUALITY) {
1671                            /*
1672                             * If we are scaling up (in either width or height - since we know
1673                             * the image will stay proportional we just check if either are
1674                             * being scaled up), directly using a single BICUBIC will give us
1675                             * better results then using Chris Campbell's incremental scaling
1676                             * operation (and take a lot less time).
1677                             * 
1678                             * If we are scaling down, we must use the incremental scaling
1679                             * algorithm for the best result.
1680                             */
1681                            if (targetWidth > currentWidth || targetHeight > currentHeight) {
1682                                    if (DEBUG)
1683                                            log(1,
1684                                                            "QUALITY scale-up, a single BICUBIC scale operation will be used...");
1685    
1686                                    /*
1687                                     * BILINEAR and BICUBIC look similar the smaller the scale jump
1688                                     * upwards is, if the scale is larger BICUBIC looks sharper and
1689                                     * less fuzzy. But most importantly we have to use BICUBIC to
1690                                     * match the contract of the QUALITY rendering scalingMethod.
1691                                     * This note is just here for anyone reading the code and
1692                                     * wondering how they can speed their own calls up.
1693                                     */
1694                                    result = scaleImage(src, targetWidth, targetHeight,
1695                                                    RenderingHints.VALUE_INTERPOLATION_BICUBIC);
1696                            } else {
1697                                    if (DEBUG)
1698                                            log(1,
1699                                                            "QUALITY scale-down, incremental scaling will be used...");
1700    
1701                                    /*
1702                                     * Originally we wanted to use BILINEAR interpolation here
1703                                     * because it takes 1/3rd the time that the BICUBIC
1704                                     * interpolation does, however, when scaling large images down
1705                                     * to most sizes bigger than a thumbnail we witnessed noticeable
1706                                     * "softening" in the resultant image with BILINEAR that would
1707                                     * be unexpectedly annoying to a user expecting a "QUALITY"
1708                                     * scale of their original image. Instead BICUBIC was chosen to
1709                                     * honor the contract of a QUALITY scale of the original image.
1710                                     */
1711                                    result = scaleImageIncrementally(src, targetWidth,
1712                                                    targetHeight, scalingMethod,
1713                                                    RenderingHints.VALUE_INTERPOLATION_BICUBIC);
1714                            }
1715                    }
1716    
1717                    if (DEBUG)
1718                            log(0, "Resized Image in %d ms", System.currentTimeMillis() - t);
1719    
1720                    // Apply any optional operations (if specified).
1721                    if (ops != null && ops.length > 0)
1722                            result = apply(result, ops);
1723    
1724                    return result;
1725            }
1726    
1727            /**
1728             * Used to apply a {@link Rotation} and then <code>0</code> or more
1729             * {@link BufferedImageOp}s to a given image and return the result.
1730             * <p/>
1731             * <strong>TIP</strong>: This operation leaves the original <code>src</code>
1732             * image unmodified. If the caller is done with the <code>src</code> image
1733             * after getting the result of this operation, remember to call
1734             * {@link BufferedImage#flush()} on the <code>src</code> to free up native
1735             * resources and make it easier for the GC to collect the unused image.
1736             * 
1737             * @param src
1738             *            The image that will have the rotation applied to it.
1739             * @param rotation
1740             *            The rotation that will be applied to the image.
1741             * @param ops
1742             *            Zero or more optional image operations (e.g. sharpen, blur,
1743             *            etc.) that can be applied to the final result before returning
1744             *            the image.
1745             * 
1746             * @return a new {@link BufferedImage} representing <code>src</code> rotated
1747             *         by the given amount and any optional ops applied to it.
1748             * 
1749             * @throws IllegalArgumentException
1750             *             if <code>src</code> is <code>null</code>.
1751             * @throws IllegalArgumentException
1752             *             if <code>rotation</code> is <code>null</code>.
1753             * @throws ImagingOpException
1754             *             if one of the given {@link BufferedImageOp}s fails to apply.
1755             *             These exceptions bubble up from the inside of most of the
1756             *             {@link BufferedImageOp} implementations and are explicitly
1757             *             defined on the imgscalr API to make it easier for callers to
1758             *             catch the exception (if they are passing along optional ops
1759             *             to be applied). imgscalr takes detailed steps to avoid the
1760             *             most common pitfalls that will cause {@link BufferedImageOp}s
1761             *             to fail, even when using straight forward JDK-image
1762             *             operations.
1763             * 
1764             * @see Rotation
1765             */
1766            public static BufferedImage rotate(BufferedImage src, Rotation rotation,
1767                            BufferedImageOp... ops) throws IllegalArgumentException,
1768                            ImagingOpException {
1769                    long t = System.currentTimeMillis();
1770    
1771                    if (src == null)
1772                            throw new IllegalArgumentException("src cannot be null");
1773                    if (rotation == null)
1774                            throw new IllegalArgumentException("rotation cannot be null");
1775    
1776                    if (DEBUG)
1777                            log(0, "Rotating Image [%s]...", rotation);
1778    
1779                    /*
1780                     * Setup the default width/height values from our image.
1781                     * 
1782                     * In the case of a 90 or 270 (-90) degree rotation, these two values
1783                     * flip-flop and we will correct those cases down below in the switch
1784                     * statement.
1785                     */
1786                    int newWidth = src.getWidth();
1787                    int newHeight = src.getHeight();
1788    
1789                    /*
1790                     * We create a transform per operation request as (oddly enough) it ends
1791                     * up being faster for the VM to create, use and destroy these instances
1792                     * than it is to re-use a single AffineTransform per-thread via the
1793                     * AffineTransform.setTo(...) methods which was my first choice (less
1794                     * object creation); after benchmarking this explicit case and looking
1795                     * at just how much code gets run inside of setTo() I opted for a new AT
1796                     * for every rotation.
1797                     * 
1798                     * Besides the performance win, trying to safely reuse AffineTransforms
1799                     * via setTo(...) would have required ThreadLocal instances to avoid
1800                     * race conditions where two or more resize threads are manipulating the
1801                     * same transform before applying it.
1802                     * 
1803                     * Misusing ThreadLocals are one of the #1 reasons for memory leaks in
1804                     * server applications and since we have no nice way to hook into the
1805                     * init/destroy Servlet cycle or any other initialization cycle for this
1806                     * library to automatically call ThreadLocal.remove() to avoid the
1807                     * memory leak, it would have made using this library *safely* on the
1808                     * server side much harder.
1809                     * 
1810                     * So we opt for creating individual transforms per rotation op and let
1811                     * the VM clean them up in a GC. I only clarify all this reasoning here
1812                     * for anyone else reading this code and being tempted to reuse the AT
1813                     * instances of performance gains; there aren't any AND you get a lot of
1814                     * pain along with it.
1815                     */
1816                    AffineTransform tx = new AffineTransform();
1817    
1818                    switch (rotation) {
1819                    case CW_90:
1820                            /*
1821                             * A 90 or -90 degree rotation will cause the height and width to
1822                             * flip-flop from the original image to the rotated one.
1823                             */
1824                            newWidth = src.getHeight();
1825                            newHeight = src.getWidth();
1826    
1827                            // Reminder: newWidth == result.getHeight() at this point
1828                            tx.translate(newWidth, 0);
1829                            tx.rotate(Math.toRadians(90));
1830    
1831                            break;
1832    
1833                    case CW_270:
1834                            /*
1835                             * A 90 or -90 degree rotation will cause the height and width to
1836                             * flip-flop from the original image to the rotated one.
1837                             */
1838                            newWidth = src.getHeight();
1839                            newHeight = src.getWidth();
1840    
1841                            // Reminder: newHeight == result.getWidth() at this point
1842                            tx.translate(0, newHeight);
1843                            tx.rotate(Math.toRadians(-90));
1844                            break;
1845    
1846                    case CW_180:
1847                            tx.translate(newWidth, newHeight);
1848                            tx.rotate(Math.toRadians(180));
1849                            break;
1850    
1851                    case FLIP_HORZ:
1852                            tx.translate(newWidth, 0);
1853                            tx.scale(-1.0, 1.0);
1854                            break;
1855    
1856                    case FLIP_VERT:
1857                            tx.translate(0, newHeight);
1858                            tx.scale(1.0, -1.0);
1859                            break;
1860                    }
1861    
1862                    // Create our target image we will render the rotated result to.
1863                    BufferedImage result = createOptimalImage(src, newWidth, newHeight);
1864                    Graphics2D g2d = (Graphics2D) result.createGraphics();
1865    
1866                    /*
1867                     * Render the resultant image to our new rotatedImage buffer, applying
1868                     * the AffineTransform that we calculated above during rendering so the
1869                     * pixels from the old position are transposed to the new positions in
1870                     * the resulting image correctly.
1871                     */
1872                    g2d.drawImage(src, tx, null);
1873                    g2d.dispose();
1874    
1875                    if (DEBUG)
1876                            log(0, "Rotation Applied in %d ms, result [width=%d, height=%d]",
1877                                            System.currentTimeMillis() - t, result.getWidth(),
1878                                            result.getHeight());
1879    
1880                    // Apply any optional operations (if specified).
1881                    if (ops != null && ops.length > 0)
1882                            result = apply(result, ops);
1883    
1884                    return result;
1885            }
1886    
1887            /**
1888             * Used to write out a useful and well-formatted log message by any piece of
1889             * code inside of the imgscalr library.
1890             * <p/>
1891             * If a message cannot be logged (logging is disabled) then this method
1892             * returns immediately.
1893             * <p/>
1894             * <strong>NOTE</strong>: Because Java will auto-box primitive arguments
1895             * into Objects when building out the <code>params</code> array, care should
1896             * be taken not to call this method with primitive values unless
1897             * {@link Scalr#DEBUG} is <code>true</code>; otherwise the VM will be
1898             * spending time performing unnecessary auto-boxing calculations.
1899             * 
1900             * @param depth
1901             *            The indentation level of the log message.
1902             * @param message
1903             *            The log message in <a href=
1904             *            "http://download.oracle.com/javase/6/docs/api/java/util/Formatter.html#syntax"
1905             *            >format string syntax</a> that will be logged.
1906             * @param params
1907             *            The parameters that will be swapped into all the place holders
1908             *            in the original messages before being logged.
1909             * 
1910             * @see Scalr#LOG_PREFIX
1911             * @see Scalr#LOG_PREFIX_PROPERTY_NAME
1912             */
1913            protected static void log(int depth, String message, Object... params) {
1914                    if (Scalr.DEBUG) {
1915                            System.out.print(Scalr.LOG_PREFIX);
1916    
1917                            for (int i = 0; i < depth; i++)
1918                                    System.out.print("\t");
1919    
1920                            System.out.printf(message, params);
1921                            System.out.println();
1922                    }
1923            }
1924    
1925            /**
1926             * Used to create a {@link BufferedImage} with the most optimal RGB TYPE (
1927             * {@link BufferedImage#TYPE_INT_RGB} or {@link BufferedImage#TYPE_INT_ARGB}
1928             * ) capable of being rendered into from the given <code>src</code>. The
1929             * width and height of both images will be identical.
1930             * <p/>
1931             * This does not perform a copy of the image data from <code>src</code> into
1932             * the result image; see {@link #copyToOptimalImage(BufferedImage)} for
1933             * that.
1934             * <p/>
1935             * We force all rendering results into one of these two types, avoiding the
1936             * case where a source image is of an unsupported (or poorly supported)
1937             * format by Java2D causing the rendering result to end up looking terrible
1938             * (common with GIFs) or be totally corrupt (e.g. solid black image).
1939             * <p/>
1940             * Originally reported by Magnus Kvalheim from Movellas when scaling certain
1941             * GIF and PNG images.
1942             * 
1943             * @param src
1944             *            The source image that will be analyzed to determine the most
1945             *            optimal image type it can be rendered into.
1946             * 
1947             * @return a new {@link BufferedImage} representing the most optimal target
1948             *         image type that <code>src</code> can be rendered into.
1949             * 
1950             * @see <a
1951             *      href="http://www.mail-archive.com/java2d-interest@capra.eng.sun.com/msg05621.html">How
1952             *      Java2D handles poorly supported image types</a>
1953             * @see <a
1954             *      href="http://code.google.com/p/java-image-scaling/source/browse/trunk/src/main/java/com/mortennobel/imagescaling/MultiStepRescaleOp.java">Thanks
1955             *      to Morten Nobel for implementation hint</a>
1956             */
1957            protected static BufferedImage createOptimalImage(BufferedImage src) {
1958                    return createOptimalImage(src, src.getWidth(), src.getHeight());
1959            }
1960    
1961            /**
1962             * Used to create a {@link BufferedImage} with the given dimensions and the
1963             * most optimal RGB TYPE ( {@link BufferedImage#TYPE_INT_RGB} or
1964             * {@link BufferedImage#TYPE_INT_ARGB} ) capable of being rendered into from
1965             * the given <code>src</code>.
1966             * <p/>
1967             * This does not perform a copy of the image data from <code>src</code> into
1968             * the result image; see {@link #copyToOptimalImage(BufferedImage)} for
1969             * that.
1970             * <p/>
1971             * We force all rendering results into one of these two types, avoiding the
1972             * case where a source image is of an unsupported (or poorly supported)
1973             * format by Java2D causing the rendering result to end up looking terrible
1974             * (common with GIFs) or be totally corrupt (e.g. solid black image).
1975             * <p/>
1976             * Originally reported by Magnus Kvalheim from Movellas when scaling certain
1977             * GIF and PNG images.
1978             * 
1979             * @param src
1980             *            The source image that will be analyzed to determine the most
1981             *            optimal image type it can be rendered into.
1982             * @param width
1983             *            The width of the newly created resulting image.
1984             * @param height
1985             *            The height of the newly created resulting image.
1986             * 
1987             * @return a new {@link BufferedImage} representing the most optimal target
1988             *         image type that <code>src</code> can be rendered into.
1989             * 
1990             * @throws IllegalArgumentException
1991             *             if <code>width</code> or <code>height</code> are &lt; 0.
1992             * 
1993             * @see <a
1994             *      href="http://www.mail-archive.com/java2d-interest@capra.eng.sun.com/msg05621.html">How
1995             *      Java2D handles poorly supported image types</a>
1996             * @see <a
1997             *      href="http://code.google.com/p/java-image-scaling/source/browse/trunk/src/main/java/com/mortennobel/imagescaling/MultiStepRescaleOp.java">Thanks
1998             *      to Morten Nobel for implementation hint</a>
1999             */
2000            protected static BufferedImage createOptimalImage(BufferedImage src,
2001                            int width, int height) throws IllegalArgumentException {
2002                    if (width < 0 || height < 0)
2003                            throw new IllegalArgumentException("width [" + width
2004                                            + "] and height [" + height + "] must be >= 0");
2005    
2006                    return new BufferedImage(
2007                                    width,
2008                                    height,
2009                                    (src.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB
2010                                                    : BufferedImage.TYPE_INT_ARGB));
2011            }
2012    
2013            /**
2014             * Used to copy a {@link BufferedImage} from a non-optimal type into a new
2015             * {@link BufferedImage} instance of an optimal type (RGB or ARGB). If
2016             * <code>src</code> is already of an optimal type, then it is returned
2017             * unmodified.
2018             * <p/>
2019             * This method is meant to be used by any calling code (imgscalr's or
2020             * otherwise) to convert any inbound image from a poorly supported image
2021             * type into the 2 most well-supported image types in Java2D (
2022             * {@link BufferedImage#TYPE_INT_RGB} or {@link BufferedImage#TYPE_INT_ARGB}
2023             * ) in order to ensure all subsequent graphics operations are performed as
2024             * efficiently and correctly as possible.
2025             * <p/>
2026             * When using Java2D to work with image types that are not well supported,
2027             * the results can be anything from exceptions bubbling up from the depths
2028             * of Java2D to images being completely corrupted and just returned as solid
2029             * black.
2030             * 
2031             * @param src
2032             *            The image to copy (if necessary) into an optimally typed
2033             *            {@link BufferedImage}.
2034             * 
2035             * @return a representation of the <code>src</code> image in an optimally
2036             *         typed {@link BufferedImage}, otherwise <code>src</code> if it was
2037             *         already of an optimal type.
2038             * 
2039             * @throws IllegalArgumentException
2040             *             if <code>src</code> is <code>null</code>.
2041             */
2042            protected static BufferedImage copyToOptimalImage(BufferedImage src)
2043                            throws IllegalArgumentException {
2044                    if (src == null)
2045                            throw new IllegalArgumentException("src cannot be null");
2046    
2047                    // Calculate the type depending on the presence of alpha.
2048                    int type = (src.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB
2049                                    : BufferedImage.TYPE_INT_ARGB);
2050                    BufferedImage result = new BufferedImage(src.getWidth(),
2051                                    src.getHeight(), type);
2052    
2053                    // Render the src image into our new optimal source.
2054                    Graphics g = result.getGraphics();
2055                    g.drawImage(src, 0, 0, null);
2056                    g.dispose();
2057    
2058                    return result;
2059            }
2060    
2061            /**
2062             * Used to determine the scaling {@link Method} that is best suited for
2063             * scaling the image to the targeted dimensions.
2064             * <p/>
2065             * This method is intended to be used to select a specific scaling
2066             * {@link Method} when a {@link Method#AUTOMATIC} method is specified. This
2067             * method utilizes the {@link Scalr#THRESHOLD_QUALITY_BALANCED} and
2068             * {@link Scalr#THRESHOLD_BALANCED_SPEED} thresholds when selecting which
2069             * method should be used by comparing the primary dimension (width or
2070             * height) against the threshold and seeing where the image falls. The
2071             * primary dimension is determined by looking at the orientation of the
2072             * image: landscape or square images use their width and portrait-oriented
2073             * images use their height.
2074             * 
2075             * @param targetWidth
2076             *            The target width for the scaled image.
2077             * @param targetHeight
2078             *            The target height for the scaled image.
2079             * @param ratio
2080             *            A height/width ratio used to determine the orientation of the
2081             *            image so the primary dimension (width or height) can be
2082             *            selected to test if it is greater than or less than a
2083             *            particular threshold.
2084             * 
2085             * @return the fastest {@link Method} suited for scaling the image to the
2086             *         specified dimensions while maintaining a good-looking result.
2087             */
2088            protected static Method determineScalingMethod(int targetWidth,
2089                            int targetHeight, float ratio) {
2090                    // Get the primary dimension based on the orientation of the image
2091                    int length = (ratio <= 1 ? targetWidth : targetHeight);
2092    
2093                    // Default to speed
2094                    Method result = Method.SPEED;
2095    
2096                    // Figure out which scalingMethod should be used
2097                    if (length <= Scalr.THRESHOLD_QUALITY_BALANCED)
2098                            result = Method.QUALITY;
2099                    else if (length <= Scalr.THRESHOLD_BALANCED_SPEED)
2100                            result = Method.BALANCED;
2101    
2102                    if (DEBUG)
2103                            log(2, "AUTOMATIC scaling method selected: %s", result.name());
2104    
2105                    return result;
2106            }
2107    
2108            /**
2109             * Used to implement a straight-forward image-scaling operation using Java
2110             * 2D.
2111             * <p/>
2112             * This method uses the Oracle-encouraged method of
2113             * <code>Graphics2D.drawImage(...)</code> to scale the given image with the
2114             * given interpolation hint.
2115             * 
2116             * @param src
2117             *            The image that will be scaled.
2118             * @param targetWidth
2119             *            The target width for the scaled image.
2120             * @param targetHeight
2121             *            The target height for the scaled image.
2122             * @param interpolationHintValue
2123             *            The {@link RenderingHints} interpolation value used to
2124             *            indicate the method that {@link Graphics2D} should use when
2125             *            scaling the image.
2126             * 
2127             * @return the result of scaling the original <code>src</code> to the given
2128             *         dimensions using the given interpolation method.
2129             */
2130            protected static BufferedImage scaleImage(BufferedImage src,
2131                            int targetWidth, int targetHeight, Object interpolationHintValue) {
2132                    // Setup the rendering resources to match the source image's
2133                    BufferedImage result = createOptimalImage(src, targetWidth,
2134                                    targetHeight);
2135                    Graphics2D resultGraphics = result.createGraphics();
2136    
2137                    // Scale the image to the new buffer using the specified rendering hint.
2138                    resultGraphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
2139                                    interpolationHintValue);
2140                    resultGraphics.drawImage(src, 0, 0, targetWidth, targetHeight, null);
2141    
2142                    // Just to be clean, explicitly dispose our temporary graphics object
2143                    resultGraphics.dispose();
2144    
2145                    // Return the scaled image to the caller.
2146                    return result;
2147            }
2148    
2149            /**
2150             * Used to implement Chris Campbell's incremental-scaling algorithm: <a
2151             * href="http://today.java.net/pub/a/today/2007/04/03/perils
2152             * -of-image-getscaledinstance
2153             * .html">http://today.java.net/pub/a/today/2007/04/03/perils
2154             * -of-image-getscaledinstance.html</a>.
2155             * <p/>
2156             * Modifications to the original algorithm are variable names and comments
2157             * added for clarity and the hard-coding of using BICUBIC interpolation as
2158             * well as the explicit "flush()" operation on the interim BufferedImage
2159             * instances to avoid resource leaking.
2160             * 
2161             * @param src
2162             *            The image that will be scaled.
2163             * @param targetWidth
2164             *            The target width for the scaled image.
2165             * @param targetHeight
2166             *            The target height for the scaled image.
2167             * @param scalingMethod
2168             *            The scaling method specified by the user (or calculated by
2169             *            imgscalr) to use for this incremental scaling operation.
2170             * @param interpolationHintValue
2171             *            The {@link RenderingHints} interpolation value used to
2172             *            indicate the method that {@link Graphics2D} should use when
2173             *            scaling the image.
2174             * 
2175             * @return an image scaled to the given dimensions using the given rendering
2176             *         hint.
2177             */
2178            protected static BufferedImage scaleImageIncrementally(BufferedImage src,
2179                            int targetWidth, int targetHeight, Method scalingMethod,
2180                            Object interpolationHintValue) {
2181                    boolean hasReassignedSrc = false;
2182                    int incrementCount = 0;
2183                    int currentWidth = src.getWidth();
2184                    int currentHeight = src.getHeight();
2185    
2186                    /*
2187                     * The original QUALITY mode, representing Chris Campbell's algorithm,
2188                     * is to step down by 1/2s every time when scaling the image
2189                     * incrementally. Users pointed out that using this method to scale
2190                     * images with noticeable straight lines left them really jagged in
2191                     * smaller thumbnail format.
2192                     * 
2193                     * After investigation it was discovered that scaling incrementally by
2194                     * smaller increments was the ONLY way to make the thumbnail sized
2195                     * images look less jagged and more accurate; almost matching the
2196                     * accuracy of Mac's built in thumbnail generation which is the highest
2197                     * quality resize I've come across (better than GIMP Lanczos3 and
2198                     * Windows 7).
2199                     * 
2200                     * A divisor of 7 was chose as using 5 still left some jaggedness in the
2201                     * image while a divisor of 8 or higher made the resulting thumbnail too
2202                     * soft; like our OP_ANTIALIAS convolve op had been forcibly applied to
2203                     * the result even if the user didn't want it that soft.
2204                     * 
2205                     * Using a divisor of 7 for the ULTRA_QUALITY seemed to be the sweet
2206                     * spot.
2207                     * 
2208                     * NOTE: Below when the actual fraction is used to calculate the small
2209                     * portion to subtract from the current dimension, this is a
2210                     * progressively smaller and smaller chunk. When the code was changed to
2211                     * do a linear reduction of the image of equal steps for each
2212                     * incremental resize (e.g. say 50px each time) the result was
2213                     * significantly worse than the progressive approach used below; even
2214                     * when a very high number of incremental steps (13) was tested.
2215                     */
2216                    int fraction = (scalingMethod == Method.ULTRA_QUALITY ? 7 : 2);
2217    
2218                    do {
2219                            int prevCurrentWidth = currentWidth;
2220                            int prevCurrentHeight = currentHeight;
2221    
2222                            /*
2223                             * If the current width is bigger than our target, cut it in half
2224                             * and sample again.
2225                             */
2226                            if (currentWidth > targetWidth) {
2227                                    currentWidth -= (currentWidth / fraction);
2228    
2229                                    /*
2230                                     * If we cut the width too far it means we are on our last
2231                                     * iteration. Just set it to the target width and finish up.
2232                                     */
2233                                    if (currentWidth < targetWidth)
2234                                            currentWidth = targetWidth;
2235                            }
2236    
2237                            /*
2238                             * If the current height is bigger than our target, cut it in half
2239                             * and sample again.
2240                             */
2241    
2242                            if (currentHeight > targetHeight) {
2243                                    currentHeight -= (currentHeight / fraction);
2244    
2245                                    /*
2246                                     * If we cut the height too far it means we are on our last
2247                                     * iteration. Just set it to the target height and finish up.
2248                                     */
2249    
2250                                    if (currentHeight < targetHeight)
2251                                            currentHeight = targetHeight;
2252                            }
2253    
2254                            /*
2255                             * Stop when we cannot incrementally step down anymore.
2256                             * 
2257                             * This used to use a || condition, but that would cause problems
2258                             * when using FIT_EXACT such that sometimes the width OR height
2259                             * would not change between iterations, but the other dimension
2260                             * would (e.g. resizing 500x500 to 500x250).
2261                             * 
2262                             * Now changing this to an && condition requires that both
2263                             * dimensions do not change between a resize iteration before we
2264                             * consider ourselves done.
2265                             */
2266                            if (prevCurrentWidth == currentWidth
2267                                            && prevCurrentHeight == currentHeight)
2268                                    break;
2269    
2270                            if (DEBUG)
2271                                    log(2, "Scaling from [%d x %d] to [%d x %d]", prevCurrentWidth,
2272                                                    prevCurrentHeight, currentWidth, currentHeight);
2273    
2274                            // Render the incremental scaled image.
2275                            BufferedImage incrementalImage = scaleImage(src, currentWidth,
2276                                            currentHeight, interpolationHintValue);
2277    
2278                            /*
2279                             * Before re-assigning our interim (partially scaled)
2280                             * incrementalImage to be the new src image before we iterate around
2281                             * again to process it down further, we want to flush() the previous
2282                             * src image IF (and only IF) it was one of our own temporary
2283                             * BufferedImages created during this incremental down-sampling
2284                             * cycle. If it wasn't one of ours, then it was the original
2285                             * caller-supplied BufferedImage in which case we don't want to
2286                             * flush() it and just leave it alone.
2287                             */
2288                            if (hasReassignedSrc)
2289                                    src.flush();
2290    
2291                            /*
2292                             * Now treat our incremental partially scaled image as the src image
2293                             * and cycle through our loop again to do another incremental
2294                             * scaling of it (if necessary).
2295                             */
2296                            src = incrementalImage;
2297    
2298                            /*
2299                             * Keep track of us re-assigning the original caller-supplied source
2300                             * image with one of our interim BufferedImages so we know when to
2301                             * explicitly flush the interim "src" on the next cycle through.
2302                             */
2303                            hasReassignedSrc = true;
2304    
2305                            // Track how many times we go through this cycle to scale the image.
2306                            incrementCount++;
2307                    } while (currentWidth != targetWidth || currentHeight != targetHeight);
2308    
2309                    if (DEBUG)
2310                            log(2, "Incrementally Scaled Image in %d steps.", incrementCount);
2311    
2312                    /*
2313                     * Once the loop has exited, the src image argument is now our scaled
2314                     * result image that we want to return.
2315                     */
2316                    return src;
2317            }
2318    }