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.Rectangle2D;
023    import java.awt.image.AreaAveragingScaleFilter;
024    import java.awt.image.BufferedImage;
025    import java.awt.image.BufferedImageOp;
026    import java.awt.image.ColorModel;
027    import java.awt.image.ConvolveOp;
028    import java.awt.image.IndexColorModel;
029    import java.awt.image.Kernel;
030    
031    import javax.imageio.ImageIO;
032    
033    /*
034     * NOTE: Moving to an enum-based approach to handling styling in the API as
035     * using the BufferedImageOp approach wasn't scaling; a lot of ops, like AffineTransforms,
036     * require instance-to-instance customization and configuration before being
037     * applied; trying to do that at the API level while exposing the exact instance you
038     * need to use doesn't work or scale and leads to a confusing API.
039     * 
040     * Instead with 4.0, will take an approach of letting people set ENUMs that trigger
041     * default behaviors for the most common functionality.
042     */
043    
044    /**
045     * Class used to implement performant, good-quality and intelligent image
046     * scaling algorithms in native Java 2D. This class utilizes the Java2D
047     * "best practices" for image-scaling, ensuring that images are hardware
048     * accelerated at all times if provided by the platform and host-VM.
049     * <p/>
050     * Hardware acceleration also includes execution of optional caller-supplied
051     * {@link BufferedImageOp}s that are applied to the resultant images before
052     * returning them.
053     * <h3>Image Proportions</h3>
054     * All scaling operations implemented by this class maintain the proportion of
055     * the original image. If image-cropping is desired the caller will need to
056     * perform those edits before calling one of the <code>resize</code> methods
057     * provided by this class.
058     * <p/>
059     * In order to maintain the proportionality of the original images, this class
060     * implements the following behavior:
061     * <ol>
062     * <li>If the image is LANDSCAPE-oriented or SQUARE, treat the
063     * <code>targetWidth</code> as the primary dimension and re-calculate the
064     * <code>targetHeight</code> regardless of what is passed in.</li>
065     * <li>If image is PORTRAIT-oriented, treat the <code>targetHeight</code> as the
066     * primary dimension and re-calculate the <code>targetWidth</code> regardless of
067     * what is passed in.</li>
068     * <li>If a {@link Mode} value of {@link Mode#FIT_TO_WIDTH} or
069     * {@link Mode#FIT_TO_HEIGHT} is passed in to the <code>resize</code> method,
070     * the orientation is ignored and the scaled image is fit to the dimension the
071     * user specified with the {@link Mode}.</li>
072     * </ol>
073     * Recalculation of the secondary dimensions is extremely cheap to the point of
074     * it being negligible and this design provides users with better
075     * expected-behavior from the library; which is why this approach was chosen.
076     * <h3>Image Quality</h3>
077     * This class implements a few different methods for scaling an image, providing
078     * either the best-looking result, the fastest result or a balanced result
079     * between the two depending on the scaling hint provided (see {@link Method}).
080     * <p/>
081     * This class also implements the incremental scaling algorithm presented by
082     * Chris Campbell in his <a href="http://today.java
083     * .net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html">Perils of
084     * Image.getScaledInstance()</a> article in order to give the best-looking
085     * results to images scaled down below roughly 800px in size where using a
086     * single scaling operation (even with
087     * {@link RenderingHints#VALUE_INTERPOLATION_BICUBIC} interpolation) would
088     * produce a much worse-looking result.
089     * <p/>
090     * Minor modifications are made to Campbell's original implementation in the
091     * form of:
092     * <ol>
093     * <li>Instead of accepting a user-supplied interpolation method,
094     * {@link RenderingHints#VALUE_INTERPOLATION_BICUBIC} interpolation is always
095     * used. This was done after A/B comparison testing with large images
096     * down-scaled to thumbnail sizes showed noticeable "blurring" when BILINEAR
097     * interpolation was used. Given that Campbell's algorithm is only used in
098     * QUALITY mode when down-scaling, it was determined that the user's expectation
099     * of a much less blurry picture would require that BICUBIC be the default
100     * interpolation in order to meet the QUALITY expectation.</li>
101     * <li>After each iteration of the do-while loop that incrementally scales the
102     * source image down, an effort is made to explicitly call
103     * {@link BufferedImage#flush()} on the interim temporary {@link BufferedImage}
104     * instances created by the algorithm in an attempt to ensure a more complete GC
105     * cycle by the VM when cleaning up the temporary instances.</li>
106     * <li>Extensive comments have been added to increase readability of the code.</li>
107     * <li>Variable names have been expanded to increase readability of the code.</li>
108     * </ol>
109     * <p/>
110     * <strong>NOTE</strong>: This class does not call {@link BufferedImage#flush()}
111     * on any of the <em>source images</em> passed in by calling code; it is up to
112     * the original caller to dispose of their source images when they are no longer
113     * needed so the VM can most efficiently GC them.
114     * <h3>Generated Image Types</h3>
115     * Java2D provides support for a number of different image types defined as
116     * <code>BufferedImage.TYPE_*</code> variables, unfortunately not all image
117     * types are supported equally in Java2D. Some more obscure image types either
118     * have poor or no support, leading to severely degraded quality when an attempt
119     * is made by imgscalr to create a scaled instance <em>of the same type</em> as
120     * the source image.
121     * <p/>
122     * To avoid imgscalr generating significantly worse-looking results than
123     * alternative scaling approaches (e.g.
124     * {@link Image#getScaledInstance(int, int, int)}), all resultant images
125     * generated by imgscalr are one of two types:
126     * <ol>
127     * <li>{@link BufferedImage#TYPE_INT_RGB}</li>
128     * <li>{@link BufferedImage#TYPE_INT_ARGB}</li>
129     * </ol>
130     * depending on if the source image utilizes transparency or not.
131     * <p/>
132     * This is a recommended approach by the Java2D team for dealing with poorly (or
133     * non) supported image types. More can be read about this issue <a href=
134     * "http://www.mail-archive.com/java2d-interest@capra.eng.sun.com/msg05621.html"
135     * >here</a>.
136     * <h3>Logging</h3>
137     * This class implements all its debug logging via the
138     * {@link #log(String, Object...)} method. At this time logging is done directly
139     * to <code>System.out</code> via the <code>printf</code> method. This allows
140     * the logging to be light weight and easy to capture while adding no
141     * dependencies to the library.
142     * <p/>
143     * Implementation of logging in this class is as efficient as possible; avoiding
144     * any calls to the logger or passing of arguments if logging is not enabled.
145     * <h3>GIF Transparency</h3>
146     * Unfortunately in Java 6 and earlier, support for GIF's
147     * {@link IndexColorModel} is sub-par, both in accurate color-selection and in
148     * maintaining transparency when moving to an image of type
149     * {@link BufferedImage#TYPE_INT_ARGB}; because of this issue when a GIF image
150     * is processed by imgscalr and the result saved as a GIF file, it is possible
151     * to lose the alpha channel of a transparent image or in the case of applying
152     * an optional {@link BufferedImageOp}, lose the entire picture all together in
153     * the result. Scalr currently does nothing to work around this manually because
154     * it is a defect in the platform that is half-fixed in Java 7 and all
155     * workarounds are relatively expensive, in the form of hand-creating and
156     * setting RGB values pixel-by-pixel with a custom {@link ColorModel} in the
157     * scaled image.
158     * <p>
159     * <strong>Workaround</strong>: A workaround to this issue with all version of
160     * Java is to simply save a GIF as a PNG; no change to your code needs to be
161     * made except when the image is saved out, e.g. using {@link ImageIO}. When a
162     * file type of "PNG" is used, both the transparency and high color quality will
163     * be maintained.
164     * <p>
165     * If the issue with optional {@link BufferedImageOp}s destroying GIF image
166     * content is ever fixed in the platform, saving out resulting images as GIFs
167     * should suddenly start working.
168     * <p>
169     * More can be read about the issue <a
170     * href="http://gman.eichberger.de/2007/07/transparent-gifs-in-java.html"
171     * >here</a> and <a
172     * href="http://ubuntuforums.org/archive/index.php/t-1060128.html">here</a>.
173     * 
174     * @author Riyad Kalla (software@thebuzzmedia.com)
175     */
176    public class Scalr {
177            /**
178             * Flag used to indicate if debugging output has been enabled by setting the
179             * "imgscalr.debug" system property to <code>true</code>. This value will be
180             * <code>false</code> if the "imgscalr.debug" system property is undefined
181             * or set to <code>false</code>.
182             * <p/>
183             * This system property can be set on startup with:<br/>
184             * <code>
185             * -Dimgscalr.debug=true
186             * </code> or by calling {@link System#setProperty(String, String)} before
187             * this class is loaded.
188             * <p/>
189             * Default value is <code>false</code>.
190             */
191            public static final boolean DEBUG = Boolean.getBoolean("imgscalr.debug");
192    
193            /**
194             * Prefix to every log message this library logs. Using a well-defined
195             * prefix helps make it easier both visually and programmatically to scan
196             * log files for messages produced by this library.
197             * <p/>
198             * The value is "[imgscalr] " (including the space).
199             */
200            public static final String LOG_PREFIX = "[imgscalr] ";
201    
202            /**
203             * A {@link ConvolveOp} using a very light "blur" kernel that acts like an
204             * anti-aliasing filter (softens the image a bit) when applied to an image.
205             * <p/>
206             * A common request by users of the library was that they wished to "soften"
207             * resulting images when scaling them down drastically. After quite a bit of
208             * A/B testing, the kernel used by this Op was selected as the closest match
209             * for the target which was the softer results from the deprecated
210             * {@link AreaAveragingScaleFilter} (which is used internally by the
211             * deprecated {@link Image#getScaledInstance(int, int, int)} method that
212             * imgscalr is meant to replace).
213             * <p/>
214             * This ConvolveOp uses a 3x3 kernel with the values: .0f, .08f, .0f, .08f,
215             * .68f, .08f, .0f, .08f, .0f
216             * <p/>
217             * For those that have worked with ConvolveOps before, this Op uses the
218             * {@link ConvolveOp#EDGE_NO_OP} instruction to not process the pixels along
219             * the very edge of the image (otherwise EDGE_ZERO_FILL would create a
220             * black-border around the image). If you have not worked with a ConvolveOp
221             * before, it just means this default OP will "do the right thing" and not
222             * give you garbage results.
223             * <p/>
224             * This ConvolveOp uses no {@link RenderingHints} values as internally the
225             * {@link ConvolveOp} class only uses hints when doing a color conversion
226             * between the source and destination {@link BufferedImage} targets.
227             * imgscalr allows the {@link ConvolveOp} to create its own destination
228             * image every time, so no color conversion is ever needed and thus no
229             * hints.
230             * <h3>Performance</h3>
231             * Use of this (and other) {@link ConvolveOp}s are hardware accelerated when
232             * possible. For more information on if your image op is hardware
233             * accelerated or not, check the source code of the underlying JDK class
234             * that actually executes the Op code, <a href=
235             * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html"
236             * >sun.awt.image.ImagingLib</a>.
237             * <h3>Known Issues</h3>
238             * In all versions of Java (tested up to Java 7 preview Build 131), running
239             * this op against a GIF with transparency and attempting to save the
240             * resulting image as a GIF results in a corrupted/empty file. The file must
241             * be saved out as a PNG to maintain the transparency.
242             */
243            public static final ConvolveOp OP_ANTIALIAS = new ConvolveOp(
244                            new Kernel(3, 3, new float[] { .0f, .08f, .0f, .08f, .68f, .08f,
245                                            .0f, .08f, .0f }), ConvolveOp.EDGE_NO_OP, null);
246    
247            /**
248             * Static initializer used to prepare some of the variables used by this
249             * class.
250             */
251            static {
252                    if (DEBUG)
253                            log("Debug output ENABLED");
254            }
255    
256            /**
257             * Used to define the different scaling hints that the algorithm can use.
258             */
259            public static enum Method {
260                    /**
261                     * Used to indicate that the scaling implementation should decide which
262                     * method to use in order to get the best looking scaled image in the
263                     * least amount of time.
264                     * <p/>
265                     * The scaling algorithm will use the
266                     * {@link Scalr#THRESHOLD_QUALITY_BALANCED} or
267                     * {@link Scalr#THRESHOLD_BALANCED_SPEED} thresholds as cut-offs to
268                     * decide between selecting the <code>QUALITY</code>,
269                     * <code>BALANCED</code> or <code>SPEED</code> scaling algorithms.
270                     * <p/>
271                     * By default the thresholds chosen will give nearly the best looking
272                     * result in the fastest amount of time. We intent this method to work
273                     * for 80% of people looking to scale an image quickly and get a good
274                     * looking result.
275                     */
276                    AUTOMATIC,
277                    /**
278                     * Used to indicate that the scaling implementation should scale as fast
279                     * as possible and return a result. For smaller images (800px in size)
280                     * this can result in noticeable aliasing but it can be a few magnitudes
281                     * times faster than using the QUALITY method.
282                     */
283                    SPEED,
284                    /**
285                     * Used to indicate that the scaling implementation should use a scaling
286                     * operation balanced between SPEED and QUALITY. Sometimes SPEED looks
287                     * too low quality to be useful (e.g. text can become unreadable when
288                     * scaled using SPEED) but using QUALITY mode will increase the
289                     * processing time too much. This mode provides a "better than SPEED"
290                     * quality in a "less than QUALITY" amount of time.
291                     */
292                    BALANCED,
293                    /**
294                     * Used to indicate that the scaling implementation should do everything
295                     * it can to create as nice of a result as possible. This approach is
296                     * most important for smaller pictures (800px or smaller) and less
297                     * important for larger pictures as the difference between this method
298                     * and the SPEED method become less and less noticeable as the
299                     * source-image size increases. Using the AUTOMATIC method will
300                     * automatically prefer the QUALITY method when scaling an image down
301                     * below 800px in size.
302                     */
303                    QUALITY;
304            }
305    
306            /**
307             * Used to define the different modes of resizing that the algorithm can
308             * use.
309             */
310            public static enum Mode {
311                    /**
312                     * Used to indicate that the scaling implementation should calculate
313                     * dimensions for the resultant image by looking at the image's
314                     * orientation and generating proportional dimensions that best fit into
315                     * the target width and height given
316                     * 
317                     * See "Image Proportions" in the class description for more detail.
318                     */
319                    AUTOMATIC,
320                    /**
321                     * Used to indicate that the scaling implementation should calculate
322                     * dimensions for the resultant image that best-fit within the given
323                     * width, regardless of the orientation of the image.
324                     */
325                    FIT_TO_WIDTH,
326                    /**
327                     * Used to indicate that the scaling implementation should calculate
328                     * dimensions for the resultant image that best-fit within the given
329                     * height, regardless of the orientation of the image.
330                     */
331                    FIT_TO_HEIGHT;
332            }
333    
334            /**
335             * Threshold (in pixels) at which point the scaling operation using the
336             * {@link Method#AUTOMATIC} method will decide if a {@link Method#BALANCED}
337             * method will be used (if smaller than or equal to threshold) or a
338             * {@link Method#SPEED} method will be used (if larger than threshold).
339             * <p/>
340             * The bigger the image is being scaled to, the less noticeable degradations
341             * in the image becomes and the faster algorithms can be selected.
342             * <p/>
343             * The value of this threshold (1600) was chosen after visual, by-hand, A/B
344             * testing between different types of images scaled with this library; both
345             * photographs and screenshots. It was determined that images below this
346             * size need to use a {@link Method#BALANCED} scale method to look decent in
347             * most all cases while using the faster {@link Method#SPEED} method for
348             * images bigger than this threshold showed no noticeable degradation over a
349             * <code>BALANCED</code> scale.
350             */
351            public static final int THRESHOLD_BALANCED_SPEED = 1600;
352    
353            /**
354             * Threshold (in pixels) at which point the scaling operation using the
355             * {@link Method#AUTOMATIC} method will decide if a {@link Method#QUALITY}
356             * method will be used (if smaller than or equal to threshold) or a
357             * {@link Method#BALANCED} method will be used (if larger than threshold).
358             * <p/>
359             * The bigger the image is being scaled to, the less noticeable degradations
360             * in the image becomes and the faster algorithms can be selected.
361             * <p/>
362             * The value of this threshold (800) was chosen after visual, by-hand, A/B
363             * testing between different types of images scaled with this library; both
364             * photographs and screenshots. It was determined that images below this
365             * size need to use a {@link Method#QUALITY} scale method to look decent in
366             * most all cases while using the faster {@link Method#BALANCED} method for
367             * images bigger than this threshold showed no noticeable degradation over a
368             * <code>QUALITY</code> scale.
369             */
370            public static final int THRESHOLD_QUALITY_BALANCED = 800;
371    
372            /**
373             * Resize a given image (maintaining its original proportion) to a width and
374             * height of the given <code>targetSize</code> using the scaling method of
375             * {@link Method#AUTOMATIC}.
376             * 
377             * @param src
378             *            The image that will be scaled.
379             * @param targetSize
380             *            The target width and height (square) that you wish the image
381             *            to fit within.
382             * 
383             * @return the proportionally scaled image with either a width or height of
384             *         the given target size.
385             * 
386             * @throws IllegalArgumentException
387             *             if <code>targetSize</code> is &lt; 0.
388             */
389            public static BufferedImage resize(BufferedImage src, int targetSize)
390                            throws IllegalArgumentException {
391                    return resize(src, Method.AUTOMATIC, Mode.AUTOMATIC, targetSize,
392                                    targetSize, (BufferedImageOp) null);
393            }
394    
395            /**
396             * Resize a given image (maintaining its original proportion) to a width and
397             * height of the given <code>targetSize</code> (or fitting the image to the
398             * given WIDTH or HEIGHT depending on the {@link Mode} specified) using the
399             * scaling method of {@link Method#AUTOMATIC}.
400             * 
401             * @param src
402             *            The image that will be scaled.
403             * @param resizeMode
404             *            Used to indicate how imgscalr should calculate the final
405             *            target size for the image, either fitting the image to the
406             *            given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image
407             *            to the given height ({@link Mode#FIT_TO_HEIGHT}). If
408             *            {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate
409             *            proportional dimensions for the scaled image based on its
410             *            orientation (landscape, square or portrait). Unless you have
411             *            very specific size requirements, most of the time you just
412             *            want to use {@link Mode#AUTOMATIC} to "do the right thing".
413             * @param targetSize
414             *            The target width and height (square) that you wish the image
415             *            to fit within.
416             * 
417             * @return the proportionally scaled image with either a width or height of
418             *         the given target size.
419             * 
420             * @throws IllegalArgumentException
421             *             if <code>targetSize</code> is &lt; 0.
422             */
423            public static BufferedImage resize(BufferedImage src, Mode resizeMode,
424                            int targetSize) throws IllegalArgumentException {
425                    return resize(src, Method.AUTOMATIC, resizeMode, targetSize,
426                                    targetSize, (BufferedImageOp) null);
427            }
428    
429            /**
430             * Resize a given image (maintaining its original proportion) to a width and
431             * height of the given <code>targetSize</code> using the scaling method of
432             * {@link Method#AUTOMATIC} and applying the given {@link BufferedImageOp}
433             * to the final result before returning it if one is provided.
434             * <p/>
435             * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are
436             * hardware accelerated operations, but many of the most popular (like
437             * {@link ConvolveOp}) are. For more information on if your image op is
438             * hardware accelerated or not, check the source code of the underlying JDK
439             * class that actually executes the Op code, <a href=
440             * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html"
441             * >sun.awt.image.ImagingLib</a>.
442             * 
443             * @param src
444             *            The image that will be scaled.
445             * @param targetSize
446             *            The target width and height (square) that you wish the image
447             *            to fit within.
448             * @param ops
449             *            Zero or more optional image operations (e.g. sharpen, blur,
450             *            etc.) that can be applied to the final result before returning
451             *            the image.
452             * 
453             * @return the proportionally scaled image with either a width or height of
454             *         the given target size.
455             * 
456             * @throws IllegalArgumentException
457             *             if <code>targetSize</code> is &lt; 0.
458             * 
459             * @see #OP_ANTIALIAS
460             */
461            public static BufferedImage resize(BufferedImage src, int targetSize,
462                            BufferedImageOp... ops) throws IllegalArgumentException {
463                    return resize(src, Method.AUTOMATIC, Mode.AUTOMATIC, targetSize,
464                                    targetSize, ops);
465            }
466    
467            /**
468             * Resize a given image (maintaining its original proportion) to a width and
469             * height of the given <code>targetSize</code> (or fitting the image to the
470             * given WIDTH or HEIGHT depending on the {@link Mode} specified) using the
471             * scaling method of {@link Method#AUTOMATIC} and applying the given
472             * {@link BufferedImageOp} to the final result before returning it if one is
473             * provided.
474             * <p/>
475             * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are
476             * hardware accelerated operations, but many of the most popular (like
477             * {@link ConvolveOp}) are. For more information on if your image op is
478             * hardware accelerated or not, check the source code of the underlying JDK
479             * class that actually executes the Op code, <a href=
480             * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html"
481             * >sun.awt.image.ImagingLib</a>.
482             * 
483             * @param src
484             *            The image that will be scaled.
485             * @param resizeMode
486             *            Used to indicate how imgscalr should calculate the final
487             *            target size for the image, either fitting the image to the
488             *            given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image
489             *            to the given height ({@link Mode#FIT_TO_HEIGHT}). If
490             *            {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate
491             *            proportional dimensions for the scaled image based on its
492             *            orientation (landscape, square or portrait). Unless you have
493             *            very specific size requirements, most of the time you just
494             *            want to use {@link Mode#AUTOMATIC} to "do the right thing".
495             * @param targetSize
496             *            The target width and height (square) that you wish the image
497             *            to fit within.
498             * @param ops
499             *            Zero or more optional image operations (e.g. sharpen, blur,
500             *            etc.) that can be applied to the final result before returning
501             *            the image.
502             * 
503             * @return the proportionally scaled image with either a width or height of
504             *         the given target size.
505             * 
506             * @throws IllegalArgumentException
507             *             if <code>targetSize</code> is &lt; 0.
508             * 
509             * @see #OP_ANTIALIAS
510             */
511            public static BufferedImage resize(BufferedImage src, Mode resizeMode,
512                            int targetSize, BufferedImageOp... ops)
513                            throws IllegalArgumentException {
514                    return resize(src, Method.AUTOMATIC, resizeMode, targetSize,
515                                    targetSize, ops);
516            }
517    
518            /**
519             * Resize a given image (maintaining its proportion) to the target width and
520             * height using the scaling method of {@link Method#AUTOMATIC}.
521             * <p/>
522             * <strong>TIP</strong>: See the class description to understand how this
523             * class handles recalculation of the <code>targetWidth</code> or
524             * <code>targetHeight</code> depending on the image's orientation in order
525             * to maintain the original proportion.
526             * 
527             * @param src
528             *            The image that will be scaled.
529             * @param targetWidth
530             *            The target width that you wish the image to have.
531             * @param targetHeight
532             *            The target height that you wish the image to have.
533             * 
534             * @return the proportionally scaled image with either a width or height of
535             *         the given target size.
536             * 
537             * @throws IllegalArgumentException
538             *             if <code>targetSize</code> is &lt; 0.
539             */
540            public static BufferedImage resize(BufferedImage src, int targetWidth,
541                            int targetHeight) throws IllegalArgumentException {
542                    return resize(src, Method.AUTOMATIC, Mode.AUTOMATIC, targetWidth,
543                                    targetHeight, (BufferedImageOp) null);
544            }
545    
546            /**
547             * Resize a given image (maintaining its proportion) to the target width and
548             * height (or fitting the image to the given WIDTH or HEIGHT depending on
549             * the {@link Mode} specified) using the scaling method of
550             * {@link Method#AUTOMATIC}.
551             * <p/>
552             * <strong>TIP</strong>: See the class description to understand how this
553             * class handles recalculation of the <code>targetWidth</code> or
554             * <code>targetHeight</code> depending on the image's orientation in order
555             * to maintain the original proportion.
556             * 
557             * @param src
558             *            The image that will be scaled.
559             * @param resizeMode
560             *            Used to indicate how imgscalr should calculate the final
561             *            target size for the image, either fitting the image to the
562             *            given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image
563             *            to the given height ({@link Mode#FIT_TO_HEIGHT}). If
564             *            {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate
565             *            proportional dimensions for the scaled image based on its
566             *            orientation (landscape, square or portrait). Unless you have
567             *            very specific size requirements, most of the time you just
568             *            want to use {@link Mode#AUTOMATIC} to "do the right thing".
569             * @param targetWidth
570             *            The target width that you wish the image to have.
571             * @param targetHeight
572             *            The target height that you wish the image to have.
573             * 
574             * @return the proportionally scaled image with either a width or height of
575             *         the given target size.
576             * 
577             * @throws IllegalArgumentException
578             *             if <code>targetSize</code> is &lt; 0.
579             */
580            public static BufferedImage resize(BufferedImage src, Mode resizeMode,
581                            int targetWidth, int targetHeight) throws IllegalArgumentException {
582                    return resize(src, Method.AUTOMATIC, resizeMode, targetWidth,
583                                    targetHeight, (BufferedImageOp) null);
584            }
585    
586            /**
587             * Resize a given image (maintaining its proportion) to the target width and
588             * height using the scaling method of {@link Method#AUTOMATIC} and applying
589             * the given {@link BufferedImageOp} to the final result before returning it
590             * if one is provided.
591             * <p/>
592             * <strong>TIP</strong>: See the class description to understand how this
593             * class handles recalculation of the <code>targetWidth</code> or
594             * <code>targetHeight</code> depending on the image's orientation in order
595             * to maintain the original proportion.
596             * <p/>
597             * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are
598             * hardware accelerated operations, but many of the most popular (like
599             * {@link ConvolveOp}) are. For more information on if your image op is
600             * hardware accelerated or not, check the source code of the underlying JDK
601             * class that actually executes the Op code, <a href=
602             * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html"
603             * >sun.awt.image.ImagingLib</a>.
604             * 
605             * @param src
606             *            The image that will be scaled.
607             * @param targetWidth
608             *            The target width that you wish the image to have.
609             * @param targetHeight
610             *            The target height that you wish the image to have.
611             * @param ops
612             *            Zero or more optional image operations (e.g. sharpen, blur,
613             *            etc.) that can be applied to the final result before returning
614             *            the image.
615             * 
616             * @return the proportionally scaled image with either a width or height of
617             *         the given target size.
618             * 
619             * @throws IllegalArgumentException
620             *             if <code>targetSize</code> is &lt; 0.
621             * 
622             * @see #OP_ANTIALIAS
623             */
624            public static BufferedImage resize(BufferedImage src, int targetWidth,
625                            int targetHeight, BufferedImageOp... ops)
626                            throws IllegalArgumentException {
627                    return resize(src, Method.AUTOMATIC, Mode.AUTOMATIC, targetWidth,
628                                    targetHeight, ops);
629            }
630    
631            /**
632             * Resize a given image (maintaining its proportion) to the target width and
633             * height (or fitting the image to the given WIDTH or HEIGHT depending on
634             * the {@link Mode} specified) using the scaling method of
635             * {@link Method#AUTOMATIC} and applying the given {@link BufferedImageOp}
636             * to the final result before returning it if one is provided.
637             * <p/>
638             * <strong>TIP</strong>: See the class description to understand how this
639             * class handles recalculation of the <code>targetWidth</code> or
640             * <code>targetHeight</code> depending on the image's orientation in order
641             * to maintain the original proportion.
642             * <p/>
643             * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are
644             * hardware accelerated operations, but many of the most popular (like
645             * {@link ConvolveOp}) are. For more information on if your image op is
646             * hardware accelerated or not, check the source code of the underlying JDK
647             * class that actually executes the Op code, <a href=
648             * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html"
649             * >sun.awt.image.ImagingLib</a>.
650             * 
651             * @param src
652             *            The image that will be scaled.
653             * @param resizeMode
654             *            Used to indicate how imgscalr should calculate the final
655             *            target size for the image, either fitting the image to the
656             *            given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image
657             *            to the given height ({@link Mode#FIT_TO_HEIGHT}). If
658             *            {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate
659             *            proportional dimensions for the scaled image based on its
660             *            orientation (landscape, square or portrait). Unless you have
661             *            very specific size requirements, most of the time you just
662             *            want to use {@link Mode#AUTOMATIC} to "do the right thing".
663             * @param targetWidth
664             *            The target width that you wish the image to have.
665             * @param targetHeight
666             *            The target height that you wish the image to have.
667             * @param ops
668             *            Zero or more optional image operations (e.g. sharpen, blur,
669             *            etc.) that can be applied to the final result before returning
670             *            the image.
671             * 
672             * @return the proportionally scaled image with either a width or height of
673             *         the given target size.
674             * 
675             * @throws IllegalArgumentException
676             *             if <code>targetSize</code> is &lt; 0.
677             * 
678             * @see #OP_ANTIALIAS
679             */
680            public static BufferedImage resize(BufferedImage src, Mode resizeMode,
681                            int targetWidth, int targetHeight, BufferedImageOp... ops)
682                            throws IllegalArgumentException {
683                    return resize(src, Method.AUTOMATIC, resizeMode, targetWidth,
684                                    targetHeight, ops);
685            }
686    
687            /**
688             * Resize a given image (maintaining its original proportion) to a width and
689             * height of the given <code>targetSize</code> using the given scaling
690             * method.
691             * 
692             * @param src
693             *            The image that will be scaled.
694             * @param scalingMethod
695             *            The method used for scaling the image; preferring speed to
696             *            quality or a balance of both.
697             * @param targetSize
698             *            The target width and height (square) that you wish the image
699             *            to fit within.
700             * 
701             * @return the proportionally scaled image with either a width or height of
702             *         the given target size.
703             * 
704             * @throws IllegalArgumentException
705             *             if <code>scalingMethod</code> is <code>null</code> or if
706             *             <code>targetSize</code> is &lt; 0.
707             */
708            public static BufferedImage resize(BufferedImage src, Method scalingMethod,
709                            int targetSize) throws IllegalArgumentException {
710                    return resize(src, scalingMethod, Mode.AUTOMATIC, targetSize,
711                                    targetSize, (BufferedImageOp) null);
712            }
713    
714            /**
715             * Resize a given image (maintaining its original proportion) to a width and
716             * height of the given <code>targetSize</code> (or fitting the image to the
717             * given WIDTH or HEIGHT depending on the {@link Mode} specified) using the
718             * given scaling method.
719             * 
720             * @param src
721             *            The image that will be scaled.
722             * @param scalingMethod
723             *            The method used for scaling the image; preferring speed to
724             *            quality or a balance of both.
725             * @param resizeMode
726             *            Used to indicate how imgscalr should calculate the final
727             *            target size for the image, either fitting the image to the
728             *            given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image
729             *            to the given height ({@link Mode#FIT_TO_HEIGHT}). If
730             *            {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate
731             *            proportional dimensions for the scaled image based on its
732             *            orientation (landscape, square or portrait). Unless you have
733             *            very specific size requirements, most of the time you just
734             *            want to use {@link Mode#AUTOMATIC} to "do the right thing".
735             * @param targetSize
736             *            The target width and height (square) that you wish the image
737             *            to fit within.
738             * 
739             * @return the proportionally scaled image with either a width or height of
740             *         the given target size.
741             * 
742             * @throws IllegalArgumentException
743             *             if <code>scalingMethod</code> is <code>null</code> or if
744             *             <code>targetSize</code> is &lt; 0.
745             */
746            public static BufferedImage resize(BufferedImage src, Method scalingMethod,
747                            Mode resizeMode, int targetSize) throws IllegalArgumentException {
748                    return resize(src, scalingMethod, resizeMode, targetSize, targetSize,
749                                    (BufferedImageOp) null);
750            }
751    
752            /**
753             * Resize a given image (maintaining its original proportion) to a width and
754             * height of the given <code>targetSize</code> using the given scaling
755             * method and applying the given {@link BufferedImageOp} to the final result
756             * before returning it if one is provided.
757             * <p/>
758             * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are
759             * hardware accelerated operations, but many of the most popular (like
760             * {@link ConvolveOp}) are. For more information on if your image op is
761             * hardware accelerated or not, check the source code of the underlying JDK
762             * class that actually executes the Op code, <a href=
763             * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html"
764             * >sun.awt.image.ImagingLib</a>.
765             * 
766             * @param src
767             *            The image that will be scaled.
768             * @param scalingMethod
769             *            The method used for scaling the image; preferring speed to
770             *            quality or a balance of both.
771             * @param targetSize
772             *            The target width and height (square) that you wish the image
773             *            to fit within.
774             * @param ops
775             *            Zero or more optional image operations (e.g. sharpen, blur,
776             *            etc.) that can be applied to the final result before returning
777             *            the image.
778             * 
779             * @return the proportionally scaled image with either a width or height of
780             *         the given target size.
781             * 
782             * @throws IllegalArgumentException
783             *             if <code>scalingMethod</code> is <code>null</code> or if
784             *             <code>targetSize</code> is &lt; 0.
785             * 
786             * @see #OP_ANTIALIAS
787             */
788            public static BufferedImage resize(BufferedImage src, Method scalingMethod,
789                            int targetSize, BufferedImageOp... ops)
790                            throws IllegalArgumentException {
791                    return resize(src, scalingMethod, Mode.AUTOMATIC, targetSize,
792                                    targetSize, ops);
793            }
794    
795            /**
796             * Resize a given image (maintaining its original proportion) to a width and
797             * height of the given <code>targetSize</code> (or fitting the image to the
798             * given WIDTH or HEIGHT depending on the {@link Mode} specified) using the
799             * given scaling method and applying the given {@link BufferedImageOp} to
800             * the final result before returning it if one is provided.
801             * <p/>
802             * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are
803             * hardware accelerated operations, but many of the most popular (like
804             * {@link ConvolveOp}) are. For more information on if your image op is
805             * hardware accelerated or not, check the source code of the underlying JDK
806             * class that actually executes the Op code, <a href=
807             * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html"
808             * >sun.awt.image.ImagingLib</a>.
809             * 
810             * @param src
811             *            The image that will be scaled.
812             * @param scalingMethod
813             *            The method used for scaling the image; preferring speed to
814             *            quality or a balance of both.
815             * @param resizeMode
816             *            Used to indicate how imgscalr should calculate the final
817             *            target size for the image, either fitting the image to the
818             *            given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image
819             *            to the given height ({@link Mode#FIT_TO_HEIGHT}). If
820             *            {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate
821             *            proportional dimensions for the scaled image based on its
822             *            orientation (landscape, square or portrait). Unless you have
823             *            very specific size requirements, most of the time you just
824             *            want to use {@link Mode#AUTOMATIC} to "do the right thing".
825             * @param targetSize
826             *            The target width and height (square) that you wish the image
827             *            to fit within.
828             * @param ops
829             *            Zero or more optional image operations (e.g. sharpen, blur,
830             *            etc.) that can be applied to the final result before returning
831             *            the image.
832             * 
833             * @return the proportionally scaled image with either a width or height of
834             *         the given target size.
835             * 
836             * @throws IllegalArgumentException
837             *             if <code>scalingMethod</code> is <code>null</code> or if
838             *             <code>targetSize</code> is &lt; 0.
839             * 
840             * @see #OP_ANTIALIAS
841             */
842            public static BufferedImage resize(BufferedImage src, Method scalingMethod,
843                            Mode resizeMode, int targetSize, BufferedImageOp... ops)
844                            throws IllegalArgumentException {
845                    return resize(src, scalingMethod, resizeMode, targetSize, targetSize,
846                                    ops);
847            }
848    
849            /**
850             * Resize a given image (maintaining its proportion) to the target width and
851             * height using the given scaling method.
852             * <p/>
853             * <strong>TIP</strong>: See the class description to understand how this
854             * class handles recalculation of the <code>targetWidth</code> or
855             * <code>targetHeight</code> depending on the image's orientation in order
856             * to maintain the original proportion.
857             * 
858             * @param src
859             *            The image that will be scaled.
860             * @param scalingMethod
861             *            The method used for scaling the image; preferring speed to
862             *            quality or a balance of both.
863             * @param targetWidth
864             *            The target width that you wish the image to have.
865             * @param targetHeight
866             *            The target height that you wish the image to have.
867             * 
868             * @return the proportionally scaled image no bigger than the given width
869             *         and height.
870             * 
871             * @throws IllegalArgumentException
872             *             if <code>scalingMethod</code> is <code>null</code>, if
873             *             <code>targetWidth</code> is &lt; 0 or if
874             *             <code>targetHeight</code> is &lt; 0.
875             */
876            public static BufferedImage resize(BufferedImage src, Method scalingMethod,
877                            int targetWidth, int targetHeight) throws IllegalArgumentException {
878                    return resize(src, scalingMethod, Mode.AUTOMATIC, targetWidth,
879                                    targetHeight, (BufferedImageOp) null);
880            }
881    
882            /**
883             * Resize a given image (maintaining its proportion) to the target width and
884             * height (or fitting the image to the given WIDTH or HEIGHT depending on
885             * the {@link Mode} specified) using the given scaling method.
886             * <p/>
887             * <strong>TIP</strong>: See the class description to understand how this
888             * class handles recalculation of the <code>targetWidth</code> or
889             * <code>targetHeight</code> depending on the image's orientation in order
890             * to maintain the original proportion.
891             * 
892             * @param src
893             *            The image that will be scaled.
894             * @param scalingMethod
895             *            The method used for scaling the image; preferring speed to
896             *            quality or a balance of both.
897             * @param resizeMode
898             *            Used to indicate how imgscalr should calculate the final
899             *            target size for the image, either fitting the image to the
900             *            given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image
901             *            to the given height ({@link Mode#FIT_TO_HEIGHT}). If
902             *            {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate
903             *            proportional dimensions for the scaled image based on its
904             *            orientation (landscape, square or portrait). Unless you have
905             *            very specific size requirements, most of the time you just
906             *            want to use {@link Mode#AUTOMATIC} to "do the right thing".
907             * @param targetWidth
908             *            The target width that you wish the image to have.
909             * @param targetHeight
910             *            The target height that you wish the image to have.
911             * 
912             * @return the proportionally scaled image no bigger than the given width
913             *         and height.
914             * 
915             * @throws IllegalArgumentException
916             *             if <code>scalingMethod</code> is <code>null</code>, if
917             *             <code>targetWidth</code> is &lt; 0 or if
918             *             <code>targetHeight</code> is &lt; 0.
919             */
920            public static BufferedImage resize(BufferedImage src, Method scalingMethod,
921                            Mode resizeMode, int targetWidth, int targetHeight)
922                            throws IllegalArgumentException {
923                    return resize(src, scalingMethod, resizeMode, targetWidth,
924                                    targetHeight, (BufferedImageOp) null);
925            }
926    
927            /**
928             * Resize a given image (maintaining its proportion) to the target width and
929             * height using the given scaling method and applying the given
930             * {@link BufferedImageOp} to the final result before returning it if one is
931             * provided.
932             * <p/>
933             * <strong>TIP</strong>: See the class description to understand how this
934             * class handles recalculation of the <code>targetWidth</code> or
935             * <code>targetHeight</code> depending on the image's orientation in order
936             * to maintain the original proportion.
937             * <p/>
938             * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are
939             * hardware accelerated operations, but many of the most popular (like
940             * {@link ConvolveOp}) are. For more information on if your image op is
941             * hardware accelerated or not, check the source code of the underlying JDK
942             * class that actually executes the Op code, <a href=
943             * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html"
944             * >sun.awt.image.ImagingLib</a>.
945             * 
946             * @param src
947             *            The image that will be scaled.
948             * @param scalingMethod
949             *            The method used for scaling the image; preferring speed to
950             *            quality or a balance of both.
951             * @param targetWidth
952             *            The target width that you wish the image to have.
953             * @param targetHeight
954             *            The target height that you wish the image to have.
955             * @param ops
956             *            Zero or more optional image operations (e.g. sharpen, blur,
957             *            etc.) that can be applied to the final result before returning
958             *            the image.
959             * 
960             * @return the proportionally scaled image no bigger than the given width
961             *         and height.
962             * 
963             * @throws IllegalArgumentException
964             *             if <code>scalingMethod</code> is <code>null</code>, if
965             *             <code>targetWidth</code> is &lt; 0 or if
966             *             <code>targetHeight</code> is &lt; 0.
967             * 
968             * @see #OP_ANTIALIAS
969             */
970            public static BufferedImage resize(BufferedImage src, Method scalingMethod,
971                            int targetWidth, int targetHeight, BufferedImageOp... ops) {
972                    return resize(src, scalingMethod, Mode.AUTOMATIC, targetWidth,
973                                    targetHeight, ops);
974            }
975    
976            /**
977             * Resize a given image (maintaining its proportion) to the target width and
978             * height (or fitting the image to the given WIDTH or HEIGHT depending on
979             * the {@link Mode} specified) using the given scaling method and applying
980             * the given {@link BufferedImageOp} to the final result before returning it
981             * if one is provided.
982             * <p/>
983             * <strong>TIP</strong>: See the class description to understand how this
984             * class handles recalculation of the <code>targetWidth</code> or
985             * <code>targetHeight</code> depending on the image's orientation in order
986             * to maintain the original proportion.
987             * <p/>
988             * <strong>Performance</strong>: Not all {@link BufferedImageOp}s are
989             * hardware accelerated operations, but many of the most popular (like
990             * {@link ConvolveOp}) are. For more information on if your image op is
991             * hardware accelerated or not, check the source code of the underlying JDK
992             * class that actually executes the Op code, <a href=
993             * "http://www.docjar.com/html/api/sun/awt/image/ImagingLib.java.html"
994             * >sun.awt.image.ImagingLib</a>.
995             * 
996             * @param src
997             *            The image that will be scaled.
998             * @param scalingMethod
999             *            The method used for scaling the image; preferring speed to
1000             *            quality or a balance of both.
1001             * @param resizeMode
1002             *            Used to indicate how imgscalr should calculate the final
1003             *            target size for the image, either fitting the image to the
1004             *            given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image
1005             *            to the given height ({@link Mode#FIT_TO_HEIGHT}). If
1006             *            {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate
1007             *            proportional dimensions for the scaled image based on its
1008             *            orientation (landscape, square or portrait). Unless you have
1009             *            very specific size requirements, most of the time you just
1010             *            want to use {@link Mode#AUTOMATIC} to "do the right thing".
1011             * @param targetWidth
1012             *            The target width that you wish the image to have.
1013             * @param targetHeight
1014             *            The target height that you wish the image to have.
1015             * @param ops
1016             *            Zero or more optional image operations (e.g. sharpen, blur,
1017             *            etc.) that can be applied to the final result before returning
1018             *            the image.
1019             * 
1020             * @return the proportionally scaled image no bigger than the given width
1021             *         and height.
1022             * 
1023             * @throws IllegalArgumentException
1024             *             if <code>src</code> is <code>null</code>, if
1025             *             <code>scalingMethod</code> is <code>null</code>, if
1026             *             <code>resizeMethod</code> is <code>null</code>, if
1027             *             <code>targetWidth</code> is &lt; 0 or if
1028             *             <code>targetHeight</code> is &lt; 0.
1029             * 
1030             * @see #OP_ANTIALIAS
1031             */
1032            public static BufferedImage resize(BufferedImage src, Method scalingMethod,
1033                            Mode resizeMode, int targetWidth, int targetHeight,
1034                            BufferedImageOp... ops) throws IllegalArgumentException {
1035                    if (src == null)
1036                            throw new IllegalArgumentException(
1037                                            "src cannot be null, a valid BufferedImage instance must be provided.");
1038                    if (scalingMethod == null)
1039                            throw new IllegalArgumentException(
1040                                            "scalingMethod cannot be null. A good default value is Method.AUTOMATIC.");
1041                    if (resizeMode == null)
1042                            throw new IllegalArgumentException(
1043                                            "resizeMode cannot be null. A good default value is Mode.AUTOMATIC.");
1044                    if (targetWidth < 0)
1045                            throw new IllegalArgumentException("targetWidth must be >= 0");
1046                    if (targetHeight < 0)
1047                            throw new IllegalArgumentException("targetHeight must be >= 0");
1048    
1049                    BufferedImage result = null;
1050    
1051                    long startTime = System.currentTimeMillis();
1052    
1053                    // Clear the 'null' ops arg passed in from other API methods
1054                    if (ops != null && ops.length == 1 && ops[0] == null)
1055                            ops = null;
1056    
1057                    int currentWidth = src.getWidth();
1058                    int currentHeight = src.getHeight();
1059    
1060                    // <= 1 is a square or landscape-oriented image, > 1 is a portrait.
1061                    float ratio = ((float) currentHeight / (float) currentWidth);
1062    
1063                    if (DEBUG)
1064                            log("START Resizing Source Image [size=%dx%d, mode=%s, orientation=%s, ratio(H/W)=%f] to [targetSize=%dx%d]",
1065                                            currentWidth, currentHeight, resizeMode,
1066                                            (ratio <= 1 ? "Landscape/Square" : "Portrait"), ratio,
1067                                            targetWidth, targetHeight);
1068    
1069                    /*
1070                     * The proportion of the picture must be honored, the way that is done
1071                     * is to figure out if the image is in a LANDSCAPE/SQUARE or PORTRAIT
1072                     * orientation and depending on its orientation, use the primary
1073                     * dimension (width for LANDSCAPE/SQUARE and height for PORTRAIT) to
1074                     * recalculate the alternative (height and width respectively) value
1075                     * that adheres to the existing ratio. This helps make life easier for
1076                     * the caller as they don't need to pre-compute proportional dimensions
1077                     * before calling the API, they can just specify the dimensions they
1078                     * would like the image to roughly fit within and it will do the right
1079                     * thing without mangling the result.
1080                     */
1081                    if ((ratio <= 1 && resizeMode == Mode.AUTOMATIC)
1082                                    || (resizeMode == Mode.FIT_TO_WIDTH)) {
1083                            // First make sure we need to do any work in the first place
1084                            if (targetWidth == src.getWidth())
1085                                    return src;
1086    
1087                            // Save for detailed logging (this is cheap).
1088                            int originalTargetHeight = targetHeight;
1089    
1090                            /*
1091                             * Landscape or Square Orientation: Ignore the given height and
1092                             * re-calculate a proportionally correct value based on the
1093                             * targetWidth.
1094                             */
1095                            targetHeight = Math.round((float) targetWidth * ratio);
1096    
1097                            if (DEBUG && originalTargetHeight != targetHeight)
1098                                    log("Auto-Corrected targetHeight [from=%d to=%d] to honor image proportions",
1099                                                    originalTargetHeight, targetHeight);
1100                    } else {
1101                            // First make sure we need to do any work in the first place
1102                            if (targetHeight == src.getHeight())
1103                                    return src;
1104    
1105                            // Save for detailed logging (this is cheap).
1106                            int originalTargetWidth = targetWidth;
1107    
1108                            /*
1109                             * Portrait Orientation: Ignore the given width and re-calculate a
1110                             * proportionally correct value based on the targetHeight.
1111                             */
1112                            targetWidth = Math.round((float) targetHeight / ratio);
1113    
1114                            if (DEBUG && originalTargetWidth != targetWidth)
1115                                    log("Auto-Corrected targetWidth [from=%d to=%d] to honor image proportions",
1116                                                    originalTargetWidth, targetWidth);
1117                    }
1118    
1119                    // If AUTOMATIC was specified, determine the real scaling method.
1120                    if (scalingMethod == Scalr.Method.AUTOMATIC)
1121                            scalingMethod = determineScalingMethod(targetWidth, targetHeight,
1122                                            ratio);
1123    
1124                    if (DEBUG)
1125                            log("Scaling Image to [size=%dx%d] using the %s method...",
1126                                            targetWidth, targetHeight, scalingMethod);
1127    
1128                    // Now we scale the image
1129                    if (scalingMethod == Scalr.Method.SPEED) {
1130                            result = scaleImage(src, targetWidth, targetHeight,
1131                                            RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
1132                    } else if (scalingMethod == Scalr.Method.BALANCED) {
1133                            result = scaleImage(src, targetWidth, targetHeight,
1134                                            RenderingHints.VALUE_INTERPOLATION_BILINEAR);
1135                    } else if (scalingMethod == Scalr.Method.QUALITY) {
1136                            /*
1137                             * If we are scaling up (in either width or height - since we know
1138                             * the image will stay proportional we just check if either are
1139                             * being scaled up), directly using a single BICUBIC will give us
1140                             * better results then using Chris Campbell's incremental scaling
1141                             * operation (and take a lot less time). If we are scaling down, we
1142                             * must use the incremental scaling algorithm for the best result.
1143                             */
1144                            if (targetWidth > currentWidth || targetHeight > currentHeight) {
1145                                    log("\tQUALITY Up-scale, single BICUBIC scaling will be used...");
1146    
1147                                    /*
1148                                     * BILINEAR and BICUBIC look similar the smaller the scale jump
1149                                     * upwards is, if the scale is larger BICUBIC looks sharper and
1150                                     * less fuzzy. But most importantly we have to use BICUBIC to
1151                                     * match the contract of the QUALITY rendering method. This note
1152                                     * is just here for anyone reading the code and wondering how
1153                                     * they can speed their own calls up.
1154                                     */
1155                                    result = scaleImage(src, targetWidth, targetHeight,
1156                                                    RenderingHints.VALUE_INTERPOLATION_BICUBIC);
1157                            } else {
1158                                    log("\tQUALITY Down-scale, incremental scaling will be used...");
1159    
1160                                    /*
1161                                     * Originally we wanted to use BILINEAR interpolation here
1162                                     * because it takes 1/3rd the time that the BICUBIC
1163                                     * interpolation does, however, when scaling large images down
1164                                     * to most sizes bigger than a thumbnail we witnessed noticeable
1165                                     * "softening" in the resultant image with BILINEAR that would
1166                                     * be unexpectedly annoying to a user expecting a "QUALITY"
1167                                     * scale of their original image. Instead BICUBIC was chosen to
1168                                     * honor the contract of a QUALITY scale of the original image.
1169                                     */
1170                                    result = scaleImageIncrementally(src, targetWidth,
1171                                                    targetHeight,
1172                                                    RenderingHints.VALUE_INTERPOLATION_BICUBIC);
1173                            }
1174                    }
1175    
1176                    // Apply the image ops if any were provided
1177                    if (ops != null && ops.length > 0) {
1178                            if (DEBUG)
1179                                    log("Applying %d Image Ops to Result", ops.length);
1180    
1181                            for (BufferedImageOp op : ops) {
1182                                    // In case a null op was passed in, skip it instead of dying
1183                                    if (op == null)
1184                                            continue;
1185    
1186                                    long opStartTime = System.currentTimeMillis();
1187                                    Rectangle2D dims = op.getBounds2D(result);
1188    
1189                                    /*
1190                                     * We must manually create the target image; we cannot rely on
1191                                     * the null-dest filter() method to create a valid destination
1192                                     * for us thanks to this JDK bug that has been filed for almost
1193                                     * a decade: http://bugs.sun.com/bugdatabase/view_bug.
1194                                     * do;jsessionid=33b25bf937f467791ff5792cb9dc?bug_id=4965606
1195                                     */
1196                                    BufferedImage dest = new BufferedImage((int) Math.round(dims
1197                                                    .getWidth()), (int) Math.round(dims.getHeight()),
1198                                                    result.getType());
1199    
1200                                    result = op.filter(result, dest);
1201    
1202                                    if (DEBUG)
1203                                            log("\tOp Applied in %d ms, Resultant Image [width=%d, height=%d], Op: %s",
1204                                                            (System.currentTimeMillis() - opStartTime),
1205                                                            result.getWidth(), result.getHeight(), op);
1206                            }
1207                    }
1208    
1209                    if (DEBUG) {
1210                            long elapsedTime = System.currentTimeMillis() - startTime;
1211                            log("END Source Image Scaled from [%dx%d] to [%dx%d] and %d BufferedImageOp(s) Applied in %d ms",
1212                                            currentWidth, currentHeight, result.getWidth(),
1213                                            result.getHeight(), (ops == null ? 0 : ops.length),
1214                                            elapsedTime);
1215                    }
1216    
1217                    return result;
1218            }
1219    
1220            /**
1221             * Helper method used to ensure a message is loggable before it is logged
1222             * and then pre-pend a universal prefix to all log messages generated by
1223             * this library to make the log entries easy to parse visually or
1224             * programmatically.
1225             * <p/>
1226             * If a message cannot be logged (logging is disabled) then this method
1227             * returns immediately.
1228             * <p/>
1229             * <strong>NOTE</strong>: Because Java will auto-box primitive arguments
1230             * into Objects when building out the <code>params</code> array, care should
1231             * be taken not to call this method with primitive values unless
1232             * {@link #DEBUG} is <code>true</code>; otherwise the VM will be spending
1233             * time performing unnecessary auto-boxing calculations.
1234             * 
1235             * @param message
1236             *            The log message in <a href=
1237             *            "http://download.oracle.com/javase/6/docs/api/java/util/Formatter.html#syntax"
1238             *            >format string syntax</a> that will be logged.
1239             * @param params
1240             *            The parameters that will be swapped into all the place holders
1241             *            in the original messages before being logged.
1242             * 
1243             * @see #LOG_PREFIX
1244             */
1245            protected static void log(String message, Object... params) {
1246                    if (DEBUG)
1247                            System.out.printf(LOG_PREFIX + message + '\n', params);
1248            }
1249    
1250            /**
1251             * Used to determine the scaling {@link Method} that is best suited for
1252             * scaling the image to the targeted dimensions.
1253             * <p/>
1254             * This method is intended to be used to select a specific scaling
1255             * {@link Method} when a {@link Method#AUTOMATIC} method is specified. This
1256             * method utilizes the {@link #THRESHOLD_QUALITY_BALANCED} and
1257             * {@link #THRESHOLD_BALANCED_SPEED} thresholds when selecting which method
1258             * should be used by comparing the primary dimension (width or height)
1259             * against the threshold and seeing where the image falls. The primary
1260             * dimension is determined by looking at the orientation of the image:
1261             * landscape or square images use their width and portrait-oriented images
1262             * use their height.
1263             * 
1264             * @param targetWidth
1265             *            The target width for the scaled image.
1266             * @param targetHeight
1267             *            The target height for the scaled image.
1268             * @param ratio
1269             *            A height/width ratio used to determine the orientation of the
1270             *            image so the primary dimension (width or height) can be
1271             *            selected to test if it is greater than or less than a
1272             *            particular threshold.
1273             * 
1274             * @return the fastest {@link Method} suited for scaling the image to the
1275             *         specified dimensions while maintaining a good-looking result.
1276             */
1277            protected static Method determineScalingMethod(int targetWidth,
1278                            int targetHeight, float ratio) {
1279                    // Get the primary dimension based on the orientation of the image
1280                    int length = (ratio <= 1 ? targetWidth : targetHeight);
1281    
1282                    // Default to speed
1283                    Method result = Method.SPEED;
1284    
1285                    // Figure out which method should be used
1286                    if (length <= THRESHOLD_QUALITY_BALANCED)
1287                            result = Method.QUALITY;
1288                    else if (length <= THRESHOLD_BALANCED_SPEED)
1289                            result = Method.BALANCED;
1290    
1291                    if (DEBUG)
1292                            log("AUTOMATIC Scaling Method Selected [%s] for Image [size=%dx%d]",
1293                                            result.name(), targetWidth, targetHeight);
1294    
1295                    return result;
1296            }
1297    
1298            /**
1299             * Used to implement a straight-forward image-scaling operation using Java
1300             * 2D.
1301             * <p/>
1302             * This method uses the Snoracle-encouraged method of
1303             * <code>Graphics2D.drawImage(...)</code> to scale the given image with the
1304             * given interpolation hint.
1305             * 
1306             * @param src
1307             *            The image that will be scaled.
1308             * @param targetWidth
1309             *            The target width for the scaled image.
1310             * @param targetHeight
1311             *            The target height for the scaled image.
1312             * @param interpolationHintValue
1313             *            The {@link RenderingHints} interpolation value used to
1314             *            indicate the method that {@link Graphics2D} should use when
1315             *            scaling the image.
1316             * 
1317             * @return the result of scaling the original <code>src</code> to the given
1318             *         dimensions using the given interpolation method.
1319             */
1320            protected static BufferedImage scaleImage(BufferedImage src,
1321                            int targetWidth, int targetHeight, Object interpolationHintValue) {
1322                    /*
1323                     * Determine the RGB-based TYPE of image (plain RGB or RGB + Alpha) that
1324                     * we want to render the scaled instance into. We force all rendering
1325                     * results into one of these two types, avoiding the case where a source
1326                     * image is of an unsupported (or poorly supported) format by Java2D and
1327                     * the written results, when attempting to re-create and write out that
1328                     * format, is garbage.
1329                     * 
1330                     * Originally reported by Magnus Kvalheim from Movellas when scaling
1331                     * certain GIF and PNG images.
1332                     * 
1333                     * More information about Java2D and poorly supported image types:
1334                     * http:/
1335                     * /www.mail-archive.com/java2d-interest@capra.eng.sun.com/msg05621.html
1336                     * 
1337                     * Thanks to Morten Nobel for the implementation hint:
1338                     * http://code.google
1339                     * .com/p/java-image-scaling/source/browse/trunk/src/main
1340                     * /java/com/mortennobel/imagescaling/MultiStepRescaleOp.java
1341                     */
1342                    int imageType = (src.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB
1343                                    : BufferedImage.TYPE_INT_ARGB);
1344    
1345                    // Setup the rendering resources to match the source image's
1346                    BufferedImage result = new BufferedImage(targetWidth, targetHeight,
1347                                    imageType);
1348                    Graphics2D resultGraphics = result.createGraphics();
1349    
1350                    // Scale the image to the new buffer using the specified rendering hint.
1351                    resultGraphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
1352                                    interpolationHintValue);
1353                    resultGraphics.drawImage(src, 0, 0, targetWidth, targetHeight, null);
1354    
1355                    // Just to be clean, explicitly dispose our temporary graphics object
1356                    resultGraphics.dispose();
1357    
1358                    // Return the scaled image to the caller.
1359                    return result;
1360            }
1361    
1362            /**
1363             * Used to implement Chris Campbell's incremental-scaling algorithm: <a
1364             * href="http://today.java.net/pub/a/today/2007/04/03/perils
1365             * -of-image-getscaledinstance
1366             * .html">http://today.java.net/pub/a/today/2007/04/03/perils
1367             * -of-image-getscaledinstance.html</a>.
1368             * <p/>
1369             * Modifications to the original algorithm are variable names and comments
1370             * added for clarity and the hard-coding of using BICUBIC interpolation as
1371             * well as the explicit "flush()" operation on the interim BufferedImage
1372             * instances to avoid resource leaking.
1373             * 
1374             * @param src
1375             *            The image that will be scaled.
1376             * @param targetWidth
1377             *            The target width for the scaled image.
1378             * @param targetHeight
1379             *            The target height for the scaled image.
1380             * @param interpolationHintValue
1381             *            The {@link RenderingHints} interpolation value used to
1382             *            indicate the method that {@link Graphics2D} should use when
1383             *            scaling the image.
1384             * 
1385             * @return an image scaled to the given dimensions using the given rendering
1386             *         hint.
1387             */
1388            protected static BufferedImage scaleImageIncrementally(BufferedImage src,
1389                            int targetWidth, int targetHeight, Object interpolationHintValue) {
1390                    boolean hasReassignedSrc = false;
1391                    int incrementCount = 0;
1392                    int currentWidth = src.getWidth();
1393                    int currentHeight = src.getHeight();
1394    
1395                    do {
1396                            /*
1397                             * If the current width is bigger than our target, cut it in half
1398                             * and sample again.
1399                             */
1400                            if (currentWidth > targetWidth) {
1401                                    currentWidth /= 2;
1402    
1403                                    /*
1404                                     * If we cut the width too far it means we are on our last
1405                                     * iteration. Just set it to the target width and finish up.
1406                                     */
1407                                    if (currentWidth < targetWidth)
1408                                            currentWidth = targetWidth;
1409                            }
1410    
1411                            /*
1412                             * If the current height is bigger than our target, cut it in half
1413                             * and sample again.
1414                             */
1415    
1416                            if (currentHeight > targetHeight) {
1417                                    currentHeight /= 2;
1418    
1419                                    /*
1420                                     * If we cut the height too far it means we are on our last
1421                                     * iteration. Just set it to the target height and finish up.
1422                                     */
1423    
1424                                    if (currentHeight < targetHeight)
1425                                            currentHeight = targetHeight;
1426                            }
1427    
1428                            // Render the incremental scaled image.
1429                            BufferedImage incrementalImage = scaleImage(src, currentWidth,
1430                                            currentHeight, interpolationHintValue);
1431    
1432                            /*
1433                             * Before re-assigning our interim (partially scaled)
1434                             * incrementalImage to be the new src image before we iterate around
1435                             * again to process it down further, we want to flush() the previous
1436                             * src image IF (and only IF) it was one of our own temporary
1437                             * BufferedImages created during this incremental down-sampling
1438                             * cycle. If it wasn't one of ours, then it was the original
1439                             * caller-supplied BufferedImage in which case we don't want to
1440                             * flush() it and just leave it alone.
1441                             */
1442                            if (hasReassignedSrc)
1443                                    src.flush();
1444    
1445                            /*
1446                             * Now treat our incremental partially scaled image as the src image
1447                             * and cycle through our loop again to do another incremental
1448                             * scaling of it (if necessary).
1449                             */
1450                            src = incrementalImage;
1451    
1452                            /*
1453                             * Keep track of us re-assigning the original caller-supplied source
1454                             * image with one of our interim BufferedImages so we know when to
1455                             * explicitly flush the interim "src" on the next cycle through.
1456                             */
1457                            if (!hasReassignedSrc)
1458                                    hasReassignedSrc = true;
1459    
1460                            // Track how many times we go through this cycle to scale the image.
1461                            incrementCount++;
1462                    } while (currentWidth != targetWidth || currentHeight != targetHeight);
1463    
1464                    if (DEBUG)
1465                            log("\tScaled Image in %d steps", incrementCount);
1466    
1467                    /*
1468                     * Once the loop has exited, the src image argument is now our scaled
1469                     * result image that we want to return.
1470                     */
1471                    return src;
1472            }
1473    }