All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alee.utils.ImageUtils Maven / Gradle / Ivy

The newest version!
/*
 * This file is part of WebLookAndFeel library.
 *
 * WebLookAndFeel library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * WebLookAndFeel library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with WebLookAndFeel library.  If not, see .
 */

package com.alee.utils;

import com.alee.api.annotations.NotNull;
import com.alee.api.annotations.Nullable;
import com.alee.api.resource.Resource;
import com.alee.api.ui.DisabledCopySupplier;
import com.alee.api.ui.TransparentCopySupplier;
import com.alee.graphics.filters.ShadowFilter;
import com.alee.utils.collection.ImmutableList;
import com.mortennobel.imagescaling.ResampleOp;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.geom.Area;
import java.awt.image.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.*;

/**
 * @author Mikle Garin
 */
public final class ImageUtils
{
    /**
     * Viewable image formats.
     */
    public static final List SUPPORTED_IMAGES = new ImmutableList (
            "png", "apng", "gif", "agif", "jpg", "jpeg", "jpeg2000", "bmp"
    );

    /**
     * Grayscale image filter.
     */
    private static final ColorConvertOp GRAYSCALE_FILTER = new ColorConvertOp ( ColorSpace.getInstance ( ColorSpace.CS_GRAY ), null );

    /**
     * Disabled icons cache.
     */
    protected static final Map DISABLED_ICONS_CACHE = new WeakHashMap ( 50 );

    /**
     * Transparent icons cache.
     */
    protected static final Map> TRANSPARENT_ICONS_CACHE = new WeakHashMap> ( 10 );

    /**
     * Private constructor to avoid instantiation.
     */
    private ImageUtils ()
    {
        throw new UtilityException ( "Utility classes are not meant to be instantiated" );
    }

    /**
     * Returns whether or not loading of the image with extension mentioned in the specified name is supported.
     *
     * @param name image name with extension in it
     * @return {@code true} if loading of the image with extension mentioned in the specified name is supported, {@code false} otherwise
     */
    public static boolean isImageSupported ( @Nullable final String name )
    {
        return SUPPORTED_IMAGES.contains ( FileUtils.getFileExtPart ( name, false ).toLowerCase ( Locale.ROOT ) );
    }

    /**
     * Returns whether or not specified {@link BufferedImage} pixel is fully transparent.
     *
     * @param bufferedImage {@link BufferedImage}
     * @param x             {@link BufferedImage} pixel X index
     * @param y             {@link BufferedImage} pixel Y index
     * @return {@code true} if specified {@link BufferedImage} pixel is fully transparent, {@code false} otherwise
     */
    public static boolean isTransparent ( @NotNull final BufferedImage bufferedImage, final int x, final int y )
    {
        return ( bufferedImage.getRGB ( x, y ) >> 24 & 0xFF ) > 0;
    }

    /**
     * Returns new empty {@link BufferedImage} compatible with default screen {@link GraphicsConfiguration}.
     *
     * @param bufferedImage {@link BufferedImage} to use size and transparency settings from
     * @return new empty {@link BufferedImage} compatible with default screen {@link GraphicsConfiguration}
     */
    @NotNull
    public static BufferedImage createCompatibleImage ( @NotNull final BufferedImage bufferedImage )
    {
        return createCompatibleImage ( bufferedImage.getWidth (), bufferedImage.getHeight (), bufferedImage.getTransparency () );
    }

    /**
     * Returns new {@link BufferedImage} compatible with default screen {@link GraphicsConfiguration}.
     *
     * @param width  {@link BufferedImage} width
     * @param height {@link BufferedImage} height
     * @return new {@link BufferedImage} compatible with default screen {@link GraphicsConfiguration}
     */
    @NotNull
    public static BufferedImage createCompatibleImage ( final int width, final int height )
    {
        return SystemUtils.getGraphicsConfiguration ().createCompatibleImage ( width, height );
    }

    /**
     * Returns new {@link BufferedImage} compatible with default screen {@link GraphicsConfiguration}.
     *
     * @param width        new {@link BufferedImage} width
     * @param height       new {@link BufferedImage} height
     * @param transparency new {@link BufferedImage} {@link Transparency} type
     * @return new {@link BufferedImage} compatible with default screen {@link GraphicsConfiguration}
     */
    @NotNull
    public static BufferedImage createCompatibleImage ( final int width, final int height, final int transparency )
    {
        return SystemUtils.getGraphicsConfiguration ().createCompatibleImage ( width, height, transparency );
    }

    /**
     * Returns whether or not specified {@link BufferedImage} is compatible with default screen {@link GraphicsConfiguration}.
     *
     * @param bufferedImage {@link BufferedImage} to check
     * @return {@code true} if specified {@link BufferedImage} is compatible with default screen {@link GraphicsConfiguration},
     * {@code false} otherwise
     */
    public static boolean isCompatibleImage ( @NotNull final BufferedImage bufferedImage )
    {
        return bufferedImage.getColorModel ().equals ( SystemUtils.getGraphicsConfiguration ().getColorModel () );
    }

