BufferedImageUtils.java

/*
 * SmartSprites Project
 *
 * Copyright (C) 2007-2009, Stanisław Osiński.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of  source code must  retain the above  copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following  disclaimer in  the documentation  and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of the SmartSprites Project nor the names of its contributors
 *   may  be used  to endorse  or  promote  products derived   from  this  software
 *   without specific prior written permission.
 *
 * - We kindly request that you include in the end-user documentation provided with
 *   the redistribution and/or in the software itself an acknowledgement equivalent
 *   to  the  following: "This product includes software developed by the SmartSprites
 *   Project."
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"  AND
 * ANY EXPRESS OR  IMPLIED WARRANTIES, INCLUDING,  BUT NOT LIMITED  TO, THE IMPLIED
 * WARRANTIES  OF  MERCHANTABILITY  AND  FITNESS  FOR  A  PARTICULAR  PURPOSE   ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE  FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL,  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL  DAMAGES
 * (INCLUDING, BUT  NOT LIMITED  TO, PROCUREMENT  OF SUBSTITUTE  GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS;  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND  ON
 * ANY  THEORY  OF  LIABILITY,  WHETHER  IN  CONTRACT,  STRICT  LIABILITY,  OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE)  ARISING IN ANY WAY  OUT OF THE USE  OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.carrot2.util;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.CompositeContext;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.util.HashSet;
import java.util.Set;

/**
 * Various utility methods for working with {@link BufferedImage}s.
 */
public class BufferedImageUtils {

    /**
     * Returns <code>true</code> if the provided image has partially transparent areas (alpha channel).
     *
     * @param image
     *            the image
     *
     * @return true, if successful
     */
    public static boolean hasPartialTransparency(BufferedImage image) {
        final Raster alphaRaster = image.getAlphaRaster();
        if (image.getTransparency() != Transparency.TRANSLUCENT || alphaRaster == null) {
            return false;
        }

        int[] pixels = alphaRaster.getPixels(0, 0, alphaRaster.getWidth(), alphaRaster.getHeight(), (int[]) null);
        for (int i : pixels) {
            if (i != 0 && i != 255) {
                return true;
            }
        }

        return false;
    }

    /**
     * Returns <code>true</code> if the provided image has any kind of transparent areas.
     *
     * @param image
     *            the image
     *
     * @return true, if successful
     */
    public static boolean hasTransparency(BufferedImage image) {
        final Raster alphaRaster = image.getAlphaRaster();
        if (image.getTransparency() != Transparency.TRANSLUCENT || alphaRaster == null) {
            return false;
        }

        int[] pixels = alphaRaster.getPixels(0, 0, alphaRaster.getWidth(), alphaRaster.getHeight(), (int[]) null);
        for (int i : pixels) {
            if (i != 255) {
                return true;
            }
        }

        return false;
    }

    /**
     * Returns the number of distinct colors (excluding transparency) in the <code>image</code>.
     *
     * @param image
     *            the image
     *
     * @return the int
     */
    public static int countDistinctColors(BufferedImage image) {
        return getDistinctColors(image).length;
    }

    /**
     * Returns the <code>image</code>'s distinct colors in an RGB format, discarding transparency information.
     *
     * @param image
     *            the image
     *
     * @return the distinct colors
     */
    public static int[] getDistinctColors(BufferedImage image) {
        return getDistinctColors(image, 0);
    }

    /**
     * Returns the <code>image</code>'s distinct colors in an RGB format, discarding transparency information. Adds
     * <code>padding</code> empty slots at the beginning of the returned array.
     *
     * @param image
     *            the image
     * @param padding
     *            the padding
     *
     * @return the distinct colors
     */
    public static int[] getDistinctColors(BufferedImage image, int padding) {
        final int width = image.getWidth();
        final int height = image.getHeight();

        final Set<Integer> colors = new HashSet<>();

        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                final int pixel = image.getRGB(x, y);

                // Count only colors for which alpha is not fully transparent
                if ((pixel & 0xff000000) != 0x00000000) {
                    colors.add(Integer.valueOf(pixel & 0x00ffffff));
                }
            }
        }

        final int[] colorMap = new int[colors.size() + padding];
        int index = padding;
        for (Integer color : colors) {
            colorMap[index] = color;
            index++;
        }

        return colorMap;
    }

    /**
     * Returns a two dimensional array of the <code>image</code>'s RGB values, including transparency.
     *
     * @param image
     *            the image
     *
     * @return the rgb
     */
    public static int[][] getRgb(BufferedImage image) {
        final int width = image.getWidth();
        final int height = image.getHeight();

        final int[][] rgb = new int[width][height];

        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                rgb[x][y] = image.getRGB(x, y);
            }
        }

        return rgb;
    }

    /**
     * Performs matting of the <code>source</code> image using <code>matteColor</code>. Matting is rendering partial
     * transparencies using solid color as if the original image was put on top of a bitmap filled with
     * <code>matteColor</code>.
     *
     * @param source
     *            the source
     * @param matteColor
     *            the matte color
     *
     * @return the buffered image
     */
    public static BufferedImage matte(BufferedImage source, Color matteColor) {
        final int width = source.getWidth();
        final int height = source.getHeight();

        // A workaround for possibly different custom image types we can get:
        // draw a copy of the image
        final BufferedImage sourceConverted = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
        sourceConverted.getGraphics().drawImage(source, 0, 0, null);

        final BufferedImage matted = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);

        final BufferedImage matte = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
        final int matteRgb = matteColor.getRGB();
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                matte.setRGB(x, y, matteRgb);
            }
        }

        CompositeContext context = AlphaComposite.DstOver.createContext(matte.getColorModel(),
                sourceConverted.getColorModel(), null);
        context.compose(matte.getRaster(), sourceConverted.getRaster(), matted.getRaster());

        return matted;
    }

    /**
     * Draws <code>image</code> on the <code>canvas</code> placing the top left corner of <code>image</code> at
     * <code>x</code> / <code>y</code> offset from the top left corner of <code>canvas</code>.
     *
     * @param image
     *            the image
     * @param canvas
     *            the canvas
     * @param x
     *            the x
     * @param y
     *            the y
     */
    public static void drawImage(BufferedImage image, BufferedImage canvas, int x, int y) {
        final int[] imgRGB = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth());
        canvas.setRGB(x, y, image.getWidth(), image.getHeight(), imgRGB, 0, image.getWidth());
    }

    /**
     * Instantiates a new buffered image utils.
     */
    private BufferedImageUtils() {
    }
}