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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 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 < 0 or if
874 * <code>targetHeight</code> is < 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 < 0 or if
918 * <code>targetHeight</code> is < 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 < 0 or if
966 * <code>targetHeight</code> is < 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 < 0 or if
1028 * <code>targetHeight</code> is < 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 }