    /**
     * Returns {@link BufferedImage} loaded from the specified {@link Resource}.
     * Resulting {@link BufferedImage} is also made compatible with default screen {@link GraphicsConfiguration}.
     *
     * @param resource {@link Resource} to load {@link BufferedImage} from
     * @return {@link BufferedImage} loaded from the specified {@link Resource}
     */
    @NotNull
    public static BufferedImage loadCompatibleImage ( @NotNull final Resource resource )
    {
        try
        {
            return toCompatibleImage ( loadBufferedImage ( resource ) );
        }
        catch ( final Exception e )
        {
            throw new UtilityException ( "Unable to load compatible BufferedImage: " + resource, e );
        }
    }

    /**
     * Returns {@link BufferedImage} compatible with default screen {@link GraphicsConfiguration}.
     * Specified {@link BufferedImage} might be copied or simply returned "as is" as a result of calling this method.
     *
     * @param bufferedImage {@link BufferedImage} to create copy of that will be compatible with default screen {@link
     *                      GraphicsConfiguration}
     * @return {@link BufferedImage} compatible with default screen {@link GraphicsConfiguration}.
     */
    @NotNull
    public static BufferedImage toCompatibleImage ( @NotNull final BufferedImage bufferedImage )
    {
        final BufferedImage compatibleImage;
        if ( isCompatibleImage ( bufferedImage ) )
        {
            compatibleImage = bufferedImage;
        }
        else
        {
            compatibleImage = createCompatibleImage ( bufferedImage );
            final Graphics2D g2d = compatibleImage.createGraphics ();
            g2d.drawImage ( bufferedImage, 0, 0, null );
            g2d.dispose ();
        }
        return compatibleImage;
    }

    /**
     * Returns {@link BufferedImage} loaded from the specified {@link Resource}.
     *
     * @param resource {@link Resource} to load {@link BufferedImage} from
     * @return {@link BufferedImage} loaded from the specified {@link Resource}
     */
    @NotNull
    public static BufferedImage loadBufferedImage ( @NotNull final Resource resource )
    {
        return loadBufferedImage ( resource.getInputStream () );
    }

    /**
     * Returns {@link BufferedImage} loaded from the specified {@link InputStream}.
     *
     * @param inputStream {@link InputStream} to load {@link BufferedImage} from
     * @return {@link BufferedImage} loaded from the specified {@link InputStream}
     */
    @NotNull
    public static BufferedImage loadBufferedImage ( @NotNull final InputStream inputStream )
    {
        try
        {
            return ImageIO.read ( inputStream );
        }
        catch ( final Exception e )
        {
            throw new UtilityException ( "Unable to load BufferedImage: " + inputStream, e );
        }
    }

    /**
     * Returns {@link BufferedImage} converted from the specified {@link Image}.
     *
     * @param image {@link Image} to convert
     * @return {@link BufferedImage} converted from the specified {@link Image}
     */
    @Nullable
    public static BufferedImage toBufferedImage ( @Nullable final Image image )
    {
        final BufferedImage bufferedImage;
        if ( image instanceof BufferedImage )
        {
            bufferedImage = ( BufferedImage ) image;
        }
        else if ( image != null && image.getWidth ( null ) > 0 && image.getHeight ( null ) > 0 )
        {
            final BufferedImage bi = createCompatibleImage ( image.getWidth ( null ), image.getHeight ( null ), Transparency.TRANSLUCENT );
            final Graphics2D g2d = bi.createGraphics ();
            g2d.drawImage ( image, 0, 0, null );
            g2d.dispose ();
            bufferedImage = bi;
        }
        else
        {
            bufferedImage = null;
        }
        return bufferedImage;
    }

    /**
     * Returns {@link BufferedImage} converted from the specified {@link Image}.
     *
     * @param image {@link Image} to convert
     * @return {@link BufferedImage} converted from the specified {@link Image}
     */
    @NotNull
    public static BufferedImage toNonNullBufferedImage ( @NotNull final Image image )
    {
        final BufferedImage bufferedImage;
        if ( image instanceof BufferedImage )
        {
            bufferedImage = ( BufferedImage ) image;
        }
        else if ( image.getWidth ( null ) > 0 && image.getHeight ( null ) > 0 )
        {
            final BufferedImage bi = createCompatibleImage ( image.getWidth ( null ), image.getHeight ( null ), Transparency.TRANSLUCENT );
            final Graphics2D g2d = bi.createGraphics ();
            g2d.drawImage ( image, 0, 0, null );
            g2d.dispose ();
            bufferedImage = bi;
        }
        else
        {
            throw new UtilityException ( "Unable to convert Image to BufferedImage: " + image );
        }
        return bufferedImage;
    }

    /**
     * Returns {@link BufferedImage} converted from the specified {@link Icon}.
     *
     * @param icon {@link Icon} to convert
     * @return {@link BufferedImage} converted from the specified {@link Icon}
     */
    @Nullable
    public static BufferedImage toBufferedImage ( @Nullable final Icon icon )
    {
        return icon != null ? toNonNullBufferedImage ( icon ) : null;
    }

    /**
     * Returns {@link BufferedImage} converted from the specified {@link Icon}.
     *
     * @param icon {@link Icon} to convert
     * @return {@link BufferedImage} converted from the specified {@link Icon}
     */
    @NotNull
    public static BufferedImage toNonNullBufferedImage ( @NotNull final Icon icon )
    {
        final BufferedImage bufferedImage;
        if ( icon instanceof ImageIcon && ( ( ImageIcon ) icon ).getImage () != null )
        {
            bufferedImage = toNonNullBufferedImage ( ( ( ImageIcon ) icon ).getImage () );
        }
        else
        {
            bufferedImage = paintToBufferedImage ( icon );
        }
        return bufferedImage;
    }

