Hallo liebe Forengemeinde,
mich würde mal interessieren, was in Java der Performanteste Weg für einen Software-Rendering-Ansatz ist.
Manche schwören ja auf MemoryImageSource, andere meinen, dass man am besten VolatileImage verwendet und wieder anderer meinen, ein WriteableRaster sei das Maß aller Dinge.
Was muss man beachten und was sollte man am besten verwenden, wenn man seinen eigenen kleinen Software-Renderer schreiben will?
Ich habe zum Test mal folgenden Code geschrieben. Da kann man relativ gut sehen, welche Performance-Probleme es bei einem WriteableRaster schon bei einer Auflößung von 800x600 gibt.
Habe ich da irgendwas komplett falsch verstanden? Ich habe nämlich schon Software-Renderer in Java gesehen, die um einiges schneller waren...
[Java]import static java.lang.Math.PI;
import static java.lang.Math.cos;
import static java.lang.Math.round;
import static java.lang.Math.sin;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class XPaintTest extends JPanel implements Runnable {
private static final long serialVersionUID = -276514233223047772L;
BufferedImage bim = new BufferedImage(800, 600, BufferedImage.TYPE_INT_ARGB);
BufferedImage img;
int[] pos = new int[2];
public XPaintTest() {
setPreferredSize(new Dimension(800, 600));
new Thread(this).start();
img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics g = img.createGraphics();
g.setColor(Color.CYAN);
g.fillRect(25, 0, 50, 25);
g.setColor(Color.RED);
g.fillRect(0,25,100,25);
g.setColor(Color.BLACK);
g.fillOval(12, 50, 12, 12);
g.fillOval(80, 50, 12, 12);
}
public void transform(int[] xy, double transX, double transY, double rotation, double rotX, double rotY, double xShear, double yShear, double xScale, double yScale) {
double ix = xy[0];
double iy = xy[1];
ix -= transX;
iy -= transY;
ix -= rotX;
iy -= rotY;
double theta = PI / 180. * rotation;
double sin = sin(theta);
double cos = cos(theta);
double anchorx = ix;
double anchory = iy;
ix = anchorx * cos + anchory * sin;
iy = anchory * cos - anchorx * sin;
ix /= xScale;
iy /= yScale;
anchorx = ix;
anchory = iy;
ix = anchorx - xShear * anchory;
iy = anchory - yShear * anchorx;
ix += rotX;
iy += rotY;
xy[0] = (int) ix;
xy[1] = (int) iy;
}
public boolean getPixelFromPosition(int w, int h, int[] pos, double x, double y, double transX, double transY, double rotation, double rotX, double rotY, double xShear, double yShear, double xScale, double yScale) {
if (pos == null)
throw new IllegalArgumentException("Error, the argument \"pos\" could not be \"null\"!");
pos[0] = (int) x;
pos[1] = (int) y;
transform(pos, transX, transY, rotation, rotX, rotY, xShear, yShear, xScale, yScale);
if (pos[0] < 0 || pos[1] < 0 || pos[0] >= w || pos[1] >= h)
return false;
return true;
}
public void getPositionFromPixel(int[] pos, int px, int py, double transX, double transY, double rotation, double rotX, double rotY, double xShear, double yShear, double xScale, double yScale) {
pos[0]=0;
pos[1]=0;
double ix = px;
double iy = py;
ix -= rotX;
iy -= rotY;
double anchorx = ix;
double anchory = iy;
ix = anchorx + xShear * anchory;
iy = anchory + yShear * anchorx;
ix *= xScale;
iy *= yScale;
double theta = PI / 180. * -rotation;
double sin = sin(theta);
double cos = cos(theta);
anchorx = ix;
anchory = iy;
ix = anchorx * cos + anchory * sin;
iy = anchory * cos - anchorx * sin;
ix += rotX;
iy += rotY;
ix += transX;
iy += transY;
ix = round(ix);
iy = round(iy);
pos[0] = (int) ix;
pos[1] = (int) iy;
}
double rot = 0;
Rectangle rect = new Rectangle(200, 200, 100, 100);
int[] color = new int[4];
int[] xColor = new int[4];
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
WritableRaster raster = bim.getRaster();
for (int x = 0; x < raster.getWidth(); x++)
for (int y = 0; y < raster.getHeight(); y++) {
color[0] = y/2+x/10;
color[1] = x*y/100;
color[2] = x/2+y/10;
color[3] = 255;
raster.setPixel(x, y, color);
}
// draw a rotated rectangle
{
double rotation = rot += 3.5;
double xScale = 2;
double yScale = 2;
double rotPointX = rect.width / 2;
double rotPointY = rect.height / 2;
// calculate the bounds
int minx = Integer.MAX_VALUE;
int miny = Integer.MAX_VALUE;
int maxx = Integer.MIN_VALUE;
int maxy = Integer.MIN_VALUE;
getPositionFromPixel(pos, -1, -1, rect.x, rect.y, rotation, rotPointX, rotPointY, 0, 0, xScale, yScale);
if (pos[0] < minx) minx = pos[0];
if (pos[1] < miny) miny = pos[1];
if (pos[0] > maxx) maxx = pos[0];
if (pos[1] > maxy) maxy = pos[1];
getPositionFromPixel(pos, -1, rect.height + 1, rect.x, rect.y, rotation, rotPointX, rotPointY, 0, 0, xScale, yScale);
if (pos[0] < minx) minx = pos[0];
if (pos[1] < miny) miny = pos[1];
if (pos[0] > maxx) maxx = pos[0];
if (pos[1] > maxy) maxy = pos[1];
getPositionFromPixel(pos, rect.width + 1, rect.height + 1, rect.x, rect.y, rotation, rotPointX, rotPointY, 0, 0, xScale, yScale);
if (pos[0] < minx) minx = pos[0];
if (pos[1] < miny) miny = pos[1];
if (pos[0] > maxx) maxx = pos[0];
if (pos[1] > maxy) maxy = pos[1];
getPositionFromPixel(pos, rect.width + 1, -1, rect.x, rect.y, rotation, rotPointX, rotPointY, 0, 0, xScale, yScale);
if (pos[0] < minx) minx = pos[0];
if (pos[1] < miny) miny = pos[1];
if (pos[0] > maxx) maxx = pos[0];
if (pos[1] > maxy) maxy = pos[1];
if (minx < 0) minx = 0;
if (miny < 0) miny = 0;
if (maxx > raster.getWidth()) maxx = raster.getWidth();
if (maxy > raster.getHeight()) maxy = raster.getHeight();
int argb;
for (int x = minx; x <= maxx; x++)
for (int y = miny; y <= maxy; y++) {
if (getPixelFromPosition(rect.width, rect.height, pos, x, y, rect.x, rect.y, rotation, rotPointX, rotPointY, 0, 0, xScale, yScale)) {
argb = img.getRGB(pos[0], pos[1]);
color[3] = (argb >> 24) & 0xff;
color[0] = (argb >> 16) & 0xff;
color[1] = (argb >> 8) & 0xff;
color[2] = (argb) & 0xff;
if (x >= 0 && x < raster.getWidth() && y >= 0 && y < raster.getHeight())
if (color[3] > 0)
raster.setPixel(x, y, color);
}
}
}
g.drawImage(bim, 0, 0, null);
}
@Override
public void run() {
while (true) {
repaint();
try {
Thread.sleep(60);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
JFrame f = new JFrame("XPaintTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new BorderLayout());
f.add(new XPaintTest());
f.pack();
f.setVisible(true);
}
}
[/Java]
PS: Wie behandelt man eigendlich den Alphawert eines Pixels wenn man die vorherige Farbe noch sehen soll?
mich würde mal interessieren, was in Java der Performanteste Weg für einen Software-Rendering-Ansatz ist.
Manche schwören ja auf MemoryImageSource, andere meinen, dass man am besten VolatileImage verwendet und wieder anderer meinen, ein WriteableRaster sei das Maß aller Dinge.
Was muss man beachten und was sollte man am besten verwenden, wenn man seinen eigenen kleinen Software-Renderer schreiben will?
Ich habe zum Test mal folgenden Code geschrieben. Da kann man relativ gut sehen, welche Performance-Probleme es bei einem WriteableRaster schon bei einer Auflößung von 800x600 gibt.
Habe ich da irgendwas komplett falsch verstanden? Ich habe nämlich schon Software-Renderer in Java gesehen, die um einiges schneller waren...
[Java]import static java.lang.Math.PI;
import static java.lang.Math.cos;
import static java.lang.Math.round;
import static java.lang.Math.sin;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class XPaintTest extends JPanel implements Runnable {
private static final long serialVersionUID = -276514233223047772L;
BufferedImage bim = new BufferedImage(800, 600, BufferedImage.TYPE_INT_ARGB);
BufferedImage img;
int[] pos = new int[2];
public XPaintTest() {
setPreferredSize(new Dimension(800, 600));
new Thread(this).start();
img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics g = img.createGraphics();
g.setColor(Color.CYAN);
g.fillRect(25, 0, 50, 25);
g.setColor(Color.RED);
g.fillRect(0,25,100,25);
g.setColor(Color.BLACK);
g.fillOval(12, 50, 12, 12);
g.fillOval(80, 50, 12, 12);
}
public void transform(int[] xy, double transX, double transY, double rotation, double rotX, double rotY, double xShear, double yShear, double xScale, double yScale) {
double ix = xy[0];
double iy = xy[1];
ix -= transX;
iy -= transY;
ix -= rotX;
iy -= rotY;
double theta = PI / 180. * rotation;
double sin = sin(theta);
double cos = cos(theta);
double anchorx = ix;
double anchory = iy;
ix = anchorx * cos + anchory * sin;
iy = anchory * cos - anchorx * sin;
ix /= xScale;
iy /= yScale;
anchorx = ix;
anchory = iy;
ix = anchorx - xShear * anchory;
iy = anchory - yShear * anchorx;
ix += rotX;
iy += rotY;
xy[0] = (int) ix;
xy[1] = (int) iy;
}
public boolean getPixelFromPosition(int w, int h, int[] pos, double x, double y, double transX, double transY, double rotation, double rotX, double rotY, double xShear, double yShear, double xScale, double yScale) {
if (pos == null)
throw new IllegalArgumentException("Error, the argument \"pos\" could not be \"null\"!");
pos[0] = (int) x;
pos[1] = (int) y;
transform(pos, transX, transY, rotation, rotX, rotY, xShear, yShear, xScale, yScale);
if (pos[0] < 0 || pos[1] < 0 || pos[0] >= w || pos[1] >= h)
return false;
return true;
}
public void getPositionFromPixel(int[] pos, int px, int py, double transX, double transY, double rotation, double rotX, double rotY, double xShear, double yShear, double xScale, double yScale) {
pos[0]=0;
pos[1]=0;
double ix = px;
double iy = py;
ix -= rotX;
iy -= rotY;
double anchorx = ix;
double anchory = iy;
ix = anchorx + xShear * anchory;
iy = anchory + yShear * anchorx;
ix *= xScale;
iy *= yScale;
double theta = PI / 180. * -rotation;
double sin = sin(theta);
double cos = cos(theta);
anchorx = ix;
anchory = iy;
ix = anchorx * cos + anchory * sin;
iy = anchory * cos - anchorx * sin;
ix += rotX;
iy += rotY;
ix += transX;
iy += transY;
ix = round(ix);
iy = round(iy);
pos[0] = (int) ix;
pos[1] = (int) iy;
}
double rot = 0;
Rectangle rect = new Rectangle(200, 200, 100, 100);
int[] color = new int[4];
int[] xColor = new int[4];
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
WritableRaster raster = bim.getRaster();
for (int x = 0; x < raster.getWidth(); x++)
for (int y = 0; y < raster.getHeight(); y++) {
color[0] = y/2+x/10;
color[1] = x*y/100;
color[2] = x/2+y/10;
color[3] = 255;
raster.setPixel(x, y, color);
}
// draw a rotated rectangle
{
double rotation = rot += 3.5;
double xScale = 2;
double yScale = 2;
double rotPointX = rect.width / 2;
double rotPointY = rect.height / 2;
// calculate the bounds
int minx = Integer.MAX_VALUE;
int miny = Integer.MAX_VALUE;
int maxx = Integer.MIN_VALUE;
int maxy = Integer.MIN_VALUE;
getPositionFromPixel(pos, -1, -1, rect.x, rect.y, rotation, rotPointX, rotPointY, 0, 0, xScale, yScale);
if (pos[0] < minx) minx = pos[0];
if (pos[1] < miny) miny = pos[1];
if (pos[0] > maxx) maxx = pos[0];
if (pos[1] > maxy) maxy = pos[1];
getPositionFromPixel(pos, -1, rect.height + 1, rect.x, rect.y, rotation, rotPointX, rotPointY, 0, 0, xScale, yScale);
if (pos[0] < minx) minx = pos[0];
if (pos[1] < miny) miny = pos[1];
if (pos[0] > maxx) maxx = pos[0];
if (pos[1] > maxy) maxy = pos[1];
getPositionFromPixel(pos, rect.width + 1, rect.height + 1, rect.x, rect.y, rotation, rotPointX, rotPointY, 0, 0, xScale, yScale);
if (pos[0] < minx) minx = pos[0];
if (pos[1] < miny) miny = pos[1];
if (pos[0] > maxx) maxx = pos[0];
if (pos[1] > maxy) maxy = pos[1];
getPositionFromPixel(pos, rect.width + 1, -1, rect.x, rect.y, rotation, rotPointX, rotPointY, 0, 0, xScale, yScale);
if (pos[0] < minx) minx = pos[0];
if (pos[1] < miny) miny = pos[1];
if (pos[0] > maxx) maxx = pos[0];
if (pos[1] > maxy) maxy = pos[1];
if (minx < 0) minx = 0;
if (miny < 0) miny = 0;
if (maxx > raster.getWidth()) maxx = raster.getWidth();
if (maxy > raster.getHeight()) maxy = raster.getHeight();
int argb;
for (int x = minx; x <= maxx; x++)
for (int y = miny; y <= maxy; y++) {
if (getPixelFromPosition(rect.width, rect.height, pos, x, y, rect.x, rect.y, rotation, rotPointX, rotPointY, 0, 0, xScale, yScale)) {
argb = img.getRGB(pos[0], pos[1]);
color[3] = (argb >> 24) & 0xff;
color[0] = (argb >> 16) & 0xff;
color[1] = (argb >> 8) & 0xff;
color[2] = (argb) & 0xff;
if (x >= 0 && x < raster.getWidth() && y >= 0 && y < raster.getHeight())
if (color[3] > 0)
raster.setPixel(x, y, color);
}
}
}
g.drawImage(bim, 0, 0, null);
}
@Override
public void run() {
while (true) {
repaint();
try {
Thread.sleep(60);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
JFrame f = new JFrame("XPaintTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new BorderLayout());
f.add(new XPaintTest());
f.pack();
f.setVisible(true);
}
}
[/Java]
PS: Wie behandelt man eigendlich den Alphawert eines Pixels wenn man die vorherige Farbe noch sehen soll?
Zuletzt bearbeitet: