# Pixel eines BufferedImage bearbeiten (Performance)



## Marlon (17. Jun 2011)

Hallo!
Ein sich in der Open Beta befindendes Echtzeitrollenspiel läuft flüssig mit relativ konstanten 40 fps.
Nun soll das bestehende Programm um Beleuchtungseffekte (Lichter und Schatten) erweitert werden und somit habe ich einen ersten Ansatz implementiert, welcher die Pixel eines BufferedImages (das Offscreen Image, welches über den gesamten Bildschirmbereich gelegt wird und Dunkelheit, Schatten und Lichter darstellen soll) auslesen und bearbeiten soll.
Allerdings führt folgende Einbindung dazu, dass das Spiel nicht mehr gleichmässig läuft (FPS nicht mehr konstant) und insgesamt viel langsamer:


```
private void updateLightImage() {
		int[] pixel = ((DataBufferInt)image.getRaster().getDataBuffer()).getData();
		
        for(int p=0;p<pixel.length;p++) {
            pixel[p]=(brightness << 24) | (0 << 16) | (0 << 8) | 0;
        }
	}
```

Weiss jemand, wie man dieses Problem umgehen/besser lösen könnte. Oder hat jemand andere Tipps zu diesem Thema?
Vielen Dank im Vorraus!
Marlon


----------



## Marco13 (17. Jun 2011)

Hm ... ???:L Erstens müßte das ja nur gemacht werden, wenn 'brightness' sich ändert, und zweitens... warum manuell und Pixelweise? Ein einfaches

```
Graphics2D g = image.createGraphics();
g.setColor(colorWithBrightness);
g.fillRect(0,0,w,h);
g.dispose();
```
sollte es doch tun!?


----------



## SlaterB (17. Jun 2011)

das wäre mit Begründung überzeugender, wie soll das grundsätzlich betrachtet schneller sein?,
muss das nicht letztlich auch die Pixel-Werte des Arrays verändern? da wird ja wohl kaum System.arrayCopy() oder ähnliches intern helfen,
stattdessen noch der Overhead der Methodenaufrufe, des Graphics-Objekt usw.

das neue Pixel '(brightness << 24) | (0 << 16) | (0 << 8) | 0' kann man in diesem Fall vielleicht nur einmal ausrechnen,
aber das sieht mir eh nur nach einem Beispiel aus, und solcher Kleinkram wird nicht die Welt retten,

ich kann nicht wirklich hier etwas beitragen aber wenn dann ist so ein BufferedImage an sich die Grundquelle alles Langsamkeit und mit diesen Java-Mitteln nicht schnell zu bekommen?


----------



## Marco13 (17. Jun 2011)

BufferedImages (vor allem die int-basierten) verwenden intern "Magie" (viel, viel, schwarze und kryptische Magie :shock: ). Nach außen hin wird glaubich unter dem Namen "Managed Images" zusammengefasst. Innen sind das höchst implementierungsspefifische Pipelines für die Maloperationen und ein (Grafik)speichermanagement, das auf Dinge zurückgreift, die von einem int[] Array schon _sehr_ weit weg ist. Natürlich ist das immernoch kein "fundiertes Argument" dafür, dass ein fillRect schneller sein muss, aber... nach einigem Wühlen im Sun-Quellcode bin ich mir ganz unfundiert und subjektiv ziemlich sicher, dass das so ist  Kommt im Zweifelsfall auf einen Versuch an...

EDIT: Um es zumindest noch etwas weiter zu begründen: 
Eine (leider sehr Alte) Quelle für die Bedenken: BufferedImage as Good as Butter, Part II | Java.net
Eine etwas neuere: Java Graphics: Code Snippets, Musings, and Discoveries: Images: Managed Images
Und eine von jemandem, der aus Erfahrung spechen dürfte: Java Image Processing - Managed Images and Performance