    /**
     * Returns {@link BufferedImage} with the specified {@link Icon} painted on it.
     *
     * @param icon {@link Icon} to paint
     * @return {@link BufferedImage} with the specified {@link Icon} painted on it
     */
    @NotNull
    private static BufferedImage paintToBufferedImage ( @NotNull final Icon icon )
    {
        final BufferedImage bufferedImage = createCompatibleImage ( icon.getIconWidth (), icon.getIconHeight (), Transparency.TRANSLUCENT );
        final Graphics2D g2d = bufferedImage.createGraphics ();
        icon.paintIcon ( null, g2d, 0, 0 );
        g2d.dispose ();
        return bufferedImage;
    }

    /**
     * Returns {@link BufferedImage} converted from the specified {@link RenderedImage}.
     *
     * @param renderedImage {@link RenderedImage} to convert
     * @return {@link BufferedImage} converted from the specified {@link RenderedImage}
     */
    @Nullable
    public static BufferedImage toBufferedImage ( @Nullable final RenderedImage renderedImage )
    {
        return renderedImage != null ? toNonNullBufferedImage ( renderedImage ) : null;
    }

    /**
     * Returns {@link BufferedImage} converted from the specified {@link RenderedImage}.
     *
     * @param renderedImage {@link RenderedImage} to convert
     * @return {@link BufferedImage} converted from the specified {@link RenderedImage}
     */
    @NotNull
    public static BufferedImage toNonNullBufferedImage ( @NotNull final RenderedImage renderedImage )
    {
        final BufferedImage bufferedImage;
        if ( renderedImage instanceof BufferedImage )
        {
            bufferedImage = ( BufferedImage ) renderedImage;
        }
        else
        {
            final ColorModel cm = renderedImage.getColorModel ();
            final int width = renderedImage.getWidth ();
            final int height = renderedImage.getHeight ();
            final WritableRaster raster = cm.createCompatibleWritableRaster ( width, height );
            final boolean isAlphaPremultiplied = cm.isAlphaPremultiplied ();
            final Hashtable properties = new Hashtable ();
            final String[] keys = renderedImage.getPropertyNames ();
            if ( keys != null )
            {
                for ( final String key : keys )
                {
                    properties.put ( key, renderedImage.getProperty ( key ) );
                }
            }

            bufferedImage = new BufferedImage ( cm, raster, isAlphaPremultiplied, properties );
            renderedImage.copyData ( raster );
        }
        return bufferedImage;
    }

    /**
     * Returns {@link BufferedImage} containing a copy of currently displayed {@link Icon} frame.
     *
     * @param icon {@link Icon} to create {@link BufferedImage} for
     * @return {@link BufferedImage} containing a copy of currently displayed {@link Icon} frame
     */
    @NotNull
    public static BufferedImage copyToBufferedImage ( @NotNull final Icon icon )
    {
        return copyToBufferedImage ( toNonNullBufferedImage ( icon ) );
    }

    /**
     * Returns {@link BufferedImage} containing a copy of {@link Image}.
     *
     * @param image {@link Image} to create {@link BufferedImage} for
     * @return {@link BufferedImage} containing a copy of {@link Image}
     */
    @NotNull
    public static BufferedImage copyToBufferedImage ( @NotNull final Image image )
    {
        return copyToBufferedImage ( toNonNullBufferedImage ( image ) );
    }

    /**
     * Returns {@link BufferedImage} containing a copy of {@link BufferedImage}.
     *
     * @param bufferedImage {@link BufferedImage} to create {@link BufferedImage} for
     * @return {@link BufferedImage} containing a copy of {@link BufferedImage}
     */
    @NotNull
    public static BufferedImage copyToBufferedImage ( @NotNull final BufferedImage bufferedImage )
    {
        final BufferedImage newImage = createCompatibleImage ( bufferedImage );
        final Graphics2D g2d = newImage.createGraphics ();
        g2d.drawImage ( bufferedImage, 0, 0, null );
        g2d.dispose ();
        return newImage;
    }

    /**
     * Returns {@link ImageIcon} loaded from the specified {@link Resource}.
     * This method doesn't use {@link ImageIO} because loaded image might not be represented by a single {@link BufferedImage}.
     * For example GIF icons consist of many frames that are handled internally by {@link ImageIcon}.
     *
     * @param resource {@link Resource} to load {@link ImageIcon} from
     * @return {@link ImageIcon} loaded from the specified {@link Resource}
     */
    @NotNull
    public static ImageIcon loadImageIcon ( @NotNull final Resource resource )
    {
        return loadImageIcon ( resource.getInputStream () );
    }

    /**
     * Returns {@link ImageIcon} loaded from the specified {@link Resource}.
     * This method doesn't use {@link ImageIO} because loaded image might not be represented by a single {@link BufferedImage}.
     * For example GIF icons consist of many frames that are handled internally by {@link ImageIcon}.
     *
     * @param inputStream {@link InputStream} to load {@link ImageIcon} from
     * @return {@link ImageIcon} loaded from the specified {@link Resource}
     */
    @NotNull
    public static ImageIcon loadImageIcon ( @NotNull final InputStream inputStream )
    {
        return new ImageIcon ( IOUtils.toByteArray ( inputStream ) );
    }

