/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.tools.idea.rendering; import junit.framework.TestCase; import org.jetbrains.android.AndroidTestCase; import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; public class ImageUtilsTest extends TestCase { public void testScaleImage() throws Exception { @SuppressWarnings("UndesirableClassUsage") BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB_PRE); Graphics g = image.getGraphics(); g.setColor(new Color(0xFF00FF00, true)); g.fillRect(0, 0, image.getWidth(), image.getHeight()); g.setColor(new Color(0xFFFF0000, true)); g.fillRect(25, 25, 50, 50); g.dispose(); BufferedImage scaled = ImageUtils.scale(image, 0.5, 0.5); assertEquals(50, scaled.getWidth()); assertEquals(50, scaled.getHeight()); assertEquals(0xFF00FF00, scaled.getRGB(0, 0)); assertEquals(0xFF00FF00, scaled.getRGB(49, 49)); assertEquals(0xFFFF0000, scaled.getRGB(25, 25)); scaled = ImageUtils.scale(image, 2.0, 2.0); assertEquals(200, scaled.getWidth()); assertEquals(200, scaled.getHeight()); assertEquals(0xFF00FF00, scaled.getRGB(0, 0)); assertEquals(0xFF00FF00, scaled.getRGB(48, 48)); assertEquals(0xFFFF0000, scaled.getRGB(100, 100)); assertEquals(0xFF00FF00, scaled.getRGB(199, 199)); scaled = ImageUtils.scale(image, 0.25, 0.25); assertEquals(25, scaled.getWidth()); assertEquals(25, scaled.getHeight()); assertEquals(0xFF00FF00, scaled.getRGB(0, 0)); assertEquals(0xFF00FF00, scaled.getRGB(24, 24)); assertEquals(0xFFFF0000, scaled.getRGB(13, 13)); scaled = ImageUtils.scale(image, 0.25, 0.25, 75, 95); assertEquals(100, scaled.getWidth()); assertEquals(120, scaled.getHeight()); assertEquals(0xFF00FF00, scaled.getRGB(0, 0)); assertEquals(0xFF00FF00, scaled.getRGB(24, 24)); assertEquals(0xFFFF0000, scaled.getRGB(13, 13)); } public void testRotation() throws IOException { String path = AndroidTestCase.getAbsoluteTestDataPath() + File.separator + "render" + File.separator + "imageutils"; BufferedImage srcImage = readImage(path, "0.png"); BufferedImage expected90 = readImage(path, "90.png"); BufferedImage expected180 = readImage(path, "180.png"); BufferedImage expected270 = readImage(path, "270.png"); assertEqualsImage(expected90, ImageUtils.rotateByRightAngle(srcImage, 90)); assertEqualsImage(expected180, ImageUtils.rotateByRightAngle(srcImage, 180)); assertEqualsImage(expected270, ImageUtils.rotateByRightAngle(srcImage, 270)); // verify that rotating the image 4 times by 90 degrees doesn't lose any data BufferedImage img = readImage(path, "0.png"); for (int i = 0; i < 4; i++) { img = ImageUtils.rotateByRightAngle(img, 90); } assertEqualsImage(srcImage, img); } public void testAddMargin() { BufferedImage image = createTestImage(100, 200, BufferedImage.TYPE_INT_ARGB, Color.RED); BufferedImage zeroMargin = ImageUtils.addMargin(image, 0); assertEqualsImage(image, zeroMargin); BufferedImage margin1 = ImageUtils.addMargin(image, 1); assertEquals(margin1.getWidth(), image.getWidth() + 2); assertEquals(margin1.getHeight(), image.getHeight() + 2); image = createTestImage(50, 50, BufferedImage.TYPE_INT_RGB, Color.BLUE); margin1 = ImageUtils.addMargin(image, 1); assertFalse(image.getColorModel().hasAlpha()); assertTrue(margin1.getColorModel().hasAlpha()); assertEqualsImageExcludingMargin(image, margin1, 1); } private static BufferedImage createTestImage(int w, int h, int imageType, Color fill) { @SuppressWarnings("UndesirableClassUsage") BufferedImage image = new BufferedImage(w, h, imageType); Graphics g = image.getGraphics(); g.setColor(fill); g.fillRect(0, 0, image.getWidth(), image.getHeight()); g.dispose(); return image; } private static BufferedImage readImage(String folder, String fileName) throws IOException { File f = new File(folder, fileName); return ImageIO.read(f); } private static void assertEqualsImage(BufferedImage expected, BufferedImage actual) { assertEqualsImageExcludingMargin(expected, actual, 0); } private static void assertEqualsImageExcludingMargin(BufferedImage expected, BufferedImage actual, int margin) { assertEquals(expected.getWidth(), actual.getWidth() - 2 * margin); assertEquals(expected.getHeight(), actual.getHeight() - 2 * margin); for (int x = 0; x < expected.getWidth(); x++) { for (int y = 0; y < expected.getHeight(); y++) { assertEquals(expected.getRGB(x, y), actual.getRGB(x + margin, y + margin)); } } } public void testCropBlank() throws Exception { BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB_PRE); Graphics g = image.getGraphics(); g.setColor(new Color(0, true)); g.fillRect(0, 0, image.getWidth(), image.getHeight()); g.dispose(); BufferedImage crop = ImageUtils.cropBlank(image, null); assertNull(crop); } public void testCropBlankPre() throws Exception { BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB_PRE); Graphics g = image.getGraphics(); g.setColor(new Color(0, true)); g.fillRect(0, 0, image.getWidth(), image.getHeight()); g.dispose(); BufferedImage crop = ImageUtils.cropBlank(image, new Rectangle(5, 5, 80, 80)); assertNull(crop); } public void testCropTopLine() throws Exception { BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB_PRE); Graphics g = image.getGraphics(); g.setColor(new Color(0, false)); g.fillRect(0, 1, image.getWidth(), image.getHeight() - 1); g.dispose(); BufferedImage crop = ImageUtils.cropBlank(image, null); assertNotNull(crop); assertEquals(image.getWidth(), crop.getWidth()); assertEquals(image.getHeight() - 1, crop.getHeight()); } public void testCropNonBlank() throws Exception { BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB_PRE); Graphics g = image.getGraphics(); g.setColor(new Color(0, false)); g.fillRect(0, 0, image.getWidth(), image.getHeight()); g.dispose(); BufferedImage crop = ImageUtils.cropBlank(image, null); assertNotNull(crop); assertEquals(crop.getWidth(), image.getWidth()); assertEquals(crop.getHeight(), image.getHeight()); } public void testCropSomething() throws Exception { BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB_PRE); Graphics g = image.getGraphics(); g.setColor(new Color(0, true)); g.fillRect(0, 0, image.getWidth(), image.getHeight()); g.setColor(new Color(0xFF00FF00, true)); g.fillRect(25, 25, 50, 50); g.dispose(); BufferedImage crop = ImageUtils.cropBlank(image, null); assertNotNull(crop); assertEquals(50, crop.getWidth()); assertEquals(50, crop.getHeight()); assertEquals(0xFF00FF00, crop.getRGB(0, 0)); assertEquals(0xFF00FF00, crop.getRGB(49, 49)); } public void testCropSomethingPre() throws Exception { BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB_PRE); Graphics g = image.getGraphics(); g.setColor(new Color(0, true)); g.fillRect(0, 0, image.getWidth(), image.getHeight()); g.setColor(new Color(0xFF00FF00, true)); g.fillRect(25, 25, 50, 50); g.dispose(); BufferedImage crop = ImageUtils.cropBlank(image, new Rectangle(0, 0, 100, 100)); assertNotNull(crop); assertEquals(50, crop.getWidth()); assertEquals(50, crop.getHeight()); assertEquals(0xFF00FF00, crop.getRGB(0, 0)); assertEquals(0xFF00FF00, crop.getRGB(49, 49)); } public void testCropSomethingPre2() throws Exception { BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB_PRE); Graphics g = image.getGraphics(); g.setColor(new Color(0, true)); g.fillRect(0, 0, image.getWidth(), image.getHeight()); g.setColor(new Color(0xFF00FF00, true)); g.fillRect(25, 25, 50, 50); g.dispose(); BufferedImage crop = ImageUtils.cropBlank(image, new Rectangle(5, 5, 80, 80)); assertNotNull(crop); assertEquals(50, crop.getWidth()); assertEquals(50, crop.getHeight()); assertEquals(0xFF00FF00, crop.getRGB(0, 0)); assertEquals(0xFF00FF00, crop.getRGB(49, 49)); } /** * Paints a set of {@link Rectangle} object out of a rendered {@link BufferedImage} * such that the resulting image is transparent except for a minimum bounding * rectangle of the selected elements. * * @param image the source image * @param rectangles the set of rectangles to copy * @param boundingBox the bounding rectangle of the set of rectangles to copy, can be * computed by {@link ImageUtils#getBoundingRectangle} * @param scale a scale factor to apply to the result, e.g. 0.5 to shrink the * destination down 50%, 1.0 to leave it alone and 2.0 to zoom in by * doubling the image size * @return a rendered image, or null */ public static BufferedImage drawRectangles(BufferedImage image, java.util.List<Rectangle> rectangles, Rectangle boundingBox, double scale) { // This code is not a test. When I implemented image cropping, I first implemented // it for BufferedImages (since it's easier; easy image painting, easy scaling, // easy transparency handling, etc). However, this meant that we would need to // convert the SWT images from the ImageOverlay to BufferedImages, crop and convert // back; not ideal, so I rewrote it in SWT (see SwtUtils). However, I // don't want to throw away the code in case we start keeping BufferedImages rather // than SWT images or need it for other purposes, but rather than place it in the // production codebase I'm leaving this utility here in the associated ImageUtils // test class. It was used like this: // @formatter:off // // BufferedImage wholeImage = SwtUtils.convertToAwt(image); // BufferedImage result = ImageUtils.cropSelection(wholeImage, // rectangles, boundingBox, scale); // e.image = SwtUtils.convertToSwt(image.getDevice(), result, true, // DRAG_TRANSPARENCY); // // @formatter:on if (boundingBox == null) { return null; } int destWidth = (int) (scale * boundingBox.width); int destHeight = (int) (scale * boundingBox.height); BufferedImage dest = new BufferedImage(destWidth, destHeight, image.getType()); Graphics2D g = dest.createGraphics(); for (Rectangle bounds : rectangles) { int dx1 = bounds.x - boundingBox.x; int dy1 = bounds.y - boundingBox.y; int dx2 = dx1 + bounds.width; int dy2 = dy1 + bounds.height; dx1 *= scale; dy1 *= scale; dx2 *= scale; dy2 *= scale; int sx1 = bounds.x; int sy1 = bounds.y; int sx2 = sx1 + bounds.width; int sy2 = sy1 + bounds.height; g.drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null); } g.dispose(); return dest; } }