Search code examples
javaimageswingcolors

How to change the color of an icon based on user action?


I am writing an application where the user can change the color of the image on screen by choosing a new color. I have a simple image - only 40x40 - like this: Character Head

I have tried many things: pixel replacement, ColorModel, RGBFilter, etc. I can't figure this image stuff out. In the process of trying, I have learned about BufferedImage and I can get the .png's into that format and displayed on screen. I can turn the shape into a solid blob of color using pixel replacement, but the results are horrible.

From what I've gleaned, I want to use a ColorModel or Filter, but I am stumped. Here is a simple app that demonstrates where I am.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;


public class ChangeImageColor {
  public static void main(String[] args) {
    final ChangeColor cc = new ChangeColor();
    java.awt.EventQueue.invokeLater( new Runnable() { @Override
      public void run() { cc.setVisible( true ); }}
    ); // invokeLater
  } // main
} // ChangeImageColor

  class ChangeColor extends JFrame {
    String  CHAR_HEAD    = "res/images/char/Head.png";
    JLabel  imageHead    = new JLabel( new ImageIcon( CHAR_HEAD ) );
    JButton buttonChoose = new JButton( "Choose Color" );

    public ChangeColor() {
      this.setSize( 200, 200 );
      this.setLayout( new BorderLayout() );

      buttonChoose.addActionListener( listenerButton );

      this.add( imageHead, BorderLayout.CENTER );
      this.add( buttonChoose, BorderLayout.SOUTH );
    } // constructor

    ActionListener listenerButton = new ActionListener() {
      @Override public void actionPerformed( ActionEvent event ) {
        Color newColor = JColorChooser.showDialog( null, "Choose Color", Color.RED );
        ImageIcon icon = new ImageIcon( CHAR_HEAD );

        System.out.println( "~" + newColor );

        // *****************
        // insert code to change color of Head.png to shades of chosen color
        // *****************

        imageHead.setIcon( icon );
      } // actionPerformed
    };

  } // class

(I've never worked with dynamic colors and images, so I'm way over my head. Thanks in advance for your help.)

Edit: Here is a "before" and "after" image of what I would like after a darker color is chosen:

enter image description here enter image description here


Solution

  • Perhaps there is a more elegant and efficient version but if you have a BufferedImage you could try something like:

    BufferedImage image;
    for(int y = 0; y < image.getHeight(); y++)
        for(int x = 0; x < image.getWidth(); x++)
        {
            Color imageColor = new Color(image.getRGB(x, y));
            //mix imageColor and desired color 
            image.setRGB(x, y, imageColor.getRGB());
        }
    

    Maybe this is the real problem: Mixing the two colors can be done by multiplying them...

    Edit:

    private Color multiply(Color color1, Color color2)
    {
        float[] color1Components = color1.getRGBComponents(null);   
        float[] color2Components = color2.getRGBColorComponents(null);
        float[] newComponents = new float[3]; 
    
        for(int i = 0; i < 3; i++)
            newComponents[i] = color1Components[i] * color2Components[i];
    
        return new Color(newComponents[0], newComponents[1], newComponents[2],
            color1Components[3]);
    }