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