2D-Grafik SWT mit Transform zeichnet ungenau

Marco13

Top Contributor
Hallo

Erstaunlich, über welch vermeintlich elementare Probleme man stolpert, wenn man sich mal von Swing <3 abwendet, und versucht, mit SWT etwas zu zeichnen:

Das folgende Beispiel sollte nur eine Linie zeichnen. Die 'Transform' für den GC wird dabei ständig um 1 Grad weitergedreht. Immer wenn der Winkel bei 45°+n*90° liegt (die Linie also diagonal stehen sollte), "verschiebt" sich die Zeichnung ein Stück und die Linie wird an eine falsche Position gezeichnet :autsch:

Java:
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Transform;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class RotateTest {
	static int angle = 0;
	public static void main(String[] args) {
		final Display display = new Display();
		final Shell shell = new Shell(display);
		shell.setText("Matrix Tranformations");
		shell.setLayout(new FillLayout());

		final Transform transform = new Transform(display);

		final Canvas canvas = new Canvas(shell, SWT.DOUBLE_BUFFERED);
		canvas.addPaintListener(new PaintListener() {
			public void paintControl(PaintEvent e) {
				GC gc = e.gc;
				gc.setAdvanced(true);
				transform.translate(100, 100);
				transform.rotate(1);
				angle++;
				transform.translate(-100, -100);
				gc.setTransform(transform);
				gc.drawLine(100, 100, 200, 100);
				System.out.println("Angle "+angle);
			}
		});

		Thread t = new Thread(new Runnable() {
			@Override
			public void run() {
				while (!display.isDisposed()) {
					display.asyncExec(new Runnable() {

						@Override
						public void run() {
							if (!canvas.isDisposed()) {
								canvas.redraw();
							}
						}
					});
					try {
						Thread.sleep(20);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}

				}
			}
		});
		t.start();

		shell.setSize(400, 400);
		shell.open();
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch())
				display.sleep();
		}
		display.dispose();
	}
}


Hat jemand eine Idee, woran das liegt? Vielleicht an irgendwelchen "hardwarenahen" und ggf. Betriebssystemspezifischen Aspekten von 'Transform'? Sollte man 'Transform' vielleicht gar nicht benutzen? Welche Möglichkeiten gibt es, mit SWT eine beliebig transformierte Vektorgrafik zu zeichnen, ohne dass die Linien kreuz und quer verlaufen oder plötzlich verschwinden? Oder braucht man tatsächlich solche Krämpfe wie den "Graphics2DRenderer" von diesem Beispiel um "schöne" Grafiken mit SWT zeichnen zu können? Bei Linien könnte man die Endpunkte vielleicht noch manuell transformieren, aber bei komplexeren Objekten ist das ja auch keine Option mehr... :bahnhof:
 

damtre

Aktives Mitglied
Ich habe mich mit dem Zeichnen unter SWT noch nicht wirklich auseinandergesetzt, bisher blieb es bei dem Platzieren von Komponenten! ;)

Aber es existiert ein Bug der bekannt ist der Gerade bei 45, 135, 225 oder 315 Grad beim Zeichnen Fehler verursacht.

Ich konnte das Problem lösen, indem ich bei mir vor der Zeile mit der drawLine folgende Anweisung hinzufügte.

Java:
e.gc.getGCData().state |= 1 << 14;

So wie ich es aber verstehe ist diese Anweisung Betriebssystemabhängig, was bedeutet, dass du für jedes System den rechten Wert anpassen müsstest. Dadurch verlierst du natürlich einen Teil der Plattforumunabhängigkeit. Aber vielleicht brauchst du das ja auch nur für dich und deinen PC! ;)

Ich habe übrigends Windows 7 64-bit.
Hoffe ich konnte dir helfen...


Ach ja!
Gelesen habe ich das u.a. hier:
1. https://bugs.eclipse.org/bugs/show_bug.cgi?id=253670#c2 (2. Kommentar)
und hier
2. http://dev.eclipse.org/viewcvs/viewvc.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet353.java?view=co