    /**
     * Returns {@link ImageIcon} converted from the specified {@link Image}.
     *
     * @param image {@link Image} to convert
     * @return {@link ImageIcon} converted from the specified {@link Image}
     */
    @Nullable
    public static ImageIcon toImageIcon ( @Nullable final Image image )
    {
        return image != null ? toNonNullImageIcon ( image ) : null;
    }

    /**
     * Returns {@link ImageIcon} converted from the specified {@link Image}.
     *
     * @param image {@link Image} to convert
     * @return {@link ImageIcon} converted from the specified {@link Image}
     */
    @NotNull
    public static ImageIcon toNonNullImageIcon ( @NotNull final Image image )
    {
        return new ImageIcon ( image );
    }

    /**
     * Returns {@link ImageIcon} converted from the specified {@link Icon}.
     *
     * @param icon {@link Icon} to convert
     * @return {@link ImageIcon} converted from the specified {@link Icon}
     */
    @Nullable
    public static ImageIcon toImageIcon ( @Nullable final Icon icon )
    {
        return icon != null ? toNonNullImageIcon ( icon ) : null;
    }

    /**
     * Returns {@link ImageIcon} converted from the specified {@link Icon}.
     *
     * @param icon {@link Icon} to convert
     * @return {@link ImageIcon} converted from the specified {@link Icon}
     */
    @NotNull
    public static ImageIcon toNonNullImageIcon ( @NotNull final Icon icon )
    {
        final ImageIcon imageIcon;
        if ( icon instanceof ImageIcon )
        {
            imageIcon = ( ImageIcon ) icon;
        }
        else
        {
            imageIcon = new ImageIcon ( toNonNullBufferedImage ( icon ) );
        }
        return imageIcon;
    }

    /**
     * Returns new or cached copy of specified {@link Icon} made look disabled.
     *
     * @param icon {@link Icon} to retrieve disabled copy for
     * @return new or cached copy of specified {@link Icon} made look disabled
     */
    @NotNull
    public static Icon getDisabledCopy ( @NotNull final Icon icon )
    {
        final Icon disabledIcon;
        if ( DISABLED_ICONS_CACHE.containsKey ( icon ) )
        {
            disabledIcon = DISABLED_ICONS_CACHE.get ( icon );
        }
        else
        {
            synchronized ( DISABLED_ICONS_CACHE )
            {
                if ( DISABLED_ICONS_CACHE.containsKey ( icon ) )
                {
                    disabledIcon = DISABLED_ICONS_CACHE.get ( icon );
                }
                else
                {
                    disabledIcon = createDisabledCopy ( icon );
                    DISABLED_ICONS_CACHE.put ( icon, disabledIcon );
                }
            }
        }
        return disabledIcon;
    }

    /**
     * Returns {@link Icon} that is copy of the specified {@link Icon} made look disabled.
     *
     * @param icon {@link Icon} to create disabled copy for
     * @return {@link Icon} that is copy of the specified {@link Icon} made look disabled
     */
    @NotNull
    public static Icon createDisabledCopy ( @NotNull final Icon icon )
    {
        final Icon disabledCopy;
        if ( icon instanceof DisabledCopySupplier )
        {
            disabledCopy = ( ( DisabledCopySupplier ) icon ).createDisabledCopy ();
        }
        else
        {
            disabledCopy = new ImageIcon ( createDisabledCopy ( toNonNullBufferedImage ( icon ) ) );
        }
        return disabledCopy;
    }

    /**
     * Returns {@link BufferedImage} that is copy of the specified {@link Image} made look disabled.
     *
     * @param image {@link Image} to create disabled copy for
     * @return {@link BufferedImage} that is copy of the specified {@link Image} made look disabled
     */
    @NotNull
    public static BufferedImage createDisabledCopy ( @NotNull final Image image )
    {
        return createDisabledCopy ( toNonNullBufferedImage ( image ) );
    }

    /**
     * Returns {@link BufferedImage} that is copy of the specified {@link BufferedImage} made look disabled.
     *
     * @param bufferedImage {@link BufferedImage} to create disabled copy for
     * @return {@link BufferedImage} that is copy of the specified {@link BufferedImage} made look disabled
     */
    @NotNull
    public static BufferedImage createDisabledCopy ( @NotNull final BufferedImage bufferedImage )
    {
        // Creating image copy
        final BufferedImage disabledCopy = createCompatibleImage ( bufferedImage );

        // Painting original image as semi-transparent
        final Graphics2D g2d = disabledCopy.createGraphics ();
        GraphicsUtils.setupAlphaComposite ( g2d, 0.7f );
        g2d.drawImage ( bufferedImage, 0, 0, null );
        g2d.dispose ();

        // Applying grayscale filter
        GRAYSCALE_FILTER.filter ( disabledCopy, disabledCopy );

        return disabledCopy;
    }

