How to replace color with another color in BufferedImage
So, I have an image file that has a volcano on it. Everything else is 0xFFFF00FF (opaque magenta). I want to replace every pixel that contains this color with 0 (transparent). So far, my method looks like this:
public static BufferedImage replace(BufferedImage image, int target, int preferred) {
int width = image.getWidth();
int height = image.getHeight();
BufferedImage newImage = new BufferedImage(width, height, image.getType());
int color;
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
color = image.getRGB(i, j);
if (color == target) {
newImage.setRGB(i, j, preferred);
}
else {
newImage.setRGB(i, j, color);
}
}
}
return newImage;
}
This works great but seems to be VERY slow. I've seen someone do it differently, but I have no idea what's going on. If anyone knows a better way to do this, I'd really love to hear it.
source to share
To avoid repeating pixels across, change the base ColorModel . Here's an example. Below is a snippet where the author takes the original BufferedImage and applies the new color model.
private static BufferedImage createImage() {
int width = 200;
int height = 200;
// Generate the source pixels for our image
// Lets just keep it to a simple blank image for now
byte[] pixels = new byte[width * height];
DataBuffer dataBuffer = new DataBufferByte(pixels, width*height, 0);
SampleModel sampleModel = new SinglePixelPackedSampleModel(
DataBuffer.TYPE_BYTE, width, height, new int[] {(byte)0xf});
WritableRaster raster = Raster.createWritableRaster(
sampleModel, dataBuffer, null);
return new BufferedImage(createColorModel(0), raster, false, null);
}
private static ColorModel createColorModel(int n) {
// Create a simple color model with all values mapping to
// a single shade of gray
// nb. this could be improved by reusing the byte arrays
byte[] r = new byte[16];
byte[] g = new byte[16];
byte[] b = new byte[16];
for (int i = 0; i < r.length; i++) {
r[i] = (byte) n;
g[i] = (byte) n;
b[i] = (byte) n;
}
return new IndexColorModel(4, 16, r, g, b);
}
private BufferedImage image = createImage();
image = new BufferedImage(createColorModel(e.getX()), image.getRaster(), false, null);
source to share
While I haven't had a chance to test this completely yet , using LookupOp can benefit from speeding up:
public class ColorMapper
extends LookupTable {
private final int[] from;
private final int[] to;
public ColorMapper(Color from,
Color to) {
super(0, 4);
this.from = new int[] {
from.getRed(),
from.getGreen(),
from.getBlue(),
from.getAlpha(),
};
this.to = new int[] {
to.getRed(),
to.getGreen(),
to.getBlue(),
to.getAlpha(),
};
}
@Override
public int[] lookupPixel(int[] src,
int[] dest) {
if (dest == null) {
dest = new int[src.length];
}
int[] newColor = (Arrays.equals(src, from) ? to : src);
System.arraycopy(newColor, 0, dest, 0, newColor.length);
return dest;
}
}
Using it is as easy as creating a LookupOp:
Color from = Color.decode("#ff00ff");
Color to = new Color(0, true);
BufferedImageOp lookup = new LookupOp(new ColorMapper(from, to), null);
BufferedImage convertedImage = lookup.filter(image, null);
source to share
You can get an array of pixels[]
buffered image like so
int[] pixels = ((DataBufferInt) newImg().getDataBuffer()).getData();
and then set the colors this way
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
color = pixels[y * width + x];
if (color == target) {
pixels[y * width + x] = preferred;
}
else {
pixels[y * width + x] = color;
}
}
}
This is a slight speedup using setRGB()
source to share