I'm trying to write a method for my game that will take an image, the hex value for the old color, and the hex value for the new color and then convert all pixels of the old color to the new color. Right now, the method paints the entire image the new color, as if the if statement is not working at all. This the method:
private void convertColors(BufferedImage img, int oldColor, int newColor)
{
Graphics g = img.getGraphics();
g.setColor(new Color(newColor));
Color old = new Color(oldColor);
for(int x = 0; x < img.getWidth(); x++)
{
for(int y = 0; y < img.getHeight(); y++)
{
Color tmp = new Color(img.getRGB(x, y));
if(tmp.equals(old));
{
System.out.println("Temp=" + tmp.toString() + "Old=" + old.toString() + "New=" + g.getColor().toString());
g.fillRect(x, y, 1, 1);
}
}
}
g.dispose();
}
*The hex for oldColor is 0xFFFFFF (white) and for newColor is 0xFF0000 (red).
Using the println method I get these kind of results:
Temp=java.awt.Color[r=0,g=0,b=0]Old=java.awt.Color[r=255,g=255,b=255]New=java.awt.Color[r=255,g=0,b=0]
Temp=java.awt.Color[r=255,g=255,b=255]Old=java.awt.Color[r=255,g=255,b=255]New=java.awt.Color[r=255,g=0,b=0]
The scond line looks correct, the temp color and the old are the same, but that is obviously not the case with the first. I have also tried creating a new BufferedImage and copy over the pixels but that leaves the same result... Does the equals method not work as I think it does or does this entire method just not work and there's a better way to do this? Thanks for you help in advance.
Just remove the ; after if(tmp.equals(old)).
Otherwise you compare the colors and do nothing after the comparsion and always choose the new color.
Besides that you need to reorganize your code a little bit to make it slightly more efficient:
Graphics g = img.getGraphics();
g.setColor(new Color(newColor));
for(int x = 0; x < img.getWidth(); x++) {
for(int y = 0; y < img.getHeight(); y++) {
if(img.getRGB(x, y) == oldColor) {//check if pixel color matches old color
g.fillRect(x, y, 1, 1);//fill the pixel with the right color
}
}
}
g.dispose();
Just because i was interested in the topic: relying on image filters you could do all that via:
class ColorSwapFilter extends RGBImageFilter {
int newColor, oldColor;
public ColorSwapFilter(int newColor, int oldColor) {
canFilterIndexColorModel = true;
this.newColor = newColor;
this.oldColor = oldColor;
}
#Override
public int filterRGB(int x, int y, int rgb) {
return rgb == oldColor ? newColor : oldColor;
}
}
which should be called via
BufferedImage img;//your image
ColorSwapFilter filter = new ColorSwapFilter(...,...);//your colors to be swapped.
ImageProducer producer = img.getSource();
producer = new FilteredImageSource(producer, filter);
Image im = Toolkit.getDefaultToolkit().createImage(producer);
You have a semicolon direct after your if statement if(tmp.equals(old));
This esentially tells Java that your if statement has only one command associated with it, and that command is a single semicolon, effectively meaning "do nothing". If you remove it, it will restore the condition on the block of code beneath it, which right now is just running every time regardless of the condition.
I got it to work; this is the working convertColors method:
private BufferedImage convertColors(BufferedImage img, int oldColor, int newColor)
{
int [] pixels = new int [img.getWidth() * img.getHeight()];
img.getRGB(0, 0, img.getWidth(), img.getHeight(), pixels, 0, img.getWidth());
Color old = new Color(oldColor);
Color newC = new Color(newColor);
for(int x = 0; x < img.getWidth(); x++)
{
for(int y = 0; y < img.getHeight(); y++)
{
Color tmp = new Color(pixels[x + y * img.getWidth()]);
if(tmp.equals(old))
{
pixels[x + y * img.getWidth()] = newC.getRGB();
}
}
}
img.setRGB(0, 0, img.getWidth(), img.getHeight(), pixels, 0, img.getWidth());
return newImg;
}
Related
I am working on a school project with swing-Java. I am trying to draw image with drawRect(x,y,width,height) method (later I will do changes to pixels, that's why I need to draw pixels one-by-one). So, I have written this code:
private void drawImage(Graphics graphics) {
int width = image.getWidth();
int height = image.getHeight();
for (int w = 0; w < width; w++) {
for (int h = 0; h < height; h++) {
int color = image.getRGB(w, h);
graphics.setColor(new Color(color));
graphics.drawRect(w, h, 1, 1);
}
}
}
This is the result:
As you can see, some pixels are drawn with white color. But when I use this code,
graphics.drawImage(image, 0, 0, this);
Image is drawn as it should:
Why image.getRGB(x,y) returns white color at some points? What am I missing here?
I was steered over to this forum when I asked my lecturer for advice on a piece of code for a group project. The general idea is that there are two images on top of each other, the user can wipe the top image away to reveal the one underneath.
Using some other projects from this forum, I have managed to get the basics running, however I am struggling to get the code to the starting point once the user lets go of the mouse.
I would also appreciate any advice regarding how to convert this to using a touch screen. I have looked at the multitouch code within the processing app, however it does not allow me to add images to this, and if I try and use the computer software it does not seem to like the multitouch. Is there any way around this?
The code I currently have is below, I will be greatful so any advice or input- thanks in advance!
PImage img, front;
int xstart, ystart, xend, yend;
int ray;
void setup()
{
size(961, 534);
img = loadImage("back.jpg");
front = loadImage("front.jpg");
xstart = 0;
ystart = 0;
xend = img.width;
yend = img.height;
ray = 50;
}
void draw()
{
{
img.loadPixels();
front.loadPixels();
// loop over image pixels
for (int x = xstart; x < xend; x++)
{
for (int y = ystart; y < yend; y++ )
{
int loc = x + y*img.width;
float dd = dist(mouseX, mouseY, x, y);
// pixels distance less than ray
if (mousePressed && dd < 50)
{
// set equal pixel
front.pixels[loc] = img.pixels[loc];
}
else
{
if (!mousePressed)
{
// reset- this is what I have not been able to work as of yet
front.pixels[loc] = ;
}
}
}
}
img.updatePixels();
front.updatePixels();
// show front image
image(front, 0, 0);
}
}
I recommend to use a mask instead of changing the pixels of the image. Create an empty image and associated it as mask to the the image:
img = loadImage("back.jpg");
front = loadImage("front.jpg");
mask = createImage(img.width, img.height, RGB);
img.mask(mask);
If you now draw both images, then you can only "see" the front image:
image(front, 0, 0);
image(img, 0, 0);
Set the color of the mask (255, 255, 255) instead of changing the pixel of front:
mask.pixels[loc] = color(255, 255, 255);
and reapply the mask to the image
img.mask(mask);
When the mouse button is released, the pixels of the mask have to be changed back to (0, 0, 0) or simply create a new and empty mask:
mask = createImage(img.width, img.height, RGB);
See the example where I applied the suggestions to your original code:
PImage img, front, mask;
int xstart, ystart, xend, yend;
int ray;
void setup() {
size(961, 534);
img = loadImage("back.jpg");
front = loadImage("front.jpg");
mask = createImage(img.width, img.height, RGB);
img.mask(mask);
xstart = 0;
ystart = 0;
xend = img.width;
yend = img.height;
ray = 50;
}
void draw() {
img.loadPixels();
front.loadPixels();
// loop over image pixels
for (int x = xstart; x < xend; x++) {
for (int y = ystart; y < yend; y++ ) {
int loc = x + y*img.width;
float dd = dist(mouseX, mouseY, x, y);
if (mousePressed && dd < 50) {
mask.pixels[loc] = color(255, 255, 255);
}
else {
if (!mousePressed) {
//mask = createImage(img.width, img.height, RGB);
mask.pixels[loc] = color(0, 0, 0);
}
}
}
}
mask.updatePixels();
img.mask(mask);
// show front image
image(front, 0, 0);
image(img, 0, 0);
}
I am trying to automatically change the color for a set of icons.
Every icon has a white filled layer and the other part is transparent.
Here is an example: (in this case it's green, just to make it visible)
I tried to do the following:
private static BufferedImage colorImage(BufferedImage image) {
int width = image.getWidth();
int height = image.getHeight();
for (int xx = 0; xx < width; xx++) {
for (int yy = 0; yy < height; yy++) {
Color originalColor = new Color(image.getRGB(xx, yy));
System.out.println(xx + "|" + yy + " color: " + originalColor.toString() + "alpha: "
+ originalColor.getAlpha());
if (originalColor.equals(Color.WHITE) && originalColor.getAlpha() == 255) {
image.setRGB(xx, yy, Color.BLUE.getRGB());
}
}
}
return image;
}
The problem I have is that every pixel I get has the same value:
32|18 color: java.awt.Color[r=255,g=255,b=255]alpha: 255
So my result is just a colored square.
How can I achieve to change the color of the non-transparent parts only? And why is it, that all pixels have even the same alpha value? I guess that's my main problem: That the alpha value isn't read correctly.
Why it doesn't work, I don't know, this will.
This changes all the pixles to blue, maintaining their alpha values...
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class TestColorReplace {
public static void main(String[] args) {
try {
BufferedImage img = colorImage(ImageIO.read(new File("NWvnS.png")));
ImageIO.write(img, "png", new File("Test.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
private static BufferedImage colorImage(BufferedImage image) {
int width = image.getWidth();
int height = image.getHeight();
WritableRaster raster = image.getRaster();
for (int xx = 0; xx < width; xx++) {
for (int yy = 0; yy < height; yy++) {
int[] pixels = raster.getPixel(xx, yy, (int[]) null);
pixels[0] = 0;
pixels[1] = 0;
pixels[2] = 255;
raster.setPixel(xx, yy, pixels);
}
}
return image;
}
}
The problem is, that
Color originalColor = new Color(image.getRGB(xx, yy));
discards all the alpha values. Instead you have to use
Color originalColor = new Color(image.getRGB(xx, yy), true);
to keep alpha values available.
If your bitmap is already set in an ImageView, just do :
imageView.setColorFilter(Color.RED);
to set all non transparent pixels to red.
Since we will always be replacing only the first three bands of the RGB pixel, more effective way to achieve the same without unnecessary allocation of new arrays would be:
private static void colorImageAndPreserveAlpha(BufferedImage img, Color c) {
WritableRaster raster = img.getRaster();
int[] pixel = new int[] {c.getRed(),c.getGreen(),c.getBlue()};
for (int x = 0; x < raster.getWidth(); x++)
for (int y = 0; y < raster.getHeight(); y++)
for (int b = 0; b < pixel.length; b++)
raster.setSample(x,y,b,pixel[b]);
}
I have this code:
{
Robot robot = new Robot();
Color inputColor = new Color();
Rectangle rectangle = new Rectangle(0, 0, 1365, 770);
BufferedImage image = robot.createScreenCapture(rectangle);
for(int x = 0; x < rectangle.getWidth(); x++)
{
for (int y = 0; y < rectangle.getHeight(); y++)
{
if (image.getRGB(x, y) == inputColor.getRGB())
{
return 1;
break;
}
}
}
}
it is supposed to, and does, take a screenshot and find in it a pixel specified by the inputColor. However the program requirements have changed, and now it needs to find a string of pixels 5 long that match a given string. Is there an easy way to specify this with the existing code, or will I need to change it? I mean, can I keep the existing code and define inputColor as a string with the values of the 5 pixels, or do I need to change the whole algorithm?
I think something like this would work. Not the best efficiency, but its a bone to chew.
int[] pixels = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth())) {
for (int i = 0; i < pixels.length; i++) {
if (Array.equals(search, Array.copyOfRange(pixels, i, i + search.length)) {
//found it
}
}
search would be an array of integers(your colors).
I have two BufferedImages I loaded in from pngs. The first contains an image, the second an alpha mask for the image.
I want to create a combined image from the two, by applying the alpha mask. My google-fu fails me.
I know how to load/save the images, I just need the bit where I go from two BufferedImages to one BufferedImage with the right alpha channel.
I'm too late with this answer, but maybe it is of use for someone anyway. This is a simpler and more efficient version of Michael Myers' method:
public void applyGrayscaleMaskToAlpha(BufferedImage image, BufferedImage mask)
{
int width = image.getWidth();
int height = image.getHeight();
int[] imagePixels = image.getRGB(0, 0, width, height, null, 0, width);
int[] maskPixels = mask.getRGB(0, 0, width, height, null, 0, width);
for (int i = 0; i < imagePixels.length; i++)
{
int color = imagePixels[i] & 0x00ffffff; // Mask preexisting alpha
int alpha = maskPixels[i] << 24; // Shift blue to alpha
imagePixels[i] = color | alpha;
}
image.setRGB(0, 0, width, height, imagePixels, 0, width);
}
It reads all the pixels into an array at the beginning, thus requiring only one for-loop. Also, it directly shifts the blue byte to the alpha (of the mask color), instead of first masking the red byte and then shifting it.
Like the other methods, it assumes both images have the same dimensions.
I played recently a bit with this stuff, to display an image over another one, and to fade an image to gray.
Also masking an image with a mask with transparency (my previous version of this message!).
I took my little test program and tweaked it a bit to get the wanted result.
Here are the relevant bits:
TestMask() throws IOException
{
m_images = new BufferedImage[3];
m_images[0] = ImageIO.read(new File("E:/Documents/images/map.png"));
m_images[1] = ImageIO.read(new File("E:/Documents/images/mapMask3.png"));
Image transpImg = TransformGrayToTransparency(m_images[1]);
m_images[2] = ApplyTransparency(m_images[0], transpImg);
}
private Image TransformGrayToTransparency(BufferedImage image)
{
ImageFilter filter = new RGBImageFilter()
{
public final int filterRGB(int x, int y, int rgb)
{
return (rgb << 8) & 0xFF000000;
}
};
ImageProducer ip = new FilteredImageSource(image.getSource(), filter);
return Toolkit.getDefaultToolkit().createImage(ip);
}
private BufferedImage ApplyTransparency(BufferedImage image, Image mask)
{
BufferedImage dest = new BufferedImage(
image.getWidth(), image.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = dest.createGraphics();
g2.drawImage(image, 0, 0, null);
AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.DST_IN, 1.0F);
g2.setComposite(ac);
g2.drawImage(mask, 0, 0, null);
g2.dispose();
return dest;
}
The remainder just display the images in a little Swing panel.
Note that the mask image is gray levels, black becoming full transparency, white becoming full opaque.
Although you have resolved your problem, I though I could share my take on it. It uses a slightly more Java-ish method, using standard classes to process/filter images.
Actually, my method uses a bit more memory (making an additional image) and I am not sure it is faster (measuring respective performances could be interesting), but it is slightly more abstract.
At least, you have choice! :-)
Your solution could be improved by fetching the RGB data more than one pixel at a time(see http://java.sun.com/javase/6/docs/api/java/awt/image/BufferedImage.html), and by not creating three Color objects on every iteration of the inner loop.
final int width = image.getWidth();
int[] imgData = new int[width];
int[] maskData = new int[width];
for (int y = 0; y < image.getHeight(); y++) {
// fetch a line of data from each image
image.getRGB(0, y, width, 1, imgData, 0, 1);
mask.getRGB(0, y, width, 1, maskData, 0, 1);
// apply the mask
for (int x = 0; x < width; x++) {
int color = imgData[x] & 0x00FFFFFF; // mask away any alpha present
int maskColor = (maskData[x] & 0x00FF0000) << 8; // shift red into alpha bits
color |= maskColor;
imgData[x] = color;
}
// replace the data
image.setRGB(0, y, width, 1, imgData, 0, 1);
}
For those who are using alpha in the original image.
I wrote this code in Koltin, the key point here is that if you have the alpha on your original image you need to multiply these channels.
Koltin Version:
val width = this.width
val imgData = IntArray(width)
val maskData = IntArray(width)
for(y in 0..(this.height - 1)) {
this.getRGB(0, y, width, 1, imgData, 0, 1)
mask.getRGB(0, y, width, 1, maskData, 0, 1)
for (x in 0..(this.width - 1)) {
val maskAlpha = (maskData[x] and 0x000000FF)/ 255f
val imageAlpha = ((imgData[x] shr 24) and 0x000000FF) / 255f
val rgb = imgData[x] and 0x00FFFFFF
val alpha = ((maskAlpha * imageAlpha) * 255).toInt() shl 24
imgData[x] = rgb or alpha
}
this.setRGB(0, y, width, 1, imgData, 0, 1)
}
Java version (just translated from Kotlin)
int width = image.getWidth();
int[] imgData = new int[width];
int[] maskData = new int[width];
for (int y = 0; y < image.getHeight(); y ++) {
image.getRGB(0, y, width, 1, imgData, 0, 1);
mask.getRGB(0, y, width, 1, maskData, 0, 1);
for (int x = 0; x < image.getWidth(); x ++) {
//Normalize (0 - 1)
float maskAlpha = (maskData[x] & 0x000000FF)/ 255f;
float imageAlpha = ((imgData[x] >> 24) & 0x000000FF) / 255f;
//Image without alpha channel
int rgb = imgData[x] & 0x00FFFFFF;
//Multiplied alpha
int alpha = ((int) ((maskAlpha * imageAlpha) * 255)) << 24;
//Add alpha to image
imgData[x] = rgb | alpha;
}
image.setRGB(0, y, width, 1, imgData, 0, 1);
}
Actually, I've figured it out. This is probably not a fast way of doing it, but it works:
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
Color c = new Color(image.getRGB(x, y));
Color maskC = new Color(mask.getRGB(x, y));
Color maskedColor = new Color(c.getRed(), c.getGreen(), c.getBlue(),
maskC.getRed());
resultImg.setRGB(x, y, maskedColor.getRGB());
}
}