diff --git a/src/main/java/org/imgscalr/Scalr.java b/src/main/java/org/imgscalr/Scalr.java index 8ae612c..d08911c 100644 --- a/src/main/java/org/imgscalr/Scalr.java +++ b/src/main/java/org/imgscalr/Scalr.java @@ -444,6 +444,12 @@ public static enum Mode { * how it makes the image look, that is what this mode is for. */ FIT_EXACT, + /** + * Used to indicate that the scaling implementation should calculate + * dimensions for the largest image that fit within the bounding box, + * without cropping or distortion, retaining the original proportions. + */ + BEST_FIT_BOTH, /** * Used to indicate that the scaling implementation should calculate * dimensions for the resultant image that best-fit within the given @@ -1615,7 +1621,24 @@ public static BufferedImage resize(BufferedImage src, Method scalingMethod, * just specify the dimensions they would like the image to roughly fit * within and it will do the right thing without mangling the result. */ - if (resizeMode != Mode.FIT_EXACT) { + if (resizeMode == Mode.FIT_EXACT) { + if (DEBUG) + log(1, + "Resize Mode FIT_EXACT used, no width/height checking or re-calculation will be done."); + } else if (resizeMode == Mode.BEST_FIT_BOTH) { + float requestedHeightScaling = ((float) targetHeight / (float) currentHeight); + float requestedWidthScaling = ((float) targetWidth / (float) currentWidth); + float actualScaling = Math.min(requestedHeightScaling, requestedWidthScaling); + + targetHeight = Math.round((float) currentHeight * actualScaling); + targetWidth = Math.round((float) currentWidth * actualScaling); + + if (targetHeight == currentHeight && targetWidth == currentWidth) + return src; + + if (DEBUG) + log(1, "Auto-Corrected width and height based on scalingRatio %d.", actualScaling); + } else { if ((ratio <= 1 && resizeMode == Mode.AUTOMATIC) || (resizeMode == Mode.FIT_TO_WIDTH)) { // First make sure we need to do any work in the first place @@ -1655,10 +1678,6 @@ public static BufferedImage resize(BufferedImage src, Method scalingMethod, "Auto-Corrected targetWidth [from=%d to=%d] to honor image proportions.", originalTargetWidth, targetWidth); } - } else { - if (DEBUG) - log(1, - "Resize Mode FIT_EXACT used, no width/height checking or re-calculation will be done."); } // If AUTOMATIC was specified, determine the real scaling method. diff --git a/src/test/java/org/imgscalr/ScalrResizeTest.java b/src/test/java/org/imgscalr/ScalrResizeTest.java index 1d2153e..db47ed3 100644 --- a/src/test/java/org/imgscalr/ScalrResizeTest.java +++ b/src/test/java/org/imgscalr/ScalrResizeTest.java @@ -159,4 +159,56 @@ public void testResizeFitExact() { Assert.assertEquals(i2.getWidth(), 500); Assert.assertEquals(i2.getHeight(), 250); } + + @Test + public void testResizeAutoVsFitBoth() { + // FitBoth will not allow the minor axis to grow beyond the specified box. The four commented + // tests show how this interacts + + // For landscape images, AUTO will let targetWidth decide scaling, even if targetHeight is violated + BufferedImage landscape = new BufferedImage(500, 250, BufferedImage.TYPE_INT_RGB); + testResizeAutoVsBoth(landscape, 500, 250, 500, 250, 500, 250); + testResizeAutoVsBoth(landscape, 500, 500, 500, 250, 500, 250); + + testResizeAutoVsBoth(landscape, 800, 300, 800, 400, 600, 300); // FitBoth restricts y to 300, and adjusts x + testResizeAutoVsBoth(landscape, 800, 400, 800, 400, 800, 400); + testResizeAutoVsBoth(landscape, 800, 500, 800, 400, 800, 400); + + testResizeAutoVsBoth(landscape, 250, 150, 250, 125, 250, 125); + testResizeAutoVsBoth(landscape, 250, 125, 250, 125, 250, 125); + testResizeAutoVsBoth(landscape, 250, 100, 250, 125, 200, 100); // FitBoth imposes smaller y, and adjusts x + + // For portrait images, AUTO will let targetHeight decide scaling, even if targetWidth is violated + BufferedImage portrait = new BufferedImage(250, 500, BufferedImage.TYPE_INT_RGB); + testResizeAutoVsBoth(portrait, 250, 500, 250, 500, 250, 500); + testResizeAutoVsBoth(portrait, 500, 500, 250, 500, 250, 500); + + testResizeAutoVsBoth(portrait, 300, 800, 400, 800, 300, 600); // FitBoth restricts x to 800, and adjusts y + testResizeAutoVsBoth(portrait, 400, 800, 400, 800, 400, 800); + testResizeAutoVsBoth(portrait, 500, 800, 400, 800, 400, 800); + + testResizeAutoVsBoth(portrait, 150, 250, 125, 250, 125, 250); + testResizeAutoVsBoth(portrait, 125, 250, 125, 250, 125, 250); + testResizeAutoVsBoth(portrait, 100, 250, 125, 250, 100, 200); // FitBoth imposes smaller xj, and adjusts y + + // Squares are treated as a landscape + BufferedImage square = new BufferedImage(500, 500, BufferedImage.TYPE_INT_RGB); + testResizeAutoVsBoth(square, 500, 500, 500, 500, 500, 500); + testResizeAutoVsBoth(square, 800, 800, 800, 800, 800, 800); + testResizeAutoVsBoth(square, 400, 400, 400, 400, 400, 400); + testResizeAutoVsBoth(square, 800, 600, 800, 800, 600, 600); // FixBoth restricts both dimensions + + } + + // resize to (w,h) using AUTO and FIT_BOTH modes, then compare auto (w,h) and fitBoth (w,h) + private void testResizeAutoVsBoth (BufferedImage i, int targetWidth, int targetHeight, int autoWidth, int autoHeight, int fitBothWidth, int fitBothHeight) { + BufferedImage auto = Scalr.resize(i, Mode.AUTOMATIC, targetWidth, targetHeight); + BufferedImage fitBoth = Scalr.resize(i, Mode.BEST_FIT_BOTH, targetWidth, targetHeight); + + Assert.assertEquals (autoWidth, auto.getWidth()); + Assert.assertEquals(autoHeight, auto.getHeight()); + + Assert.assertEquals(fitBothWidth, fitBoth.getWidth()); + Assert.assertEquals(fitBothHeight, fitBoth.getHeight()); + } } \ No newline at end of file