    /**
     * Returns new or cached copy of specified {@link Icon} made look disabled.
     *
     * @param icon    {@link Icon} to retrieve disabled copy for
     * @param opacity opacity value, must be between 0 and 1
     * @return new or cached copy of specified {@link Icon} made look disabled
     */
    @NotNull
    public static Icon getTransparentCopy ( @NotNull final Icon icon, final float opacity )
    {
        final Map transparentCopies;
        if ( TRANSPARENT_ICONS_CACHE.containsKey ( icon ) )
        {
            transparentCopies = TRANSPARENT_ICONS_CACHE.get ( icon );
        }
        else
        {
            synchronized ( TRANSPARENT_ICONS_CACHE )
            {
                if ( TRANSPARENT_ICONS_CACHE.containsKey ( icon ) )
                {
                    transparentCopies = TRANSPARENT_ICONS_CACHE.get ( icon );
                }
                else
                {
                    transparentCopies = new HashMap ( 1 );
                    TRANSPARENT_ICONS_CACHE.put ( icon, transparentCopies );
                }
            }
        }
        final Icon transparentIcon;
        if ( transparentCopies.containsKey ( opacity ) )
        {
            transparentIcon = transparentCopies.get ( opacity );
        }
        else
        {
            synchronized ( TRANSPARENT_ICONS_CACHE )
            {
                if ( transparentCopies.containsKey ( opacity ) )
                {
                    transparentIcon = transparentCopies.get ( opacity );
                }
                else
                {
                    transparentIcon = createTransparentCopy ( icon, opacity );
                    transparentCopies.put ( opacity, transparentIcon );
                }
            }
        }
        return transparentIcon;
    }

    /**
     * Returns {@link Icon} that is copy of the specified {@link Icon} made look disabled.
     *
     * @param icon    {@link Icon} to create disabled copy for
     * @param opacity opacity value, must be between 0 and 1
     * @return {@link Icon} that is copy of the specified {@link Icon} made look disabled
     */
    @NotNull
    public static Icon createTransparentCopy ( @NotNull final Icon icon, final float opacity )
    {
        final Icon transparentCopy;
        if ( icon instanceof TransparentCopySupplier )
        {
            transparentCopy = ( ( TransparentCopySupplier ) icon ).createTransparentCopy ( opacity );
        }
        else
        {
            transparentCopy = new ImageIcon ( createTransparentCopy ( toNonNullBufferedImage ( icon ), opacity ) );
        }
        return transparentCopy;
    }

    /**
     * Returns {@link BufferedImage} that is copy of the specified {@link Image} made semi-transparent.
     *
     * @param image   {@link Image} to create semi-transparent copy for
     * @param opacity opacity value, must be between 0 and 1
     * @return {@link BufferedImage} that is copy of the specified {@link Image} made semi-transparent
     */
    @NotNull
    public static BufferedImage createTransparentCopy ( @NotNull final Image image, final float opacity )
    {
        return createTransparentCopy ( toNonNullBufferedImage ( image ), opacity );
    }

    /**
     * Returns {@link BufferedImage} that is copy of the specified {@link BufferedImage} made semi-transparent.
     *
     * @param bufferedImage {@link BufferedImage} to create semi-transparent copy for
     * @param opacity       opacity value, must be between 0 and 1
     * @return {@link BufferedImage} that is copy of the specified {@link BufferedImage} made semi-transparent
     */
    @NotNull
    public static BufferedImage createTransparentCopy ( @NotNull final BufferedImage bufferedImage, final float opacity )
    {
        final BufferedImage transparentCopy = createCompatibleImage (
                bufferedImage.getWidth (),
                bufferedImage.getHeight (),
                Transparency.TRANSLUCENT
        );

        final Graphics2D g2d = transparentCopy.createGraphics ();
        GraphicsUtils.setupAlphaComposite ( g2d, opacity );
        g2d.drawImage ( bufferedImage, 0, 0, null );
        g2d.dispose ();

        return transparentCopy;
    }

    /**
     * Returns new {@link BufferedImage} with the specified {@link Icon} painted on it within the specified {@link Shape}.
     *
     * @param icon  {@link Icon} to cut
     * @param shape {@link Shape} to cut
     * @return new {@link BufferedImage} with the specified {@link Icon} painted on it within the specified {@link Shape}
     */
    public static BufferedImage cutImage ( @NotNull final Icon icon, @NotNull final Shape shape )
    {
        return cutImage ( toNonNullBufferedImage ( icon ), shape );
    }

    /**
     * Returns new {@link BufferedImage} with the specified {@link Image} painted on it within the specified {@link Shape}.
     *
     * @param image {@link Image} to cut
     * @param shape {@link Shape} to cut
     * @return new {@link BufferedImage} with the specified {@link Image} painted on it within the specified {@link Shape}
     */
    public static BufferedImage cutImage ( @NotNull final Image image, @NotNull final Shape shape )
    {
        return cutImage ( toNonNullBufferedImage ( image ), shape );
    }

    /**
     * Returns new {@link BufferedImage} with the specified {@link BufferedImage} painted on it within the specified {@link Shape}.
     *
     * @param bufferedImage {@link Image} to cut
     * @param shape         {@link Shape} to cut
     * @return new {@link BufferedImage} with the specified {@link BufferedImage} painted on it within the specified {@link Shape}
     */
    public static BufferedImage cutImage ( @NotNull final BufferedImage bufferedImage, @NotNull final Shape shape )
    {
        final BufferedImage cutImage = createCompatibleImage ( bufferedImage );
        final Graphics2D g2d = cutImage.createGraphics ();
        GraphicsUtils.setupAntialias ( g2d );
        g2d.setPaint ( Color.WHITE );
        g2d.fill ( shape );
        g2d.setComposite ( AlphaComposite.getInstance ( AlphaComposite.SRC_IN ) );
        g2d.drawImage ( bufferedImage, 0, 0, null );
        g2d.dispose ();
        return cutImage;
    }

