I am trying to create several similar visual styles for my programs, each with a different color theme. To do this, I have implemented the use of icons to represent the different states of JCheckBox
s and JRadioButton
s. Instead of making one full set of icons for every possible color, is there any way I can just take one set and change the hue/saturation/luminosity/alpha of the image before displaying it?
In color theory, a tint is a mixture of a color with white, which increases lightness, while a shade is a mixture with black, which increases darkness. Both processes affect the resulting color mixture's relative saturation. A tone is produced either by mixing a color with gray, or by both tinting and shading.
There is a way, but you'll have to make use of some BufferedImage transformations. Once you create them, cache them or save them off to be easily reused later. Essentially, you want to start off with a black image (source color #000000) that only uses the alpha layer to turn off pixels (also providing smooth anti-aliasing). For example, in your source image, every pixel is black, but the alpha channel differs from pixel to pixel.
First, read this article for some background information: http://www.javalobby.org/articles/ultimate-image/
Once you get done with that primer, you need to load your image into a BufferedImage:
BufferedImage loadImg = ImageUtil.loadImage("C:/Images/myimg.png");
Next you need to create a new BufferedImage to make the transform into:
public BufferedImage colorImage(BufferedImage loadImg, int red, int green, int blue) { BufferedImage img = new BufferedImage(loadImg.getWidth(), loadImg.getHeight(), BufferedImage.TRANSLUCENT); Graphics2D graphics = img.createGraphics(); Color newColor = new Color(red, green, blue, 0 /* alpha needs to be zero */); graphics.setXORMode(newColor); graphics.drawImage(loadImg, null, 0, 0); graphics.dispose(); return img; }
Essentially, the setXORMode will XOR the color you provide with the color in the source image. If the source image is black, then whatever color you provide will be written as you specify it. With the new color using "0" for the alpha channel, the original alpha channel values will be respected. The end result is the composite you are looking for.
Edit:
You can load the initial BufferedImage in one of two ways. The easiest is to use Java's newer ImageIO API: http://download.oracle.com/javase/6/docs/api/javax/imageio/ImageIO.html to load the file directly to a BufferedImage. The call would look something like this:
BufferedImage img = ImageIO.read(url);
Alternatively, you can create a method to read the image using the ToolKit.
public BufferedImage loadImage(String url) { ImageIcon icon = new ImageIcon(url); Image image = icon.getImage(); // Create empty BufferedImage, sized to Image BufferedImage buffImage = new BufferedImage( image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB); // Draw Image into BufferedImage Graphics g = buffImage.getGraphics(); g.drawImage(image, 0, 0, null); return buffImage; }
Of course, if you pay attention, we have to do the exact same thing to read the image into a buffered image as we do to tint it. In short, if you changed the signature of the colorImage
method to accept the Image
object you only need to make a couple changes to the getWidth() and getHeight() methods to get it to work.
To calculate average for each color component and keep original alpha:
public static void tint(BufferedImage image, Color color) { for (int x = 0; x < image.getWidth(); x++) { for (int y = 0; y < image.getHeight(); y++) { Color pixelColor = new Color(image.getRGB(x, y), true); int r = (pixelColor.getRed() + color.getRed()) / 2; int g = (pixelColor.getGreen() + color.getGreen()) / 2; int b = (pixelColor.getBlue() + color.getBlue()) / 2; int a = pixelColor.getAlpha(); int rgba = (a << 24) | (r << 16) | (g << 8) | b; image.setRGB(x, y, rgba); } } }
This works best for my case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With