[Solved]Gradient fill a BufferedImage

Hi,
I’d like to make a fancy death animation for my combat system but got stuck on the second half of my idea. My combat is like the one from RPG Maker games. I got a BufferedImage of the current enemy in the center of the frame, like this:

When an enemy dies he will gradually turn white. That is the first part of my idea. But the second part is the tricky one. I want fill the BufferedImage with a transparent gradient from top to bottom.

Is there a way in plain java to do so? I already googled it but didn’t find anything :clue:


	private boolean checkFadingColor(int valueToCheck) {
		boolean allSet = true;
		int temp = 0;
		int check = 0;
		for (int i = 0; i < pixels.size(); i++) {
			if (pixels.get(i).color != valueToCheck) {
				allSet = false;
				for (int bit = 24; bit >= 0; bit -= 8) {
					if (bit == 24) {
						temp = (pixels.get(i).color & (0xFF << bit)) >> bit;
						if (temp == -1) {
							temp = 0xFF;
							pixels.get(i).color = (pixels.get(i).color & ~(0xFF << bit)) | (temp << bit);
						}
						else if (temp == 0xFF) {
							continue;
						}
						else {
							check = (valueToCheck & (0xFF << bit)) >> bit;
							if (temp != check) {
								if (check == -1) {
									check = 0xFF;
								}
								if (temp < check)
									temp++;
								else if (temp > check)
									temp--;
								pixels.get(i).color = (pixels.get(i).color & ~(0xFF << bit)) | (temp << bit);
							}
						}


					}
					else {
						check = (valueToCheck & (0xFF << bit)) >> bit;
						temp = (pixels.get(i).color & (0xFF << bit)) >> bit;
						if (temp != check) {
							if (temp < check)
								temp++;
							else if (temp > check)
								temp--;
							pixels.get(i).color = (pixels.get(i).color & ~(0xFF << bit)) | (temp << bit);
						}
					}
				}
			}
		}
		return allSet;
	}



Something like this from one of my projects?

What it does is, it fills all the pixels in a BufferedImage to a specific color, with each pixel’s R, G, and B values being added or subtracted until it is equal to the specified color’s equivalent R, G, and B values.

This method is done once per tick. You call this method every 60 ticks per second.

EDIT: Forgot to mention, you need a Boolean variable to check to see if all R, G, and B values are exactly equal to the specified color’s R, G, and B values respectively. If you don’t set this, it will break away prematurely.

You googled for it?
http://lmgtfy.com/?q=gradient+fill+java

Okay, I kinda found a way but it’s way to slow to use it…

First I get the outline of my BufferedImage


	
	/**
	 * @see http://stackoverflow.com/questions/18591749/create-java-awt-geom-area-from-points
	 */
	public static Area getOutline(BufferedImage image, Color color, boolean include, int tolerance) {
        Area area = new Area();
        for (int x=0; x<image.getWidth(); x++) {
            for (int y=0; y<image.getHeight(); y++) {
                Color pixel = new Color(image.getRGB(x,y));
                if (include) {
                    if (isIncluded(color, pixel, tolerance)) {
                        Rectangle r = new Rectangle(x,y,1,1);
                        area.add(new Area(r));
                    }
                } else {
                    if (!isIncluded(color, pixel, tolerance)) {
                        Rectangle r = new Rectangle(x,y,1,1);
                        area.add(new Area(r));
                    }
                }
            }
        }
        return area;
    }

    private static boolean isIncluded(Color target, Color pixel, int tolerance) {
        int rT = target.getRed();
        int gT = target.getGreen();
        int bT = target.getBlue();
        int rP = pixel.getRed();
        int gP = pixel.getGreen();
        int bP = pixel.getBlue();
        return(
            (rP-tolerance<=rT) && (rT<=rP+tolerance) &&
            (gP-tolerance<=gT) && (gT<=gP+tolerance) &&
            (bP-tolerance<=bT) && (bT<=bP+tolerance) );
    }

From that I create an image I can draw:



    public BufferedImage getImageWithGradientColor(BufferedImage original, Color color) {
    	area = (area == null) ? Images.getOutline(original, color, true, 10) : area;
        final BufferedImage result = new BufferedImage(original.getWidth(), original.getHeight(), BufferedImage.TYPE_INT_ARGB);
        
        Graphics2D g = result.createGraphics();
        
        g.setClip(area);

        g.setPaint(
        		new GradientPaint(	0, 0, Images.alphaColor(Color.WHITE, 0), 
        							0, (int)(original.getHeight()*(gradientHeight)), Color.WHITE) 
        	);
        
        g.fillRect(0,0,original.getHeight(),original.getWidth());
        
        g.draw(area);

        return result;
    }

I think creating a new BufferedImage every frame is slowing it down. Is there a better/cleaner way to do so?

I haven’t looked at the rest of your code, but you could probably cache [icode]result[/icode] images by size, so as to not to create a new image every frame:


private Map<Dimension, BufferedImage> imageCache; //initialized elsewhere, using a Dimension to lookup images based on size

public BufferedImage getImageWithGradientColor(BufferedImage original, Color color) {
    area = (area == null) ? Images.getOutline(original, color, true, 10) : area;
    BufferedImage result = null;
    Dimension lookup = new Dimension(original.getWidth(), original.getHeight());
    if (imageCache.contains(lookup))
        result = imageCache.get(lookup);
    else {
        result = new BufferedImage(original.getWidth(), original.getHeight(), BufferedImage.TYPE_INT_ARGB);
        imageCache.put(lookup, result);
    }
    Graphics2D g = result.createGraphics();   
    g.setClip(area);
    g.setPaint(new GradientPaint(0, 0, Images.alphaColor(Color.WHITE, 0), 0, (int) (original.getHeight() * (gradientHeight)), Color.WHITE));
    g.fillRect(0,0,original.getHeight(),original.getWidth()); 
    g.draw(area);
    g.dispose();
    return result;
}

This only works if you aren’t doing anything else with the images afterwards that depends on them being new each time, but if you simply draw the output of getImageWithGradientColor(), you should be fine to use a cache.

EDIT: You should also profile your app to see what parts are actually slow or not. It could very well be that your getOutline() is the slow part.

Okay, finally solved it!
Result is not perfect, but it works ;D

I pre render the frames and store them in an array list. I had to change isIncluded() to consider alpha values.