Da steht folgendes im Code :
// Because of bug 253670, drawRectangle is incorrect when the rotation is near 45, 135, 225 or 315 degrees.
// Uncomment this next line and adjust the bitfield for your platform. See GC#DRAW_OFFSET.
//e.gc.getGCData().state |= 1 << 9;

Hier steht zwar was von drawRectangle, aber es ist wohl auch bei anderen Zeichenmethoden der Fall.
 
Zuletzt bearbeitet:

Ariol

Top Contributor
Sehr seltsam. Ich kenn mich mit SWT nicht so gut aus, aber für mich sieht das nach einem Bug in
Code:
drawLine
aus.

So sieht man's besser:
Java:
	                GC gc = e.gc;
	                gc.setAdvanced(true);

	                transform.translate(100, 100);
	                transform.rotate(1);
	                transform.translate(-100, -100);
	                gc.setTransform(transform);

	                gc.setBackground(new Color(display, 255,0,0));
	                gc.fillRectangle(0, 0, 200, 200);
	                gc.drawLine(100, 100, 100, 0);	                
	                
	                System.out.println("Angle "+ (++angle));

BTW: Linux Mint (Ubuntu 11.04) 64-bit + Java 1.6.0.26 + SWT 3.7.0v3735b
 

Marco13

Top Contributor
@damtre: Danke, das sieht schonmal interessant aus. Ich hatte zwar nach ein paar Stichworten im Web gesucht, aber irgendwie nichts dazu gefunden (wenn der Bug-Report auch das Wort "Transform" nicht enthält :noe: ;)). Das DRAW_OFFSET scheint nicht in der API zu stehen, scheint also wirklich irgendwas implementierungsspezifisches zu sein. Leider werde ich es vermutlich erst nächste Woche genauer testen können.

@Ariol: Das Problem tritt auch bei sowas wie 'drawOval' auf (bei 'fillOval' anscheinend nicht, sieht im wahrsten Sinne des Wortes schräg aus :autsch: ). Dass du gesagt hast, dass du dich mit SWT nicht so gut auskennst, könnte mich jetzt davon abhalten, diese Frage zu stellen, aber ...: Muss so ein erstelltes "Color"-Objekt nicht irgendwann auch explizit wieder 'disposed' werden?
Irgendwie fühlt sich SWT im Vergleich zu Swing schon ziemlich "sperrig" an...
Und dass bei sowas elementarem so ein offensichtlicher Fehler auftritt... irgendwie komisch ???:L
 

twseitex

Mitglied
Hi, ein Auszug aus dem Quellcode meines Java-Audio-Player für Java SE,
der Bilder animiert (siehe audio, flash and java) und standardgemäß rotiert,
wenn in den GUI-Einstellungen keine Bilder angegeben wurden.

Ich nutze AffineTransform. Das Rotieren klappt lückenlos.

Die Auszüge skizzieren den Ablauf.

// --------------------------------------------------------------------------
// Zeichenfläche: Zeichenfläche aus JFrame.getGraphics()
// bzw. JPanel.getGraphics()
// kann ist relativ links oben wie ein AWT-Element,
// also wie der Container, in der die Zeichenfläche
// liegt (AWT-Ursprung)
// Der Ursprung der Zeichenfläche ist aber maßgeblich
// für alle Malaktionen z.B. OHNE Positions-Angaben,
// denn es wird immer relativ zum Ursprung der
// Zeichenfläche und nicht des Containers, in der die
// Zeichenfläche liegt, gemalt.
// Der Ursprung der Zeichenfläche ist also Offset
// zum AWT-Ursprung.
// Ein Punkt auf der Zeichenfläche ist Class Point.
// Der Punkt kann bewegt werden (auch um offset per
// translate())
// Eine Distanz auf der Zeichenfläche ist Class Point2D
// Ein typischer Fall:
// Ein im Fenster (JFrame oder JPanel) soll rotieren.
// Die Bildposition kann aber ev. garnicht angegeben werden.
// z.B. ohne Rotation per Graphics
// graphicsAnzeigen_Ausgabe_Graphics1D.drawImage
// (this.bufimgBildPuffer,
// intAnzeigen_XposAnzeige,
// intAnzeigen_YposAnzeige,
// intAnzeigen_BildBreite,
// intAnzeigen_BildHoehe,
// containerAnzeigen_Ausgabe
// );
// mit Rotation per Graphics2D
// graphics2dAnzeigen_Ausgabe_Graphics2D.drawImage
// (this.bufimgBildPuffer,
// afftransZV1603,
// containerAnzeigen_Ausgabe
// );
// Für die Rotaton muss die Zeichenfläche auf die zentrierte
// Position verschoben werden, dann rotieren und dann
// Zeichenfläche zurück auf alte Position.
// Wie man sieht, ist Java Meilen entfernt von einer konsistenten
// API der Grafikverarbeitung.
// --------------------------------------------------------------------------