Der (etwas schwammige und grob vereinfachte, nagel' mich darauf nicht fest) Grundtenor ist, dass wenn man sich erstmal den Speicherbereich des Bildes für direkten Zugriff (als int[] Array) geholt hat, Sun intern nicht mehr so viele Möglichkeiten hat, das Bild z.B. im VRAM zu halten, und das eben die Performance in den Keller drücken kann. Aber wie gesagt, das war nur ein Hinweis - Ausprobieren und ggf. Sun-Code lesen könnte da noch mehr Einsichten oder Sicherheit bringen.


----------



## Marlon (17. Jun 2011)

Marco13 hat gesagt.:


> Hm ... ???:L Erstens müßte das ja nur gemacht werden, wenn 'brightness' sich ändert, und zweitens... warum manuell und Pixelweise? Ein einfaches
> 
> ```
> Graphics2D g = image.createGraphics();
> ...



Jetzt wird es interessant. 
Ich muss ja erstmal alle Pixel "dunkel" machen (siehe brightness), um dann anschliessend die Stellen an denen Lichter stehen aufzuhellen, also den Alpha zu verändern.

Bilder sagen mehr als tausend Worte: http://www.asantee.net/ethanon/screenshots/eth075_3.jpg

Vielleicht gibt es aber auch tatsächlich eine Möglichkeit, dies eleganter zu lösen (ohne vorher jeden einzelnen Pixel anzupacken).

Edit: Ich möchte noch hinzufügen dass ich bei dem Spiel jedes Bild als BufferedImage UND als VolatileImage im Speicher halte. Gezeichnet werden natürlich (ultraschnell, mit bis zu 240 FPS) lediglich die VolatileImages, jedoch habe ich noch nicht herausgefunden ob und wenn dann wie es möglich ist, diese VolatileImages direkt zu verändern, so dass die Performance nicht in den Keller geht.

Edit2: Das Bild ist übrigens nicht von dem Spiel noch aus meiner Hand, es handelt sich um eine 2D Engine, welche ich im Netz gefunden habe und leider noch nicht fertiggestellt worden ist.


----------



## SlaterB (17. Jun 2011)

hübsches Bild, 


nur um noch eine offensichtliche Frage zu stellen:
wie entstehen denn dort die Lichtquellen und die Schatten oder ist das bisher nur fest vorgegeben?

wenn das alles schon existiert (und nicht gerade deine Frage ist), dann sind das doch 100x aufwendigere Methoden als jetzt 5 Pixel zu ändern


----------



## Marlon (17. Jun 2011)

Entschuldigung, das gepostete Bild entspricht nicht meiner Arbeit, es soll lediglich andeuten, was ich erreichen möchte, bzw. wie Lichter und Schatten aussehen sollten.
Das Spiel welches mit Schatten und Licht beseelt werden soll heisst Forgotten Elements:
http://www.mmog-welt.de/bilder/forgottenelements3-538x403.jpg
Forgotten Elements Online Action MMORPG


----------



## Marco13 (17. Jun 2011)

Analog zum oben beschriebenen Verfahren würden sich für solche Punktförmigen Lichtquellen, die mit der Entfernung dunkler werden, dann vermutlich raidale "GradientPaint" anbieten...


----------



## Marlon (17. Jun 2011)

Marco13 hat gesagt.:


> Analog zum oben beschriebenen Verfahren würden sich für solche Punktförmigen Lichtquellen, die mit der Entfernung dunkler werden, dann vermutlich raidale "GradientPaint" anbieten...



Oh wow, ich denke das könnte klappen.
Werde mich damit mal genauer befassen und mich dann nochmal melden.
Danke!


----------



## Marlon (27. Jul 2011)

Ich habe mich nun länger und intensiver mit dem oben genannten Problem befasst und nach Lösungen gesucht, ein BufferedImage (bzw. VolatileImage) ohne grossartigen Performanceverlust bei jedem Frameschritt zu bearbeiten und anzuzeigen.
Leider erfolglos!
Falls noch jemand weiter weiss oder sich mal intensiver mit dem Thema beschäftigt hat bin ich ihm unendlich dankbar, wenn er mir ein paar Hilfestellungen geben könnte!


----------



## schalentier (27. Jul 2011)

Also ich vermute, wenn du wirklich jedes Pixel beleuchten willst (egal mit welchem Algorithmus), geht das in Echtzeit nur mit einem Shader in der Grafikkarte (also wenn du Java nimmst, OpenGL und GSGL).

Was du versuchen kannst sind Lightmaps. Also vorberechnete Tiles, die nur Alphainformationen enthalten und ueber die eigentlichen Textur-Tiles gezeichnet werden. Die generierst du einmalig am Anfang (z.B. 128 Stueck mit steigendem Alphawert) und speicherst pro Tile einen Helligkeitswert, anhand welchem du eine entsprechende Lightmap zeichnest. Aber auch hier wird wohl Java2D/Swing recht schnell in die Knie gehen.


----------



## Empire Phoenix (27. Jul 2011)

Versuch mal folgendes:
ein halbtransparentes schwarz weeiß bild, das du über dein hauptbufferdeimage renderst. Dieses kann dann auch eine geringere auflösung haben was schonmal performance sparen sollte.


----------



## Marco13 (27. Jul 2011)

Ich kapier's nicht. Das hier malt ein Bild im dunkeln, und die Maus ist eine Taschenschlampe...

```
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.MultipleGradientPaint.CycleMethod;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;


class LightEffectPanel extends JPanel implements MouseMotionListener
{   
    private Point point = new Point(0,0);
    private BufferedImage image;
    
    public LightEffectPanel()
    {
        BufferedImage i = null;
        try {
            i = ImageIO.read(new File("image.jpg"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        image = new BufferedImage(i.getWidth(), i.getHeight(), 
                BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = image.createGraphics();
        g.drawImage(i,0,0,null);
        g.dispose();
        addMouseMotionListener(this);
    }
    
    @Override
    protected void paintComponent(Graphics gr)
    {
        super.paintComponent(gr);
        Graphics2D g = (Graphics2D)gr;
        g.drawImage(image, 0,0,null);
        
        g.setComposite(AlphaComposite.SrcOver);
        Point2D center = new Point2D.Float(point.x, point.y);
        float[] dist = {0.0f, 1.0f};
        Color[] colors = {new Color(255,255,255,0), new Color(16,16,16,230) };
        RadialGradientPaint p =
             new RadialGradientPaint(
                center, 100, dist, colors, CycleMethod.NO_CYCLE);
        g.setPaint(p);
        g.fillRect(0,0,getWidth(),getHeight());
    }

    @Override
    public void mouseDragged(MouseEvent e) {
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        point = e.getPoint();
        repaint();
    }
}

class LightEffectTest
{
    public static void main(String args[])
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                new LightEffectTest();
            }
        });
    }
    
    
    public LightEffectTest()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().add(new LightEffectPanel());
        f.setSize(600,600);
        f.setVisible(true);
    }
    
}
```
Was genau meinst du? :bahnhof:


----------



## Marlon (1. Aug 2011)

Danke für den Code, hat mir sehr geholfen!
Für ein einzelnes Licht funktioniert das ganze auch super und läuft recht fix, allerdings wird es problematisch, wenn mehrere Lichter (Kerzen, Lagerfeuer usw...) auf dem Bildschirm positioniert werden, da sich diese dann gegenseitig verdunkeln (es werden mehrere Bilder übereinander gezeichnet).
Gibt es eine Möglichkeit, meherere Lichtquellen mit diesem Verfahren darzustellen?


----------



## Marco13 (1. Aug 2011)

So ähnlich, nur schnell hingetippt, geht vielleicht auch eleganter...

```
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.MultipleGradientPaint.CycleMethod;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;


