# Eigener Graphics-Context



## Steev (5. Mai 2010)

Guten Abend liebe Forengemeinde,

ich habe da mal eine Frage, bei der ihr mir hoffentlich weiterhelfen könnt.
Ich habe vor einiger Zeit eine kleine Game-Engine programmiert. Für die Renderung verwende ich ganz normal ein Graphics2D-Objekt, den ich über eine Rendermethode alle Zeichenobjekte erben lasse. Nun wollte ich gerne das Backend meiner Engine so umstellen, dass man einfach die Renderung auf OpenGL oder DirectX umschalten kann.
Also habe ich eine eigene Graphics-Interface und Klasse erstellt, die ich nun anstatt wie bisher Graphics2D an die Rendermethoden übergebe. Das funktioniert soweit auch. Nur bekomme ich leider seltsamme Seiteneffekte.
- auf meinem Rechner (Windows 7 AMD Phenom II X4, NVidia GeForce GTX 280, 8 GB RAM, DirectX 11, Java 6 u 18 (JDK)) werden Bilder sowie Rectangles gerendert, oval, arc, roundrect usw. werden nicht gerendert (!)
- auf meinem Virtuellen PC (Windows XP SP3) werden keine Bilder gerendert, dafür aber rect, oval, roundrect, arc usw.
- auf meinem Notebook (Windows XP SP3) wird alles gerendert

Aufgrund der verschiedenen Laufzeitverhalten verstehe ich nicht so ganz, was ich falsch gemacht habe.

Meine Graphics-Klasse mappt die (für mich) wichtigsten Graphics2D-Methoden (vorerst habe ich nur Swing implementiert). Zu diesem Zweck wird über eine setSource-Methode die "Kernel"-Instanz pro Rendervorgang (vor der Renderung) an die Graphics-Instanz übergeben und als Attribut gespeichert (Ich weis, sollte man nicht machen, geht aber nicht anders...). Während der Renderung wird dann über das gespeicherte Attribut gearbeitet.