    /**
     * Returns thumbnail {@link BufferedImage} of the specified {@link BufferedImage}.
     * Note that this method can only scale specified {@link BufferedImage} down, it will not scale it up.
     * Also n that resulting {@link BufferedImage} will not have width and height larger than specified maximum ones, but might be less.
     *
     * @param bufferedImage {@link BufferedImage} to create thumbnail for
     * @param maxSize       maximum thumbnail {@link BufferedImage} size
     * @return thumbnail {@link BufferedImage} of the specified {@link BufferedImage}
     */
    @NotNull
    public static BufferedImage createImageThumbnail ( @NotNull final BufferedImage bufferedImage, @NotNull final Dimension maxSize )
    {
        return createImageThumbnail ( bufferedImage, maxSize.width, maxSize.height );
    }

    /**
     * Returns thumbnail {@link BufferedImage} of the specified {@link BufferedImage}.
     * Note that this method can only scale specified {@link BufferedImage} down, it will not scale it up.
     * Also n that resulting {@link BufferedImage} will not have width and height larger than specified maximum ones, but might be less.
     *
     * @param bufferedImage {@link BufferedImage} to create thumbnail for
     * @param maxWidth      maximum thumbnail {@link BufferedImage} width
     * @param maxHeight     maximum thumbnail {@link BufferedImage} height
     * @return thumbnail {@link BufferedImage} of the specified {@link BufferedImage}
     */
    @NotNull
    public static BufferedImage createImageThumbnail ( @NotNull final BufferedImage bufferedImage, final int maxWidth, final int maxHeight )
    {
        final BufferedImage preview;
        if ( bufferedImage.getWidth () > maxWidth || bufferedImage.getHeight () > maxHeight )
        {
            if ( maxHeight * ( ( float ) bufferedImage.getWidth () / bufferedImage.getHeight () ) <= maxWidth )
            {
                preview = createImageThumbnail ( bufferedImage, Math.max ( maxHeight,
                        Math.round ( maxHeight * ( ( float ) bufferedImage.getWidth () / bufferedImage.getHeight () ) ) ) );
            }
            else
            {
                preview = createImageThumbnail ( bufferedImage, Math.max ( maxWidth,
                        Math.round ( maxWidth * ( ( float ) bufferedImage.getHeight () / bufferedImage.getWidth () ) ) ) );
            }
        }
        else
        {
            preview = bufferedImage;
        }
        return preview;
    }

    /**
     * Returns thumbnail {@link BufferedImage} of the specified {@link BufferedImage}.
     * Note that this method can only scale specified {@link BufferedImage} down, it will not scale it up.
     * Also note that resulting {@link BufferedImage} will not have width and height larger than specified maximum, but might be less.
     *
     * @param bufferedImage {@link BufferedImage} to create thumbnail for
     * @param maxSize       maximum thumbnail {@link BufferedImage} width and height
     * @return thumbnail {@link BufferedImage} of the specified {@link BufferedImage}
     */
    @NotNull
    public static BufferedImage createImageThumbnail ( @NotNull final BufferedImage bufferedImage, final int maxSize )
    {
        final BufferedImage preview;
        if ( bufferedImage.getWidth () <= maxSize && bufferedImage.getHeight () <= maxSize )
        {
            // Simply return source image, but make sure it has compatible type
            preview = toCompatibleImage ( bufferedImage );
        }
        else
        {
            // Calculate resulting width and height
            final int width;
            final int height;
            if ( bufferedImage.getWidth () > bufferedImage.getHeight () )
            {
                width = maxSize;
                height = Math.round ( ( float ) maxSize * bufferedImage.getHeight () / bufferedImage.getWidth () );
            }
            else if ( bufferedImage.getWidth () < bufferedImage.getHeight () )
            {
                height = maxSize;
                width = Math.round ( ( float ) maxSize * bufferedImage.getWidth () / bufferedImage.getHeight () );
            }
            else
            {
                width = height = maxSize;
            }

            // Creating scaled down image
            if ( width >= 3 && height >= 3 )
            {
                // Using Java Image Scaling library approach
                final ResampleOp scaleOp = new ResampleOp ( width, height );
                preview = scaleOp.filter ( bufferedImage, createCompatibleImage ( bufferedImage ) );
            }
            else
            {
                // Scaling down a very small image
                preview = createCompatibleImage ( Math.max ( 1, width ), Math.max ( 1, height ), Transparency.TRANSLUCENT );
                final Graphics2D g2d = preview.createGraphics ();
                GraphicsUtils.setupImageQuality ( g2d );
                g2d.drawImage ( bufferedImage, 0, 0, width, height, null );
                g2d.dispose ();
            }
        }
        return preview;
    }

    /**
     * Returns dominant {@link Color} for the specified {@link BufferedImage}.
     *
     * @param image {@link BufferedImage} to determine dominant {@link Color} for
     * @return dominant {@link Color} for the specified {@link BufferedImage}
     */
    @NotNull
    public static Color getDominantColor ( @NotNull final BufferedImage image )
    {
        int red = 0;
        int green = 0;
        int blue = 0;
        for ( int i = 0; i < image.getWidth (); i++ )
        {
            for ( int j = 0; j < image.getHeight (); j++ )
            {
                final int rgb = image.getRGB ( i, j );
                red += rgb >> 16 & 0xFF;
                green += rgb >> 8 & 0xFF;
                blue += rgb & 0xFF;
            }
        }
        final int count = image.getWidth () * image.getHeight ();
        return new Color ( red / count, green / count, blue / count );
    }

