net.rptools.lib.image.ImageUtil.java Source code

Java tutorial

Introduction

Here is the source code for net.rptools.lib.image.ImageUtil.java

Source

/*
 * 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 net.rptools.lib.image;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.PixelGrabber;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JPanel;

import org.apache.commons.io.FileUtils;

/**
 * 
 * @author trevor
 */
public class ImageUtil {

    public static final String HINT_TRANSPARENCY = "hintTransparency";

    // TODO: perhaps look at reintroducing this later
    //private static GraphicsConfiguration graphicsConfig = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();

    public static final FilenameFilter SUPPORTED_IMAGE_FILE_FILTER = new FilenameFilter() {
        public boolean accept(File dir, String name) {
            name = name.toLowerCase();
            //         TODO: FJE: When we move to Java 6, use <code>ImageIO.getReaderFileSuffixes()</code> instead
            return name.endsWith(".png") || name.endsWith(".gif") || name.endsWith(".jpg") || name.endsWith(".jpeg")
                    || name.endsWith(".bmp");
        }
    };

    //   public static void setGraphicsConfiguration(GraphicsConfiguration config) {
    //      graphicsConfig = config;
    //   }
    //
    /**
     * Load the image. Does not create a graphics configuration compatible version.
     */
    public static Image getImage(File file) throws IOException {
        return bytesToImage(FileUtils.readFileToByteArray(file));
    }

    /**
     * Load the image in the classpath. Does not create a graphics configuration compatible version.
     */
    public static Image getImage(String image) throws IOException {
        ByteArrayOutputStream dataStream = new ByteArrayOutputStream(8192);

        int bite;
        InputStream inStream = ImageUtil.class.getClassLoader().getResourceAsStream(image);
        if (inStream == null) {
            throw new IOException("Image not found: " + image);
        }
        inStream = new BufferedInputStream(inStream);
        while ((bite = inStream.read()) >= 0) {
            dataStream.write(bite);
        }
        return bytesToImage(dataStream.toByteArray());
    }

    public static Image getImage(String image, int w, int h) throws IOException {
        return resizeImage(getImage(image), w, h);
    }

    public static BufferedImage getCompatibleImage(String image) throws IOException {
        return getCompatibleImage(image, null);
    }

    public static BufferedImage getCompatibleImage(String image, Map<String, Object> hints) throws IOException {
        return createCompatibleImage(getImage(image), hints);
    }

    /**
     * Create a copy of the image that is compatible with the current graphics context
     * 
     * @param img
     * @return
     */
    public static BufferedImage createCompatibleImage(Image img) {
        return createCompatibleImage(img, null);
    }

    public static BufferedImage createCompatibleImage(Image img, Map<String, Object> hints) {
        if (img == null) {
            return null;
        }
        return createCompatibleImage(img, img.getWidth(null), img.getHeight(null), hints);
    }

    public static BufferedImage createCompatibleImage(int width, int height, int transparency) {
        return new BufferedImage(width, height, transparency);
    }

    /**
     * Create a copy of the image that is compatible with the current graphics context and scaled to the supplied size
     */
    public static BufferedImage createCompatibleImage(Image img, int width, int height, Map<String, Object> hints) {
        width = Math.max(width, 1);
        height = Math.max(height, 1);

        int transparency;
        if (hints != null && hints.containsKey(HINT_TRANSPARENCY)) {
            transparency = (Integer) hints.get(HINT_TRANSPARENCY);
        } else {
            transparency = pickBestTransparency(img);
        }
        BufferedImage compImg = new BufferedImage(width, height, transparency);

        Graphics2D g = null;
        try {
            g = compImg.createGraphics();
            g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

            g.drawImage(img, 0, 0, width, height, null);
        } finally {
            if (g != null) {
                g.dispose();
            }
        }
        return compImg;
    }

