[java] Alpha Blending Revisit

Started by
7 comments, last by howard 23 years, 8 months ago
Among the 8 alpha blending techniques available in Java, which one requires the least processing power from the CPU and RAM? I wonder about this because I am planning on adding fading effects to my game when the player dies, and sadly the game eats up a great deal of system resources during a fade done with an image filter and the SRC_OVER AlphaComposite. Examples are greatly appreciated as well. Thanks in advance -Howard
-Howard
Advertisement
If you are using AlphaComposite, I assume you are using Java2 and Swing?

How are you doing a fade using SRC_OVER? This sounds questionable. I can''t think of a good way to do fades this way. (I''d be interested in hearing about it if you have though )

Alpha blending an entire screen is going to *hurt like hell*. You might be better off writing to an offscreen buffer, access the pixel data directly, then write the buffer to screen. Especially since you are going to be doing fades, which don''t require the flexibility of an alpha channel at all.

Now that I think about it, that sounds a lot like java.awt.image.LookupOp . You can set up a lookup table that maps a particular color to the next one in the fade progression. This would be faster than an alpha blend, or a direct pixel modify, because there is no computing done, just a table lookup. You can save the modified image(s) and run back through the same table or create a new table to represent the next step.

I can post code if you have trouble with it.

ManaSink
My Favorite is the ImageProducer/Consumer method. Put your image into a consumer (pixelgrabber), then use the pixel array to make a new producer (memoryimagesource subclass) then each update call a function of the memoryimagesource subclass, or any class that implements the producer interface, that has access to the pixel array. Make a new Image from that producer and call Image.flush() all the time so that the image grabs its pixels from the producer, as the producer is constantly modifying the alpha channel. This way doesn''t seem to be CPU intensive at all.
quote:Original post by ManaSink

If you are using AlphaComposite, I assume you are using Java2 and Swing?

How are you doing a fade using SRC_OVER? This sounds questionable. I can''t think of a good way to do fades this way. (I''d be interested in hearing about it if you have though )

Alpha blending an entire screen is going to *hurt like hell*. You might be better off writing to an offscreen buffer, access the pixel data directly, then write the buffer to screen. Especially since you are going to be doing fades, which don''t require the flexibility of an alpha channel at all.

Now that I think about it, that sounds a lot like java.awt.image.LookupOp . You can set up a lookup table that maps a particular color to the next one in the fade progression. This would be faster than an alpha blend, or a direct pixel modify, because there is no computing done, just a table lookup. You can save the modified image(s) and run back through the same table or create a new table to represent the next step.

I can post code if you have trouble with it.

ManaSink


Thanks for your input. Well, I _attempted_ a fade by setting a float variable that has the value of

float fadeValue = 0.9f
AlphaComposite myAlpha;

Then in the update method... (I use update(Graphics g) for double buffering, but I don''t know if it''s better to do the drawing in paint(Graphics g) :-/)

// a check that makes sure fadeValue > 0 goes here
myAlpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER,fadeValue);
Graphics2D g2 = (Graphics2D) g;
g2.fillRect(0,0,w,h);
g2.setComposite(myAlpha);
g2.drawImage(offscreen,0,0,this);
fadeValue -= 0.1;

I don''t know if it works... it was going too slow and irresponsive that I ended up ctrl-alt-del to close the application (yes, I''m doing an application, not applet). I suppose it should work the way I think it should if run on a high power machine, but supposedly a little fade shouldn''t require a machine that could run Quake 3 smoothly on high resolution without a 3D card.

Personally I have never worked with java.awt.image.LookupOp, so any codes/examples would be nice... And about accessing the pixels directly, I know how to buffer an image and get access to the pixels, but screenful of pixels... that''s at least 640X480 = 287,200 pixels... would modifying the value of each pixel really be fast enough to run a fade smoothly? Again, code examples will be nice, as I like to learn by examples.

Once again, thanks for your help.



-Howard
-Howard
Yes, doing a AlphaComposite on the whole screen can be slow. I experienced this. Most likely you can just have a few transparent alphachanneled objects. Save the origonal AlphaComposite so you can set it back right after you draw a alpha object. You can still have a decent framerate doing this. Also most the other blending types look bad so you probably want to stay with SRC_OVER.
author of the Helping Phriendly Book
Thanks for your input.

How can you accomplish a fade by having just a few transparent alphachannel objects (what that is I do not know either ). Can you explain a little further?

Thanks.