    /**
     * Returns new {@link ImageIcon} with specified {@link Icon} painted on it and rotated 90 degrees clockwise.
     *
     * @param icon {@link Icon} to rotate
     * @return new {@link ImageIcon} with specified {@link Icon} painted on it and rotated 90 degrees clockwise.
     */
    @NotNull
    public static ImageIcon rotateIcon90CW ( @NotNull final Icon icon )
    {
        return new ImageIcon ( rotateImage90CW ( toNonNullBufferedImage ( icon ) ) );
    }

    /**
     * Returns new {@link BufferedImage} with specified {@link Image} painted on it and rotated 90 degrees clockwise.
     *
     * @param image {@link Image} to rotate
     * @return new {@link BufferedImage} with specified {@link Image} painted on it and rotated 90 degrees clockwise.
     */
    @NotNull
    public static BufferedImage rotateImage90CW ( @NotNull final Image image )
    {
        return rotateImage90CW ( toNonNullBufferedImage ( image ) );
    }

    /**
     * Returns new {@link BufferedImage} with specified {@link BufferedImage} painted on it and rotated 90 degrees clockwise.
     *
     * @param bufferedImage {@link BufferedImage} to rotate
     * @return new {@link BufferedImage} with specified {@link BufferedImage} painted on it and rotated 90 degrees clockwise.
     */
    @NotNull
    public static BufferedImage rotateImage90CW ( @NotNull final BufferedImage bufferedImage )
    {
        final BufferedImage rotatedImage = createCompatibleImage ( bufferedImage );
        final Graphics2D g2d = rotatedImage.createGraphics ();
        g2d.translate ( bufferedImage.getHeight (), 0 );
        g2d.rotate ( Math.PI / 2 );
        g2d.drawImage ( bufferedImage, 0, 0, null );
        g2d.dispose ();
        return rotatedImage;
    }

    /**
     * Returns new {@link ImageIcon} with specified {@link Icon} painted on it and rotated 90 degrees counter-clockwise.
     *
     * @param icon {@link Icon} to rotate
     * @return new {@link ImageIcon} with specified {@link Icon} painted on it and rotated 90 degrees counter-clockwise.
     */
    @NotNull
    public static ImageIcon rotateIcon90CCW ( @NotNull final Icon icon )
    {
        return new ImageIcon ( rotateImage90CCW ( toBufferedImage ( icon ) ) );
    }

    /**
     * Returns new {@link BufferedImage} with specified {@link Image} painted on it and rotated 90 degrees counter-clockwise.
     *
     * @param image {@link Image} to rotate
     * @return new {@link BufferedImage} with specified {@link Image} painted on it and rotated 90 degrees counter-clockwise.
     */
    @NotNull
    public static BufferedImage rotateImage90CCW ( @NotNull final Image image )
    {
        return rotateImage90CCW ( toNonNullBufferedImage ( image ) );
    }

    /**
     * Returns new {@link BufferedImage} with specified {@link BufferedImage} painted on it and rotated 90 degrees counter-clockwise.
     *
     * @param bufferedImage {@link BufferedImage} to rotate
     * @return new {@link BufferedImage} with specified {@link BufferedImage} painted on it and rotated 90 degrees counter-clockwise.
     */
    @NotNull
    public static BufferedImage rotateImage90CCW ( @NotNull final BufferedImage bufferedImage )
    {
        final BufferedImage rotatedImage = createCompatibleImage ( bufferedImage );
        final Graphics2D g2d = rotatedImage.createGraphics ();
        g2d.translate ( 0, bufferedImage.getWidth () );
        g2d.rotate ( -Math.PI / 2 );
        g2d.drawImage ( bufferedImage, 0, 0, null );
        g2d.dispose ();
        return rotatedImage;
    }

    /**
     * Returns new {@link ImageIcon} with specified {@link Icon} painted on it and rotated 180 degrees.
     *
     * @param icon {@link Icon} to rotate
     * @return new {@link ImageIcon} with specified {@link Icon} painted on it and rotated 180 degrees.
     */
    @NotNull
    public static ImageIcon rotateIcon180 ( @NotNull final Icon icon )
    {
        return new ImageIcon ( rotateImage180 ( toBufferedImage ( icon ) ) );
    }

    /**
     * Returns new {@link BufferedImage} with specified {@link Image} painted on it and rotated 180 degrees.
     *
     * @param image {@link Image} to rotate
     * @return new {@link BufferedImage} with specified {@link Image} painted on it and rotated 180 degrees.
     */
    @NotNull
    public static BufferedImage rotateImage180 ( @NotNull final Image image )
    {
        return rotateImage180 ( toNonNullBufferedImage ( image ) );
    }

    /**
     * Returns new {@link BufferedImage} with specified {@link BufferedImage} painted on it and rotated 180 degrees.
     *
     * @param bufferedImage {@link BufferedImage} to rotate
     * @return new {@link BufferedImage} with specified {@link BufferedImage} painted on it and rotated 180 degrees.
     */
    @NotNull
    public static BufferedImage rotateImage180 ( @NotNull final BufferedImage bufferedImage )
    {
        final BufferedImage rotatedImage = createCompatibleImage ( bufferedImage );
        final Graphics2D g2d = rotatedImage.createGraphics ();
        g2d.translate ( bufferedImage.getWidth (), bufferedImage.getHeight () );
        g2d.rotate ( Math.PI );
        g2d.drawImage ( bufferedImage, 0, 0, null );
        g2d.dispose ();
        return rotatedImage;
    }