    /**
     * Look at the image and determine which Transparency is most appropriate. If it finds any translucent pixels it
     * returns Transparency.TRANSLUCENT, if it finds at least one purely transparent pixel and no translucent pixels it
     * will return Transparency.BITMASK, in all other cases it returns Transparency.OPAQUE, including errors
     * 
     * @param image
     * @return one of Transparency constants
     */
    public static int pickBestTransparency(Image image) {
        // Take a shortcut if possible
        if (image instanceof BufferedImage) {
            return pickBestTransparency((BufferedImage) image);
        }

        // Legacy method
        // NOTE: This is a horrible memory hog
        int width = image.getWidth(null);
        int height = image.getHeight(null);
        int[] pixelArray = new int[width * height];
        PixelGrabber pg = new PixelGrabber(image, 0, 0, width, height, pixelArray, 0, width);
        try {
            pg.grabPixels();
        } catch (InterruptedException e) {
            System.err.println("interrupted waiting for pixels!");
            return Transparency.OPAQUE;
        }
        if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
            System.err.println("image fetch aborted or errored");
            return Transparency.OPAQUE;
        }
        // Look for specific pixels
        boolean foundTransparent = false;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                // Get the next pixel
                int pixel = pixelArray[y * width + x];
                int alpha = (pixel >> 24) & 0xff;