private AffineTransform afftransZV1603=null; // Rotationsobjekt


// +++++ und Rotationsbogenmass an die Java-Programmkomponente
// Affine Transformation
// übergeben, das die Rotation der Bilddaten berechnen kann.

// ----- Rotationsobjekt erzeugen
afftransZV1603 = new AffineTransform();
boZV1005=(afftransZV1603!=null);

if(boZV1005)
{
// ----- Rotation um Anker
if(boZV1602)
{
// ----- ankerbezogene Rotation
// Anker ist intXposAnzeige und intYposAnzeige
afftransZV1603.setToRotation(dblZV1606,
intZV1018,
intZV1019
);
}
else
{
// ----- Rotation ohne Anker
afftransZV1603.setToRotation(dblZV1606);
}
}

// Hinweis: Rotation entlang einer Distanz (Vektor) als Kreisbogen
// möglich, aber man muss dann den Vektor angeben
// setToRotation(double vecx,double vecy,double anchorx,double anchory)
// wobei intern berechnet wird Math.atan2(vecy, vecx)
// wobei Anker entfallen kann

// +++++ Rotation erlaubt, also malen mit Transform
// drawImage hat keine Error-Art, also try-catch nicht nötig
if(boZV04)
{
graphics2dZV1028.drawImage(bufImgZV1030, // this.bufimgBildPuffer,
afftransZV1603,
objJFrameZV00
);
// Es wird die Dimension des gepufferten Bildes verwendet !
// liefert true wenn geladen UND gerendert
// false wenn noch am Laden
// wird anstelle this.bufimgBildPuffer
// der Puffer this.imageBildObjekt verwendet,
// dann wird ein leeres Bild rotiert in Dimension
// laut createImage()
}
else
{
graphics2dZV1028.drawImage(bufImgZV1030, // this.bufimgBildPuffer,
afftransZV1603,
objJPanelZV01
);
// Es wird die Dimension des gepufferten Bildes verwendet !
// liefert true wenn geladen UND gerendert
// false wenn noch am Laden
// wird anstelle this.bufimgBildPuffer
// der Puffer this.imageBildObjekt verwendet,
// dann wird ein leeres Bild rotiert in Dimension
// laut createImage()
}
 

Marco13

Top Contributor
???:L Das ist nicht in code-Tags, enthält gräßliche Variablennamen, und was das mit der "konsistenten API" bedeuten soll, weiß ich nicht. Swing ist IMHO sehr konsistent. Und insbesondere hat das mit SWT nicht viel zu tun. Aber vielleicht war das ja gar nicht so wichtig.
 

twseitex

Mitglied
Hi,

ohne Nutzung von Klassen wie import org.eclipse.swt.SWT
also mit Nutzugn von purem Java SE ist die Graphic API alles andere
als immer schnell und geschweige denn feundlich: Das Rotieren aus
3 Schritten, wovon nur 1 rotiert ist für mich inkonsistent in der API,
die somit zerlegt abbbildet und nicht am Objekt orientiert. Das Modulare
im Java ist erheblicher als die Bildung von freundlichen Klassen für
den Anwender. Das lässt Java sehr unfreundlich erscheinen, dafür
flexibel aber aufwändig .... die Existenz von Spezialversionen an
Java sei aussen vor gelassen. ... Das Erfinden des Rades erfolgt x-mal
täglich bei Javanutzern. Daher auch das Forum.

