It is quite possible, but i dont think it’d be accellerated.
This post got me interested, and i hacked up a bit of a test.
It’s not very fast, and I just guessed the math, so can’t guarantee that it’s fully correct.
AdditiveComposite.java:
import java.awt.*;
import java.awt.image.*;
public class AdditiveComposite implements Composite
{
public AdditiveComposite()
{
super();
}
public CompositeContext createContext(ColorModel srcColorModel, ColorModel dstColorModel, RenderingHints hints)
{
return new AdditiveCompositeContext();
}
}
AdditiveCompositeContext.java:
import java.awt.*;
import java.awt.image.*;
public class AdditiveCompositeContext implements CompositeContext
{
public AdditiveCompositeContext(){};
public void compose(Raster src, Raster dstIn, WritableRaster dstOut)
{
int w1 = src.getWidth();
int h1 = src.getHeight();
int chan1 = src.getNumBands();
int w2 = dstIn.getWidth();
int h2 = dstIn.getHeight();
int chan2 = dstIn.getNumBands();
int minw = Math.min(w1, w2);
int minh = Math.min(h1, h2);
int minCh = Math.min(chan1, chan2);
//This bit is horribly inefficient,
//getting individual pixels rather than all at once.
for(int x = 0; x < dstIn.getWidth(); x++) {
for(int y = 0; y < dstIn.getHeight(); y++) {
float[] pxSrc = null;
pxSrc = src.getPixel(x, y, pxSrc);
float[] pxDst = null;
pxDst = dstIn.getPixel(x, y, pxDst);
float alpha = 255;
if(pxSrc.length > 3) {
alpha = pxSrc[3];
}
for(int i = 0; i < 3 && i < minCh; i++) {
pxDst[i] = Math.min(255, (pxSrc[i] * (alpha / 255)) + (pxDst[i]));
dstOut.setPixel(x, y, pxDst);
}
}
}
}
public void dispose(){}
}
Prog.java sets up a simple JFrame with a custom JPanel that draws wandering spotlights:
import java.awt.*;
import javax.swing.*;
import java.awt.image.*;
public class Prog
{
public static void main(String[] a) {
JFrame frame = new JFrame();
MyPanel cp = new MyPanel();
frame.setContentPane(cp);
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
while(true) {
cp.moveSpots();
frame.repaint();
try {
Thread.sleep(50);
} catch(Exception e) {
break;
}
}
}
}
class Spotlight {
float x, y, vx, vy;
int w, h;
Color c;
static Composite acomp = new AdditiveComposite();
public Spotlight(Color colour, int xStart, int yStart, int width, int height)
{
c = colour;
x = xStart;
y = yStart;
w = width;
h = height;
}
public void paint(Graphics2D g) {
Composite oldComp = g.getComposite();
g.setComposite(acomp); //comment out this line to see the difference
g.setColor(c);
g.fillOval((int)x+0, (int)y+0, w-10, h-10);
}
public void moveRandom(int minX, int minY, int maxX, int maxY) {
vx += 8 * (Math.random() * 0.2 - 0.1);
vy += 8 * (Math.random() * 0.2 - 0.1);
vx *= 0.98;
vy *= 0.98;
if(x > maxX - w) {
vx = vx < 0 ? vx : -vx;
}
if(x < minX) {
vx = vx > 0 ? vx : -vx;
}
if(y > maxY - h) {
vy = vy < 0 ? vy : -vy;
}
if(y < minY) {
vy = vy > 0 ? vy : -vy;
}
x += vx;
y += vy;
}
}
class MyPanel extends JPanel {
Spotlight[] spots = null;
public MyPanel() {
spots = new Spotlight[6];
spots[0] = new Spotlight(new Color(255, 0, 0, 128), 200, 0, 200, 200);
spots[1] = new Spotlight(new Color(0, 255, 0, 128), 100, 200, 200, 200);
spots[2] = new Spotlight(new Color(0, 0, 255, 128), 0, 0, 200, 200);
spots[3] = new Spotlight(new Color(255, 0, 0, 128), 200, 0, 200, 200);
spots[4] = new Spotlight(new Color(0, 255, 0, 128), 100, 200, 200, 200);
spots[5] = new Spotlight(new Color(0, 0, 255, 128), 0, 0, 200, 200);
}
public void moveSpots() {
if(spots == null) return;
for(int i = 0; i < 6; i++) {
if(spots[i] != null) {
spots[i].moveRandom(0, 0, this.getWidth(), this.getHeight());
} else {
break;
}
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g.setColor(Color.black);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
for(int i = 0; i < 6; i++) {
if(spots[i] != null) {
spots[i].paint(g2);
} else {
break;
}
}
}
}
The graphics code seems pretty smart about providing the compose() function with only the area of interest, so i think if you use it for limited small effects, it might run OK.
I noticed that my compose() function was being given 64x64 tiles, rather than all the image at once. There’s mention in the API of multithreading, but the result doesn’t appear to use it.
Digging down into the java source code, i found their alphacomposite creates BufferedImages from the rasters, then calls a native function called Blit. I didn’t look deeper than a quick glance at that file, didn’t really understand it. There seems to be a ton of boilerplate in Java.