                // Is there translucency or just pure transparency ?
                if (alpha > 0 && alpha < 255) {
                    return Transparency.TRANSLUCENT;
                }
                if (alpha == 0 && !foundTransparent) {
                    foundTransparent = true;
                }
            }
        }
        return foundTransparent ? Transparency.BITMASK : Transparency.OPAQUE;
    }

    public static int pickBestTransparency(BufferedImage image) {
        // See if we can short circuit
        ColorModel colorModel = image.getColorModel();
        if (colorModel.getTransparency() == Transparency.OPAQUE) {
            return Transparency.OPAQUE;
        }
        // Get the pixels
        int width = image.getWidth();
        int height = image.getHeight();

        // Look for specific pixels
        boolean foundTransparent = false;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                // Get the next pixel
                int pixel = image.getRGB(x, y);
                int alpha = (pixel >> 24) & 0xff;

                // Is there translucency or just pure transparency ?
                if (alpha > 0 && alpha < 255) {
                    return Transparency.TRANSLUCENT;
                }
                if (alpha == 0 && !foundTransparent) {
                    foundTransparent = true;
                }
            }
        }
        return foundTransparent ? Transparency.BITMASK : Transparency.OPAQUE;
    }

    public static byte[] imageToBytes(BufferedImage image) throws IOException {
        return imageToBytes(image, "jpg");
    }

    public static byte[] imageToBytes(BufferedImage image, String format) throws IOException {
        ByteArrayOutputStream outStream = new ByteArrayOutputStream(10000);
        ImageIO.write(image, format, outStream);

        return outStream.toByteArray();
    }

    private static final JPanel observer = new JPanel();

    /**
     * Converts a byte array into an {@link Image} instance.
     * 
     * @param imageBytes
     *            bytes to convert
     * @return
     * @throws IOException
     */
    public static Image bytesToImage(byte[] imageBytes) throws IOException {
        if (imageBytes == null) {
            throw new IOException("Could not load image - no data provided");
        }
        boolean interrupted = false;
        Throwable exception = null;
        Image image = null;
        image = Toolkit.getDefaultToolkit().createImage(imageBytes);
        MediaTracker tracker = new MediaTracker(observer);
        tracker.addImage(image, 0);
        do {
            try {
                interrupted = false;
                tracker.waitForID(0); // This is the only method that throws an exception
            } catch (InterruptedException t) {
                interrupted = true;
                continue;
            } catch (Throwable t) {
                exception = t;
            }
        } while (interrupted);
        if (image == null || exception != null || image.getWidth(null) <= 0 || image.getHeight(null) <= 0) {
            // Try the newer way (although it pretty much sucks rocks)
            image = ImageIO.read(new ByteArrayInputStream(imageBytes));
        }
        if (image == null) {
            throw new IOException("Could not load image", exception);
        }
        return image;
    }

    public static void clearImage(BufferedImage image) {
        if (image == null)
            return;

        Graphics2D g = null;
        try {
            g = (Graphics2D) image.getGraphics();
            Composite oldComposite = g.getComposite();
            g.setComposite(AlphaComposite.Clear);
            g.fillRect(0, 0, image.getWidth(), image.getHeight());
            g.setComposite(oldComposite);
        } finally {
            if (g != null) {
                g.dispose();
            }
        }
    }

    public static BufferedImage rgbToGrayscale(BufferedImage image) {
        if (image == null) {
            return null;
        }
        BufferedImage returnImage = new BufferedImage(image.getWidth(), image.getHeight(),
                pickBestTransparency(image));
        for (int y = 0; y < image.getHeight(); y++) {
            for (int x = 0; x < image.getWidth(); x++) {
                int encodedPixel = image.getRGB(x, y);

                int alpha = (encodedPixel >> 24) & 0xff;
                int red = (encodedPixel >> 16) & 0xff;
                int green = (encodedPixel >> 8) & 0xff;
                int blue = (encodedPixel) & 0xff;

                int average = (int) ((red + blue + green) / 3.0);

                // y = 0.3R + 0.59G + 0.11B luminance formula
                int value = (alpha << 24) + (average << 16) + (average << 8) + average;
                returnImage.setRGB(x, y, value);
            }
        }
        return returnImage;
    }

    private static final int[][] outlineNeighborMap = { { 0, -1, 100 }, // N
            { 1, 0, 100 }, // E
            { 0, 1, 100 }, // S
            { -1, 0, 100 } // W
            , { -1, -1 }, // NW
            { 1, -1 }, // NE
            { -1, 1 }, // SW
            { 1, 1 }, // SE
    };

    public static BufferedImage createOutline(BufferedImage sourceImage, Color color) {
        if (sourceImage == null) {
            return null;
        }
        BufferedImage image = new BufferedImage(sourceImage.getWidth() + 2, sourceImage.getHeight() + 2,
                Transparency.BITMASK);

        for (int row = 0; row < image.getHeight(); row++) {
            for (int col = 0; col < image.getWidth(); col++) {
                int sourceX = col - 1;
                int sourceY = row - 1;

                // Pixel under current location
                if (sourceX >= 0 && sourceY >= 0 && sourceX <= sourceImage.getWidth() - 1
                        && sourceY <= sourceImage.getHeight() - 1) {
                    int sourcePixel = sourceImage.getRGB(sourceX, sourceY);
                    if (sourcePixel >> 24 != 0) {
                        // Not an empty pixel, don't overwrite it
                        continue;
                    }
                }
                for (int i = 0; i < outlineNeighborMap.length; i++) {
                    int[] neighbor = outlineNeighborMap[i];
                    int x = sourceX + neighbor[0];
                    int y = sourceY + neighbor[1];

                    if (x >= 0 && y >= 0 && x <= sourceImage.getWidth() - 1 && y <= sourceImage.getHeight() - 1) {
                        if ((sourceImage.getRGB(x, y) >> 24) != 0) {
                            image.setRGB(col, row, color.getRGB());
                            break;
                        }
                    }
                }
            }
        }
        return image;
    }

    /**
     * Flip the image and return a new image
     * 
     * @param direction
     *            0-nothing, 1-horizontal, 2-vertical, 3-both
     * @return
     */
    public static BufferedImage flip(BufferedImage image, int direction) {
        BufferedImage workImage = new BufferedImage(image.getWidth(), image.getHeight(), image.getTransparency());

        boolean flipHorizontal = (direction & 1) == 1;
        boolean flipVertical = (direction & 2) == 2;

        int workW = image.getWidth() * (flipHorizontal ? -1 : 1);
        int workH = image.getHeight() * (flipVertical ? -1 : 1);
        int workX = flipHorizontal ? image.getWidth() : 0;
        int workY = flipVertical ? image.getHeight() : 0;

        Graphics2D wig = workImage.createGraphics();
        wig.drawImage(image, workX, workY, workW, workH, null);
        wig.dispose();

        return workImage;
    }

    /*
     * Jamz: Some common image utility methods
     */
    public static ImageIcon resizeImage(ImageIcon imageIcon) {
        // Default to 30x30 w/h not passed
        return resizeImage(imageIcon, 30, 30);
    }

    public static ImageIcon resizeImage(ImageIcon imageIcon, int w, int h) {
        return new ImageIcon(imageIcon.getImage().getScaledInstance(w, h, Image.SCALE_SMOOTH));
    }

    public static Image resizeImage(Image image) {
        // Default to 30x30 w/h not passed
        return resizeImage(image, 30, 30);
    }

    public static Image resizeImage(Image image, int w, int h) {
        // Default to 30x30 w/h not passed
        return image.getScaledInstance(w, h, Image.SCALE_SMOOTH);
    }
}