Dass die Variablennamen grässlich sind, stimmt. Aber die skizzieren
nicht den Ablauf, sondern die Kommentare zu den Javafunktionen,
deren "Bedienung" man in der Klassenbeschreibung nachlesen kann.

Die Methodenfolge und nicht deren Bedienung oder Variablennamen
ermöglichen eine Grafikrotation per Java SE ohne Aussetzer.

Und diese Erfahrung habe ich mitgeteilt. Mein Ziel war es nicht,
einen Swing-Tellerrand einzuhalten. Und das ist aus meiner Sicht
wichtig.
 

damtre

Aktives Mitglied
Muss so ein erstelltes "Color"-Objekt nicht irgendwann auch explizit wieder 'disposed' werden?

In SWT zapfen Bilder, Farben, Fonts, etc. immer Betriebssystemressourcen an. Das bedeutet, dass du diese Objekte immer disposen solltest, wenn du sie nicht mehr benötigst. Das ist bei ein paar Objekten dieser Art vielleicht nicht so tragisch, aber man sollte es sich einfach besser für die Zukunft angewöhnen. Um zur Laufzeit nicht unnötig Ressourcen zu verschwenden. ;)

Wenn du Interesse hast mehr darüber zu erfahren wie du Betriebssystemressourcen sparen und vor allen Dingen wie du es dir mit dem disposen der selbst erzeugten Objekte (das könnten nämlich mit der Zeit ganz schön viele werden...) Vereinfachen kannst, dann könnte folgender Link was für dich sein.

Managing Operating System Resources

Grüße
 
T

Tomate_Salat

Gast
@twseitex:
1.)
icon4.gif
Java™ Quelltext ist bitte in
Java:
[/B]-Tags zu schreiben:[/SIZE] [noparse][code=Java]hier Java Code einfügen
[/noparse]

2.) Diese Variablennamen sind einfach nicht zu rechtfertigen. Wenn ich solche namen einsetzen würde, würden mich meine Kollegen hier zurecht töten. (Und ja: mit diesen namen wirkt Java wirklich Unfreundlich und gemein)
3.) SWT != Swing. Ja SWT ist sogar was komplett anderes. Vllt verwechselt du es mit AWT. SWT wird nicht standardmäßig mit Java SE ausgeliefert.
 

jgh

Top Contributor
sorry, auch wenn es ein wenig offtopic ist:
Java:
e.gc.getGCData().state |= 1 << 14;

kann mir bitte jemand den Teil [c] |= 1 << 14[/c] kurz erklären, oder ein Stichwort dafür zum googlen nennen?
Thx in advance
 
G

Gast2

Gast
sorry, auch wenn es ein wenig offtopic ist:
Java:
e.gc.getGCData().state |= 1 << 14;

kann mir bitte jemand den Teil [c] |= 1 << 14[/c] kurz erklären, oder ein Stichwort dafür zum googlen nennen?
Thx in advance

Such mal nach Bit Operatoren
Hier kleines Bsp.
Java Blog Buch : 02.05.02 “Fortgeschrittene” Operatoren
Bitweiser Operator ? Wikipedia
Guido Krger - JAVA 1.1 lernen - Kapitel 5 Bitweise Operatoren

In SWT werden einige Statuus mit Bitoperatoren angegeben. Denke mal weil es Enums erst ab Java 1.5 gegeben hat.
 
Zuletzt bearbeitet von einem Moderator:

Marco13

Top Contributor
@SirWayne: Draw2D "kenne" ich (d.h. ich weiß von seiner Existenz :oops: ) und es ist nicht unwahrscheinlich, dass ich es genau für dieses Problem in Zukunft verwenden werde. Eigentlich sollte das nur ein Test von etwas völlig anderem sein, was ("nur mal kurz, irgendwie") gezeichnet werden sollte. Das Zeichnen an sich würde später vielleicht mit Draw2D erfolgen, oder ganz anders... Aber abgesehen davon, dass ich denke, dass es nicht schaden kann, wenn man zumindest in Grundzügen auch mal den "Low Level" Teil kennenlernt: Wenn Linien zeichnen (vgl. J. Bresenham, 1962(!)) schon zu solchen Problemen führt, fördert das nicht gerade das Vertrauen darin, dass SWT für solche Sachen überhaupt geeignet ist....

