[ all classes ]
[ org.imgscalr ]
Coverage Summary for Class: Scalr (org.imgscalr)
| Class | Method, % | Line, % |
|---|---|---|
| Scalr | 60.9% (14/ 23) | 37% (113/ 305) |
| Scalr$1 | 0% (0/ 1) | 0% (0/ 1) |
| Scalr$Method | 100% (2/ 2) | 100% (6/ 6) |
| Scalr$Mode | 100% (2/ 2) | 100% (6/ 6) |
| Scalr$Rotation | 0% (0/ 1) | 0% (0/ 6) |
| total | 62.1% (18/ 29) | 38.6% (125/ 324) |
1 /**
2 * Copyright 2011 The Buzz Media, LLC
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.imgscalr;
17
18 import java.awt.Color;
19 import java.awt.Graphics;
20 import java.awt.Graphics2D;
21 import java.awt.Image;
22 import java.awt.RenderingHints;
23 import java.awt.Transparency;
24 import java.awt.color.ColorSpace;
25 import java.awt.geom.AffineTransform;
26 import java.awt.geom.Rectangle2D;
27 import java.awt.image.AreaAveragingScaleFilter;
28 import java.awt.image.BufferedImage;
29 import java.awt.image.BufferedImageOp;
30 import java.awt.image.ColorConvertOp;
31 import java.awt.image.ColorModel;
32 import java.awt.image.ConvolveOp;
33 import java.awt.image.ImagingOpException;
34 import java.awt.image.IndexColorModel;
35 import java.awt.image.Kernel;
36 import java.awt.image.RasterFormatException;
37 import java.awt.image.RescaleOp;
38
39 import javax.imageio.ImageIO;
40
41 /**
42 * Class used to implement performant, high-quality and intelligent image
43 * scaling and manipulation algorithms in native Java 2D.
44 *

45 * This class utilizes the Java2D "best practices" for image manipulation,
46 * ensuring that all operations (even most user-provided {@link BufferedImageOp}
47 * s) are hardware accelerated if provided by the platform and host-VM.
48 *

49 *
Image Quality

50 * This class implements a few different methods for scaling an image, providing
51 * either the best-looking result, the fastest result or a balanced result
52 * between the two depending on the scaling hint provided (see {@link Method}).
53 *

54 * This class also implements an optimized version of the incremental scaling
55 * algorithm presented by Chris Campbell in his Perils of
57 * Image.getScaledInstance() article in order to give the best-looking image
58 * resize results (e.g. generating thumbnails that aren't blurry or jagged).
59 *

60 * The results generated by imgscalr using this method, as compared to a single
61 * {@link RenderingHints#VALUE_INTERPOLATION_BICUBIC} scale operation look much
62 * better, especially when using the {@link Method#ULTRA_QUALITY} method.
63 *

64 * Only when scaling using the {@link Method#AUTOMATIC} method will this class
65 * look at the size of the image before selecting an approach to scaling the
66 * image. If {@link Method#QUALITY} is specified, the best-looking algorithm
67 * possible is always used.
68 *

69 * Minor modifications are made to Campbell's original implementation in the
70 * form of:
71 *

72 *
Instead of accepting a user-supplied interpolation method,
73 * {@link RenderingHints#VALUE_INTERPOLATION_BICUBIC} interpolation is always
74 * used. This was done after A/B comparison testing with large images
75 * down-scaled to thumbnail sizes showed noticeable "blurring" when BILINEAR
76 * interpolation was used. Given that Campbell's algorithm is only used in
77 * QUALITY mode when down-scaling, it was determined that the user's expectation
78 * of a much less blurry picture would require that BICUBIC be the default
79 * interpolation in order to meet the QUALITY expectation.

80 *
After each iteration of the do-while loop that incrementally scales the
81 * source image down, an explicit effort is made to call
82 * {@link BufferedImage#flush()} on the interim temporary {@link BufferedImage}
83 * instances created by the algorithm in an attempt to ensure a more complete GC
84 * cycle by the VM when cleaning up the temporary instances (this is in addition
85 * to disposing of the temporary {@link Graphics2D} references as well).

86 *
Extensive comments have been added to increase readability of the code.

87 *
Variable names have been expanded to increase readability of the code.

88 *

89 *

90 * NOTE: This class does not call {@link BufferedImage#flush()}
91 * on any of the source images passed in by calling code; it is up to
92 * the original caller to dispose of their source images when they are no longer
93 * needed so the VM can most efficiently GC them.
94 *
Image Proportions

95 * All scaling operations implemented by this class maintain the proportions of
96 * the original image unless a mode of {@link Mode#FIT_EXACT} is specified; in
97 * which case the orientation and proportion of the source image is ignored and
98 * the image is stretched (if necessary) to fit the exact dimensions given.
99 *

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 *

104 *
If the image is LANDSCAPE-oriented or SQUARE, treat the
105 * targetWidth as the primary dimension and re-calculate the
106 * targetHeight regardless of what is passed in.

107 *
If image is PORTRAIT-oriented, treat the targetHeight as the
108 * primary dimension and re-calculate the targetWidth regardless of
109 * what is passed in.

110 *
If a {@link Mode} value of {@link Mode#FIT_TO_WIDTH} or
111 * {@link Mode#FIT_TO_HEIGHT} is passed in to the resize 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).

118 *

119 *
Optimized Image Handling

120 * Java2D provides support for a number of different image types defined as
121 * BufferedImage.TYPE_* variables, unfortunately not all image
122 * types are supported equally in the Java2D rendering pipeline.
123 *

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 of the same type 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 *

131 * imgscalr specifically accounts for and automatically hands
132 * ALL of these pain points for you internally by shuffling all
133 * images into one of two types:
134 *

135 *
{@link BufferedImage#TYPE_INT_RGB}

136 *
{@link BufferedImage#TYPE_INT_ARGB}

137 *

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 *<EMAIL_ADDRESS>142 * >here.
143 *

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 *
GIF Transparency

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 *

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 *

166 * Workaround: 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 *

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 *

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 *

178 * More can be read about the issue here and here.
182 *
Thread Safety

183 * The {@link Scalr} class is thread-safe (as all the methods
184 * are static); 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 *
Logging

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 System.out via the printf 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 *

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<EMAIL_ADDRESS>201 * @since 1.1
202 */
203 public class Scalr {
204 /**
205 * System property name used to define the debug boolean flag.
206 *

207 * Value is "imgscalr.debug".
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 *

214 * Value is "imgscalr.logPrefix".
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 * "imgscalr.debug" system property to true. This
221 * value will be false if the "imgscalr.debug"
222 * system property is undefined or set to false.
223 *

224 * This property can be set on startup with:

225 *
226 * -Dimgscalr.debug=true
227 *  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 *

231 * Default value is false.
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 *

240 * This property can be set on startup with:

241 *
242 * -Dimgscalr.logPrefix=<YOUR PREFIX HERE>
243 *  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 *

247 * Default value is "[imgscalr] " (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 *

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 *

264 * This ConvolveOp uses a 3x3 kernel with the values:
265 *

266 *

267 * .0f
268 * .08f
269 * .0f
270 *

271 *

272 * .08f
273 * .68f
274 * .08f
275 *

276 *

277 * .0f
278 * .08f
279 * .0f
280 *

281 *

282 *

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 *

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 *
Performance

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, sun.awt.image.ImagingLib.
303 *
Known Issues

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 *

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 *

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 *

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<EMAIL_ADDRESS>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 *

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 QUALITY,
371 * BALANCED or SPEED scaling algorithms.
372 *

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<EMAIL_ADDRESS>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 *

441 * It is recommended that you use one of the other FIT_TO
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 largest image that fit within the bounding box,
450 * without cropping or distortion, retaining the original proportions.
451 */
452 BEST_FIT_BOTH,
453 /**
454 * Used to indicate that the scaling implementation should calculate
455 * dimensions for the resultant image that best-fit within the given
456 * width, regardless of the orientation of the image.
457 */
458 FIT_TO_WIDTH,
459 /**
460 * Used to indicate that the scaling implementation should calculate
461 * dimensions for the resultant image that best-fit within the given
462 * height, regardless of the orientation of the image.
463 */
464 FIT_TO_HEIGHT;
465 }
466
467 /**
468 * Used to define the different types of rotations that can be applied to an
469 * image during a resize operation.
470 *
471 * @author Riyad Kalla<EMAIL_ADDRESS>472 * @since 3.2
473 */
474 public static enum Rotation {
475 /**
476 * 90-degree, clockwise rotation (to the right). This is equivalent to a
477 * quarter-turn of the image to the right; moving the picture on to its
478 * right side.
479 */
480 CW_90,
481 /**
482 * 180-degree, clockwise rotation (to the right). This is equivalent to
483 * 1 half-turn of the image to the right; rotating the picture around
484 * until it is upside down from the original position.
485 */
486 CW_180,
487 /**
488 * 270-degree, clockwise rotation (to the right). This is equivalent to
489 * a quarter-turn of the image to the left; moving the picture on to its
490 * left side.
491 */
492 CW_270,
493 /**
494 * Flip the image horizontally by reflecting it around the y axis.
495 *

496 * This is not a standard rotation around a center point, but instead
497 * creates the mirrored reflection of the image horizontally.
498 *

499 * More specifically, the vertical orientation of the image stays the
500 * same (the top stays on top, and the bottom on bottom), but the right
501 * and left sides flip. This is different than a standard rotation where
502 * the top and bottom would also have been flipped.
503 */
504 FLIP_HORZ,
505 /**
506 * Flip the image vertically by reflecting it around the x axis.
507 *

508 * This is not a standard rotation around a center point, but instead
509 * creates the mirrored reflection of the image vertically.
510 *

511 * More specifically, the horizontal orientation of the image stays the
512 * same (the left stays on the left and the right stays on the right),
513 * but the top and bottom sides flip. This is different than a standard
514 * rotation where the left and right would also have been flipped.
515 */
516 FLIP_VERT;
517 }
518
519 /**
520 * Threshold (in pixels) at which point the scaling operation using the
521 * {@link Method#AUTOMATIC} method will decide if a {@link Method#BALANCED}
522 * method will be used (if smaller than or equal to threshold) or a
523 * {@link Method#SPEED} method will be used (if larger than threshold).
524 *

525 * The bigger the image is being scaled to, the less noticeable degradations
526 * in the image becomes and the faster algorithms can be selected.
527 *

528 * The value of this threshold (1600) was chosen after visual, by-hand, A/B
529 * testing between different types of images scaled with this library; both
530 * photographs and screenshots. It was determined that images below this
531 * size need to use a {@link Method#BALANCED} scale method to look decent in
532 * most all cases while using the faster {@link Method#SPEED} method for
533 * images bigger than this threshold showed no noticeable degradation over a
534 * BALANCED scale.
535 */
536 public static final int THRESHOLD_BALANCED_SPEED = 1600;
537
538 /**
539 * Threshold (in pixels) at which point the scaling operation using the
540 * {@link Method#AUTOMATIC} method will decide if a {@link Method#QUALITY}
541 * method will be used (if smaller than or equal to threshold) or a
542 * {@link Method#BALANCED} method will be used (if larger than threshold).
543 *

544 * The bigger the image is being scaled to, the less noticeable degradations
545 * in the image becomes and the faster algorithms can be selected.
546 *

547 * The value of this threshold (800) was chosen after visual, by-hand, A/B
548 * testing between different types of images scaled with this library; both
549 * photographs and screenshots. It was determined that images below this
550 * size need to use a {@link Method#QUALITY} scale method to look decent in
551 * most all cases while using the faster {@link Method#BALANCED} method for
552 * images bigger than this threshold showed no noticeable degradation over a
553 * QUALITY scale.
554 */
555 public static final int THRESHOLD_QUALITY_BALANCED = 800;
556
557 /**
558 * Used to apply, in the order given, 1 or more {@link BufferedImageOp}s to
559 * a given {@link BufferedImage} and return the result.
560 *

561 * Feature: This implementation works around a
563 * decade-old JDK bug that can cause a {@link RasterFormatException}
564 * when applying a perfectly valid {@link BufferedImageOp}s to images.
565 *

566 * Feature: This implementation also works around
567 * {@link BufferedImageOp}s failing to apply and throwing
568 * {@link ImagingOpException}s when run against a src image
569 * type that is poorly supported. Unfortunately using {@link ImageIO} and
570 * standard Java methods to load images provides no consistency in getting
571 * images in well-supported formats. This method automatically accounts and
572 * corrects for all those problems (if necessary).
573 *

574 * It is recommended you always use this method to apply any
575 * {@link BufferedImageOp}s instead of relying on directly using the
576 * {@link BufferedImageOp#filter(BufferedImage, BufferedImage)} method.
577 *

578 * Performance: Not all {@link BufferedImageOp}s are
579 * hardware accelerated operations, but many of the most popular (like
580 * {@link ConvolveOp}) are. For more information on if your image op is
581 * hardware accelerated or not, check the source code of the underlying JDK
582 * class that actually executes the Op code, sun.awt.image.ImagingLib.
585 *

586 * TIP: This operation leaves the original src
587 * image unmodified. If the caller is done with the src image
588 * after getting the result of this operation, remember to call
589 * {@link BufferedImage#flush()} on the src to free up native
590 * resources and make it easier for the GC to collect the unused image.
591 *
592 * @param src
593 * The image that will have the ops applied to it.
594 * @param ops
595 * 1 or more ops to apply to the image.
596 *
597 * @return a new {@link BufferedImage} that represents the src
598 * with all the given operations applied to it.
599 *
600 * @throws IllegalArgumentException
601 * if src is null.
602 * @throws IllegalArgumentException
603 * if ops is null or empty.
604 * @throws ImagingOpException
605 * if one of the given {@link BufferedImageOp}s fails to apply.
606 * These exceptions bubble up from the inside of most of the
607 * {@link BufferedImageOp} implementations and are explicitly
608 * defined on the imgscalr API to make it easier for callers to
609 * catch the exception (if they are passing along optional ops
610 * to be applied). imgscalr takes detailed steps to avoid the
611 * most common pitfalls that will cause {@link BufferedImageOp}s
612 * to fail, even when using straight forward JDK-image
613 * operations.
614 */
615 public static BufferedImage apply(BufferedImage src, BufferedImageOp... ops)
616 throws IllegalArgumentException, ImagingOpException {
617 long t = -1;
618 if (DEBUG)
619 t = System.currentTimeMillis();
620
621 if (src == null)
622 throw new IllegalArgumentException("src cannot be null");
623 if (ops == null || ops.length == 0)
624 throw new IllegalArgumentException("ops cannot be null or empty");
625
626 int type = src.getType();
627
628 /*
629 * Ensure the src image is in the best supported image type before we
630 * continue, otherwise it is possible our calls below to getBounds2D and
631 * certainly filter(...) may fail if not.
632 *
633 * Java2D makes an attempt at applying most BufferedImageOps using
634 * hardware acceleration via the ImagingLib internal library.
635 *
636 * Unfortunately may of the BufferedImageOp are written to simply fail
637 * with an ImagingOpException if the operation cannot be applied with no
638 * additional information about what went wrong or attempts at
639 * re-applying it in different ways.
640 *
641 * This is assuming the failing BufferedImageOp even returns a null
642 * image after failing to apply; some simply return a corrupted/black
643 * image that result in no exception and it is up to the user to
644 * discover this.
645 *
646 * In internal testing, EVERY failure I've ever seen was the result of
647 * the source image being in a poorly-supported BufferedImage Type like
648 * BGR or ABGR (even though it was loaded with ImageIO).
649 *
650 * To avoid this nasty/stupid surprise with BufferedImageOps, we always
651 * ensure that the src image starts in an optimally supported format
652 * before we try and apply the filter.
653 */
654 if (!(type == BufferedImage.TYPE_INT_RGB || type == BufferedImage.TYPE_INT_ARGB))
655 src = copyToOptimalImage(src);
656
657 if (DEBUG)
658 log(0, "Applying %d BufferedImageOps...", ops.length);
659
660 boolean hasReassignedSrc = false;
661
662 for (int i = 0; i < ops.length; i++) {
663 long subT = -1;
664 if (DEBUG)
665 subT = System.currentTimeMillis();
666 BufferedImageOp op = ops[i];
667
668 // Skip null ops instead of throwing an exception.
669 if (op == null)
670 continue;
671
672 if (DEBUG)
673 log(1, "Applying BufferedImageOp [class=%s, toString=%s]...",
674 op.getClass(), op.toString());
675
676 /*
677 * Must use op.getBounds instead of src.getWidth and src.getHeight
678 * because we are trying to create an image big enough to hold the
679 * result of this operation (which may be to scale the image
680 * smaller), in that case the bounds reported by this op and the
681 * bounds reported by the source image will be different.
682 */
683 Rectangle2D resultBounds = op.getBounds2D(src);
684
685 // Watch out for flaky/misbehaving ops that fail to work right.
686 if (resultBounds == null)
687 throw new ImagingOpException(
688 "BufferedImageOp ["
689 + op.toString()
690 + "] getBounds2D(src) returned null bounds for the target image; this should not happen and indicates a problem with application of this type of op.");
691
692 /*
693 * We must manually create the target image; we cannot rely on the
694 * null-destination filter() method to create a valid destination
695 * for us thanks to this JDK bug that has been filed for almost a
696 * decade:
697 * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4965606
698 */
699 BufferedImage dest = createOptimalImage(src,
700 (int) Math.round(resultBounds.getWidth()),
701 (int) Math.round(resultBounds.getHeight()));
702
703 // Perform the operation, update our result to return.
704 BufferedImage result = op.filter(src, dest);
705
706 /*
707 * Flush the 'src' image ONLY IF it is one of our interim temporary
708 * images being used when applying 2 or more operations back to
709 * back. We never want to flush the original image passed in.
710 */
711 if (hasReassignedSrc)
712 src.flush();
713
714 /*
715 * Incase there are more operations to perform, update what we
716 * consider the 'src' reference to our last result so on the next
717 * iteration the next op is applied to this result and not back
718 * against the original src passed in.
719 */
720 src = result;
721
722 /*
723 * Keep track of when we re-assign 'src' to an interim temporary
724 * image, so we know when we can explicitly flush it and clean up
725 * references on future iterations.
726 */
727 hasReassignedSrc = true;
728
729 if (DEBUG)
730 log(1,
731 "Applied BufferedImageOp in %d ms, result [width=%d, height=%d]",
732 System.currentTimeMillis() - subT, result.getWidth(),
733 result.getHeight());
734 }
735
736 if (DEBUG)
737 log(0, "All %d BufferedImageOps applied in %d ms", ops.length,
738 System.currentTimeMillis() - t);
739
740 return src;
741 }
742
743 /**
744 * Used to crop the given src image from the top-left corner
745 * and applying any optional {@link BufferedImageOp}s to the result before
746 * returning it.
747 *

748 * TIP: This operation leaves the original src
749 * image unmodified. If the caller is done with the src image
750 * after getting the result of this operation, remember to call
751 * {@link BufferedImage#flush()} on the src to free up native
752 * resources and make it easier for the GC to collect the unused image.
753 *
754 * @param src
755 * The image to crop.
756 * @param width
757 * The width of the bounding cropping box.
758 * @param height
759 * The height of the bounding cropping box.
760 * @param ops
761 * 0 or more ops to apply to the image. If
762 * null or empty then src is return
763 * unmodified.
764 *
765 * @return a new {@link BufferedImage} representing the cropped region of
766 * the src image with any optional operations applied
767 * to it.
768 *
769 * @throws IllegalArgumentException
770 * if src is null.
771 * @throws IllegalArgumentException
772 * if any coordinates of the bounding crop box is invalid within
773 * the bounds of the src image (e.g. negative or
774 * too big).
775 * @throws ImagingOpException
776 * if one of the given {@link BufferedImageOp}s fails to apply.
777 * These exceptions bubble up from the inside of most of the
778 * {@link BufferedImageOp} implementations and are explicitly
779 * defined on the imgscalr API to make it easier for callers to
780 * catch the exception (if they are passing along optional ops
781 * to be applied). imgscalr takes detailed steps to avoid the
782 * most common pitfalls that will cause {@link BufferedImageOp}s
783 * to fail, even when using straight forward JDK-image
784 * operations.
785 */
786 public static BufferedImage crop(BufferedImage src, int width, int height,
787 BufferedImageOp... ops) throws IllegalArgumentException,
788 ImagingOpException {
789 return crop(src, 0, 0, width, height, ops);
790 }
791
792 /**
793 * Used to crop the given src image and apply any optional
794 * {@link BufferedImageOp}s to it before returning the result.
795 *

796 * TIP: This operation leaves the original src
797 * image unmodified. If the caller is done with the src image
798 * after getting the result of this operation, remember to call
799 * {@link BufferedImage#flush()} on the src to free up native
800 * resources and make it easier for the GC to collect the unused image.
801 *
802 * @param src
803 * The image to crop.
804 * @param x
805 * The x-coordinate of the top-left corner of the bounding box
806 * used for cropping.
807 * @param y
808 * The y-coordinate of the top-left corner of the bounding box
809 * used for cropping.
810 * @param width
811 * The width of the bounding cropping box.
812 * @param height
813 * The height of the bounding cropping box.
814 * @param ops
815 * 0 or more ops to apply to the image. If
816 * null or empty then src is return
817 * unmodified.
818 *
819 * @return a new {@link BufferedImage} representing the cropped region of
820 * the src image with any optional operations applied
821 * to it.
822 *
823 * @throws IllegalArgumentException
824 * if src is null.
825 * @throws IllegalArgumentException
826 * if any coordinates of the bounding crop box is invalid within
827 * the bounds of the src image (e.g. negative or
828 * too big).
829 * @throws ImagingOpException
830 * if one of the given {@link BufferedImageOp}s fails to apply.
831 * These exceptions bubble up from the inside of most of the
832 * {@link BufferedImageOp} implementations and are explicitly
833 * defined on the imgscalr API to make it easier for callers to
834 * catch the exception (if they are passing along optional ops
835 * to be applied). imgscalr takes detailed steps to avoid the
836 * most common pitfalls that will cause {@link BufferedImageOp}s
837 * to fail, even when using straight forward JDK-image
838 * operations.
839 */
840 public static BufferedImage crop(BufferedImage src, int x, int y,
841 int width, int height, BufferedImageOp... ops)
842 throws IllegalArgumentException, ImagingOpException {
843 long t = -1;
844 if (DEBUG)
845 t = System.currentTimeMillis();
846
847 if (src == null)
848 throw new IllegalArgumentException("src cannot be null");
849 if (x < 0 || y < 0 || width < 0 || height < 0)
850 throw new IllegalArgumentException("Invalid crop bounds: x [" + x
851 + "], y [" + y + "], width [" + width + "] and height ["
852 + height + "] must all be >= 0");
853
854 int srcWidth = src.getWidth();
855 int srcHeight = src.getHeight();
856
857 if ((x + width) > srcWidth)
858 throw new IllegalArgumentException(
859 "Invalid crop bounds: x + width [" + (x + width)
860 + "] must be <= src.getWidth() [" + srcWidth + "]");
861 if ((y + height) > srcHeight)
862 throw new IllegalArgumentException(
863 "Invalid crop bounds: y + height [" + (y + height)
864 + "] must be <= src.getHeight() [" + srcHeight
865 + "]");
866
867 if (DEBUG)
868 log(0,
869 "Cropping Image [width=%d, height=%d] to [x=%d, y=%d, width=%d, height=%d]...",
870 srcWidth, srcHeight, x, y, width, height);
871
872 // Create a target image of an optimal type to render into.
873 BufferedImage result = createOptimalImage(src, width, height);
874 Graphics g = result.getGraphics();
875
876 /*
877 * Render the region specified by our crop bounds from the src image
878 * directly into our result image (which is the exact size of the crop
879 * region).
880 */
881 g.drawImage(src, 0, 0, width, height, x, y, (x + width), (y + height),
882 null);
883 g.dispose();
884
885 if (DEBUG)
886 log(0, "Cropped Image in %d ms", System.currentTimeMillis() - t);
887
888 // Apply any optional operations (if specified).
889 if (ops != null && ops.length > 0)
890 result = apply(result, ops);
891
892 return result;
893 }
894
895 /**
896 * Used to apply padding around the edges of an image using
897 * {@link Color#BLACK} to fill the extra padded space and then return the
898 * result.
899 *

900 * The amount of padding specified is applied to all sides;
901 * more specifically, a padding of 2 would add 2
902 * extra pixels of space (filled by the given color) on the
903 * top, bottom, left and right sides of the resulting image causing the
904 * result to be 4 pixels wider and 4 pixels taller than the src
905 * image.
906 *

907 * TIP: This operation leaves the original src
908 * image unmodified. If the caller is done with the src image
909 * after getting the result of this operation, remember to call
910 * {@link BufferedImage#flush()} on the src to free up native
911 * resources and make it easier for the GC to collect the unused image.
912 *
913 * @param src
914 * The image the padding will be added to.
915 * @param padding
916 * The number of pixels of padding to add to each side in the
917 * resulting image. If this value is 0 then
918 * src is returned unmodified.
919 * @param ops
920 * 0 or more ops to apply to the image. If
921 * null or empty then src is return
922 * unmodified.
923 *
924 * @return a new {@link BufferedImage} representing src with
925 * the given padding applied to it.
926 *
927 * @throws IllegalArgumentException
928 * if src is null.
929 * @throws IllegalArgumentException
930 * if padding is < 1.
931 * @throws ImagingOpException
932 * if one of the given {@link BufferedImageOp}s fails to apply.
933 * These exceptions bubble up from the inside of most of the
934 * {@link BufferedImageOp} implementations and are explicitly
935 * defined on the imgscalr API to make it easier for callers to
936 * catch the exception (if they are passing along optional ops
937 * to be applied). imgscalr takes detailed steps to avoid the
938 * most common pitfalls that will cause {@link BufferedImageOp}s
939 * to fail, even when using straight forward JDK-image
940 * operations.
941 */
942 public static BufferedImage pad(BufferedImage src, int padding,
943 BufferedImageOp... ops) throws IllegalArgumentException,
944 ImagingOpException {
945 return pad(src, padding, Color.BLACK);
946 }
947
948 /**
949 * Used to apply padding around the edges of an image using the given color
950 * to fill the extra padded space and then return the result. {@link Color}s
951 * using an alpha channel (i.e. transparency) are supported.
952 *

953 * The amount of padding specified is applied to all sides;
954 * more specifically, a padding of 2 would add 2
955 * extra pixels of space (filled by the given color) on the
956 * top, bottom, left and right sides of the resulting image causing the
957 * result to be 4 pixels wider and 4 pixels taller than the src
958 * image.
959 *

960 * TIP: This operation leaves the original src
961 * image unmodified. If the caller is done with the src image
962 * after getting the result of this operation, remember to call
963 * {@link BufferedImage#flush()} on the src to free up native
964 * resources and make it easier for the GC to collect the unused image.
965 *
966 * @param src
967 * The image the padding will be added to.
968 * @param padding
969 * The number of pixels of padding to add to each side in the
970 * resulting image. If this value is 0 then
971 * src is returned unmodified.
972 * @param color
973 * The color to fill the padded space with. {@link Color}s using
974 * an alpha channel (i.e. transparency) are supported.
975 * @param ops
976 * 0 or more ops to apply to the image. If
977 * null or empty then src is return
978 * unmodified.
979 *
980 * @return a new {@link BufferedImage} representing src with
981 * the given padding applied to it.
982 *
983 * @throws IllegalArgumentException
984 * if src is null.
985 * @throws IllegalArgumentException
986 * if padding is < 1.
987 * @throws IllegalArgumentException
988 * if color is null.
989 * @throws ImagingOpException
990 * if one of the given {@link BufferedImageOp}s fails to apply.
991 * These exceptions bubble up from the inside of most of the
992 * {@link BufferedImageOp} implementations and are explicitly
993 * defined on the imgscalr API to make it easier for callers to
994 * catch the exception (if they are passing along optional ops
995 * to be applied). imgscalr takes detailed steps to avoid the
996 * most common pitfalls that will cause {@link BufferedImageOp}s
997 * to fail, even when using straight forward JDK-image
998 * operations.
999 */
1000 public static BufferedImage pad(BufferedImage src, int padding,
1001 Color color, BufferedImageOp... ops)
1002 throws IllegalArgumentException, ImagingOpException {
1003 long t = -1;
1004 if (DEBUG)
1005 t = System.currentTimeMillis();
1006
1007 if (src == null)
1008 throw new IllegalArgumentException("src cannot be null");
1009 if (padding < 1)
1010 throw new IllegalArgumentException("padding [" + padding
1011 + "] must be > 0");
1012 if (color == null)
1013 throw new IllegalArgumentException("color cannot be null");
1014
1015 int srcWidth = src.getWidth();
1016 int srcHeight = src.getHeight();
1017
1018 /*
1019 * Double the padding to account for all sides of the image. More
1020 * specifically, if padding is "1" we add 2 pixels to width and 2 to
1021 * height, so we have 1 new pixel of padding all the way around our
1022 * image.
1023 */
1024 int sizeDiff = (padding * 2);
1025 int newWidth = srcWidth + sizeDiff;
1026 int newHeight = srcHeight + sizeDiff;
1027
1028 if (DEBUG)
1029 log(0,
1030 "Padding Image from [originalWidth=%d, originalHeight=%d, padding=%d] to [newWidth=%d, newHeight=%d]...",
1031 srcWidth, srcHeight, padding, newWidth, newHeight);
1032
1033 boolean colorHasAlpha = (color.getAlpha() != 255);
1034 boolean imageHasAlpha = (src.getTransparency() != BufferedImage.OPAQUE);
1035
1036 BufferedImage result;
1037
1038 /*
1039 * We need to make sure our resulting image that we render into contains
1040 * alpha if either our original image OR the padding color we are using
1041 * contain it.
1042 */
1043 if (colorHasAlpha || imageHasAlpha) {
1044 if (DEBUG)
1045 log(1,
1046 "Transparency FOUND in source image or color, using ARGB image type...");
1047
1048 result = new BufferedImage(newWidth, newHeight,
1049 BufferedImage.TYPE_INT_ARGB);
1050 } else {
1051 if (DEBUG)
1052 log(1,
1053 "Transparency NOT FOUND in source image or color, using RGB image type...");
1054
1055 result = new BufferedImage(newWidth, newHeight,
1056 BufferedImage.TYPE_INT_RGB);
1057 }
1058
1059 Graphics g = result.getGraphics();
1060
1061 // Draw the border of the image in the color specified.
1062 g.setColor(color);
1063 g.fillRect(0, 0, newWidth, padding);
1064 g.fillRect(0, padding, padding, newHeight);
1065 g.fillRect(padding, newHeight - padding, newWidth, newHeight);
1066 g.fillRect(newWidth - padding, padding, newWidth, newHeight - padding);
1067
1068 // Draw the image into the center of the new padded image.
1069 g.drawImage(src, padding, padding, null);
1070 g.dispose();
1071
1072 if (DEBUG)
1073 log(0, "Padding Applied in %d ms", System.currentTimeMillis() - t);
1074
1075 // Apply any optional operations (if specified).
1076 if (ops != null && ops.length > 0)
1077 result = apply(result, ops);
1078
1079 return result;
1080 }
1081
1082 /**
1083 * Resize a given image (maintaining its original proportion) to a width and
1084 * height no bigger than targetSize and apply the given
1085 * {@link BufferedImageOp}s (if any) to the result before returning it.
1086 *

1087 * A scaling method of {@link Method#AUTOMATIC} and mode of
1088 * {@link Mode#AUTOMATIC} are used.
1089 *

1090 * TIP: This operation leaves the original src
1091 * image unmodified. If the caller is done with the src image
1092 * after getting the result of this operation, remember to call
1093 * {@link BufferedImage#flush()} on the src to free up native
1094 * resources and make it easier for the GC to collect the unused image.
1095 *
1096 * @param src
1097 * The image that will be scaled.
1098 * @param targetSize
1099 * The target width and height (square) that you wish the image
1100 * to fit within.
1101 * @param ops
1102 * 0 or more optional image operations (e.g.
1103 * sharpen, blur, etc.) that can be applied to the final result
1104 * before returning the image.
1105 *
1106 * @return a new {@link BufferedImage} representing the scaled
1107 * src image.
1108 *
1109 * @throws IllegalArgumentException
1110 * if src is null.
1111 * @throws IllegalArgumentException
1112 * if targetSize is < 0.
1113 * @throws ImagingOpException
1114 * if one of the given {@link BufferedImageOp}s fails to apply.
1115 * These exceptions bubble up from the inside of most of the
1116 * {@link BufferedImageOp} implementations and are explicitly
1117 * defined on the imgscalr API to make it easier for callers to
1118 * catch the exception (if they are passing along optional ops
1119 * to be applied). imgscalr takes detailed steps to avoid the
1120 * most common pitfalls that will cause {@link BufferedImageOp}s
1121 * to fail, even when using straight forward JDK-image
1122 * operations.
1123 */
1124 public static BufferedImage resize(BufferedImage src, int targetSize,
1125 BufferedImageOp... ops) throws IllegalArgumentException,
1126 ImagingOpException {
1127 return resize(src, Method.AUTOMATIC, Mode.AUTOMATIC, targetSize,
1128 targetSize, ops);
1129 }
1130
1131 /**
1132 * Resize a given image (maintaining its original proportion) to a width and
1133 * height no bigger than targetSize using the given scaling
1134 * method and apply the given {@link BufferedImageOp}s (if any) to the
1135 * result before returning it.
1136 *

1137 * A mode of {@link Mode#AUTOMATIC} is used.
1138 *

1139 * TIP: This operation leaves the original src
1140 * image unmodified. If the caller is done with the src image
1141 * after getting the result of this operation, remember to call
1142 * {@link BufferedImage#flush()} on the src to free up native
1143 * resources and make it easier for the GC to collect the unused image.
1144 *
1145 * @param src
1146 * The image that will be scaled.
1147 * @param scalingMethod
1148 * The method used for scaling the image; preferring speed to
1149 * quality or a balance of both.
1150 * @param targetSize
1151 * The target width and height (square) that you wish the image
1152 * to fit within.
1153 * @param ops
1154 * 0 or more optional image operations (e.g.
1155 * sharpen, blur, etc.) that can be applied to the final result
1156 * before returning the image.
1157 *
1158 * @return a new {@link BufferedImage} representing the scaled
1159 * src image.
1160 *
1161 * @throws IllegalArgumentException
1162 * if src is null.
1163 * @throws IllegalArgumentException
1164 * if scalingMethod is null.
1165 * @throws IllegalArgumentException
1166 * if targetSize is < 0.
1167 * @throws ImagingOpException
1168 * if one of the given {@link BufferedImageOp}s fails to apply.
1169 * These exceptions bubble up from the inside of most of the
1170 * {@link BufferedImageOp} implementations and are explicitly
1171 * defined on the imgscalr API to make it easier for callers to
1172 * catch the exception (if they are passing along optional ops
1173 * to be applied). imgscalr takes detailed steps to avoid the
1174 * most common pitfalls that will cause {@link BufferedImageOp}s
1175 * to fail, even when using straight forward JDK-image
1176 * operations.
1177 *
1178 * @see Method
1179 */
1180 public static BufferedImage resize(BufferedImage src, Method scalingMethod,
1181 int targetSize, BufferedImageOp... ops)
1182 throws IllegalArgumentException, ImagingOpException {
1183 return resize(src, scalingMethod, Mode.AUTOMATIC, targetSize,
1184 targetSize, ops);
1185 }
1186
1187 /**
1188 * Resize a given image (maintaining its original proportion) to a width and
1189 * height no bigger than targetSize (or fitting the image to
1190 * the given WIDTH or HEIGHT explicitly, depending on the {@link Mode}
1191 * specified) and apply the given {@link BufferedImageOp}s (if any) to the
1192 * result before returning it.
1193 *

1194 * A scaling method of {@link Method#AUTOMATIC} is used.
1195 *

1196 * TIP: This operation leaves the original src
1197 * image unmodified. If the caller is done with the src image
1198 * after getting the result of this operation, remember to call
1199 * {@link BufferedImage#flush()} on the src to free up native
1200 * resources and make it easier for the GC to collect the unused image.
1201 *
1202 * @param src
1203 * The image that will be scaled.
1204 * @param resizeMode
1205 * Used to indicate how imgscalr should calculate the final
1206 * target size for the image, either fitting the image to the
1207 * given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image
1208 * to the given height ({@link Mode#FIT_TO_HEIGHT}). If
1209 * {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate
1210 * proportional dimensions for the scaled image based on its
1211 * orientation (landscape, square or portrait). Unless you have
1212 * very specific size requirements, most of the time you just
1213 * want to use {@link Mode#AUTOMATIC} to "do the right thing".
1214 * @param targetSize
1215 * The target width and height (square) that you wish the image
1216 * to fit within.
1217 * @param ops
1218 * 0 or more optional image operations (e.g.
1219 * sharpen, blur, etc.) that can be applied to the final result
1220 * before returning the image.
1221 *
1222 * @return a new {@link BufferedImage} representing the scaled
1223 * src image.
1224 *
1225 * @throws IllegalArgumentException
1226 * if src is null.
1227 * @throws IllegalArgumentException
1228 * if resizeMode is null.
1229 * @throws IllegalArgumentException
1230 * if targetSize is < 0.
1231 * @throws ImagingOpException
1232 * if one of the given {@link BufferedImageOp}s fails to apply.
1233 * These exceptions bubble up from the inside of most of the
1234 * {@link BufferedImageOp} implementations and are explicitly
1235 * defined on the imgscalr API to make it easier for callers to
1236 * catch the exception (if they are passing along optional ops
1237 * to be applied). imgscalr takes detailed steps to avoid the
1238 * most common pitfalls that will cause {@link BufferedImageOp}s
1239 * to fail, even when using straight forward JDK-image
1240 * operations.
1241 *
1242 * @see Mode
1243 */
1244 public static BufferedImage resize(BufferedImage src, Mode resizeMode,
1245 int targetSize, BufferedImageOp... ops)
1246 throws IllegalArgumentException, ImagingOpException {
1247 return resize(src, Method.AUTOMATIC, resizeMode, targetSize,
1248 targetSize, ops);
1249 }
1250
1251 /**
1252 * Resize a given image (maintaining its original proportion) to a width and
1253 * height no bigger than targetSize (or fitting the image to
1254 * the given WIDTH or HEIGHT explicitly, depending on the {@link Mode}
1255 * specified) using the given scaling method and apply the given
1256 * {@link BufferedImageOp}s (if any) to the result before returning it.
1257 *

1258 * TIP: This operation leaves the original src
1259 * image unmodified. If the caller is done with the src image
1260 * after getting the result of this operation, remember to call
1261 * {@link BufferedImage#flush()} on the src to free up native
1262 * resources and make it easier for the GC to collect the unused image.
1263 *
1264 * @param src
1265 * The image that will be scaled.
1266 * @param scalingMethod
1267 * The method used for scaling the image; preferring speed to
1268 * quality or a balance of both.
1269 * @param resizeMode
1270 * Used to indicate how imgscalr should calculate the final
1271 * target size for the image, either fitting the image to the
1272 * given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image
1273 * to the given height ({@link Mode#FIT_TO_HEIGHT}). If
1274 * {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate
1275 * proportional dimensions for the scaled image based on its
1276 * orientation (landscape, square or portrait). Unless you have
1277 * very specific size requirements, most of the time you just
1278 * want to use {@link Mode#AUTOMATIC} to "do the right thing".
1279 * @param targetSize
1280 * The target width and height (square) that you wish the image
1281 * to fit within.
1282 * @param ops
1283 * 0 or more optional image operations (e.g.
1284 * sharpen, blur, etc.) that can be applied to the final result
1285 * before returning the image.
1286 *
1287 * @return a new {@link BufferedImage} representing the scaled
1288 * src image.
1289 *
1290 * @throws IllegalArgumentException
1291 * if src is null.
1292 * @throws IllegalArgumentException
1293 * if scalingMethod is null.
1294 * @throws IllegalArgumentException
1295 * if resizeMode is null.
1296 * @throws IllegalArgumentException
1297 * if targetSize is < 0.
1298 * @throws ImagingOpException
1299 * if one of the given {@link BufferedImageOp}s fails to apply.
1300 * These exceptions bubble up from the inside of most of the
1301 * {@link BufferedImageOp} implementations and are explicitly
1302 * defined on the imgscalr API to make it easier for callers to
1303 * catch the exception (if they are passing along optional ops
1304 * to be applied). imgscalr takes detailed steps to avoid the
1305 * most common pitfalls that will cause {@link BufferedImageOp}s
1306 * to fail, even when using straight forward JDK-image
1307 * operations.
1308 *
1309 * @see Method
1310 * @see Mode
1311 */
1312 public static BufferedImage resize(BufferedImage src, Method scalingMethod,
1313 Mode resizeMode, int targetSize, BufferedImageOp... ops)
1314 throws IllegalArgumentException, ImagingOpException {
1315 return resize(src, scalingMethod, resizeMode, targetSize, targetSize,
1316 ops);
1317 }
1318
1319 /**
1320 * Resize a given image (maintaining its original proportion) to the target
1321 * width and height and apply the given {@link BufferedImageOp}s (if any) to
1322 * the result before returning it.
1323 *

1324 * A scaling method of {@link Method#AUTOMATIC} and mode of
1325 * {@link Mode#AUTOMATIC} are used.
1326 *

1327 * TIP: See the class description to understand how this
1328 * class handles recalculation of the targetWidth or
1329 * targetHeight depending on the image's orientation in order
1330 * to maintain the original proportion.
1331 *

1332 * TIP: This operation leaves the original src
1333 * image unmodified. If the caller is done with the src image
1334 * after getting the result of this operation, remember to call
1335 * {@link BufferedImage#flush()} on the src to free up native
1336 * resources and make it easier for the GC to collect the unused image.
1337 *
1338 * @param src
1339 * The image that will be scaled.
1340 * @param targetWidth
1341 * The target width that you wish the image to have.
1342 * @param targetHeight
1343 * The target height that you wish the image to have.
1344 * @param ops
1345 * 0 or more optional image operations (e.g.
1346 * sharpen, blur, etc.) that can be applied to the final result
1347 * before returning the image.
1348 *
1349 * @return a new {@link BufferedImage} representing the scaled
1350 * src image.
1351 *
1352 * @throws IllegalArgumentException
1353 * if src is null.
1354 * @throws IllegalArgumentException
1355 * if targetWidth is < 0 or if
1356 * targetHeight is < 0.
1357 * @throws ImagingOpException
1358 * if one of the given {@link BufferedImageOp}s fails to apply.
1359 * These exceptions bubble up from the inside of most of the
1360 * {@link BufferedImageOp} implementations and are explicitly
1361 * defined on the imgscalr API to make it easier for callers to
1362 * catch the exception (if they are passing along optional ops
1363 * to be applied). imgscalr takes detailed steps to avoid the
1364 * most common pitfalls that will cause {@link BufferedImageOp}s
1365 * to fail, even when using straight forward JDK-image
1366 * operations.
1367 */
1368 public static BufferedImage resize(BufferedImage src, int targetWidth,
1369 int targetHeight, BufferedImageOp... ops)
1370 throws IllegalArgumentException, ImagingOpException {
1371 return resize(src, Method.AUTOMATIC, Mode.AUTOMATIC, targetWidth,
1372 targetHeight, ops);
1373 }
1374
1375 /**
1376 * Resize a given image (maintaining its original proportion) to the target
1377 * width and height using the given scaling method and apply the given
1378 * {@link BufferedImageOp}s (if any) to the result before returning it.
1379 *

1380 * A mode of {@link Mode#AUTOMATIC} is used.
1381 *

1382 * TIP: See the class description to understand how this
1383 * class handles recalculation of the targetWidth or
1384 * targetHeight depending on the image's orientation in order
1385 * to maintain the original proportion.
1386 *

1387 * TIP: This operation leaves the original src
1388 * image unmodified. If the caller is done with the src image
1389 * after getting the result of this operation, remember to call
1390 * {@link BufferedImage#flush()} on the src to free up native
1391 * resources and make it easier for the GC to collect the unused image.
1392 *
1393 * @param src
1394 * The image that will be scaled.
1395 * @param scalingMethod
1396 * The method used for scaling the image; preferring speed to
1397 * quality or a balance of both.
1398 * @param targetWidth
1399 * The target width that you wish the image to have.
1400 * @param targetHeight
1401 * The target height that you wish the image to have.
1402 * @param ops
1403 * 0 or more optional image operations (e.g.
1404 * sharpen, blur, etc.) that can be applied to the final result
1405 * before returning the image.
1406 *
1407 * @return a new {@link BufferedImage} representing the scaled
1408 * src image.
1409 *
1410 * @throws IllegalArgumentException
1411 * if src is null.
1412 * @throws IllegalArgumentException
1413 * if scalingMethod is null.
1414 * @throws IllegalArgumentException
1415 * if targetWidth is < 0 or if
1416 * targetHeight is < 0.
1417 * @throws ImagingOpException
1418 * if one of the given {@link BufferedImageOp}s fails to apply.
1419 * These exceptions bubble up from the inside of most of the
1420 * {@link BufferedImageOp} implementations and are explicitly
1421 * defined on the imgscalr API to make it easier for callers to
1422 * catch the exception (if they are passing along optional ops
1423 * to be applied). imgscalr takes detailed steps to avoid the
1424 * most common pitfalls that will cause {@link BufferedImageOp}s
1425 * to fail, even when using straight forward JDK-image
1426 * operations.
1427 *
1428 * @see Method
1429 */
1430 public static BufferedImage resize(BufferedImage src, Method scalingMethod,
1431 int targetWidth, int targetHeight, BufferedImageOp... ops) {
1432 return resize(src, scalingMethod, Mode.AUTOMATIC, targetWidth,
1433 targetHeight, ops);
1434 }
1435
1436 /**
1437 * Resize a given image (maintaining its original proportion) to the target
1438 * width and height (or fitting the image to the given WIDTH or HEIGHT
1439 * explicitly, depending on the {@link Mode} specified) and apply the given
1440 * {@link BufferedImageOp}s (if any) to the result before returning it.
1441 *

1442 * A scaling method of {@link Method#AUTOMATIC} is used.
1443 *

1444 * TIP: See the class description to understand how this
1445 * class handles recalculation of the targetWidth or
1446 * targetHeight depending on the image's orientation in order
1447 * to maintain the original proportion.
1448 *

1449 * TIP: This operation leaves the original src
1450 * image unmodified. If the caller is done with the src image
1451 * after getting the result of this operation, remember to call
1452 * {@link BufferedImage#flush()} on the src to free up native
1453 * resources and make it easier for the GC to collect the unused image.
1454 *
1455 * @param src
1456 * The image that will be scaled.
1457 * @param resizeMode
1458 * Used to indicate how imgscalr should calculate the final
1459 * target size for the image, either fitting the image to the
1460 * given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image
1461 * to the given height ({@link Mode#FIT_TO_HEIGHT}). If
1462 * {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate
1463 * proportional dimensions for the scaled image based on its
1464 * orientation (landscape, square or portrait). Unless you have
1465 * very specific size requirements, most of the time you just
1466 * want to use {@link Mode#AUTOMATIC} to "do the right thing".
1467 * @param targetWidth
1468 * The target width that you wish the image to have.
1469 * @param targetHeight
1470 * The target height that you wish the image to have.
1471 * @param ops
1472 * 0 or more optional image operations (e.g.
1473 * sharpen, blur, etc.) that can be applied to the final result
1474 * before returning the image.
1475 *
1476 * @return a new {@link BufferedImage} representing the scaled
1477 * src image.
1478 *
1479 * @throws IllegalArgumentException
1480 * if src is null.
1481 * @throws IllegalArgumentException
1482 * if resizeMode is null.
1483 * @throws IllegalArgumentException
1484 * if targetWidth is < 0 or if
1485 * targetHeight is < 0.
1486 * @throws ImagingOpException
1487 * if one of the given {@link BufferedImageOp}s fails to apply.
1488 * These exceptions bubble up from the inside of most of the
1489 * {@link BufferedImageOp} implementations and are explicitly
1490 * defined on the imgscalr API to make it easier for callers to
1491 * catch the exception (if they are passing along optional ops
1492 * to be applied). imgscalr takes detailed steps to avoid the
1493 * most common pitfalls that will cause {@link BufferedImageOp}s
1494 * to fail, even when using straight forward JDK-image
1495 * operations.
1496 *
1497 * @see Mode
1498 */
1499 public static BufferedImage resize(BufferedImage src, Mode resizeMode,
1500 int targetWidth, int targetHeight, BufferedImageOp... ops)
1501 throws IllegalArgumentException, ImagingOpException {
1502 return resize(src, Method.AUTOMATIC, resizeMode, targetWidth,
1503 targetHeight, ops);
1504 }
1505
1506 /**
1507 * Resize a given image (maintaining its original proportion) to the target
1508 * width and height (or fitting the image to the given WIDTH or HEIGHT
1509 * explicitly, depending on the {@link Mode} specified) using the given
1510 * scaling method and apply the given {@link BufferedImageOp}s (if any) to
1511 * the result before returning it.
1512 *

1513 * TIP: See the class description to understand how this
1514 * class handles recalculation of the targetWidth or
1515 * targetHeight depending on the image's orientation in order
1516 * to maintain the original proportion.
1517 *

1518 * TIP: This operation leaves the original src
1519 * image unmodified. If the caller is done with the src image
1520 * after getting the result of this operation, remember to call
1521 * {@link BufferedImage#flush()} on the src to free up native
1522 * resources and make it easier for the GC to collect the unused image.
1523 *
1524 * @param src
1525 * The image that will be scaled.
1526 * @param scalingMethod
1527 * The method used for scaling the image; preferring speed to
1528 * quality or a balance of both.
1529 * @param resizeMode
1530 * Used to indicate how imgscalr should calculate the final
1531 * target size for the image, either fitting the image to the
1532 * given width ({@link Mode#FIT_TO_WIDTH}) or fitting the image
1533 * to the given height ({@link Mode#FIT_TO_HEIGHT}). If
1534 * {@link Mode#AUTOMATIC} is passed in, imgscalr will calculate
1535 * proportional dimensions for the scaled image based on its
1536 * orientation (landscape, square or portrait). Unless you have
1537 * very specific size requirements, most of the time you just
1538 * want to use {@link Mode#AUTOMATIC} to "do the right thing".
1539 * @param targetWidth
1540 * The target width that you wish the image to have.
1541 * @param targetHeight
1542 * The target height that you wish the image to have.
1543 * @param ops
1544 * 0 or more optional image operations (e.g.
1545 * sharpen, blur, etc.) that can be applied to the final result
1546 * before returning the image.
1547 *
1548 * @return a new {@link BufferedImage} representing the scaled
1549 * src image.
1550 *
1551 * @throws IllegalArgumentException
1552 * if src is null.
1553 * @throws IllegalArgumentException
1554 * if scalingMethod is null.
1555 * @throws IllegalArgumentException
1556 * if resizeMode is null.
1557 * @throws IllegalArgumentException
1558 * if targetWidth is < 0 or if
1559 * targetHeight is < 0.
1560 * @throws ImagingOpException
1561 * if one of the given {@link BufferedImageOp}s fails to apply.
1562 * These exceptions bubble up from the inside of most of the
1563 * {@link BufferedImageOp} implementations and are explicitly
1564 * defined on the imgscalr API to make it easier for callers to
1565 * catch the exception (if they are passing along optional ops
1566 * to be applied). imgscalr takes detailed steps to avoid the
1567 * most common pitfalls that will cause {@link BufferedImageOp}s
1568 * to fail, even when using straight forward JDK-image
1569 * operations.
1570 *
1571 * @see Method
1572 * @see Mode
1573 */
1574 public static BufferedImage resize(BufferedImage src, Method scalingMethod,
1575 Mode resizeMode, int targetWidth, int targetHeight,
1576 BufferedImageOp... ops) throws IllegalArgumentException,
1577 ImagingOpException {
1578 long t = -1;
1579 if (DEBUG)
1580 t = System.currentTimeMillis();
1581
1582 if (src == null)
1583 throw new IllegalArgumentException("src cannot be null");
1584 if (targetWidth < 0)
1585 throw new IllegalArgumentException("targetWidth must be >= 0");
1586 if (targetHeight < 0)
1587 throw new IllegalArgumentException("targetHeight must be >= 0");
1588 if (scalingMethod == null)
1589 throw new IllegalArgumentException(
1590 "scalingMethod cannot be null. A good default value is Method.AUTOMATIC.");
1591 if (resizeMode == null)
1592 throw new IllegalArgumentException(
1593 "resizeMode cannot be null. A good default value is Mode.AUTOMATIC.");
1594
1595 BufferedImage result = null;
1596
1597 int currentWidth = src.getWidth();
1598 int currentHeight = src.getHeight();
1599
1600 // <= 1 is a square or landscape-oriented image, > 1 is a portrait.
1601 float ratio = ((float) currentHeight / (float) currentWidth);
1602
1603 if (DEBUG)
1604 log(0,
1605 "Resizing Image [size=%dx%d, resizeMode=%s, orientation=%s, ratio(H/W)=%f] to [targetSize=%dx%d]",
1606 currentWidth, currentHeight, resizeMode,
1607 (ratio <= 1 ? "Landscape/Square" : "Portrait"), ratio,
1608 targetWidth, targetHeight);
1609
1610 /*
1611 * First determine if ANY size calculation needs to be done, in the case
1612 * of FIT_EXACT, ignore image proportions and orientation and just use
1613 * what the user sent in, otherwise the proportion of the picture must
1614 * be honored.
1615 *
1616 * The way that is done is to figure out if the image is in a
1617 * LANDSCAPE/SQUARE or PORTRAIT orientation and depending on its
1618 * orientation, use the primary dimension (width for LANDSCAPE/SQUARE
1619 * and height for PORTRAIT) to recalculate the alternative (height and
1620 * width respectively) value that adheres to the existing ratio.
1621 *
1622 * This helps make life easier for the caller as they don't need to
1623 * pre-compute proportional dimensions before calling the API, they can
1624 * just specify the dimensions they would like the image to roughly fit
1625 * within and it will do the right thing without mangling the result.
1626 */
1627 if (resizeMode == Mode.FIT_EXACT) {
1628 if (DEBUG)
1629 log(1,
1630 "Resize Mode FIT_EXACT used, no width/height checking or re-calculation will be done.");
1631 } else if (resizeMode == Mode.BEST_FIT_BOTH) {
1632 float requestedHeightScaling = ((float) targetHeight / (float) currentHeight);
1633 float requestedWidthScaling = ((float) targetWidth / (float) currentWidth);
1634 float actualScaling = Math.min(requestedHeightScaling, requestedWidthScaling);
1635
1636 targetHeight = Math.round((float) currentHeight * actualScaling);
1637 targetWidth = Math.round((float) currentWidth * actualScaling);
1638
1639 if (targetHeight == currentHeight && targetWidth == currentWidth)
1640 return src;
1641
1642 if (DEBUG)
1643 log(1, "Auto-Corrected width and height based on scalingRatio %d.", actualScaling);
1644 } else {
1645 if ((ratio <= 1 && resizeMode == Mode.AUTOMATIC)
1646 || (resizeMode == Mode.FIT_TO_WIDTH)) {
1647 // First make sure we need to do any work in the first place
1648 if (targetWidth == src.getWidth())
1649 return src;
1650
1651 // Save for detailed logging (this is cheap).
1652 int originalTargetHeight = targetHeight;
1653
1654 /*
1655 * Landscape or Square Orientation: Ignore the given height and
1656 * re-calculate a proportionally correct value based on the
1657 * targetWidth.
1658 */
1659 targetHeight = (int)Math.ceil((float) targetWidth * ratio);
1660
1661 if (DEBUG && originalTargetHeight != targetHeight)
1662 log(1,
1663 "Auto-Corrected targetHeight [from=%d to=%d] to honor image proportions.",
1664 originalTargetHeight, targetHeight);
1665 } else {
1666 // First make sure we need to do any work in the first place
1667 if (targetHeight == src.getHeight())
1668 return src;
1669
1670 // Save for detailed logging (this is cheap).
1671 int originalTargetWidth = targetWidth;
1672
1673 /*
1674 * Portrait Orientation: Ignore the given width and re-calculate
1675 * a proportionally correct value based on the targetHeight.
1676 */
1677 targetWidth = Math.round((float) targetHeight / ratio);
1678
1679 if (DEBUG && originalTargetWidth != targetWidth)
1680 log(1,
1681 "Auto-Corrected targetWidth [from=%d to=%d] to honor image proportions.",
1682 originalTargetWidth, targetWidth);
1683 }
1684 }
1685
1686 // If AUTOMATIC was specified, determine the real scaling method.
1687 if (scalingMethod == Scalr.Method.AUTOMATIC)
1688 scalingMethod = determineScalingMethod(targetWidth, targetHeight,
1689 ratio);
1690
1691 if (DEBUG)
1692 log(1, "Using Scaling Method: %s", scalingMethod);
1693
1694 // Now we scale the image
1695 if (scalingMethod == Scalr.Method.SPEED) {
1696 result = scaleImage(src, targetWidth, targetHeight,
1697 RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
1698 } else if (scalingMethod == Scalr.Method.BALANCED) {
1699 result = scaleImage(src, targetWidth, targetHeight,
1700 RenderingHints.VALUE_INTERPOLATION_BILINEAR);
1701 } else if (scalingMethod == Scalr.Method.QUALITY
1702 || scalingMethod == Scalr.Method.ULTRA_QUALITY) {
1703 /*
1704 * If we are scaling up (in either width or height - since we know
1705 * the image will stay proportional we just check if either are
1706 * being scaled up), directly using a single BICUBIC will give us
1707 * better results then using Chris Campbell's incremental scaling
1708 * operation (and take a lot less time).
1709 *
1710 * If we are scaling down, we must use the incremental scaling
1711 * algorithm for the best result.
1712 */
1713 if (targetWidth > currentWidth || targetHeight > currentHeight) {
1714 if (DEBUG)
1715 log(1,
1716 "QUALITY scale-up, a single BICUBIC scale operation will be used...");
1717
1718 /*
1719 * BILINEAR and BICUBIC look similar the smaller the scale jump
1720 * upwards is, if the scale is larger BICUBIC looks sharper and
1721 * less fuzzy. But most importantly we have to use BICUBIC to
1722 * match the contract of the QUALITY rendering scalingMethod.
1723 * This note is just here for anyone reading the code and
1724 * wondering how they can speed their own calls up.
1725 */
1726 result = scaleImage(src, targetWidth, targetHeight,
1727 RenderingHints.VALUE_INTERPOLATION_BICUBIC);
1728 } else {
1729 if (DEBUG)
1730 log(1,
1731 "QUALITY scale-down, incremental scaling will be used...");
1732
1733 /*
1734 * Originally we wanted to use BILINEAR interpolation here
1735 * because it takes 1/3rd the time that the BICUBIC
1736 * interpolation does, however, when scaling large images down
1737 * to most sizes bigger than a thumbnail we witnessed noticeable
1738 * "softening" in the resultant image with BILINEAR that would
1739 * be unexpectedly annoying to a user expecting a "QUALITY"
1740 * scale of their original image. Instead BICUBIC was chosen to
1741 * honor the contract of a QUALITY scale of the original image.
1742 */
1743 result = scaleImageIncrementally(src, targetWidth,
1744 targetHeight, scalingMethod,
1745 RenderingHints.VALUE_INTERPOLATION_BICUBIC);
1746 }
1747 }
1748
1749 if (DEBUG)
1750 log(0, "Resized Image in %d ms", System.currentTimeMillis() - t);
1751
1752 // Apply any optional operations (if specified).
1753 if (ops != null && ops.length > 0)
1754 result = apply(result, ops);
1755
1756 return result;
1757 }
1758
1759 /**
1760 * Used to apply a {@link Rotation} and then 0 or more
1761 * {@link BufferedImageOp}s to a given image and return the result.
1762 *

1763 * TIP: This operation leaves the original src
1764 * image unmodified. If the caller is done with the src image
1765 * after getting the result of this operation, remember to call
1766 * {@link BufferedImage#flush()} on the src to free up native
1767 * resources and make it easier for the GC to collect the unused image.
1768 *
1769 * @param src
1770 * The image that will have the rotation applied to it.
1771 * @param rotation
1772 * The rotation that will be applied to the image.
1773 * @param ops
1774 * Zero or more optional image operations (e.g. sharpen, blur,
1775 * etc.) that can be applied to the final result before returning
1776 * the image.
1777 *
1778 * @return a new {@link BufferedImage} representing src rotated
1779 * by the given amount and any optional ops applied to it.
1780 *
1781 * @throws IllegalArgumentException
1782 * if src is null.
1783 * @throws IllegalArgumentException
1784 * if rotation is null.
1785 * @throws ImagingOpException
1786 * if one of the given {@link BufferedImageOp}s fails to apply.
1787 * These exceptions bubble up from the inside of most of the
1788 * {@link BufferedImageOp} implementations and are explicitly
1789 * defined on the imgscalr API to make it easier for callers to
1790 * catch the exception (if they are passing along optional ops
1791 * to be applied). imgscalr takes detailed steps to avoid the
1792 * most common pitfalls that will cause {@link BufferedImageOp}s
1793 * to fail, even when using straight forward JDK-image
1794 * operations.
1795 *
1796 * @see Rotation
1797 */
1798 public static BufferedImage rotate(BufferedImage src, Rotation rotation,
1799 BufferedImageOp... ops) throws IllegalArgumentException,
1800 ImagingOpException {
1801 long t = -1;
1802 if (DEBUG)
1803 t = System.currentTimeMillis();
1804
1805 if (src == null)
1806 throw new IllegalArgumentException("src cannot be null");
1807 if (rotation == null)
1808 throw new IllegalArgumentException("rotation cannot be null");
1809
1810 if (DEBUG)
1811 log(0, "Rotating Image [%s]...", rotation);
1812
1813 /*
1814 * Setup the default width/height values from our image.
1815 *
1816 * In the case of a 90 or 270 (-90) degree rotation, these two values
1817 * flip-flop and we will correct those cases down below in the switch
1818 * statement.
1819 */
1820 int newWidth = src.getWidth();
1821 int newHeight = src.getHeight();
1822
1823 /*
1824 * We create a transform per operation request as (oddly enough) it ends
1825 * up being faster for the VM to create, use and destroy these instances
1826 * than it is to re-use a single AffineTransform per-thread via the
1827 * AffineTransform.setTo(...) methods which was my first choice (less
1828 * object creation); after benchmarking this explicit case and looking
1829 * at just how much code gets run inside of setTo() I opted for a new AT
1830 * for every rotation.
1831 *
1832 * Besides the performance win, trying to safely reuse AffineTransforms
1833 * via setTo(...) would have required ThreadLocal instances to avoid
1834 * race conditions where two or more resize threads are manipulating the
1835 * same transform before applying it.
1836 *
1837 * Misusing ThreadLocals are one of the #1 reasons for memory leaks in
1838 * server applications and since we have no nice way to hook into the
1839 * init/destroy Servlet cycle or any other initialization cycle for this
1840 * library to automatically call ThreadLocal.remove() to avoid the
1841 * memory leak, it would have made using this library *safely* on the
1842 * server side much harder.
1843 *
1844 * So we opt for creating individual transforms per rotation op and let
1845 * the VM clean them up in a GC. I only clarify all this reasoning here
1846 * for anyone else reading this code and being tempted to reuse the AT
1847 * instances of performance gains; there aren't any AND you get a lot of
1848 * pain along with it.
1849 */
1850 AffineTransform tx = new AffineTransform();
1851
1852 switch (rotation) {
1853 case CW_90:
1854 /*
1855 * A 90 or -90 degree rotation will cause the height and width to
1856 * flip-flop from the original image to the rotated one.
1857 */
1858 newWidth = src.getHeight();
1859 newHeight = src.getWidth();
1860
1861 // Reminder: newWidth == result.getHeight() at this point
1862 tx.translate(newWidth, 0);
1863 tx.quadrantRotate(1);
1864
1865 break;
1866
1867 case CW_270:
1868 /*
1869 * A 90 or -90 degree rotation will cause the height and width to
1870 * flip-flop from the original image to the rotated one.
1871 */
1872 newWidth = src.getHeight();
1873 newHeight = src.getWidth();
1874
1875 // Reminder: newHeight == result.getWidth() at this point
1876 tx.translate(0, newHeight);
1877 tx.quadrantRotate(3);
1878 break;
1879
1880 case CW_180:
1881 tx.translate(newWidth, newHeight);
1882 tx.quadrantRotate(2);
1883 break;
1884
1885 case FLIP_HORZ:
1886 tx.translate(newWidth, 0);
1887 tx.scale(-1.0, 1.0);
1888 break;
1889
1890 case FLIP_VERT:
1891 tx.translate(0, newHeight);
1892 tx.scale(1.0, -1.0);
1893 break;
1894 }
1895
1896 // Create our target image we will render the rotated result to.
1897 BufferedImage result = createOptimalImage(src, newWidth, newHeight);
1898 Graphics2D g2d = (Graphics2D) result.createGraphics();
1899
1900 /*
1901 * Render the resultant image to our new rotatedImage buffer, applying
1902 * the AffineTransform that we calculated above during rendering so the
1903 * pixels from the old position are transposed to the new positions in
1904 * the resulting image correctly.
1905 */
1906 g2d.drawImage(src, tx, null);
1907 g2d.dispose();
1908
1909 if (DEBUG)
1910 log(0, "Rotation Applied in %d ms, result [width=%d, height=%d]",
1911 System.currentTimeMillis() - t, result.getWidth(),
1912 result.getHeight());
1913
1914 // Apply any optional operations (if specified).
1915 if (ops != null && ops.length > 0)
1916 result = apply(result, ops);
1917
1918 return result;
1919 }
1920
1921 /**
1922 * Used to write out a useful and well-formatted log message by any piece of
1923 * code inside of the imgscalr library.
1924 *

1925 * If a message cannot be logged (logging is disabled) then this method
1926 * returns immediately.
1927 *

1928 * NOTE: Because Java will auto-box primitive arguments
1929 * into Objects when building out the params array, care should
1930 * be taken not to call this method with primitive values unless
1931 * {@link Scalr#DEBUG} is true; otherwise the VM will be
1932 * spending time performing unnecessary auto-boxing calculations.
1933 *
1934 * @param depth
1935 * The indentation level of the log message.
1936 * @param message
1937 * The log message in format string syntax that will be logged.
1940 * @param params
1941 * The parameters that will be swapped into all the place holders
1942 * in the original messages before being logged.
1943 *
1944 * @see Scalr#LOG_PREFIX
1945 * @see Scalr#LOG_PREFIX_PROPERTY_NAME
1946 */
1947 protected static void log(int depth, String message, Object... params) {
1948 if (Scalr.DEBUG) {
1949 System.out.print(Scalr.LOG_PREFIX);
1950
1951 for (int i = 0; i < depth; i++)
1952 System.out.print("\t");
1953
1954 System.out.printf(message, params);
1955 System.out.println();
1956 }
1957 }
1958
1959 /**
1960 * Used to create a {@link BufferedImage} with the most optimal RGB TYPE (
1961 * {@link BufferedImage#TYPE_INT_RGB} or {@link BufferedImage#TYPE_INT_ARGB}
1962 * ) capable of being rendered into from the given src. The
1963 * width and height of both images will be identical.
1964 *

1965 * This does not perform a copy of the image data from src into
1966 * the result image; see {@link #copyToOptimalImage(BufferedImage)} for
1967 * that.
1968 *

1969 * We force all rendering results into one of these two types, avoiding the
1970 * case where a source image is of an unsupported (or poorly supported)
1971 * format by Java2D causing the rendering result to end up looking terrible
1972 * (common with GIFs) or be totally corrupt (e.g. solid black image).
1973 *

1974 * Originally reported by Magnus Kvalheim from Movellas when scaling certain
1975 * GIF and PNG images.
1976 *
1977 * @param src
1978 * The source image that will be analyzed to determine the most
1979 * optimal image type it can be rendered into.
1980 *
1981 * @return a new {@link BufferedImage} representing the most optimal target
1982 * image type that src can be rendered into.
1983 *
1984 * @see <a
1985 *<EMAIL_ADDRESS>1986 * Java2D handles poorly supported image types
1987 * @see Thanks
1989 * to Morten Nobel for implementation hint
1990 */
1991 protected static BufferedImage createOptimalImage(BufferedImage src) {
1992 return createOptimalImage(src, src.getWidth(), src.getHeight());
1993 }
1994
1995 /**
1996 * Used to create a {@link BufferedImage} with the given dimensions and the
1997 * most optimal RGB TYPE ( {@link BufferedImage#TYPE_INT_RGB} or
1998 * {@link BufferedImage#TYPE_INT_ARGB} ) capable of being rendered into from
1999 * the given src.
2000 *

2001 * This does not perform a copy of the image data from src into
2002 * the result image; see {@link #copyToOptimalImage(BufferedImage)} for
2003 * that.
2004 *

2005 * We force all rendering results into one of these two types, avoiding the
2006 * case where a source image is of an unsupported (or poorly supported)
2007 * format by Java2D causing the rendering result to end up looking terrible
2008 * (common with GIFs) or be totally corrupt (e.g. solid black image).
2009 *

2010 * Originally reported by Magnus Kvalheim from Movellas when scaling certain
2011 * GIF and PNG images.
2012 *
2013 * @param src
2014 * The source image that will be analyzed to determine the most
2015 * optimal image type it can be rendered into.
2016 * @param width
2017 * The width of the newly created resulting image.
2018 * @param height
2019 * The height of the newly created resulting image.
2020 *
2021 * @return a new {@link BufferedImage} representing the most optimal target
2022 * image type that src can be rendered into.
2023 *
2024 * @throws IllegalArgumentException
2025 * if width or height are < 0.
2026 *
2027 * @see <a
2028 *<EMAIL_ADDRESS>2029 * Java2D handles poorly supported image types
2030 * @see Thanks
2032 * to Morten Nobel for implementation hint
2033 */
2034 protected static BufferedImage createOptimalImage(BufferedImage src,
2035 int width, int height) throws IllegalArgumentException {
2036 if (width <= 0 || height <= 0)
2037 throw new IllegalArgumentException("width [" + width
2038 + "] and height [" + height + "] must be > 0");
2039
2040 return new BufferedImage(
2041 width,
2042 height,
2043 (src.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB
2044 : BufferedImage.TYPE_INT_ARGB));
2045 }
2046
2047 /**
2048 * Used to copy a {@link BufferedImage} from a non-optimal type into a new
2049 * {@link BufferedImage} instance of an optimal type (RGB or ARGB). If
2050 * src is already of an optimal type, then it is returned
2051 * unmodified.
2052 *

2053 * This method is meant to be used by any calling code (imgscalr's or
2054 * otherwise) to convert any inbound image from a poorly supported image
2055 * type into the 2 most well-supported image types in Java2D (
2056 * {@link BufferedImage#TYPE_INT_RGB} or {@link BufferedImage#TYPE_INT_ARGB}
2057 * ) in order to ensure all subsequent graphics operations are performed as
2058 * efficiently and correctly as possible.
2059 *

2060 * When using Java2D to work with image types that are not well supported,
2061 * the results can be anything from exceptions bubbling up from the depths
2062 * of Java2D to images being completely corrupted and just returned as solid
2063 * black.
2064 *
2065 * @param src
2066 * The image to copy (if necessary) into an optimally typed
2067 * {@link BufferedImage}.
2068 *
2069 * @return a representation of the src image in an optimally
2070 * typed {@link BufferedImage}, otherwise src if it was
2071 * already of an optimal type.
2072 *
2073 * @throws IllegalArgumentException
2074 * if src is null.
2075 */
2076 protected static BufferedImage copyToOptimalImage(BufferedImage src)
2077 throws IllegalArgumentException {
2078 if (src == null)
2079 throw new IllegalArgumentException("src cannot be null");
2080
2081 // Calculate the type depending on the presence of alpha.
2082 int type = (src.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB
2083 : BufferedImage.TYPE_INT_ARGB);
2084 BufferedImage result = new BufferedImage(src.getWidth(),
2085 src.getHeight(), type);
2086
2087 // Render the src image into our new optimal source.
2088 Graphics g = result.getGraphics();
2089 g.drawImage(src, 0, 0, null);
2090 g.dispose();
2091
2092 return result;
2093 }
2094
2095 /**
2096 * Used to determine the scaling {@link Method} that is best suited for
2097 * scaling the image to the targeted dimensions.
2098 *

2099 * This method is intended to be used to select a specific scaling
2100 * {@link Method} when a {@link Method#AUTOMATIC} method is specified. This
2101 * method utilizes the {@link Scalr#THRESHOLD_QUALITY_BALANCED} and
2102 * {@link Scalr#THRESHOLD_BALANCED_SPEED} thresholds when selecting which
2103 * method should be used by comparing the primary dimension (width or
2104 * height) against the threshold and seeing where the image falls. The
2105 * primary dimension is determined by looking at the orientation of the
2106 * image: landscape or square images use their width and portrait-oriented
2107 * images use their height.
2108 *
2109 * @param targetWidth
2110 * The target width for the scaled image.
2111 * @param targetHeight
2112 * The target height for the scaled image.
2113 * @param ratio
2114 * A height/width ratio used to determine the orientation of the
2115 * image so the primary dimension (width or height) can be
2116 * selected to test if it is greater than or less than a
2117 * particular threshold.
2118 *
2119 * @return the fastest {@link Method} suited for scaling the image to the
2120 * specified dimensions while maintaining a good-looking result.
2121 */
2122 protected static Method determineScalingMethod(int targetWidth,
2123 int targetHeight, float ratio) {
2124 // Get the primary dimension based on the orientation of the image
2125 int length = (ratio <= 1 ? targetWidth : targetHeight);
2126
2127 // Default to speed
2128 Method result = Method.SPEED;
2129
2130 // Figure out which scalingMethod should be used
2131 if (length <= Scalr.THRESHOLD_QUALITY_BALANCED)
2132 result = Method.QUALITY;
2133 else if (length <= Scalr.THRESHOLD_BALANCED_SPEED)
2134 result = Method.BALANCED;
2135
2136 if (DEBUG)
2137 log(2, "AUTOMATIC scaling method selected: %s", result.name());
2138
2139 return result;
2140 }
2141
2142 /**
2143 * Used to implement a straight-forward image-scaling operation using Java
2144 * 2D.
2145 *

2146 * This method uses the Oracle-encouraged method of
2147 * Graphics2D.drawImage(...) to scale the given image with the
2148 * given interpolation hint.
2149 *
2150 * @param src
2151 * The image that will be scaled.
2152 * @param targetWidth
2153 * The target width for the scaled image.
2154 * @param targetHeight
2155 * The target height for the scaled image.
2156 * @param interpolationHintValue
2157 * The {@link RenderingHints} interpolation value used to
2158 * indicate the method that {@link Graphics2D} should use when
2159 * scaling the image.
2160 *
2161 * @return the result of scaling the original src to the given
2162 * dimensions using the given interpolation method.
2163 */
2164 protected static BufferedImage scaleImage(BufferedImage src,
2165 int targetWidth, int targetHeight, Object interpolationHintValue) {
2166 // Setup the rendering resources to match the source image's
2167 BufferedImage result = createOptimalImage(src, targetWidth,
2168 targetHeight);
2169 Graphics2D resultGraphics = result.createGraphics();
2170
2171 // Scale the image to the new buffer using the specified rendering hint.
2172 resultGraphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
2173 interpolationHintValue);
2174 resultGraphics.drawImage(src, 0, 0, targetWidth, targetHeight, null);
2175
2176 // Just to be clean, explicitly dispose our temporary graphics object
2177 resultGraphics.dispose();
2178
2179 // Return the scaled image to the caller.
2180 return result;
2181 }
2182
2183 /**
2184 * Used to implement Chris Campbell's incremental-scaling algorithm: http://today.java.net/pub/a/today/2007/04/03/perils
2188 * -of-image-getscaledinstance.html.
2189 *

2190 * Modifications to the original algorithm are variable names and comments
2191 * added for clarity and the hard-coding of using BICUBIC interpolation as
2192 * well as the explicit "flush()" operation on the interim BufferedImage
2193 * instances to avoid resource leaking.
2194 *
2195 * @param src
2196 * The image that will be scaled.
2197 * @param targetWidth
2198 * The target width for the scaled image.
2199 * @param targetHeight
2200 * The target height for the scaled image.
2201 * @param scalingMethod
2202 * The scaling method specified by the user (or calculated by
2203 * imgscalr) to use for this incremental scaling operation.
2204 * @param interpolationHintValue
2205 * The {@link RenderingHints} interpolation value used to
2206 * indicate the method that {@link Graphics2D} should use when
2207 * scaling the image.
2208 *
2209 * @return an image scaled to the given dimensions using the given rendering
2210 * hint.
2211 */
2212 protected static BufferedImage scaleImageIncrementally(BufferedImage src,
2213 int targetWidth, int targetHeight, Method scalingMethod,
2214 Object interpolationHintValue) {
2215 boolean hasReassignedSrc = false;
2216 int incrementCount = 0;
2217 int currentWidth = src.getWidth();
2218 int currentHeight = src.getHeight();
2219
2220 /*
2221 * The original QUALITY mode, representing Chris Campbell's algorithm,
2222 * is to step down by 1/2s every time when scaling the image
2223 * incrementally. Users pointed out that using this method to scale
2224 * images with noticeable straight lines left them really jagged in
2225 * smaller thumbnail format.
2226 *
2227 * After investigation it was discovered that scaling incrementally by
2228 * smaller increments was the ONLY way to make the thumbnail sized
2229 * images look less jagged and more accurate; almost matching the
2230 * accuracy of Mac's built in thumbnail generation which is the highest
2231 * quality resize I've come across (better than GIMP Lanczos3 and
2232 * Windows 7).
2233 *
2234 * A divisor of 7 was chose as using 5 still left some jaggedness in the
2235 * image while a divisor of 8 or higher made the resulting thumbnail too
2236 * soft; like our OP_ANTIALIAS convolve op had been forcibly applied to
2237 * the result even if the user didn't want it that soft.
2238 *
2239 * Using a divisor of 7 for the ULTRA_QUALITY seemed to be the sweet
2240 * spot.
2241 *
2242 * NOTE: Below when the actual fraction is used to calculate the small
2243 * portion to subtract from the current dimension, this is a
2244 * progressively smaller and smaller chunk. When the code was changed to
2245 * do a linear reduction of the image of equal steps for each
2246 * incremental resize (e.g. say 50px each time) the result was
2247 * significantly worse than the progressive approach used below; even
2248 * when a very high number of incremental steps (13) was tested.
2249 */
2250 int fraction = (scalingMethod == Method.ULTRA_QUALITY ? 7 : 2);
2251
2252 do {
2253 int prevCurrentWidth = currentWidth;
2254 int prevCurrentHeight = currentHeight;
2255
2256 /*
2257 * If the current width is bigger than our target, cut it in half
2258 * and sample again.
2259 */
2260 if (currentWidth > targetWidth) {
2261 currentWidth -= (currentWidth / fraction);
2262
2263 /*
2264 * If we cut the width too far it means we are on our last
2265 * iteration. Just set it to the target width and finish up.
2266 */
2267 if (currentWidth < targetWidth)
2268 currentWidth = targetWidth;
2269 }
2270
2271 /*
2272 * If the current height is bigger than our target, cut it in half
2273 * and sample again.
2274 */
2275
2276 if (currentHeight > targetHeight) {
2277 currentHeight -= (currentHeight / fraction);
2278
2279 /*
2280 * If we cut the height too far it means we are on our last
2281 * iteration. Just set it to the target height and finish up.
2282 */
2283
2284 if (currentHeight < targetHeight)
2285 currentHeight = targetHeight;
2286 }
2287
2288 /*
2289 * Stop when we cannot incrementally step down anymore.
2290 *
2291 * This used to use a || condition, but that would cause problems
2292 * when using FIT_EXACT such that sometimes the width OR height
2293 * would not change between iterations, but the other dimension
2294 * would (e.g. resizing 500x500 to 500x250).
2295 *
2296 * Now changing this to an && condition requires that both
2297 * dimensions do not change between a resize iteration before we
2298 * consider ourselves done.
2299 */
2300 if (prevCurrentWidth == currentWidth
2301 && prevCurrentHeight == currentHeight)
2302 break;
2303
2304 if (DEBUG)
2305 log(2, "Scaling from [%d x %d] to [%d x %d]", prevCurrentWidth,
2306 prevCurrentHeight, currentWidth, currentHeight);
2307
2308 // Render the incremental scaled image.
2309 BufferedImage incrementalImage = scaleImage(src, currentWidth,
2310 currentHeight, interpolationHintValue);
2311
2312 /*
2313 * Before re-assigning our interim (partially scaled)
2314 * incrementalImage to be the new src image before we iterate around
2315 * again to process it down further, we want to flush() the previous
2316 * src image IF (and only IF) it was one of our own temporary
2317 * BufferedImages created during this incremental down-sampling
2318 * cycle. If it wasn't one of ours, then it was the original
2319 * caller-supplied BufferedImage in which case we don't want to
2320 * flush() it and just leave it alone.
2321 */
2322 if (hasReassignedSrc)
2323 src.flush();
2324
2325 /*
2326 * Now treat our incremental partially scaled image as the src image
2327 * and cycle through our loop again to do another incremental
2328 * scaling of it (if necessary).
2329 */
2330 src = incrementalImage;
2331
2332 /*
2333 * Keep track of us re-assigning the original caller-supplied source
2334 * image with one of our interim BufferedImages so we know when to
2335 * explicitly flush the interim "src" on the next cycle through.
2336 */
2337 hasReassignedSrc = true;
2338
2339 // Track how many times we go through this cycle to scale the image.
2340 incrementCount++;
2341 } while (currentWidth != targetWidth || currentHeight != targetHeight);
2342
2343 if (DEBUG)
2344 log(2, "Incrementally Scaled Image in %d steps.", incrementCount);
2345
2346 /*
2347 * Once the loop has exited, the src image argument is now our scaled
2348 * result image that we want to return.
2349 */
2350 return src;
2351 }
2352 }


We're sorry but finance-manager doesn't work properly without JavaScript enabled. Please enable it to continue.