class LightEffectPanel2 extends JPanel implements MouseMotionListener
{
    private Point point = new Point(0,0);
    private BufferedImage image;
    private BufferedImage shadow;

    public LightEffectPanel2()
    {
        BufferedImage i = null;
        try {
            i = ImageIO.read(new File("image.jpg"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        image = new BufferedImage(i.getWidth(), i.getHeight(),
                BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = image.createGraphics();
        g.drawImage(i,0,0,null);
        g.dispose();

        shadow = new BufferedImage(i.getWidth(), i.getHeight(),
                BufferedImage.TYPE_INT_ARGB);

        addMouseMotionListener(this);
    }

    @Override
    protected void paintComponent(Graphics gr)
    {
        super.paintComponent(gr);
        Graphics2D g = (Graphics2D)gr;
        g.drawImage(image, 0,0,null);

        drawLights();

        g.drawImage(shadow, 0,0, null);
    }

    private void drawLights()
    {
        Graphics2D g = shadow.createGraphics();
        g.setComposite(AlphaComposite.Src);
        g.setColor(new Color(0,0,16,240));
        g.fillRect(0,0,getWidth(),getHeight());

        drawLight(g, new Point(100,100));
        drawLight(g, point);

        g.dispose();
    }

    private void drawLight(Graphics2D g, Point pt)
    {
        float radius = 100;
        g.setComposite(AlphaComposite.DstOut);
        Point2D center = new Point2D.Float(pt.x, pt.y);
        float[] dist = {0.0f, 1.0f};
        Color[] colors = {new Color(255,255,255,255), new Color(0,0,0,0) };
        RadialGradientPaint p =
             new RadialGradientPaint(
                center, radius, dist, colors, CycleMethod.NO_CYCLE);
        g.setPaint(p);
        g.fillOval(pt.x-(int)radius,pt.y-(int)radius,(int)radius*2,(int)radius*2);
    }



    @Override
    public void mouseDragged(MouseEvent e) {
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        point = e.getPoint();
        repaint();
    }
}

class LightEffectTest2
{
    public static void main(String args[])
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                new LightEffectTest2();
            }
        });
    }