    /**
     * Returns shadow image based on provided shape.
     *
     * @param width       shadow image width
     * @param height      shadow image height
     * @param shape       shadow shape
     * @param shadowWidth shadow width
     * @param opacity     shadow opacity
     * @param clip        whether or not should clip shadow form
     * @return shadow image based on provided shape
     */
    @NotNull
    public static BufferedImage createShadowImage ( final int width, final int height, @NotNull final Shape shape, final int shadowWidth,
                                                    final float opacity, final boolean clip )
    {
        // Creating template image
        final BufferedImage bi = createCompatibleImage ( width, height, Transparency.TRANSLUCENT );
        final Graphics2D ig = bi.createGraphics ();
        GraphicsUtils.setupAntialias ( ig );
        ig.setPaint ( Color.BLACK );
        ig.fill ( shape );
        ig.dispose ();

        // Creating shadow image
        final ShadowFilter sf = new ShadowFilter ( shadowWidth, 0, 0, opacity );
        final BufferedImage shadow = sf.filter ( bi, null );

        // Clipping shadow image
        if ( clip )
        {
            final Graphics2D g2d = shadow.createGraphics ();
            GraphicsUtils.setupAntialias ( g2d );
            g2d.setComposite ( AlphaComposite.getInstance ( AlphaComposite.SRC_IN ) );
            g2d.setPaint ( ColorUtils.transparent () );
            g2d.fill ( shape );
            g2d.dispose ();
        }

        return shadow;
    }

    /**
     * Returns shadow image based on provided shape.
     *
     * @param width       shadow image width
     * @param shape       shadow shape
     * @param shadowWidth shadow width
     * @param opacity     shadow opacity
     * @return shadow image based on provided shape
     */
    @NotNull
    public static BufferedImage createInnerShadowImage ( final int width, @NotNull final Shape shape, final int shadowWidth,
                                                         final float opacity )
    {
        // Creating template image
        final BufferedImage bi = createCompatibleImage ( width, width, Transparency.TRANSLUCENT );
        final Graphics2D ig = bi.createGraphics ();
        GraphicsUtils.setupAntialias ( ig );
        final Area area = new Area ( new Rectangle ( 0, 0, width, width ) );
        area.exclusiveOr ( new Area ( shape ) );
        ig.setPaint ( Color.BLACK );
        ig.fill ( area );
        ig.dispose ();

        // Creating shadow image
        final ShadowFilter sf = new ShadowFilter ( shadowWidth, 0, 0, opacity );
        final BufferedImage shadow = sf.filter ( bi, null );

        // Clipping shadow image
        final Graphics2D g2d = shadow.createGraphics ();
        GraphicsUtils.setupAntialias ( g2d );
        g2d.setComposite ( AlphaComposite.getInstance ( AlphaComposite.SRC_IN ) );
        g2d.setPaint ( ColorUtils.transparent () );
        g2d.fill ( area );
        g2d.dispose ();

        return shadow.getSubimage ( shadowWidth, shadowWidth, width - shadowWidth * 2, width - shadowWidth * 2 );
    }

    /**
     * Returns {@link BufferedImage} decoded from Base64 string.
     *
     * @param imageData image data decode
     * @return {@link BufferedImage} decoded from Base64 string
     */
    @Nullable
    public static BufferedImage decodeImage ( @Nullable final String imageData )
    {
        BufferedImage image = null;
        if ( TextUtils.notEmpty ( imageData ) )
        {
            final String rawImageData = EncryptionUtils.base64decode ( imageData );
            if ( rawImageData != null )
            {
                final byte[] bytes = rawImageData.getBytes ();
                final ByteArrayInputStream bis = new ByteArrayInputStream ( bytes );
                try
                {
                    image = ImageIO.read ( bis );
                    bis.close ();
                }
                catch ( final Exception ex )
                {
                    final String msg = "Unable to decode image icon";
                    LoggerFactory.getLogger ( ImageUtils.class ).error ( msg, ex );
                    try
                    {
                        bis.close ();
                    }
                    catch ( final IOException ignored )
                    {
                        //
                    }
                }
            }
        }
        return image;
    }

    /**
     * Returns image data encoded in Base64 string.
     *
     * @param image image to encode
     * @return image data encoded in Base64 string
     */
    @Nullable
    public static String encodeImage ( @Nullable final BufferedImage image )
    {
        String imageString = null;
        if ( image != null )
        {
            final ByteArrayOutputStream bos = new ByteArrayOutputStream ();
            try
            {
                ImageIO.write ( image, "png", bos );
                imageString = EncryptionUtils.base64encode ( new String ( bos.toByteArray () ) );
                bos.close ();
            }
            catch ( final IOException ex )
            {
                final String msg = "Unable to encode image icon";
                LoggerFactory.getLogger ( ImageUtils.class ).error ( msg, ex );
                try
                {
                    bos.close ();
                }
                catch ( final IOException ignored )
                {
                    //
                }
            }
        }
        return imageString;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy