Skip to content

Commit 7b68f0f

Browse files
author
Riyad Kalla
committed
Changed resize behavior to throw an IAE if src is null.
Also updated build script for upcoming 3.1 release.
1 parent 9730aa0 commit 7b68f0f

File tree

2 files changed

+147
-153
lines changed

2 files changed

+147
-153
lines changed

build.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<property name="dir.javadoc" value="${dir.dist}/javadoc" />
66

77
<property name="version.major" value="3" />
8-
<property name="version.minor" value="0" />
8+
<property name="version.minor" value="1" />
99

1010
<property name="name.file" value="imgscalr-lib" />
1111
<property name="name.file.javadoc" value="${name.file}-${version.major}.${version.minor}-javadoc.jar" />

src/main/java/com/thebuzzmedia/imgscalr/Scalr.java

+146-152
Original file line numberDiff line numberDiff line change
@@ -1021,7 +1021,8 @@ public static BufferedImage resize(BufferedImage src, Method scalingMethod,
10211021
* and height.
10221022
*
10231023
* @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
10251026
* <code>resizeMethod</code> is <code>null</code>, if
10261027
* <code>targetWidth</code> is &lt; 0 or if
10271028
* <code>targetHeight</code> is &lt; 0.
@@ -1031,6 +1032,9 @@ public static BufferedImage resize(BufferedImage src, Method scalingMethod,
10311032
public static BufferedImage resize(BufferedImage src, Method scalingMethod,
10321033
Mode resizeMode, int targetWidth, int targetHeight,
10331034
BufferedImageOp... ops) throws IllegalArgumentException {
1035+
if (src == null)
1036+
throw new IllegalArgumentException(
1037+
"src cannot be null, a valid BufferedImage instance must be provided.");
10341038
if (scalingMethod == null)
10351039
throw new IllegalArgumentException(
10361040
"scalingMethod cannot be null. A good default value is Method.AUTOMATIC.");
@@ -1044,181 +1048,171 @@ public static BufferedImage resize(BufferedImage src, Method scalingMethod,
10441048

10451049
BufferedImage result = null;
10461050

1047-
// Make sure we have something to do first.
1048-
if (src != null) {
1049-
long startTime = System.currentTimeMillis();
1051+
long startTime = System.currentTimeMillis();
10501052

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;
10541056

1055-
int currentWidth = src.getWidth();
1056-
int currentHeight = src.getHeight();
1057+
int currentWidth = src.getWidth();
1058+
int currentHeight = src.getHeight();
10571059

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);
10601062

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;
10661089

10671090
/*
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.
10791094
*/
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);
10851096

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;
10881104

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;
10951107

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);
11031113

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("\tQUALITY Up-scale, single BICUBIC scaling will be used...");
11061146

11071147
/*
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.
11101154
*/
1111-
targetWidth = Math.round((float) targetHeight / ratio);
1155+
result = scaleImage(src, targetWidth, targetHeight,
1156+
RenderingHints.VALUE_INTERPOLATION_BICUBIC);
1157+
} else {
1158+
log("\tQUALITY Down-scale, incremental scaling will be used...");
11121159

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);
11161173
}
1174+
}
11171175

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) {
11231178
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);
11261188

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) {
11351189
/*
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
11431195
*/
1144-
if (targetWidth > currentWidth || targetHeight > currentHeight) {
1145-
log("\tQUALITY Up-scale, single BICUBIC scaling will be used...");
1146-
1147-
/*
1148-
* BILINEAR and BICUBIC look similar the smaller the scale
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("\tQUALITY 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());
11771199

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("\tOp 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);
12121201

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("\tOp Applied in %d ms, Resultant Image [width=%d, height=%d], Op: %s",
1204+
(System.currentTimeMillis() - opStartTime),
1205+
result.getWidth(), result.getHeight(), op);
12191206
}
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+
}
12221216

12231217
return result;
12241218
}

0 commit comments

Comments
 (0)