- Howard
-Howard
If you want to fade the whole screen so the frames blend together it''s probably going to hurt. I just meant that it won''t be so bad if there is limited transparent objects in the scene.
Also you might spped it up a bit by not putting this
myAlpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER,fadeValue);
in the loop, just set it up once.

What I meant earlier but probably didn''t explain is once you set the AlphaComposite it remains set. So you will want to save the old one so you can set it back when you want to end the fading effects. Kind of like this
    Composite alpha_composite;Composite regular_composite;public ClassName(){    alpha_composite = AlphaComposite.getInstance  (AlphaComposite.SRC_OVER,alpha_value);}//when you paint sayregular_composite = g.getComposite();g.setComposite(alpha_composite);//do all your drawingg.setComposite(regular_composite);    




In your case since you are fading the screen you will probably want to leave it on the alpha_composite for a couple seconds of rendering before setting it back.

author of the Helping Phriendly Book
AlphaComposite is not the way to go here, unless you can do it inside your components so there is only one write to the screen.

A sloppy implementation of LookupOp with no optimization, but should still be much faster than AlphaComposite:

          byte[] fadearray = new byte[256];  LookupOp lop = new LookupOp(new ByteLookupTable(0,fadearray),null);  double currentfade = 1.0;public void update(Graphics g){  BufferedImage buf = new BufferedImage(this.getWidth(),this.getHeight(),BufferedImage.TYPE_INT_RGB);  Graphics2D g2 = buf.createGraphics();  super.update(g2);  Graphics2D g2dout = (Graphics2D)g;  currentfade = currentfade - 0.05;  if (currentfade<0.0){    currentfade = 1.0;  }  for (int cntr=0;cntr<256;cntr++){    fadearray[cntr]=(byte)(cntr*currentfade);  }  lop.filter(buf,buf);  g2dout.drawImage(buf,0,0,this);}         


Check it out and get back to me.

ManaSink


Edited by - ManaSink on July 24, 2000 11:20:56 AM
I tried the below code. It works, but really slow... the interesting thing is that it works as fast with a big image as it does a small one. This code obviously doesn''t fit for a smooth fading for its speed is nowhere near the 10 frames per second that I set the thread to run at (my goal is 12, but even just 10 makes trouble ). Any optimization tips?



import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;

public class FadeTryOut extends Frame implements Runnable, WindowListener
{

byte[] fadearray = new byte[256];
LookupOp lop = new LookupOp(new ByteLookupTable(0,fadearray),null);
double currentfade = 1.0;
Thread thrd;
BufferedImage inferno = new BufferedImage(640,480,BufferedImage.TYPE_INT_RGB);
Image infernoImage;
MediaTracker tracker;
BufferedImageOp thresholdOp;

public FadeTryOut()
{

addWindowListener(this);

tracker = new MediaTracker(this);
setSize(100, 100);
repaint();
infernoImage = Toolkit.getDefaultToolkit().getImage("duke_wave_shadow.gif");
tracker.addImage(infernoImage, 0);

if (thrd == null)
{
thrd = new Thread(this);
thrd.start();
}
}

public void update(Graphics g)
{

currentfade -= 0.1;

if (currentfade<0.0)
{
currentfade = 1.0;
}
for (int cntr=255;cntr>= 0;cntr--)
{
fadearray[cntr]=(byte)(cntr*currentfade);
}

thresholdOp = new LookupOp(new ByteLookupTable(0, fadearray), null);

BufferedImage buf = thresholdOp.filter(inferno, null);

Graphics2D g2dout = (Graphics2D)g;
g2dout.drawImage(buf,0,0,this);
}

public void run()
{
try
{
tracker.waitForAll();
} catch (InterruptedException exc)
{
}

inferno.getGraphics().drawImage(infernoImage,0,0,this);

while (true)
{

repaint();
try
{
Thread.sleep(100);
} catch (InterruptedException exc)
{
}
}
}

public void windowOpened(WindowEvent evt)
{
}

public void windowActivated(WindowEvent evt)
{
}

public void windowClosed(WindowEvent evt)
{
}

public void windowClosing(WindowEvent evt)
{
System.exit(0);
}

public void windowIconified(WindowEvent evt)
{
}

public void windowDeiconified(WindowEvent evt)
{
}

public void windowDeactivated(WindowEvent evt)
{
}

public static void main(String[] argv)
{
Frame a = new FadeTryOut();
a.setVisible(true);
}

}
-Howard

This topic is closed to new replies.

Advertisement