    public LightEffectTest2()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().add(new LightEffectPanel2());
        f.setSize(600,600);
        f.setVisible(true);
    }

}
```

EDIT: Die main hatte noch den alten Test gestartet. Jetzt stimmts. Danke an Fu3L für den Hinweis.


----------



## Marlon (2. Aug 2011)

Hei super!
Zwar geht die Prozessorleistung total in den Keller, wenn das BufferedImage bearbeitet wird, jedoch habe ich deine hier beschriebene Funktionalität einfach auf ein VolatileImage angewendet und es funktioniert!
Die Menge der Lichter im Spiel (jedes Monster trägt eine kleine Fackel mit sich rum) spielt dabei gar keine Rolle.
Vielen vielen Dank!


----------



## Marco13 (2. Aug 2011)

Ja, wie gesagt, war nur ein schneller Test. Wenn's zuuu langsam wird, kann man mal schauen, wo man da noch was optimieren kann.


----------



## Marlon (9. Aug 2011)

Eine abschliessende Frage noch: Weisst du wie man das Programm umschreiben muss, damit die Lichtquellen nun auch eine Farbe ausstrahlen, das heisst den Leuchtkreis farblich verändern?


----------



## Marco13 (9. Aug 2011)

Ja, irgendwann wird's halt kompliziert. Grob was in der Richtung kann man mit

```
private void drawLights()
    {
        Graphics2D g = shadow.createGraphics();
        g.setComposite(AlphaComposite.Src);
        g.setColor(new Color(0,0,16,240));
        g.fillRect(0,0,getWidth(),getHeight());

        drawLight(g, new Point(100,100), Color.WHITE);
        drawLight(g, point, new Color(250,190,0));


        g.dispose();
    }

    private void drawLight(Graphics2D g, Point pt, Color lightColor)
    {
        int radius = 100;
        Point2D center = new Point2D.Float(pt.x, pt.y);
        float[] dist = {0.0f, 1.0f};
        Color[] colors = {
            new Color(
                lightColor.getRed(),
                lightColor.getGreen(),
                lightColor.getBlue(),255),
            new Color(0,0,0,0) };
        RadialGradientPaint p =
             new RadialGradientPaint(
                center, radius, dist, colors, CycleMethod.NO_CYCLE);
        g.setPaint(p);
        g.setComposite(AlphaComposite.DstOut);
        g.fillOval(pt.x-radius,pt.y-radius,radius*2,radius*2);
        g.setComposite(AlphaComposite.SrcOver);
        g.fillOval(pt.x-radius,pt.y-radius,radius*2,radius*2);
    }
```
erreichen, aber eigentlich sollten sich die Lichthelligkeiten ja addieren.... da müßte man sich vielleicht was ausgefeilteres überlegen.


----------



## Marlon (9. Aug 2011)

Ja danke erstmal! Leider führt das tatsächlich nicht zum erwünschten Ergebnis, da ja somit über den Hintergrund "drübergemalt" und somit verdeckt wird.

Vermutlich müsste man den Hintergrund gesondert anpacken und an den beleuchteten Stellen Farben "wegnehmen". Leider weiss ich nicht wie man das performant am besten angeht, da ich ja nicht wieder dahin gehen möchte jeden Pixel einzelnd zu bearbeiten.


----------



## Marco13 (9. Aug 2011)

Du solltest dir selbst klarmachen, was du willst, und wie das ganze aussehen soll.... Und das dann möglichst genau beschreiben. Rote Gegenstände sehen in grünem Licht schwarz aus, und Grüne weiß. Klingt komisch, ist aber so. Vielleicht muss man da noch irgendwo eine ColorConvertOp (Java Platform SE 6) einklinken, aber wie man das jetzt "physikalisch korrekt" berechnet (und das muss es zumindest ansatzweise sein, damit es es plausibel aussieht) weiß ich aus dem Stegreif auch nicht. Die Methode [c]void letMyRenderedStuffLookCool()[/c] gibt's halt in der Form noch nicht.


----------



## Marco13 (10. Aug 2011)

NOCH nicht.

Wieder einmal frage ich mich, warum ich das mache. Aber selbst deine zu erwartende Antwort ("Das ist so langsam ;( !", "Das braucht so viel Speicher ;( !", "Das sollte ein bißchen rötlicher sein ;( !") wird mich nicht davon abhalten, sowas in Zukunft weiterhin zu tun.


```
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.MultipleGradientPaint.CycleMethod;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Point2D;
import java.awt.image.*;
import java.io.File;
import java.io.IOException;
import java.util.*;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;