Hier mal der Quellcode meiner Graphics-Klasse.
[Java]public class DefaultEGEGraphics implements EGEGraphics {

    // public constant class attributes
    /**
     * A neutral affine transformation for reseting the transformation of the
     * source-graphics.
     */
    public static final AffineTransform EMPTY_TRANSFORM = new AffineTransform();

    // private object attributes
    private Graphics2D                  source;
    private EGETransformation           transformation;
    private EGEVector                   i_vector;
    private EGEFont                     font;
    private EGEColor                    color;
    private EGEComposite                composite;
    private Composite                   oldComposite;

    // constructors
    /**
     * This constructor creates a new graphics-object that will be used for the
     * internal rendering of ege. This constructor should only be used by the
     * internal ege-rendering-classes.
     * 
     * @param graphics
     *            the native java-swing graphics context that should be mapped.
     */
    public DefaultEGEGraphics(Graphics2D graphics) {
        setSource(graphics);
    }

    // implemented methods
    public void clearRect(double x, double y, double width, double height) {
        source.clearRect((int) x, (int) y, (int) width, (int) height);
    }

    public void clipRect(double x, double y, double width, double height) {
        source.clipRect((int) x, (int) y, (int) width, (int) height);
    }

    public EGEGraphics create() {
        return new DefaultEGEGraphics((Graphics2D) source.create());
    }

    public void drawArc(double x, double y, double width, double height, double startAngle, double arcAngle) {
        source.drawArc((int) x, (int) y, (int) width, (int) height, (int) startAngle, (int) arcAngle);
    }

    public void drawImage(ImageResource img, double x, double y) {
        source.drawImage(img.getResource(), (int) x, (int) y, null);
    }

    public void drawImage(ImageResource img, double x, double y, EGEColor bgcolor) {
        source.drawImage(img.getResource(), (int) x, (int) y, (Color) bgcolor.getSource(), null);
    }

    public void drawImage(ImageResource img, double x, double y, double width, double height) {
        source.drawImage(img.getResource(), (int) x, (int) y, (int) width, (int) height, null);
    }

    public void drawImage(ImageResource img, double x, double y, double width, double height, EGEColor bgcolor) {
        source.drawImage(img.getResource(), (int) x, (int) y, (int) width, (int) height, (Color) bgcolor.getSource(), null);
    }

    public void drawLine(double x1, double y1, double x2, double y2) {
        source.drawLine((int) x1, (int) y1, (int) x2, (int) y2);
    }

    public void drawOval(double x, double y, double width, double height) {
        source.drawOval((int) x, (int) y, (int) width, (int) height);
    }

    public void drawRect(double x, double y, double width, double height) {
        source.drawRect((int) x, (int) y, (int) width, (int) height);
    }

    public void drawRoundRect(double x, double y, double width, double height, double arcWidth, double arcHeight) {
        source.drawRoundRect((int) x, (int) y, (int) width, (int) height, (int) arcWidth, (int) arcHeight);
    }

    public void drawShape(EGEShape shape) {
        int v1_p1_x, v1_p1_y, v1_p2_x, v1_p2_y;
        for (int i = 1; i < shape.size(); i++) {
            v1_p1_x = (int) shape.get(i - 1).getX();
            v1_p1_y = (int) shape.get(i - 1).getY();
            v1_p2_x = (int) shape.get(i).getX();
            v1_p2_y = (int) shape.get(i).getY();

            source.drawLine(v1_p1_x, v1_p1_y, v1_p2_x, v1_p2_y);
        }
    }

    public void drawShape(EGEShape shape, Transformable transform) {
        if (i_vector == null)
            i_vector = new EGEVector(0., 0.);
        int v1_p1_x, v1_p1_y, v1_p2_x, v1_p2_y;
        for (int i = 1; i < shape.size(); i++) {
            shape.get(i_vector, transform, i - 1);
            v1_p1_x = (int) i_vector.getX();
            v1_p1_y = (int) i_vector.getY();
            shape.get(i_vector, transform, i);
            v1_p2_x = (int) i_vector.getX();
            v1_p2_y = (int) i_vector.getY();

            source.drawLine(v1_p1_x, v1_p1_y, v1_p2_x, v1_p2_y);
        }
    }

    public void fillArc(double x, double y, double width, double height, double startAngle, double arcAngle) {
        source.fillArc((int) x, (int) y, (int) width, (int) height, (int) startAngle, (int) arcAngle);
    }

    public void fillOval(double x, double y, double width, double height) {
        source.fillOval((int) x, (int) y, (int) width, (int) height);
    }

    public void fillRect(double x, double y, double width, double height) {
        source.fillRect((int) x, (int) y, (int) width, (int) height);

    }

    public void fillRoundRect(double x, double y, double width, double height, double arcWidth, double arcHeight) {
        source.fillRoundRect((int) x, (int) y, (int) width, (int) height, (int) arcWidth, (int) arcHeight);
    }

    public EGEColor getColor() {
        return color;
    }

    public EGEFont getFont() {
        return font;
    }

    public Transformable getTransformation() {
        return transformation;
    }

    public void setClip(double x, double y, double width, double height) {
        source.setClip((int) x, (int) y, (int) width, (int) height);
    }

    public void setColor(EGEColor c) {
        this.color = c;
        source.setColor((Color) color.getSource());
    }

    public void setFont(EGEFont font) {
        if (font == null)
            return;
        this.font = font;
    }

    public void setTransformation(Transformable transformation) {
        this.transformation.setRotation(transformation.getRotation());
        this.transformation.setX(transformation.getX());
        this.transformation.setXScale(transformation.getXScale());
        this.transformation.setXShear(transformation.getXShear());
        this.transformation.setY(transformation.getY());
        this.transformation.setYScale(transformation.getYScale());
        this.transformation.setYShear(transformation.getYShear());
        source.setTransform(EMPTY_TRANSFORM);
        source.translate(this.transformation.getX(), this.transformation.getY());
        source.rotate(this.transformation.getRotation());
        source.scale(this.transformation.getXScale(), this.transformation.getYScale());
        source.shear(this.transformation.getXShear(), this.transformation.getYShear());
    }

    public void translate(double x, double y) {
        transformation.addX(x);
        transformation.addY(y);
        source.translate(x, y);
    }

    public Graphics2D getSource() {
        return source;
    }

    public void setSource(Object source) {
        if (source instanceof Graphics2D) {
            this.source = (Graphics2D) source;
            if (transformation == null)
                transformation = new EGETransformation();
            if (font == null)
                font = EGETemplates.FONT01;
            oldComposite = ((Graphics2D) source).getComposite();
        }
    }

    public EGEComposite getComposite() {
        return composite;
    }

    public void setComposite(EGEComposite composite) {
        this.composite = composite;
        if (composite != null) {
            source.setComposite((Composite) composite.getSource());
        } else if (oldComposite != null) {
            source.setComposite(oldComposite);
        }
    }

    public void drawString(String str, double x, double y) {
        font.drawString(this, str, x, y);
    }

    public void reset() {
        transformation.setRotation(0);
        transformation.setX(0);
        transformation.setXScale(1);
        transformation.setXShear(0);
        transformation.setY(0);
        transformation.setYScale(1);
        transformation.setYShear(0);
        source.setTransform(EMPTY_TRANSFORM);
        source.translate(transformation.getX(), transformation.getY());
        source.rotate(transformation.getRotation());
        source.scale(transformation.getXScale(), transformation.getYScale());
        source.shear(transformation.getXShear(), transformation.getYShear());
        source.setClip(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
    }

    public void resetComposite() {
        source.setComposite(oldComposite);
    }

    public void rotate(double rotation) {
        transformation.setRotation(transformation.getRotation() + rotation);
        source.rotate(PI / 180. * rotation);
    }

    public void scale(double xs, double ys) {
        transformation.setXScale(transformation.getXScale() / xs);
        transformation.setYScale(transformation.getXScale() / ys);
        source.scale(xs, ys);
    }

    public void shear(double xs, double ys) {
        transformation.setXShear(transformation.getXShear() + xs);
        transformation.setYShear(transformation.getXShear() + ys);
        source.shear(xs, ys);
    }
}[/Java]

Ich hoffe ich habe mich einigermaßen verständlich ausgedrückt...
Währe nett, wenn mir jemand weiterhelfen könnte. Für (sinnvolle) Vorschläge bin ich dankbar.

Danke und Gruß
Steev


----------



## Steev (6. Mai 2010)

Weis keiner woran der Fehler liegen könnte? Was vieleicht noch wichtig ist: Die Graphics2D-Instanz kommt von einem VolatileImage. Aber vorher hat das ja auch funktioniert...


----------



## Ebenius (6. Mai 2010)

Graphics2D, nicht nur Graphics implementieren.

Ebenius


----------



## Steev (6. Mai 2010)

ich implementiere doch überhaupt keine Standard-Klasse, die Interface heist EGEGraphics, und liefert alle implementierten Methoden. Graphics und Graphics2D sind doch sowieso abstracte Klassen die in SunGraphics2D implementiert werden.


----------



## Ebenius (6. Mai 2010)

Sorry. Ich hatte Deine Problembeschreibung nicht korrekt aufgenommen. 

Ich würde testweise in den einzelnen Methoden die Farbe auf dem Graphics2D setzen. Vielleicht auch einen Stroke. Oder die Transformation.

Du hast nicht zufällig ein Test-Programm dazu als kleinen Quelltext…

Ebenius


----------



## Steev (6. Mai 2010)

Nein, ich habe leider kein Testprogamm, das sind einige hundert Klassen...

Habe ich vieleicht irgendwas grundsätzlich total falsch gemacht? Wie würdest du das machen?

Was ich gerade rausgefunden habe: ein Testprogramm, wo ich mir den Kontext selbst nochmal zu Fuß erstellt habe funktioniert einwandfrei.
Also nehme ich an, dass irgendwo in der Engine noch was anderes nicht ganz stimmt... Und da kann mir warscheinlich keiner Helfen.


----------



## Ebenius (6. Mai 2010)

Kannst Du Threading-Issues ausschließen? Mach doch mal alle Methoden synchronized. Oder lass Dir die Thread-Names ausgeben. Ohne weiter Code kann ich da erstmal kein Problem sehen… Ansonsten helfen sicher Log-Ausgaben… *schulterzuck*

Ebenius


----------



## Steev (6. Mai 2010)

Ich kann nicht alle Methoden synchronized machen, da ich sonst deathlocks bekomme. Ich werde mal mit VirtualVM gucken, was da intern passiert. Eine wirkliche Vorstellung, was da nicht läuft habe ich nicht. Wenn ich vor drawImage ein drawRect, mit den Maßen des Images schreibe, dann wird der Rect angezeigt, das Image aber nicht. Also wirklich ganz komisch... Keine Ahnung was da falsch ist... Zur Not stelle ich eine VM hoch, wo man das Problem nachstellen kann, wenn ich nicht mehr weiterkomme.
Danke für deine Hilfe.


----------



## Ebenius (6. Mai 2010)

Deadlocks? Auf den Grafikkontext kann man doch nicht aus verschiedenen Threads zugreifen? Das kann doch gar nicht hinhauen… Oder hab ich da nen Denkfehler?

Ebenius


----------



## Steev (6. Mai 2010)

Ach, du meintest nur die Graphic-Context methoden?  Ich hatte das jetzt irgendwie allumfassender verstanden.
Ich kann mir nicht vorstellen, dass das an Threads liegt, da das ja nur auf einem Rechner nicht (mehr) funktioniert.
Ich werde nochmal tief im Code rumwühlen und suchen, ob da irgendwas nicht richtig zusammenarbeitet.


----------



## Steev (7. Mai 2010)

Ich habe den Fehler gefunden 
Das war ein ganz dummer Fehler und direkt in obiger Graphics-Implementiertung enthalten:

In der Methode reset stand bisher folgendes:
[Java]source.setClip(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);[/Java]

Das ist falsch, weil der Min- und der Maxwert vorzeichenlos gleich ist. Der Clip ist aber ein rectangle, sodass die Breite und Höhe immer doppelt so hoch sein muss wie die X- und Y-Position um zentriert zu der Zeichenfläche zu sein.

Gruß
Steev


----------

