@@ -1021,7 +1021,8 @@ public static BufferedImage resize(BufferedImage src, Method scalingMethod,
1021
1021
* and height.
1022
1022
*
1023
1023
* @throws IllegalArgumentException
1024
- * if <code>scalingMethod</code> is <code>null</code>, if
1024
+ * if <code>src</code> is <code>null</code>, if
1025
+ * <code>scalingMethod</code> is <code>null</code>, if
1025
1026
* <code>resizeMethod</code> is <code>null</code>, if
1026
1027
* <code>targetWidth</code> is < 0 or if
1027
1028
* <code>targetHeight</code> is < 0.
@@ -1031,6 +1032,9 @@ public static BufferedImage resize(BufferedImage src, Method scalingMethod,
1031
1032
public static BufferedImage resize (BufferedImage src , Method scalingMethod ,
1032
1033
Mode resizeMode , int targetWidth , int targetHeight ,
1033
1034
BufferedImageOp ... ops ) throws IllegalArgumentException {
1035
+ if (src == null )
1036
+ throw new IllegalArgumentException (
1037
+ "src cannot be null, a valid BufferedImage instance must be provided." );
1034
1038
if (scalingMethod == null )
1035
1039
throw new IllegalArgumentException (
1036
1040
"scalingMethod cannot be null. A good default value is Method.AUTOMATIC." );
@@ -1044,181 +1048,171 @@ public static BufferedImage resize(BufferedImage src, Method scalingMethod,
1044
1048
1045
1049
BufferedImage result = null ;
1046
1050
1047
- // Make sure we have something to do first.
1048
- if (src != null ) {
1049
- long startTime = System .currentTimeMillis ();
1051
+ long startTime = System .currentTimeMillis ();
1050
1052
1051
- // Clear the 'null' ops arg passed in from other API methods
1052
- if (ops != null && ops .length == 1 && ops [0 ] == null )
1053
- ops = null ;
1053
+ // Clear the 'null' ops arg passed in from other API methods
1054
+ if (ops != null && ops .length == 1 && ops [0 ] == null )
1055
+ ops = null ;
1054
1056
1055
- int currentWidth = src .getWidth ();
1056
- int currentHeight = src .getHeight ();
1057
+ int currentWidth = src .getWidth ();
1058
+ int currentHeight = src .getHeight ();
1057
1059
1058
- // <= 1 is a square or landscape-oriented image, > 1 is a portrait.
1059
- float ratio = ((float ) currentHeight / (float ) currentWidth );
1060
+ // <= 1 is a square or landscape-oriented image, > 1 is a portrait.
1061
+ float ratio = ((float ) currentHeight / (float ) currentWidth );
1060
1062
1061
- if (DEBUG )
1062
- log ("START Resizing Source Image [size=%dx%d, mode=%s, orientation=%s, ratio(H/W)=%f] to [targetSize=%dx%d]" ,
1063
- currentWidth , currentHeight , resizeMode ,
1064
- (ratio <= 1 ? "Landscape/Square" : "Portrait" ), ratio ,
1065
- targetWidth , targetHeight );
1063
+ if (DEBUG )
1064
+ log ("START Resizing Source Image [size=%dx%d, mode=%s, orientation=%s, ratio(H/W)=%f] to [targetSize=%dx%d]" ,
1065
+ currentWidth , currentHeight , resizeMode ,
1066
+ (ratio <= 1 ? "Landscape/Square" : "Portrait" ), ratio ,
1067
+ targetWidth , targetHeight );
1068
+
1069
+ /*
1070
+ * The proportion of the picture must be honored, the way that is done
1071
+ * is to figure out if the image is in a LANDSCAPE/SQUARE or PORTRAIT
1072
+ * orientation and depending on its orientation, use the primary
1073
+ * dimension (width for LANDSCAPE/SQUARE and height for PORTRAIT) to
1074
+ * recalculate the alternative (height and width respectively) value
1075
+ * that adheres to the existing ratio. This helps make life easier for
1076
+ * the caller as they don't need to pre-compute proportional dimensions
1077
+ * before calling the API, they can just specify the dimensions they
1078
+ * would like the image to roughly fit within and it will do the right
1079
+ * thing without mangling the result.
1080
+ */
1081
+ if ((ratio <= 1 && resizeMode == Mode .AUTOMATIC )
1082
+ || (resizeMode == Mode .FIT_TO_WIDTH )) {
1083
+ // First make sure we need to do any work in the first place
1084
+ if (targetWidth == src .getWidth ())
1085
+ return src ;
1086
+
1087
+ // Save for detailed logging (this is cheap).
1088
+ int originalTargetHeight = targetHeight ;
1066
1089
1067
1090
/*
1068
- * The proportion of the picture must be honored, the way that is
1069
- * done is to figure out if the image is in a LANDSCAPE/SQUARE or
1070
- * PORTRAIT orientation and depending on its orientation, use the
1071
- * primary dimension (width for LANDSCAPE/SQUARE and height for
1072
- * PORTRAIT) to recalculate the alternative (height and width
1073
- * respectively) value that adheres to the existing ratio. This
1074
- * helps make life easier for the caller as they don't need to
1075
- * pre-compute proportional dimensions before calling the API, they
1076
- * can just specify the dimensions they would like the image to
1077
- * roughly fit within and it will do the right thing without
1078
- * mangling the result.
1091
+ * Landscape or Square Orientation: Ignore the given height and
1092
+ * re-calculate a proportionally correct value based on the
1093
+ * targetWidth.
1079
1094
*/
1080
- if ((ratio <= 1 && resizeMode == Mode .AUTOMATIC )
1081
- || (resizeMode == Mode .FIT_TO_WIDTH )) {
1082
- // First make sure we need to do any work in the first place
1083
- if (targetWidth == src .getWidth ())
1084
- return src ;
1095
+ targetHeight = Math .round ((float ) targetWidth * ratio );
1085
1096
1086
- // Save for detailed logging (this is cheap).
1087
- int originalTargetHeight = targetHeight ;
1097
+ if (DEBUG && originalTargetHeight != targetHeight )
1098
+ log ("Auto-Corrected targetHeight [from=%d to=%d] to honor image proportions" ,
1099
+ originalTargetHeight , targetHeight );
1100
+ } else {
1101
+ // First make sure we need to do any work in the first place
1102
+ if (targetHeight == src .getHeight ())
1103
+ return src ;
1088
1104
1089
- /*
1090
- * Landscape or Square Orientation: Ignore the given height and
1091
- * re-calculate a proportionally correct value based on the
1092
- * targetWidth.
1093
- */
1094
- targetHeight = Math .round ((float ) targetWidth * ratio );
1105
+ // Save for detailed logging (this is cheap).
1106
+ int originalTargetWidth = targetWidth ;
1095
1107
1096
- if (DEBUG && originalTargetHeight != targetHeight )
1097
- log ("Auto-Corrected targetHeight [from=%d to=%d] to honor image proportions" ,
1098
- originalTargetHeight , targetHeight );
1099
- } else {
1100
- // First make sure we need to do any work in the first place
1101
- if (targetHeight == src .getHeight ())
1102
- return src ;
1108
+ /*
1109
+ * Portrait Orientation: Ignore the given width and re-calculate a
1110
+ * proportionally correct value based on the targetHeight.
1111
+ */
1112
+ targetWidth = Math .round ((float ) targetHeight / ratio );
1103
1113
1104
- // Save for detailed logging (this is cheap).
1105
- int originalTargetWidth = targetWidth ;
1114
+ if (DEBUG && originalTargetWidth != targetWidth )
1115
+ log ("Auto-Corrected targetWidth [from=%d to=%d] to honor image proportions" ,
1116
+ originalTargetWidth , targetWidth );
1117
+ }
1118
+
1119
+ // If AUTOMATIC was specified, determine the real scaling method.
1120
+ if (scalingMethod == Scalr .Method .AUTOMATIC )
1121
+ scalingMethod = determineScalingMethod (targetWidth , targetHeight ,
1122
+ ratio );
1123
+
1124
+ if (DEBUG )
1125
+ log ("Scaling Image to [size=%dx%d] using the %s method..." ,
1126
+ targetWidth , targetHeight , scalingMethod );
1127
+
1128
+ // Now we scale the image
1129
+ if (scalingMethod == Scalr .Method .SPEED ) {
1130
+ result = scaleImage (src , targetWidth , targetHeight ,
1131
+ RenderingHints .VALUE_INTERPOLATION_NEAREST_NEIGHBOR );
1132
+ } else if (scalingMethod == Scalr .Method .BALANCED ) {
1133
+ result = scaleImage (src , targetWidth , targetHeight ,
1134
+ RenderingHints .VALUE_INTERPOLATION_BILINEAR );
1135
+ } else if (scalingMethod == Scalr .Method .QUALITY ) {
1136
+ /*
1137
+ * If we are scaling up (in either width or height - since we know
1138
+ * the image will stay proportional we just check if either are
1139
+ * being scaled up), directly using a single BICUBIC will give us
1140
+ * better results then using Chris Campbell's incremental scaling
1141
+ * operation (and take a lot less time). If we are scaling down, we
1142
+ * must use the incremental scaling algorithm for the best result.
1143
+ */
1144
+ if (targetWidth > currentWidth || targetHeight > currentHeight ) {
1145
+ log ("\t QUALITY Up-scale, single BICUBIC scaling will be used..." );
1106
1146
1107
1147
/*
1108
- * Portrait Orientation: Ignore the given width and re-calculate
1109
- * a proportionally correct value based on the targetHeight.
1148
+ * BILINEAR and BICUBIC look similar the smaller the scale jump
1149
+ * upwards is, if the scale is larger BICUBIC looks sharper and
1150
+ * less fuzzy. But most importantly we have to use BICUBIC to
1151
+ * match the contract of the QUALITY rendering method. This note
1152
+ * is just here for anyone reading the code and wondering how
1153
+ * they can speed their own calls up.
1110
1154
*/
1111
- targetWidth = Math .round ((float ) targetHeight / ratio );
1155
+ result = scaleImage (src , targetWidth , targetHeight ,
1156
+ RenderingHints .VALUE_INTERPOLATION_BICUBIC );
1157
+ } else {
1158
+ log ("\t QUALITY Down-scale, incremental scaling will be used..." );
1112
1159
1113
- if (DEBUG && originalTargetWidth != targetWidth )
1114
- log ("Auto-Corrected targetWidth [from=%d to=%d] to honor image proportions" ,
1115
- originalTargetWidth , targetWidth );
1160
+ /*
1161
+ * Originally we wanted to use BILINEAR interpolation here
1162
+ * because it takes 1/3rd the time that the BICUBIC
1163
+ * interpolation does, however, when scaling large images down
1164
+ * to most sizes bigger than a thumbnail we witnessed noticeable
1165
+ * "softening" in the resultant image with BILINEAR that would
1166
+ * be unexpectedly annoying to a user expecting a "QUALITY"
1167
+ * scale of their original image. Instead BICUBIC was chosen to
1168
+ * honor the contract of a QUALITY scale of the original image.
1169
+ */
1170
+ result = scaleImageIncrementally (src , targetWidth ,
1171
+ targetHeight ,
1172
+ RenderingHints .VALUE_INTERPOLATION_BICUBIC );
1116
1173
}
1174
+ }
1117
1175
1118
- // If AUTOMATIC was specified, determine the real scaling method.
1119
- if (scalingMethod == Scalr .Method .AUTOMATIC )
1120
- scalingMethod = determineScalingMethod (targetWidth ,
1121
- targetHeight , ratio );
1122
-
1176
+ // Apply the image ops if any were provided
1177
+ if (ops != null && ops .length > 0 ) {
1123
1178
if (DEBUG )
1124
- log ("Scaling Image to [size=%dx%d] using the %s method..." ,
1125
- targetWidth , targetHeight , scalingMethod );
1179
+ log ("Applying %d Image Ops to Result" , ops .length );
1180
+
1181
+ for (BufferedImageOp op : ops ) {
1182
+ // In case a null op was passed in, skip it instead of dying
1183
+ if (op == null )
1184
+ continue ;
1185
+
1186
+ long opStartTime = System .currentTimeMillis ();
1187
+ Rectangle2D dims = op .getBounds2D (result );
1126
1188
1127
- // Now we scale the image
1128
- if (scalingMethod == Scalr .Method .SPEED ) {
1129
- result = scaleImage (src , targetWidth , targetHeight ,
1130
- RenderingHints .VALUE_INTERPOLATION_NEAREST_NEIGHBOR );
1131
- } else if (scalingMethod == Scalr .Method .BALANCED ) {
1132
- result = scaleImage (src , targetWidth , targetHeight ,
1133
- RenderingHints .VALUE_INTERPOLATION_BILINEAR );
1134
- } else if (scalingMethod == Scalr .Method .QUALITY ) {
1135
1189
/*
1136
- * If we are scaling up (in either width or height - since we
1137
- * know the image will stay proportional we just check if either
1138
- * are being scaled up), directly using a single BICUBIC will
1139
- * give us better results then using Chris Campbell's
1140
- * incremental scaling operation (and take a lot less time). If
1141
- * we are scaling down, we must use the incremental scaling
1142
- * algorithm for the best result.
1190
+ * We must manually create the target image; we cannot rely on
1191
+ * the null-dest filter() method to create a valid destination
1192
+ * for us thanks to this JDK bug that has been filed for almost
1193
+ * a decade: http://bugs.sun.com/bugdatabase/view_bug.
1194
+ * do;jsessionid=33b25bf937f467791ff5792cb9dc?bug_id=4965606
1143
1195
*/
1144
- if (targetWidth > currentWidth || targetHeight > currentHeight ) {
1145
- log ("\t QUALITY Up-scale, single BICUBIC scaling will be used..." );
1146
-
1147
- /*
1148
- * BILINEAR and BICUBIC look similar the smaller the scale
1149
- * jump upwards is, if the scale is larger BICUBIC looks
1150
- * sharper and less fuzzy. But most importantly we have to
1151
- * use BICUBIC to match the contract of the QUALITY
1152
- * rendering method. This note is just here for anyone
1153
- * reading the code and wondering how they can speed their
1154
- * own calls up.
1155
- */
1156
- result = scaleImage (src , targetWidth , targetHeight ,
1157
- RenderingHints .VALUE_INTERPOLATION_BICUBIC );
1158
- } else {
1159
- log ("\t QUALITY Down-scale, incremental scaling will be used..." );
1160
-
1161
- /*
1162
- * Originally we wanted to use BILINEAR interpolation here
1163
- * because it takes 1/3rd the time that the BICUBIC
1164
- * interpolation does, however, when scaling large images
1165
- * down to most sizes bigger than a thumbnail we witnessed
1166
- * noticeable "softening" in the resultant image with
1167
- * BILINEAR that would be unexpectedly annoying to a user
1168
- * expecting a "QUALITY" scale of their original image.
1169
- * Instead BICUBIC was chosen to honor the contract of a
1170
- * QUALITY scale of the original image.
1171
- */
1172
- result = scaleImageIncrementally (src , targetWidth ,
1173
- targetHeight ,
1174
- RenderingHints .VALUE_INTERPOLATION_BICUBIC );
1175
- }
1176
- }
1196
+ BufferedImage dest = new BufferedImage ((int ) Math .round (dims
1197
+ .getWidth ()), (int ) Math .round (dims .getHeight ()),
1198
+ result .getType ());
1177
1199
1178
- // Apply the image ops if any were provided
1179
- if (ops != null && ops .length > 0 ) {
1180
- if (DEBUG )
1181
- log ("Applying %d Image Ops to Result" , ops .length );
1182
-
1183
- for (BufferedImageOp op : ops ) {
1184
- // In case a null op was passed in, skip it instead of dying
1185
- if (op == null )
1186
- continue ;
1187
-
1188
- long opStartTime = System .currentTimeMillis ();
1189
- Rectangle2D dims = op .getBounds2D (result );
1190
-
1191
- /*
1192
- * We must manually create the target image; we cannot rely
1193
- * on the null-dest filter() method to create a valid
1194
- * destination for us thanks to this JDK bug that has been
1195
- * filed for almost a decade:
1196
- * http://bugs.sun.com/bugdatabase/view_bug.
1197
- * do;jsessionid=33b25bf937f467791ff5792cb9dc?bug_id=4965606
1198
- */
1199
- BufferedImage dest = new BufferedImage (
1200
- (int ) Math .round (dims .getWidth ()),
1201
- (int ) Math .round (dims .getHeight ()),
1202
- result .getType ());
1203
-
1204
- result = op .filter (result , dest );
1205
-
1206
- if (DEBUG )
1207
- log ("\t Op Applied in %d ms, Resultant Image [width=%d, height=%d], Op: %s" ,
1208
- (System .currentTimeMillis () - opStartTime ),
1209
- result .getWidth (), result .getHeight (), op );
1210
- }
1211
- }
1200
+ result = op .filter (result , dest );
1212
1201
1213
- if (DEBUG ) {
1214
- long elapsedTime = System .currentTimeMillis () - startTime ;
1215
- log ("END Source Image Scaled from [%dx%d] to [%dx%d] and %d BufferedImageOp(s) Applied in %d ms" ,
1216
- currentWidth , currentHeight , result .getWidth (),
1217
- result .getHeight (), (ops == null ? 0 : ops .length ),
1218
- elapsedTime );
1202
+ if (DEBUG )
1203
+ log ("\t Op Applied in %d ms, Resultant Image [width=%d, height=%d], Op: %s" ,
1204
+ (System .currentTimeMillis () - opStartTime ),
1205
+ result .getWidth (), result .getHeight (), op );
1219
1206
}
1220
- } else
1221
- log ("No Source Image Specified, exiting..." );
1207
+ }
1208
+
1209
+ if (DEBUG ) {
1210
+ long elapsedTime = System .currentTimeMillis () - startTime ;
1211
+ log ("END Source Image Scaled from [%dx%d] to [%dx%d] and %d BufferedImageOp(s) Applied in %d ms" ,
1212
+ currentWidth , currentHeight , result .getWidth (),
1213
+ result .getHeight (), (ops == null ? 0 : ops .length ),
1214
+ elapsedTime );
1215
+ }
1222
1216
1223
1217
return result ;
1224
1218
}
0 commit comments