class LightEffectPanel4 extends JPanel implements MouseMotionListener
{
    private Point point = new Point(0,0);

    private BufferedImage inImage;
    private BufferedImage lightsImage;
    private BufferedImage lightImage;
    private BufferedImage outImage;

    public LightEffectPanel4()
    {
        inImage = loadInputImage();
        int w = inImage.getWidth();
        int h = inImage.getHeight();
        lightsImage = new BufferedImage(w,h,
                BufferedImage.TYPE_INT_ARGB);
        lightImage = new BufferedImage(w,h,
                BufferedImage.TYPE_INT_ARGB);
        outImage = new BufferedImage(w,h,
                BufferedImage.TYPE_INT_ARGB);

        addMouseMotionListener(this);
    }

    private BufferedImage loadInputImage()
    {
        BufferedImage i = null;
        try {
            i = ImageIO.read(new File("img.jpg"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        BufferedImage image = new BufferedImage(i.getWidth(), i.getHeight(),
                BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = image.createGraphics();
        g.drawImage(i,0,0,null);

        //g.setColor(Color.WHITE);
        //g.fillRect(0,0,i.getWidth(),i.getHeight());

        g.dispose();
        return image;
    }


    @Override
    protected void paintComponent(Graphics gr)
    {
        super.paintComponent(gr);
        Graphics2D g = (Graphics2D)gr;
        drawLights();
        multiply(inImage, lightsImage, outImage);
        g.drawImage(outImage, 0,0,null);
    }

    private void drawLights()
    {
        Graphics2D g = lightsImage.createGraphics();
        g.setColor(Color.BLACK);
        g.fillRect(0,0,lightsImage.getWidth(),lightsImage.getHeight());
        g.dispose();

        drawLight(point, new Color(255,230,128));
        drawLight(new Point(190,210), Color.RED);
        drawLight(new Point(160,180), Color.GREEN);
        drawLight(new Point(220,180), Color.BLUE);
    }

    private void drawLight(Point pt, Color color)
    {
        int radius = 100;
        Point2D center = new Point2D.Float(pt.x, pt.y);
        Color black = new Color(0,0,0,0);
        int steps = 10;
        float[] dist = new float[steps];
        Color[] colors = new Color[steps];
        boolean quadratic = true;
        for (int i=0; i<steps; i++)
        {
            float x = (float)i / (steps-1);
            dist[i] = x;
            float y = x;
            if (quadratic)
            {
                y *= x;
            }
            colors[i] = interpolate(color, black, y);
        }
        RadialGradientPaint p =
             new RadialGradientPaint(
                center, radius, dist, colors, CycleMethod.NO_CYCLE);

        Graphics2D g = lightImage.createGraphics();
        g.setColor(Color.BLACK);
        g.fillRect(0,0,lightImage.getWidth(),lightImage.getHeight());
        g.setPaint(p);
        g.fillOval(pt.x-radius,pt.y-radius,radius*2,radius*2);
        g.dispose();
        add(lightsImage, lightImage, lightsImage);
    }



    @Override
    public void mouseDragged(MouseEvent e) {
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        point = e.getPoint();
        repaint();
    }


    private static Color interpolate(Color c0, Color c1, float t)
    {
        int a0 = c0.getAlpha();
        int r0 = c0.getRed();
        int g0 = c0.getGreen();
        int b0 = c0.getBlue();
        int a1 = c1.getAlpha();
        int r1 = c1.getRed();
        int g1 = c1.getGreen();
        int b1 = c1.getBlue();
        int da = a1-a0;
        int dr = r1-r0;
        int dg = g1-g0;
        int db = b1-b0;
        int a = (int)(a0 + da * t);
        int r = (int)(r0 + dr * t);
        int g = (int)(g0 + dg * t);
        int b = (int)(b0 + db * t);
        return new Color(r,g,b,a);
    }

    private static int[] getData(BufferedImage i)
    {
        Raster raster = i.getRaster();
        DataBuffer dataBuffer = raster.getDataBuffer();
        if (!(dataBuffer instanceof DataBufferInt))
        {
            throw new IllegalArgumentException(
                "Image does not have a DataBufferInt");
        }
        DataBufferInt dataBufferInt = (DataBufferInt)dataBuffer;
        int data[] = dataBufferInt.getData();
        return data;
    }

    private static int getA(int argb) { return (argb >> 24) & 0xFF; }
    private static int getR(int argb) { return (argb >> 16) & 0xFF; }
    private static int getG(int argb) { return (argb >>  8) & 0xFF; }
    private static int getB(int argb) { return (argb >>  0) & 0xFF; }
    private static int getARGB(int a, int r, int g, int b)
    {
        return (a << 24) | (r << 16) | (g << 8)  | (b << 0);
    }

    private static void multiply(BufferedImage i0, BufferedImage i1, BufferedImage result)
    {
        int data0[] = getData(i0);
        int data1[] = getData(i1);
        int dataResult[] = getData(result);

        int w = i0.getWidth();
        int h = i0.getHeight();
        for (int x=0; x<w; x++)
        {
            for (int y=0;y<h; y++)
            {
                int argb0 = data0[x+y*w];
                int a0 = getA(argb0);
                int r0 = getR(argb0);
                int g0 = getG(argb0);
                int b0 = getB(argb0);
                int argb1 = data1[x+y*w];
                int a1 = getA(argb1);
                int r1 = getR(argb1);
                int g1 = getG(argb1);
                int b1 = getB(argb1);
                int ar = (a0*a1) >> 8;
                int rr = (r0*r1) >> 8;
                int gr = (g0*g1) >> 8;
                int br = (b0*b1) >> 8;
                int argb = getARGB(ar, rr, gr, br);
                dataResult[x+y*w] = argb;
            }
        }
    }

    private static void add(BufferedImage i0, BufferedImage i1, BufferedImage result)
    {
        int data0[] = getData(i0);
        int data1[] = getData(i1);
        int dataResult[] = getData(result);

        int w = i0.getWidth();
        int h = i0.getHeight();
        for (int x=0; x<w; x++)
        {
            for (int y=0;y<h; y++)
            {
                int argb0 = data0[x+y*w];
                int a0 = getA(argb0);
                int r0 = getR(argb0);
                int g0 = getG(argb0);
                int b0 = getB(argb0);
                int argb1 = data1[x+y*w];
                int a1 = getA(argb1);
                int r1 = getR(argb1);
                int g1 = getG(argb1);
                int b1 = getB(argb1);
                int ar = Math.min(255, a0+a1);
                int rr = Math.min(255, r0+r1);
                int gr = Math.min(255, g0+g1);
                int br = Math.min(255, b0+b1);
                int argb = getARGB(ar, rr, gr, br);
                dataResult[x+y*w] = argb;
            }
        }
    }


}

class LightEffectTest4
{
    public static void main(String args[])
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                new LightEffectTest4();
            }
        });
    }


