Reputation: 263
I need to toggle on/off RGB channels of an image, but I am stuck and my code is buggy.
Can you help me figure out how to do this the right way? This is my code:
The function channels is called when 1 of 3 checkboxes has changed its state and provides the arguments which are true == selected
public void channels(boolean red, boolean green, boolean blue) {
if (this.img != null) {// checks if the image is set
char r = 0xFF, g = 0xFF, b = 0xFF;
if (red == false) {
r = 0x00;
}
if (green == false) {
g = 0x00;
}
if (blue == false) {
b = 0x00;
}
BufferedImage tmp = new BufferedImage(
img.getWidth(),
img.getHeight(),
BufferedImage.TYPE_INT_RGB);
for (int i = 0; i < img.getWidth(); i++) {
for (int j = 0; j < img.getHeight(); j++) {
int rgb = img.getRGB(i, j);
int red = (rgb >> 16) & r;
int green = (rgb >> 8) & g;
int blue = (rgb >> 0) & b;
int gbr = (red << 16) | (green << 8) | blue;// EDITED
tmp.setRGB(i, j, gbr);
}
}
img = tmp;
repaint();
} else {
//show error
}
}
Thank you for your help!
Upvotes: 0
Views: 301
Reputation: 27084
How about this optimized version, with a lot less bit shifting?
public void channels(boolean showRed, boolean showGreen, boolean showBlue) {
if (this.origImg!= null) {// checks if the image is set
int channelMask = 0xff << 24 | (showRed ? 0xff : 0) << 16 | (showGreen ? 0xff : 0) << 8 | (showBlue ? 0xff : 0);
BufferedImage tmp = new BufferedImage(origImg.getWidth(), origImg.getHeight(), BufferedImage.TYPE_INT_RGB);
for (int i = 0; i < origImg.getWidth(); i++) {
for (int j = 0; j < origImg.getHeight(); j++) {
int rgb = origImg.getRGB(i, j);
tmp.setRGB(i, j, rgb & channelMask);
}
}
img = tmp;
repaint();
} else {
//show error
}
}
A faster approach yet, would probably be to use a channeled Raster
, or at least a Raster
configuration that allows band sub-sampling (see Raster.createChild(...)
method, especially the last parameter).
LookupOp
, as mentioned by @trashgod is also a good idea, and probably faster than the getRGB()/setRGB()
approach.
Upvotes: 2
Reputation: 104483
It looks like you're shifting in the bits wrong. Shouldn't it be: int gbr = (red << 16) | (green << 8) | blue;
? You basically want to shift back in the same order as how you shifted out to begin with.
Also, once you have cleared the corresponding colour, there's no way for you to get it back. You'll need to store a copy of the original image somewhere. When it's time to turn the channel back on, simply copy the original pixel from the original image back.
Assuming that you have the original image stored somewhere as origImg
, I would modify your for
loop so that if the channel is toggled on, copy from the original image.
for (int i = 0; i < img.getWidth(); i++) {
for (int j = 0; j < img.getHeight(); j++) {
int rgb = img.getRGB(i, j);
int origRGB = origImg.getRGB(i, j);
int redPixel = red ? (origRGB >> 16) & r : (rgb >> 16) & r;
int greenPixel = green ? (origRGB >> 8) & g : (rgb >> 8) & g;
int bluePixel = blue ? origRGB & b : rgb & b;
int gbr = (redPixel << 16) | (greenPixel << 8) | bluePixel;
tmp.setRGB(i, j, gbr);
}
}
Upvotes: 1