Search code examples
javaswingawtimageicongrayscale

Is there a way to gray out a portion of an ImageIcon in Java?


I am trying to gray out a portion of the picture. normalImage is 100% colored while grayedImage is 0% colored. How can I generate grayedImage to be, let's say, 50% colored on the left, and 50% grayed on the right, or vice versa?

import javax.swing.*;
import java.awt.*;

public class TestPicture {

    public static void main(String[] args) {
        JFrame jf = new JFrame("Test Picture");
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.setLayout(new BorderLayout());
        jf.setPreferredSize(new Dimension(600, 300));
        jf.setVisible(true);
        jf.pack();

        JPanel jp = new JPanel();
        JLabel jl1 = new JLabel();
        JLabel jl2 = new JLabel();
        ImageIcon normalImage = new ImageIcon("normalImage.png");
        ImageIcon grayedImage = new ImageIcon(GrayFilter.createDisabledImage(normalImage.getImage()));
        jl1.setIcon(normalImage);
        jl2.setIcon(grayedImage);
        jp.add(jl1);
        jp.add(jl2);
        jf.add(jp);

        jf.repaint();
        jf.revalidate();
    }
}

Solution

  • What if I wanted to do something more complex, such as graying out an image in a clock form? Are you familiar with League of Legends? In the game, there are skills that have cool down. As time passes, it undarkens itself going clockwise

    You could...

    Use BufferedImage#subImage to generate a "progress" slice of the color image and paint this over the top of the gray scale image, but this is only going to give you a linear progression, instead...

    You could...

    Use a AlphaComposite to generate a mask of the progress, painting this over the top of the grey scale image

    Masked Progress

    import java.awt.AlphaComposite;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.GraphicsConfiguration;
    import java.awt.GraphicsEnvironment;
    import java.awt.LinearGradientPaint;
    import java.awt.RenderingHints;
    import java.awt.Transparency;
    import java.awt.color.ColorSpace;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.geom.Arc2D;
    import java.awt.geom.Point2D;
    import java.awt.image.BufferedImage;
    import java.awt.image.ColorConvertOp;
    import java.io.IOException;
    import java.time.Duration;
    import java.time.Instant;
    import javax.imageio.ImageIO;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.Timer;
    
    public class Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        public Test() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        JFrame frame = new JFrame();
                        frame.add(new TestPane());
                        frame.pack();
                        frame.setLocationRelativeTo(null);
                        frame.setVisible(true);
                    } catch (IOException ex) {
                        ex.printStackTrace();
                    }
                }
            });
        }
    
        public class TestPane extends JPanel {
    
            private BufferedImage color;
            private BufferedImage gray;
    
            private Duration duration = Duration.ofSeconds(5);
            private Instant startedAt;
    
            private double progress;
    
            public TestPane() throws IOException {
                color = ImageIO.read(getClass().getResource("Progress.png"));
                // You don't need to do this, but my original image was transparent
                // and it was hard to see the fill effect
                color = fillImage(color);
                gray = ImageIO.read(getClass().getResource("Progress.png"));
                gray = fillImage(gray);
                ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
                op.filter(gray, gray);
    
                Timer timer = new Timer(5, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (startedAt == null) {
                            startedAt = Instant.now();
                        }
                        Instant now = Instant.now();
                        Duration runTime = Duration.between(startedAt, now);
                        progress = (double) runTime.toMillis() / (double) duration.toMillis();
                        if (progress >= 1.0) {
                            ((Timer) (e.getSource())).stop();
                            progress = 1.0;
                        }
                        System.out.println(progress);
                        repaint();
                    }
                });
                timer.start();
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(200, 200);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                RenderingHints hints = new RenderingHints(
                        RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON
                );
                g2d.setRenderingHints(hints);
                g2d.drawImage(progressEffect(), 0, 0, this);
                g2d.dispose();
            }
    
            protected BufferedImage progressEffect() {
                int width = color.getWidth();
                int height = color.getHeight();
    
                BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
                Graphics2D og = img.createGraphics();
                RenderingHints hints = new RenderingHints(
                        RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON
                );
                og.setRenderingHints(hints);
                og.drawImage(gray, 0, 0, this);
                og.drawImage(maskedEffect(), 0, 0, this);
                og.dispose();
                return img;
            }
    
            protected BufferedImage maskedEffect() {
                int width = color.getWidth();
                int height = color.getHeight();
                BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
    
                Graphics2D og = img.createGraphics();
                RenderingHints hints = new RenderingHints(
                        RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON
                );
                og.setRenderingHints(hints);
                // This will "overfill" the are so it occupies the entire image
                Arc2D.Double expose = new Arc2D.Double(-(width / 2d), -(height / 2d), width * 2d, height * 2d, 90, -(360.0d * progress), Arc2D.PIE);
                og.fill(expose);
                og.setComposite(AlphaComposite.SrcIn);
                og.drawImage(color, 0, 0, this);
                og.dispose();
                return img;
            }
    
            protected BufferedImage fillImage(BufferedImage original) {
                int width = original.getWidth();
                int height = original.getHeight();
                BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
                Graphics2D g2d = img.createGraphics();
                RenderingHints hints = new RenderingHints(
                        RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON
                );
                g2d.setRenderingHints(hints);
                LinearGradientPaint lgp = new LinearGradientPaint(
                        new Point2D.Double(0, 0),
                        new Point2D.Double(0, height),
                        new float[]{0f, 1f},
                        new Color[]{Color.YELLOW, Color.RED});
                g2d.setPaint(lgp);
                g2d.fillRect(0, 0, width, height);
                g2d.drawImage(original, 0, 0, this);
                g2d.dispose();
                return img;
            }
    
        }
    
        public static GraphicsConfiguration getGraphicsConfiguration() {
            return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
        }
    
        public static BufferedImage createCompatibleImage(int width, int height, int transparency) {
            BufferedImage image = getGraphicsConfiguration().createCompatibleImage(width, height, transparency);
            image.coerceData(true);
            return image;
        }
    
        public static void applyQualityRenderingHints(Graphics2D g2d) {
            g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
            g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
            g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
        }
    
        public static BufferedImage generateMask(BufferedImage imgSource, Color color, float alpha) {
            int imgWidth = imgSource.getWidth();
            int imgHeight = imgSource.getHeight();
    
            BufferedImage imgMask = createCompatibleImage(imgWidth, imgHeight, Transparency.TRANSLUCENT);
            Graphics2D g2 = imgMask.createGraphics();
            applyQualityRenderingHints(g2);
    
            g2.drawImage(imgSource, 0, 0, null);
            g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, alpha));
            g2.setColor(color);
    
            g2.fillRect(0, 0, imgSource.getWidth(), imgSource.getHeight());
            g2.dispose();
    
            return imgMask;
        }
    }