com.t3.image.ImageUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.t3.image.ImageUtil.java

Source

/*
 * Copyright (c) 2014 tabletoptool.com team.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 * 
 * Contributors:
 *     rptools.com team - initial implementation
 *     tabletoptool.com team - further development
 */
package com.t3.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.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;

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

import org.apache.commons.io.IOUtils;

/**
 * 
 * @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() {

        @Override
        public boolean accept(File dir, String name) {

            name = name.toLowerCase();

            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 {
        try (FileInputStream is = new FileInputStream(file)) {
            return bytesToImage(IOUtils.toByteArray(is));
        }
    }

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

        try (ByteArrayOutputStream dataStream = new ByteArrayOutputStream(8192);
                InputStream in1Stream = ImageUtil.class.getClassLoader().getResourceAsStream(image);) {

            int bite;

            if (in1Stream == null) {
                throw new IOException("Image not found: " + image);
            }

            try (BufferedInputStream inStream = new BufferedInputStream(in1Stream)) {
                while ((bite = inStream.read()) >= 0) {
                    dataStream.write(bite);
                }

                return bytesToImage(dataStream.toByteArray());
            }
        }
    }

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

    public static Image bytesToImage(byte[] imageBytes) throws IOException {

        if (imageBytes == null) {
            System.out.println("WEhaah??");
        }
        Throwable exception = null;
        Image image = null;
        try {
            image = Toolkit.getDefaultToolkit().createImage(imageBytes);
            MediaTracker tracker = new MediaTracker(observer);
            tracker.addImage(image, 0);
            tracker.waitForID(0);
        } catch (Throwable t) {
            exception = t;
        }
        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;
    }
}