@twseitex: Irgendwie vermute ich, dass das, was du vorhast, mit AffineTransform (Java Platform SE 6) erledigt werden könnte. Diese Klasse hat weniger damit zu tun, dass jemand (einfach, mit einer Zeile) irgendwas rotiert zeichnen will, sondern eher mit der Beschreibung eines mathematischen Konstrukts, das eben so ist, wie es ist. Oft hat man einen Trade-off zwischen Flexibilität und einfacher Verwendbarkeit.

Mein Ziel war es nicht, einen Swing-Tellerrand einzuhalten. Und das ist aus meiner Sicht
wichtig.

Und hier vermute ich, dass das Komma ein Wort zu weit rechts steht, aber ... das ist ja alles Off-Topic.

Es ging nur darum, dass vermeintlich elementare Zeichenoperationen mit SWT einfach nicht richtig funktionieren.

Aber ich werden bald mal die bisherigen Lösungsansätze ausprobieren.
 
G

Gast2

Gast
Kann dir leider nicht mehr sagen, hab mit SWT nur in draw2D gezeichnet. Ich denke das mächtigste Tool hierfür ist immer noch GEF, aber ist nicht trivial.
 

Marco13

Top Contributor
OK, so wie es im von damtre verlinkten Bug-Report beschrieben ist, hat es funktioniert. Ist zwar ein bißchen krampfig (man "muss" sich mit Reflection den Wert des Flags holen :autsch: ) aber es funktioniert.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
L AWT graphics2d transform AWT, Swing, JavaFX & SWT 7
O 2D-Grafik Bild abgeschnitten nach Rotation mit Affine Transform und Hintergrund Schwarz AWT, Swing, JavaFX & SWT 8
N 2D-Grafik AffineTransform - Punkte abfragen nach transform() AWT, Swing, JavaFX & SWT 3
lumo SWT Transform AWT, Swing, JavaFX & SWT 6
dereki2000 AWT Canvas zeichnet nicht AWT, Swing, JavaFX & SWT 7
M Java zeichnet nicht pixelgenau AWT, Swing, JavaFX & SWT 40
S Kann man erzwingen, dass Java 'zu Ende zeichnet'..? AWT, Swing, JavaFX & SWT 7
L JPanel zeichnet im Konstrukter erzeugten Hintergrund nicht AWT, Swing, JavaFX & SWT 10
F JPanel "zeichnet" keinen Text AWT, Swing, JavaFX & SWT 14
G Swing zeichnet zweites Fenster nicht AWT, Swing, JavaFX & SWT 3
P ... zeichnet erst im zweiten Anlauf AWT, Swing, JavaFX & SWT 10
T Graphics zeichnet nicht AWT, Swing, JavaFX & SWT 2
H Swing ListCellRenderer einer JList zeichnet sporadisch falsch AWT, Swing, JavaFX & SWT 6
N JPanel zeichnet nicht AWT, Swing, JavaFX & SWT 6
D Container des JFrame zeichnet sich nicht bei Aufruf von paintAll AWT, Swing, JavaFX & SWT 9
C Programm zeichnet ungenau :-( AWT, Swing, JavaFX & SWT 2
K Graphics-Objekt zeichnet nicht alle Bilder AWT, Swing, JavaFX & SWT 3
M Codeteilung -> Canvas zeichnet nicht mehr AWT, Swing, JavaFX & SWT 40
B View zeichnet Daten aus dem Model ohne Update AWT, Swing, JavaFX & SWT 4
M paint() zeichnet nicht korrekt AWT, Swing, JavaFX & SWT 2
N JPanel zeichnet Inhalt erst nach Größenänderung des JFrames AWT, Swing, JavaFX & SWT 3
F Java zeichnet nicht - Methode wird aber ausgeführt AWT, Swing, JavaFX & SWT 2

Ähnliche Java Themen

Neue Themen


Oben