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