    public LightEffectTest4()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().add(new LightEffectPanel4());
        f.setSize(600,600);
        f.setVisible(true);
    }

}
```


----------



## Marlon (10. Aug 2011)

Ich danke dir vielmals für deine erneute Hilfe!!!
Das ist ja schon eine ganze Menge und daraus lässt sich sicher etwas schöpfen!
Aber ich muss schliesslich ehrlich sein: Für meine Zwecke ist das zu langsam  ..
Bei dem Vorgang auf jeden Pixel zuzugreifen geht die Performance in die Knie.
Ich hoffe ich werde in den nächsten Tagen etwas hinbekommen, ansonsten fahre ich das nächste Update erstmal ohne Farben.
Nochmal DANKE! 
Marlon


----------



## Marco13 (10. Aug 2011)

Je nachdem, was langsam ist, könnte man noch ein bißchen was (oder ggf. sehr viel) einsparen. Das hängt von der größe der Lichter ab. Im Moment werden die Lichter in ein Bild gemalt, das so groß ist, wie das original. Eigentlich müßte das 'lightImage' aber nur radius*radius groß sein, und dann eben an die passende Stelle eingefügt. Wie groß sind die Lichter denn? (Und um wie viele Lichter geht es?)

Ansonsten behaupte ich mal, dass man es mit reinem Java3D (d.h. ohne OpenGL etc.) in dieser Form nicht viel schneller bekommt.


----------

