# Problem bzw. Sinn von Graphics / Graphics Context



## ernst (28. Feb 2009)

Hallo allerseits,
in meinem Programm unten habe ich ein Bild (Image) erzeugt und dann einen "zugehörigen Grafikkontext" erstellt.
Mit
[HIGHLIGHT="Java"]myimg = createImage(sx, sy);[/HIGHLIGHT]
wird ein Bild erzeugt
Mit 
[HIGHLIGHT="Java"]myg = myimg.getGraphics();[/HIGHLIGHT]
wird der sogenante Grafikkontext erzeugt.
Dann habe ich in den "zugehörigen Grafikkontext" gezeichnet mit:
[HIGHLIGHT="Java"]myg.drawOval(ort*20 , ort*20, 20, 20);    [/HIGHLIGHT]                    
und in paintComponent den Kreis gezeichnet:
[HIGHLIGHT="Java"]public void paintComponent(Graphics g){
  System.out.println("paintComponent");
  g.drawImage(myimg,0,0,null);
}[/HIGHLIGHT]

I)
1)
Mir ist leider die dahinterstehende Logik bzw. Systematik _überhaupt_ nicht klar.
Kann mir dies jemand anschaulich an Beispielen erklären?
Bitte lest zuerst mein ganzes Posting, bevor ihr antwortet, damit ihr nachvollzieghen könnt, womit ich Probleme habe.

2)
Was ist der sogenannte Grafikkontext Graphics, welchen Sinn hat er?

3)
Warum braucht man ihn in paintComponent() als Parameter?

4)
Die Bilddaten (also aus welcher Farbe jeweils ein einzelnes Pixel eines Bildes besteht) sollen nicht im Graphics-Objekt gespeichert sein.
Wo sind dieses dann gespeichert?

Die Antwort auf diese Fragen konnte ich auch nicht im Internet finden.


II)
Weitere Fragen:
1)
Warum wird beim Zeichnen eines Kreises in das Bild myimg nicht das Objekt myimg angegeben. Konkret warum wird das nicht wie folgt realisiert, d.h.
wäre es von der Logik nicht klarer, wenn dies
[HIGHLIGHT="Java"]myimg.drawOval(i , 2*i+15, 20, 20);[/HIGHLIGHT]
heissen würde?

2)
Wo wird dann mit 
[HIGHLIGHT="Java"]g.drawOval(i , 2*i+15, 20, 20);[/HIGHLIGHT]
hingezeichnet?
Hier gibt es ja kein zu g zugehöriges Bild, in das das gezeichnet
werden soll.

3)
Warum wurde es nicht so realisiert, dass man paintComponent(...) als Parameter nicht das Bild übergibt, das dann von paintComponent(...) auf den Bildschirm gezeichnet wird.
Warum der ganze Umweg mit Graphics ??

mfg
Ernst


-----------------------------------------------------------
[HIGHLIGHT="Java"]package de;
import java.awt.*;
import javax.swing.*;

public class MainVerzoegertZeichnen1 {
	  public static void main(String[] args){
	    JFrame f = new JFrame();
	    f.setSize(550,550);
	    Diagramm diagramm = new Diagramm(550, 550);
	    f.getContentPane().add(diagramm);
	    Thread t = new Thread(diagramm);
	    t.start();
	    f.setVisible(true);
	  }
	}

class Diagramm extends JPanel implements Runnable{
  private int xpAnz;
  private int ypAnz;
  private int i; 
  private Image myimg;
  private Graphics myg;
  private int sx; 
  private int sy;
  private boolean ichMaleGerade;

  public Diagramm(int xpAnz, int ypAnz){  
    i=0; 
    this.xpAnz=xpAnz;
    this.ypAnz=ypAnz;    
    myimg=null;  
    ichMaleGerade=true;    
	//myg.setColor(Color.red);    
  }

  public void paintComponent(Graphics g){
  	if(myimg==null){
  	    sx = this.getSize().width;  
  	    sy = this.getSize().height;
  	    myimg = createImage(sx, sy);
  	    myg = myimg.getGraphics();
  	}
  	myg.setColor(Color.red);  	
  	myg.drawOval(i , 2*i+15, 20, 20);
        ichMaleGerade=false;    
  	g.drawImage(myimg,0,0,null);
  }

  public void run(){
    while(i<200){
      this.repaint();
      while(ichMaleGerade==true){
      }
      ichMaleGerade=true;

      try{
        Thread.sleep(500);
      }
      catch(Exception e){}
      i = i+20;
    }
  }
}[/HIGHLIGHT]
-----------------------------------------------------------


----------



## 0x7F800000 (28. Feb 2009)

> Was ist der sogenannte Grafikkontext Graphics, welchen Sinn hat er?


Kannst es dir als so eine Art multifunktionspinsel+papier vorstellen. Wenn du was zeichnen willst brauchst du den Pinsel und das dazugehörige "Papier" (etwa ein Bild, das im dunklen Speicher rumliegt, oder die Pixeln einer GUI, die direkt auf den Bildschirm gemalt werden)


> Warum braucht man ihn in paintComponent() als Parameter?


paintComponent(Graphics g) malt den Component. Zum malen muss er wissen wohin er malt und womit er malt. Diese Informationen sind ja genau in Graphics g enthalten.


> Die Bilddaten (also aus welcher Farbe jeweils ein einzelnes Pixel eines Bildes besteht) sollen nicht im Graphics-Objekt gespeichert sein.
> Wo sind dieses dann gespeichert?


Wo sollen denn die Bilddaten gespeichert werden, außer im Bild selbst?



> Warum wird beim Zeichnen eines Kreises in das Bild myimg nicht das Objekt myimg angegeben.


wozu? Dieses "myg" gehört doch nach konstruktion zu diesem "myimg", zeichnet also direkt auf diesem Bild, wieso soll man zweimal redundante Informationen übergeben?


> Konkret warum wird das nicht wie folgt realisiert, d.h.
> wäre es von der Logik nicht klarer, wenn dies
> myimg.drawOval(i , 2*i+15, 20, 20);
> heissen würde?


das nennt sich Abstraktion. Zeichnen eines Ovals hat absolut nichts damit zu tun, ob man auf einem von zehn millionen verschiedenen Bildtypen oder zehn milliarden verschiedener Components/JComponents zeichnet. Diese methode funktioniert bei jedem einigermaßen rechteckig angeordneten Pixelhaufen. Deswegen wird es einmal in Graphics implementiert, und fertig. Kein Mensch schreibt milliarde mal irgendwas, was auch nur einmal geschrieben werden kann. Siehe DRY.


> Wo wird dann mit
> g.drawOval(i , 2*i+15, 20, 20);
> hingezeichnet?


kommt drauf an woher das g kommt. Ich kann die entsprechende Zeile im code nicht finden, daher kann ich auch zur herkunft des "g" nichts sagen.



> Warum wurde es nicht so realisiert, dass man paintComponent(...) als Parameter nicht das Bild übergibt, das dann von paintComponent(...) auf den Bildschirm gezeichnet wird.
> Warum der ganze Umweg mit Graphics ??


Schonmal ein gewöhnliches vom breiten Publikum benutztes (J)Component gesehen?
Buttons, Scrollbars, Textfields, TextAreas aller art, Drehfelder, ComboBoxes, RadioButtons und teufel weiß was sonst noch alles... wieviele von denen malen irgendwelche Bildchen? Richtig, eigentlich keine. Wieso sollte man da also irgendeine extra methode speziell zum malen von irgendwelchen Bildern erstellen, die braucht doch kein Mensch...

Übrigens: JLabels können bildchen malen, JButtons können auch bildchen malen, da muss man nur das richtige Icon setzen, da braucht man auch keine paintComponent-sachen zu überschreiben, das ist Alles schon implementiert.


----------



## ernst (1. Mrz 2009)

Andrey hat gesagt.:


> Kannst es dir als so eine Art multifunktionspinsel+papier vorstellen. Wenn du was zeichnen willst brauchst du den Pinsel und das dazugehörige "Papier" (etwa ein Bild, das im dunklen Speicher rumliegt, oder die Pixeln einer GUI, die direkt auf den Bildschirm gemalt werden)


Das mit dem Pinsel ist mir klar, aber das mit dem Papier verstehe ich nicht:
Meine Vorstellung:
Grahics besteht aus dem Pinsel (dessen Farbe man z.B. einstellen kann) _und_ einer Zeichenfläche, die später auf den Bildschirm gebracht wird. 
Ist das richtig bzw. müsste es korrekt beschrieben werden?



Andrey hat gesagt.:


> paintComponent(Graphics g) malt den Component. Zum malen muss er wissen wohin er malt und womit er malt. Diese Informationen sind ja genau in Graphics g enthalten.


a) Wohin er malt?
Wo ist in g die Information enthalten, wohin er malen soll?
Ich denke, er sol auf den Bildschirm malen?

b)
repaint() ruft u.a. paintComponent(...) auf. In den Anweisungen, aus denen repaint() intern besteht, befindet sich u.a. eine, die ein Graphics Objekt erzeugt (ich nenne dasObjekt mal pg) , das die Zeichenfläche enthält,die zu dem JFrame gehört (mit einer voreingestellten Farbe des Multifunktionspinsels). Und mit diesem Objekt pg als Parameter wird paintComponent(pg) aufgerufen.
Ist das richtig bzw. müsste es korrekt beschrieben werden?



Andrey hat gesagt.:


> Wo sollen denn die Bilddaten gespeichert werden, außer im Bild selbst?


ok, _nur_ in mying
Ist das richtig bzw. müsste es korrekt beschrieben werden?



Andrey hat gesagt.:


> wozu? Dieses "myg" gehört doch nach konstruktion zu diesem "myimg", zeichnet also direkt auf diesem Bild, wieso soll man zweimal redundante Informationen übergeben?


Das versteh ich überhaupt nicht:
Warum soll "myg" zu "myimg" gehören? Ich sehe den Zusammenhang nicht!
Meine Vorstellung:
Auf das Bild myimg soll wird gemalt werden, z.B. mit mying.drawOval(..) und dann wird dieses myimg in myg gebracht!
Ich verstehe immer noch nicht:
myg.drawOval(ort*20 , ort*20, 20, 20); 
Zuerst wird doch in das Bild myimg gemalt, erst dann wird dieses Bild in die Zeichenfläche von myg gebracht (kopiert).



Andrey hat gesagt.:


> das nennt sich Abstraktion. Zeichnen eines Ovals hat absolut nichts damit zu tun, ob man auf einem von zehn millionen verschiedenen Bildtypen oder zehn milliarden verschiedener Components/JComponents zeichnet. Diese methode funktioniert bei jedem einigermaßen rechteckig angeordneten Pixelhaufen. Deswegen wird es einmal in Graphics implementiert, und fertig. Kein Mensch schreibt milliarde mal irgendwas, was auch nur einmal geschrieben werden kann. Siehe DRY.


a) Ich verstehe nicht, was du meinst:
Wenn ich in ein Bild etwas male, verändere ich doch die Pixel dieses Bilds myimg
b) Bem:
was ist DRY ?



Andrey hat gesagt.:


> kommt drauf an woher das g kommt. Ich kann die entsprechende Zeile im code nicht finden, daher kann ich auch zur herkunft des "g" nichts sagen.


Ich korrigiere, es muss heissen:
myg.drawOval(i , 2*i+15, 20, 20);



Andrey hat gesagt.:


> Schonmal ein gewöhnliches vom breiten Publikum benutztes (J)Component gesehen?
> Buttons, Scrollbars, Textfields, TextAreas aller art, Drehfelder, ComboBoxes, RadioButtons und teufel weiß was sonst noch alles... wieviele von denen malen irgendwelche Bildchen? Richtig, eigentlich keine. Wieso sollte man da also irgendeine extra methode speziell zum malen von irgendwelchen Bildern erstellen, die braucht doch kein Mensch...


Ich weiss leider nicht, was du meinst.



Andrey hat gesagt.:


> Übrigens: JLabels können bildchen malen, JButtons können auch bildchen malen, da muss man nur das richtige Icon setzen, da braucht man auch keine paintComponent-sachen zu überschreiben, das ist Alles schon implementiert.


Ich weiss leider nicht, was du meinst.


mfg
Ernst


----------



## newcron (1. Mrz 2009)

Ich fürchte die "Pinsel und Papier" Metaffer funktioniert im bezug auf deine Fragen nicht, also solltest du sie im Verständnis für das Problem einfach mal vergessen - gehen wir also zu den harten fakten. 

Ein _Image_ repräsentiert ein Bild in einem beliebigen Format (Vektor- oder Pixelbasiert, mit 16 Milliarden Farben, mit einer Farbpalette oder in Schwarz/Weiß). In diesem sinne könntest du tatsächlich von der Leinwand sprechen. Im informatischen Sinne enthält ein Image die tatsächlichen Bilddaten (als Pixelarray in der Regel). Wenn du das weiter denkst, besteht der Bildschirm bzw. das Betriebssystem auch nur aus verschiedenen Zeichenflächen die übereinander liegen. Eine für den Desktop, eine für jedes Applikationsfenster, auf diesem Applikationsfenster sind wiederum Zeichenflächen für alle Eingabelemente wie Buttons, Checkboxen u.ä. 

Mit ein _Graphics_ Objekt liefert die notwendigen Werkzeuge um eine solche Zeichenfläche mit Inhalten zu füllen - also beispielsweise Kreise, Rechtecke, Farbverläufe oder tatsächliche Bilder darauf zu malen. Um eine bestimmte Zeichenfläche zu bearbeiten benötigst du das eine(!) zugehörige Graphics Objekt. Bedenke, dass du in einer Applikation viele zu bezeichenbare Flächen hast (jeder Button, jedes Fenster,... alle JComponents also sind eine) und für jede dieser Flächen gibt es genau einen Graphics-Kontext, mit dem man darauf zeichnen kann und der auch darauf aufpasst, dass man nicht über die Ränder dieser einzelnen Flächen hinausmalt.

Jede Zeichenfläche (also jeder JComponent hat eine Methode _paintComponant(Graphics g)_, die aufgerufen wird, um sie zu bezeichnen (was ziemlich häufig geschieht, wenn sich Komponenten oder deren Inhalt verändern, den Fokus bekommen, etc). Das übergebene Graphics Objekt ist dementsprechend der Graphics Kontext, der für diese Fläche zuständig ist - den du also brauchst, um etwas darauf zu zeichnen. (Wie gesagt, pro JComponent gibt es dafür genau eine). 

Bei Bildern sieht das etwas anders aus. Wenn du ein neues Bild erstellst (mit new BufferedImage(...) ), und etwas darauf zeichnen willst, hast du dafür erstmal keinen Graphicskontext und musst ihn dir vom Bild holen. Anders als JComponents werden Bilder in der regel nicht ständig neugezeichnet, weshalb sie keine paintComponent Methode haben und brauchen - das würde das ganze nur fürchterlich aufblähen. 

DRY steht übrigens für "Don't Repeat Yourself". 

Liebe Grüße,
newcron


----------



## ernst (1. Mrz 2009)

Hallo newcron,
habe deine Aussagen wie folgt interpretiert:

Zu jeder Zeichenkomponente (Button, Label, JFrame, ...) existiert genau ein "Zeichenrahmen" auf dem Bildschirm. Diesen Zeichenrahmen könnte man durch die Koordinaten seiner linken oberen und rechten unteren Ecke charakterisieren. Diese Koordinaten könnten dann z.B. die Attribute des zugehörigen Graphics Objekts sein. 
Ist das richtig?
Bemerkung:
Warum hat dann aber Graphics keine Attribute (laut Javadoc hat Graphics nur Methoden).


Was machen die folgenden Anweisungen:
1) 
myimg = createImage(sx, sy);
Erzeugt ein Bild
Ist das richtig?

2)
myg = myimg.getGraphics();
Erzeugt den zu myimg zugehörigen Zeichenrahmen.
Ist das richtig?

3)
myg.setColor(Color.red);  	
Setzt die Farbe des zu dem Zeichenrahmen zugehörigen Pinsel auf rot.
Ist das richtig?

4)
myg.drawOval(i , 2*i+15, 20, 20);
Malt in die zu dem Zeichenrahmen zugehörige Zeichenfläche (die man dan allerdings nicht mehr braucht, oder?) eine Ellipse _UND_ bemalt (verändert) auch gleichzeitig das Bild myimg.
Da myimg keine Methode hat, mit der man seine Pixelinhalte verändern kann, kann man dies nur über den Umweg von myg machen. Sonst hat die Verwendung von myg hier keinen Sinn (d.h. die Bemalung der Zeichenfläche von myg ist umsonst).
Ist das richtig?

6)
g.drawImage(myimg,0,0,null);
In den zu diagramm zugehörigen Zeichenrahmen wird das (durch myg.drawOval(i , 2*i+15, 20, 20)) veränderte Bild myimg gezeichnet.
Ist das richtig?

mfg
Ernst


----------



## Illuvatar (1. Mrz 2009)

Graphics ist erstmal nur eine Klasse. Die Klasse ist dazu da, dass man mit ihr irgendetwas malt. Was und wohin, das ist dieser abstrakten Oberklasse zunächst ziemlich egal.
Wenn du jetzt ein Bild erzeugst (Nummer 1: richtig) und in dieses Bild malen willst, dann sagst du dem Bild: ich will in dich malen, gib mir mal ein Graphics-Objekt. Das ist, was bei Nummer 2 passiert - die Metapher eines "Rahmens" find ich seltsam, bzw. weiß nicht was sie bedeuten soll. Wenn du in Metaphern denken willst, dann hast du jetzt einen Pinsel mit dem du direkt auf das Bild malen kannst. Intern kriegst du dann eine konkrete Subklasse von Graphics, die so implementiert ist, dass bei einem Aufruf direkt die benötigten Pixel des Bild-Objektes auf die benötigte Farbe geändert werden. Nummer 3: richtig.
Bei 4) liegt jetzt glaub das Problem: myg hat keine irgendwie extra Zeichenfläche. myimg ist die Zeichenfläche von myg. Die Klasse von myg ist schließlich genau so implementiert, dass das funktioniert. Das folgende ist natürlich nicht, wie der Code in echt aussieht, aber wie man ihn sich vorstellen kann:
[HIGHLIGHT="Java"]
private static class GraphicsForAnImage extends Graphics
{
  private BufferedImage myImage;
  ...
  public void drawOval(int x, int y, int w, int h) {
    Point[] points = ComplicatedMaths.getPointsForAnOval(x, y, w, h);
    for (Point p : points)
      myImage.setPixelColor(p.x, p.y, this.foregroundColor); // in echt heißt die Methode "setRGB"
  }
  ...
}
[/HIGHLIGHT]

(Was ist Punkt 5? )
Die Stelle, an der du jetzt bei Punkt 6 bist, ist etwas total anderes. DU hast jetzt dein Image-Objekt fertig - ein Bild, auf das ein Oval gezeichnet ist. Jetzt willst du dieses Bild auf deinem Monitor anzeigen. Auch dafür brauchst du wieder ein Graphics-Objekt - die Dinger können auch ganze Bilder malen. Allerdings ist das jetzt ein komplett anderes Graphics-Objekt. Dieses Graphics hat als Zeichenfläche jetzt einen Bereich auf deinem Bildschirm - das ist dann eine andere konkrete Implementation von Graphics! Die Klasse, die du an der Stelle hast, hat nichts mehr mit Bildern zu tun (außer dass sie Bilder malen kann). Sie wird vielmehr die Pixel, die diesesmal gezeichnet werden sollen, irgendwie an das Betriebssystem weitergeben, damit sie auf dem Bildschirm erscheinen.


----------



## 0x7F800000 (1. Mrz 2009)

ernst hat gesagt.:


> Meine Vorstellung:
> Grahics besteht aus dem Pinsel (dessen Farbe man z.B. einstellen kann) _und_ einer Zeichenfläche, die später auf den Bildschirm gebracht wird.


Naja, fast. Auf den Bildschirm muss es ja nicht gebracht werden. Du kannst ein 20000x30000px großes bild im Speicher erzeugen und an einen Plakatdrucker schicken, ohne überhaupt einen Bildschirm irgendwo in deinem System zu haben. Du kannst ein Captcha-Bildchen im Speicher eines Servers erzeugen und an den Client rausschicken, da brauchst du auch kein Bildschirm dafür: der Server kann auch eine graue Kiste sein, da kommt ein Kabel zur Stromversorgung rein, und ein Kabel zum Internet raus, braucht auch keine Bildschirme.



> Wo ist in g die Information enthalten, wohin er malen soll?
> Ich denke, er sol auf den Bildschirm malen?


Das weiß nicht mal der Teufel. Graphics ist erstmal eine abstrakte Klasse, wenn du ein Graphics objekt zu einem Bild bekommen willst, dann geht es erstmal zu createImage() die ein Graphics2D erzeugt, diese wiederum reicht es weiter an irgendeine statische GraphicsEnvironement.getLocalGraphicsEnvironement().createGraphics() methode, und die macht dann auch wiederum irgendwas... Factory über Factory über Factory in drei schichten gestapelt, und am ende wird irgendwo in den tiefen des rechners das passende konkrete Graphics-Objekt erzeugt. Woher dieser Graphics-Objekt bei swing kommt? Keine Ahnung. Kommt wohl irgendwie vom Betriebsystem von ganz weit oben bis zu JComponents runtergetropft. Wie Swing und das Betriebssystem das unter einander klären weiß ich nicht, und bin auch froh drum, dass ich das gar nicht zu wissen brauche.


> repaint() ruft u.a. paintComponent(...) auf. In den Anweisungen, aus denen repaint() intern besteht, befindet sich u.a. eine, die ein Graphics Objekt erzeugt (ich nenne dasObjekt mal pg) , das die Zeichenfläche enthält,die zu dem JFrame gehört (mit einer voreingestellten Farbe des Multifunktionspinsels). Und mit diesem Objekt pg als Parameter wird paintComponent(pg) aufgerufen.


Nein, vollkommen falsch. repaint sorgt irgendwie dafür, dass paint(Graphics g) aufgerufen wird. Das Graphics g kommt immer "von oben", also entweder von dem darüberliegenden Parent-Component oder von ganz oben (etwa swing&betriebssystem, oder einem Programmierer, der die gesammte GUI in irgendein verborgenes Bild malen will).


> Das versteh ich überhaupt nicht:
> Warum soll "myg" zu "myimg" gehören? Ich sehe den Zusammenhang nicht!


Da ist doch der Zusammenhang, steht in der zweiten Zeile deines eigenen Beitrags:

```
myg=myimg.getGraphics();
```



> Zuerst wird doch in das Bild myimg gemalt, erst dann wird dieses Bild in die Zeichenfläche von myg gebracht (kopiert).


Was soll das heißen? myimg ist die Zeichenfläche von myg.


> was ist DRY ?


DRY



> Bzgl gewöhnlicher JComponents:
> Ich weiss leider nicht, was du meinst.


So sehen die eigentlich aus: http://openbook.galileocomputing.de...16_001.htm#mj17e3a93270e42cfef0cb07da4a019cce (bis zu abbildungen runterscrollen) Wie man sieht: fast nirgends sind irgendwelche Bilder zu sehen, die ganzen Components sind etwas "Praxisorientierter".



> Bzgl Icons in JLabels:
> Ich weiss leider nicht, was du meinst.


http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JLabel.html#setIcon(javax.swing.Icon)
Und naja, google ist auch hier dein bester freund...


----------



## slawaweis (1. Mrz 2009)

Hallo ernst,

ich versuche es zuerst auch mal grundlegend zu erklären, ohne auf deine Fragen einzugehen. Du verstrickst Dich im Detail, ohne das Ganze mal von Oben betrachtet zu haben. Erst mal, man hätte "myimg.drawOval" machen können, also die Zeichenfunktion direkt am Image aufrufen. Falsch ist es schon mal nicht, es ist eine Designentscheidung, später mehr. Zuerst fangen wir beim Betriebssystem an. Auf einem OS mit grafischer Benutzeroberfläche gibt es mehrere Wege Grafik auszugeben. Da wäre natürlich der Bildschirm (oder mehrere), der Drucker, die Grafikkarte (für z.B. 3D-Grafik) oder ein Raster-Bild im Speicher, welches man dann auf der Festplatte speichern oder übers Internet versenden kann. D.h. es gibt eine Menge Ausgabearten, die alle auf verschiedene Weisen funktionieren. Bei Java kommt noch hinzu, dass die Grafikausgabe auf verschiedenen Plattformen (z.B. Windows, Mac, Linux) möglichst gleich ablaufen soll.

Nun gibt es mehrere Ausgabearten und OS-Plattformen. Sie teilen sich eine Menge gleicher Grundfunktionen, wie z.B. "zeichne Linie", "zeichne Kreis" oder "fülle Fläche". Diese Funktionen haben den selben Sinn, aber funktionieren Intern jeweils anders. Graphics und Graphics2D kapseln nun diese Funktionalität in einer Standardschnittstelle. Diese Schnittstelle ermöglicht dem Programmierer diese Grundfunktionen zu verwenden, ohne wissen zu müssen auf welcher Ausgabeart oder OS-Plattform das Programm gerade läuft. Graphics speichert selber nichts, außer Metainformationen, es delegiert nur den Sinn "fülle Fläche" an die jeweilige Ausgabeart.

Ich versuche das Ganze an einem Beispiel zu demonstrieren. Normallerweise will man mit der Grafik irgendwas ausdrücken, wie z.B. "zeichne einen Oval". Das ist der Sinn. Wo man es jetzt konkret ausdrückt, sollte sekundär sein. Deshalb schreiben wir zuerst den Sinn auf:


```
public class MyGraphicsContent
{
 public MyGraphicsContent(int ort)
  {
  this.ort = ort;
  }

 public void paintContent(Graphics g)
  {
  g.drawOval(ort*20 , ort*20, 20, 20);
  }
}
```

Das ist das, was wir ausdrücken wollen. Jetzt wollen wir es auch anzeigen. Zuerst geben wir den Sinn in einer Grafik im Speicher aus:


```
myimg = createImage(sx, sy);
MyGraphicsContent mgc = new MyGraphicsContent(100);
mgc.paintContent(myimg.getGraphics());
```

Jetzt geben wir den Sinn direkt auf den Bildschirm aus:


```
public void paintComponent(Graphics g)
 {
 super.paintComponent(g);
 MyGraphicsContent mgc = new MyGraphicsContent(100);
 mgc.paintContent(g);
 }
```

Hier sieht man das schöne Konzept von "Separation of Concerns", zu Deutsch "Trennung der Belange". In "MyGraphicsContent" können wir uns jetzt ganz dem Sinn widmen und die Ausgabe auf verschiedenen Ausgabearten (z.B. auch Drucker) ist mit ein paar Zeilen erledigt.

Jetzt noch mal zu "myimg.drawOval", also drawOval direkt auf dem Image aufrufen. Man kann so was selber realisieren:


```
public class GImage extends Image
{
 public void drawOval(int x, int y, int w, int h)
  {
  this.getGraphics().drawOval(x, y, w, h);
  }
}

// ...

Image myimg = new GImage(...);
myimg.drawOval(ort*20 , ort*20, 20, 20);
```

Man kann es also so machen, es würde auch funktionieren und bei kleinen Sachen auch ausreichen. Würden wir uns jetzt aber wieder "MyGraphicsContent" betrachten, könnten wir keine Funktion mehr mit "Graphics g" realisieren. Man müsste dann eine Fallunterscheidung von GImage, GScreen oder GPrinter machen und im Grunde auch die Zeichnenoperationen auf jeden Fall vermehren, was die Wartbarkeit sehr erschwert.

Im Grunde ist Graphics eine Abstraktion, eine sehr wichtige Eigenschaft der OO-Programierung. Die restlichen Einschränkungen von Graphics ergeben sich aus dem hohem Abstraktionsgrad von Java. Man könnte Graphics auch verstecken, nur will man das wirklich?

------------------------

Noch eine Sache zuletzt. Dein erstes Beispiel bezieht sich darauf, dass man zuerst ein Image erzeugt, dann darin malt und dann dieses Image in paintComponent ausgibt. Diese Technik nennt sich *Buffering*. Bei Betriebssystemen ist es nämlich so, dass die Ausgabe auf den Bildschirm nicht gespeichert wird. D.h. falls sich ein Bereich des Bildschirms ändern, wird paintComponent immer wieder aufgerufen. Bei sehr aufwendigen Zeichenoperationen kann das zu Flackern oder langsamen Benutzeroberflächen führen, den es wird immer wieder neu gezeichnet. Falls also sich die Darstellung im eigenen Programm nicht oft ändert, ist es sinnvoll diese in einem Offscreen-Image (Speicher) zu ändern und bei paintComponent nur das Image zu malen. Hier ist ein kurzer Artikel zum Thema:

http://de.wikipedia.org/wiki/Offscreen_(Computer)

Slawa


----------



## ernst (1. Mrz 2009)

Hier meine Sicht der Dinge mit der Bitte um Korrektur:
So wie ich dich verstanden habe, kann man ein Graphics-Objekt als ein - ich sage es mal bewußt unscharf und geschwallt - als "grafisches Informationobjekt" auffassen, in dem je nach Verwendung etwas anderes stehen kann (z.B. GraphicsForAnImage):

1) Fall:
myg = myimg.getGraphics();
In myg steht u.a. ein Verweis auf alle Pixel von myimg (wobei myg intern die von dir so konstruierte Subklasse GraphicsForAnImage sein könnte).
Ist das richtig

2) Fall:
myg.setColor(Color.red);  	
In myg steht, dass die Farbe des Stifts rot ist (wobei myg intern die von dir so konstruierte Subklasse GraphicsForAnImage sein könnte).
Ist das richtig?

3) Fall:
myg.drawOval(i , 2*i+15, 20, 20);
In myg steht u.a. ein Verweis auf alle Pixel von myimg (wobei myg intern die von dir so konstruierte Subklasse GraphicsForAnImage sein könnte).
Diese Methode verändert durch das Zeichnen der Ellipse die Pixel von myimg, die die Ellipse betreffen.
Ist das richtig?

4) Fall:
  public void paintComponent(Graphics g){
    // ...
     g.drawImage(myimg,0,0,null);
  }
Hier ist g _nicht_ mehr die Subklasse GraphicsForAnImage.
Aber was ist es sonst?
Meine Antwort:
+Einschaltung+
repaint() ist ja _intern_ irgendwie so in der Art programmiert:
---------------------
repaint(){
g = new Graphics()
wobei g ein grafisches Informationobjekt ist, das die Bildschirmkoordinaten des linken und rechten Ecks der grafischen Komponente (z.B. Fenster) enthält, die auf dem Bildschirm dargestellt werden soll. 
...
paintComponent(Graphics g){
}
---------------------
+Auschaltung+

In der Anweisung
g.drawImage(myimg,0,0,null);
können nun die Pixel von myimg auf dem Bildschirm (das grafisches Informationobjekt g weiss ja wo sich die Zeichenfläche auf dem Bildschirm - Bildschirmkoordinaten des Fensters sind, wie gerade beschrieben bekannt - befinden muss) ausgegeben werden.
Ist das richtig?

mfg
Ernst


----------



## 0x7F800000 (1. Mrz 2009)

erste 3) Punkte: ja, so in etwa
4) zum X-ten mal, nein.

Wenn dich das so sehr interessiert, kannst du ja mal in dem Installationsordner deiner JDK unter
jdk1.6.xyz/src/awt/Component
nachguggen, wie die repaint()-methode implementiert ist:
[HIGHLIGHT="Java"]
    public void repaint(long tm, int x, int y, int width, int height) {
        if (this.peer instanceof LightweightPeer) {
            // Needs to be translated to parent coordinates since
            // a parent native container provides the actual repaint
            // services.  Additionally, the request is restricted to
            // the bounds of the component.
            if (parent != null) {
                int px = this.x + ((x < 0) ? 0 : x);
                int py = this.y + ((y < 0) ? 0 : y);
                int pwidth = (width > this.width) ? this.width : width;
                int pheight = (height > this.height) ? this.height : height;
                parent.repaint(tm, px, py, pwidth, pheight);
            }
        } else {
            if (isVisible() && (this.peer != null) &&
                (width > 0) && (height > 0)) {
                PaintEvent e = new PaintEvent(this, PaintEvent.UPDATE,
                                              new Rectangle(x, y, width, height));
                Toolkit.getEventQueue().postEvent(e);           
            }
        }
    }
[/HIGHLIGHT]

bei JComponent ist das wesentlich kürzer und wesentlich verständlicher:
[HIGHLIGHT="Java"]
public void repaint(long tm, int x, int y, int width, int height) {
        RepaintManager.currentManager(this).addDirtyRegion(this, x, y, width, height);
    }
[/HIGHLIGHT]
Beide methoden haben absolut nichts mit dem Zeichenvorgang an sich zu tun.

Durch aufruf dieser Methoden wird dem System lediglich gesagt, welcher bereich neugezeichnet werden muss: wenn etwa ein gedrückter button neugezeichnet werden soll, wird der gesammte bereich, wo der Button ist, als ungültig markiert, und muss dann eben neugezeichnet werden.

Dann kommt da irgendwann ein großes gruseliges Swing-Monstrum, schaut sich das ganze an, stellt fest: aha, hier ist ein dirty region, da sollte alles neugezeichnet werden. Also wendet es sich an das Betriebssystem, bekommt von diesem alle nötige Werkzeuge, wandelt diese in ein Graphics Objekt um, damit Java-Klassen damit etwas anfangen können, und übergibt dieses Graphics-Object an irgendwelche JComponents, die dann zuerst sich selbst neuzeichnen, und dann das Graphics Objekt an ihre Kinder weiterreichen, damit die sich auch wieder neuzeichnen können. 
 Dieses Graphics objekt muss irgendwo her von "ganz weit oben" kommen, schließlich werden da keine Bildchen auf dem Heap erzeugt, sondern es wird einigermaßen direkt "auf dem Bildschirm" gemalt.
Dieses Graphics Objekt wird also sicherlich nicht von irgendwelchen kleinen unbedeutenden JComponents in der repaint methode erzeugt.


----------



## ernst (3. Mrz 2009)

Wenn repaint(...) nichts mit dem Malvorgang zu tun hat, muss das Malen "jemand" anders machen.
Versuch einer Erklärung:
Wenn z.B. durch eine Aktivität eines Anwenders ein Fenster des Bildschirms verändert wird, wird auf unterster Ebene eine Interruptserviceroutine aufgerufen, in Java wird das wohl der Handler eines Listeners sein, der dann u.a. repaint(), also repaint() _ohne_ Parameter aufruft.
Dieser stellt fest, welches Fenster (Region auf dem Bildschirm, Bildschirmkoordinaten des linkes bzw. rechten Ecks) betroffen ist und ruft dann das entsprechende repaint(...) mit Parametern, also z.B:
public void repaint(long tm, int x, int y, int width, int height) 
auf, das die neu zu zeichnende Region managen soll.
Dieses repaint(...) mit Parametern stellt einen entsprechenden Malauftrag in eine Warteschlange (Malaufragswarteschlange). Dann ist die Arbeit von repaint(...) erledigt. 
Ein Task der VJM (die – ich gehe mal davon aus - auch aus verschiedenen Tasks besteht) bearbeitet irgend wann mal diese Malaufragswarteschlange (und benutzt dazu intern sicher Funktionen des Betriebssystems) und bastelt beim Auftrag “zeichne Region X neu“ ein Graphics-Object, hier g genannt (dieses g ist ein anderes Grapics Objekt als das myg, also etwas anderes als das fiktive GraphicsForAnImage von myg.drawOval(i , 2*i+15, 20, 20)).
Dieses Graphics-Object enthält u.a. die Bildschirmkoordinaten des linken und rechten Ecks der zu zeichnenden Region des Bildschirms. Dann ruft dieser Task auf:
paintComponent(g)
In der Anweisung
g.drawImage(myimg,0,0,null);
können nun die Pixel von myimg auf dem Bildschirm (das grafisches Informationobjekt g weiss ja wo sich die Malfläche auf dem Bildschirm - d.h. die Bildschirmkoordinaten des Fensters - befindet) ausgegeben werden.
Ist das richtig?

mfg
Ernst


----------



## Ebenius (3. Mrz 2009)

Ohje, mach's am besten nicht zu kompliziert. Aber ich versuch's auch mal zu erklären. 

In Java AWT (Swing setzt auch nur darauf auf) gibt es eine EventQueue. Die Queue speichert Events die nacheinander abgearbeitet werden (FIFO). Wenn eine Maus bewegt wird, meldet das System dies an die Anwendung. Es wird ein Ereignis zur EventQueue hinzugefügt. Wird ein Fenster über ein Java Fenster gelegt, wird auch ein Ereignis (sogar mehrere verschiedene, aber das führt zu weit) in die EventQueue gelegt. Auch wenn das Java-Fenster verschoben wird, wenn es maximiert wird, wenn es verkleinert oder vergrößert wird, wenn es aus Sicht des Systems neu gezeichnet werden muss, und so weiter.

Es gibt nur einen Thread Event Dispatch Thread, der wie eine Pumpe funktioniert. Er holt den nächsten Event aus der EventQueue liefert ihn an die zugehörige Komponente aus. Eines dieser Ereignisse ist der PaintEvent. Wenn eine Komponente dieses Ereignis abarbeitet, dann wird die paint()-Methode der Komponente aufgerufen*.

Es gibt LightWeight-Komponenten (leichtgewichtig) und HeavyWeight-Komponenten (schwergewichtig). Man spricht von einer HeavyWeight-Komponente (schwergewichtig), wenn sie direkt an eine Resource im System angebunden ist, anderenfalls von einer LightWeight-Komponente. In der Regel sind in Swing alle Komponenten die auf Fenstern liegen light weight und alle Fenster heavy weight. Jede angezeigte Komponente ist immer über einen ComponentPeer angebunden; HeavyWeight-Komponenten über ihren eigenen, LightWeight-Komponenten über den der in der Hierarchy nächsten HeavyWeight-Komponente, also in der Regel über den Peer des Fensters. Der ComponentPeer stellt unter anderem das Graphics-Objekt zur Verfügung. Dieses ist die Schnittstelle für Zeichenoperationen und greift auf die vom System zur Verfügung gestellten Zeichenfunktionen zurück.

Repaint einer Komponente funktioniert so, dass in die EventQueue ein PaintEvent eingebracht wird. Sobald der Event Dispatch Thread dieses Event an die Komponente verteilt hat, wird diese, wie im den vorangegangenen Absätzen beschrieben, den Event abarbeiten, indem sie auf das Graphics-Objekt des Peers schreibt.

* Entweder direkt (bei AWT) oder über den RepaintManager, der etwas klüger ist und verhindert, dass Bereiche gezeichnet werden die von anderen gleich wieder übermalt werden. Für das Verständnis des Zeichnens ist dies aber egal.

Ebenius


----------



## ernst (4. Mrz 2009)

Hallo Ebenius,

1)
“ Man spricht von einer HeavyWeight-Komponente, wenn sie direkt an eine Ressource im System angebunden ist.“
Ich kann mir leider unter HeavyWeight-Komponente und LightWeight-Komponente  nichts konkretes vorstellen. Im Internet habe ich darüber auch nur Schwammiges gelesen.
Wenn ein Programm auf die “Ressourcen“ (also Speicher, z.B. speziell Bildschirmspeicher, oder Dateien - gibt es noch weitere?) zugreifen will, geschieht dies unter Zuhilfenahme des Betriebssystems.
Dabei ist es doch egal ob mit “HeavyWeight-Komponente“ oder “LightWeight-Komponente“  
Kannst du mir mal den Unterschied an einem konkreten Beispiel zeigen?

2)
“Jede angezeigte Komponente ist immer über einen ComponentPeer angebunden“
Was meinst du damit?
Dass beim Zugriff auf ein Fenster (d.h. letzten Endes Bildschirmspeicher), dies in Java dadurch geschieht, dass dies über Methoden einer bestimmten Klasse geschieht, z.B. der Klasse ComponentPeer?
Heißt das, dass intern ein Objekt der Klasse ComponentPeer erzeugt wird und dann mit einer Methode dieser Klasse konkret in den Bildschirmspeicher geschrieben wird?

3)
Meine Interpretation deiner Beschreibung
Wenn ein Fenster verändert wird macht der Handler des EDT intern die Anweisung repaint().
(das gleiche wird veranlasst, wenn das repaint() eines Programmierers ausgeführt wird).
Dieses repaint() (ohne Parameter) stellt einen Auftrag in die Warteschlange (=Eventqueue) des EDT.
Der EDT arbeitet diesen Auftrag irgendwann ab, indem er u.a. ein repaint(...)  mit Parameter das zu entsprechenden Komponente gehört, d.h. des zugehörigen Fensters ausführt (z.B. fenster1.repaint(...).
Intern wird beim Ausführen des repaint(...) über den Aufruf einer Methode eines zu einer Komponente (Fenster) zugehörendem ComponentPeer-Objekt das Graphics-Objekt g erzeugt. Des weiteren wird dann beim internen Abarbeiten von repaint(...) die Methode paintComponent mit diesem Graphics-Objekt g als Parameter aufgerufen.
Stimmt das oder wie kann man sich den Zusammenhang zwischen
repaint()
repaint(...)
paintComponent(Graphics g)
vorstellen?

mfg
Ernst


----------



## Ebenius (4. Mrz 2009)

Hallo Ernst,

*Zu 1. und 2.*

Dann grabe ich mal tiefer. Im Windows (ohne Java) funktioniert das ganze meinem maroden Gedächtnis zu Folge so: Jede Komponente (Frame, Button, ScrollBar, ...) hat ein eindeutiges Handle. Das Betriebssystem kennt die Handles aller angezeigten Komponten. Windows weiß, wo sich die Komponenten befinden und wie groß sie sind. Wenn Du mit der Maus irgendwohin klickst, sendet Windows eine Nachricht für genau die richtige Komponente. Die Komponente hat die Möglichkeit per Systemfunktionen in den Ihr zugewiesenen Bereich zu zeichnen.

Im AWT/Swing gibt es zwei verschiedene Sorten von Komponenten. Die HeavyWeight-Komponenten sind Komponenten wie oben beschrieben. Das Betriebssystem kennt die Komponenten, Nachrichten werden vom Betriebssystem genau für diese Komponenten gesendet, sie haben selbst die Möglichkeit zu zeichnen. Die Schnittstelle zwischen dem System und der HeavyWeight-AWT/Swing-Komponente ist der ComponentPeer. Der Peer wird aufgebaut, wenn die Komponente darstellbar (Component.isDisplayable()) wird und wird zerstört, wenn die Komponente nicht mehr darstellbar ist. Er bietet das Graphics-Objekt an, auf dem gezeichnet wird, kann die Position setzen und abfragen; Tod und Teufel. Der Name HeavyWeight rührt daher, dass die Komponenten sehr aufwändig an Systemresourcen gebunden sind.

Auf der anderen Seite gibt es LightWeight-Komponenten. Diese sind für das Betriebssystem gar nicht vorhanden. Sie haben daher auch keinen ComponentPeer. Für sie gibt es daher keine direkten Nachrichten vom System. Und die Komponenten haben auch keinen eigenen Bereich in den sie Zeichnen können. Beispiele: 
Ein JButton (LightWeight) liegt auf einem JFrame (HeavyWeight). Wenn Du auf den Button drückst, dann weiß das Betriebssystem nicht, dass da ein Button ist. Es kennt an dieser Stelle nur ein Fenster. Das Betriebssystem wird also eine Nachricht für das Fenster erstellen. Diese Nachricht wird dann innerhalb von Java an den JButton weitergeleitet.

Ein JButton (LightWeight) liegt auf einem JFrame (HeavyWeight). Der JButton soll neu zeichnen. Da der JButton keinen Peer hat weil er LightWeight ist, guckt er nach, ob sein Parent einen hat (dann bei dessen Parent, usw. bis ein Peer vorhanden ist). Letztendlich zeichnet der Button über den ComponentPeer des Fensters.

Ein JButton (LightWeight) liegt auf einem JFrame (HeavyWeight). Das Betriebssystem stellt fest, dass ein Teil dieses Fensters (also ein Rechteck) neu gezeichnet werden muss. Das Rechteck liegt genau innerhalb des Buttons, aber das weiß das Betriebssystem ja nicht, weil es den Button nicht kennt. Es sendet also eine Nachricht für das Fenster, dass der Bereich neu gezeichnet werden muss. Innerhalb Java wird dann festgestellt, dass der betroffene Bereich nur den Button berührt (über Größe und Position des Buttons relativ zu dem neu zu zeichnenden Rechteck), also wird der Button neu gezeichnet, siehe Beispiel 2.

*Zu 3.*

Deine Beschreibung stimmt fast. Allerdings meinst Du nicht "repaint(...)" sondern "paint(...)". Dort liegt auch der Unterschied. Zusammengefasst: 
paint(Graphics) zeichnet den Inhalt einer Komponente auf ein Graphics-Objekt

paintComponent(Graphics) gibt's nur im Swing, wird von paint(Graphics) aufgerufen und zeichnet nur den Inhalt (Border zum Beispiel wird per paintBorder(Graphics) gezeichnet)

repaint() legt einen PaintEvent in die EventQueue

Ebenius


----------



## ernst (4. Mrz 2009)

Ich habe geschrieben:
"Dieses repaint() (ohne Parameter) stellt einen Auftrag in die Warteschlange (=Eventqueue) des EDT."
Du hast das gleiche geschrieben:
"repaint() legt einen PaintEvent in die EventQueue"
Deswegen verstehe ich nicht, warum du schreibst:
"Deine Beschreibung stimmt fast. Allerdings meinst Du nicht "repaint(...)" sondern "paint(...)".

Deswegen habe ich eine Bitte:
Kannst du mir bitte meine Beschreibung (die ich hier wiederhole) so umformulieren bzw. konkret korrigieren, dass sie stimmt.
Dann kann ich sie zu "meinen Akten nehmen".

------------------------------------------------------------------------------
Meine Interpretation deiner Beschreibung:
Wenn ein Fenster verändert wird macht der Handler des EDT intern die Anweisung repaint().
(das gleiche wird veranlasst, wenn das repaint() eines Programmierers ausgeführt wird).
Dieses repaint() (ohne Parameter) stellt einen Auftrag in die Warteschlange (=Eventqueue) des EDT.
Der EDT arbeitet diesen Auftrag irgendwann ab, indem er u.a. ein repaint(...) mit Parameter das zu entsprechenden Komponente gehört, d.h. des zugehörigen Fensters ausführt (z.B. fenster1.repaint(...).
Intern wird beim Ausführen des repaint(...) über den Aufruf einer Methode eines zu einer Komponente (Fenster) zugehörendem ComponentPeer-Objekt das Graphics-Objekt g erzeugt. Des weiteren wird dann beim internen Abarbeiten von repaint(...) die Methode paintComponent mit diesem Graphics-Objekt g als Parameter aufgerufen.
Stimmt das oder wie kann man sich den Zusammenhang zwischen
repaint()
repaint(...)
paintComponent(Graphics g)
vorstellen?
------------------------------------------------------------------------------

mfg
Ernst


----------



## Ebenius (4. Mrz 2009)

So klappt's dann ungefähr:


ernst hat gesagt.:


> Meine Interpretation deiner Beschreibung:
> Wenn ein Fenster verändert wird macht der Handler des EDT intern die Anweisung repaint().
> (das gleiche wird veranlasst, wenn das repaint() eines Programmierers ausgeführt wird).
> Dieses repaint() (ohne Parameter) stellt einen Auftrag in die Warteschlange (=Eventqueue) des EDT.
> ...


----------



## ernst (6. Mrz 2009)

Hallo Ebenius,
ein Problem habe ich noch:
Du schreibst:
...
Der EDT arbeitet diesen Auftrag irgendwann ab, indem er u.a. ein
paint(Graphics)
...
und dann später:
...
Intern wird beim Ausführen des
paint(Graphics)
über den Aufruf einer Methode eines zu einer Komponente (Fenster) zugehörendem ComponentPeer-Objekt das Graphics-Objekt g erzeugt. Des weiteren wird dann beim internen Abarbeiten von
...

Frage:
1) Heißt das, dass in paint(Graphics g) g nur eine Referenz ist, um auf das in dann _intern in_ paint(Graphics g), vielleicht so, erzeugte Graphics-Objekt 
gg = new Graphics(...) 
mit 
g = gg zu verweisen?


2)
Mit 
fenster1.paint(...)
meinst du doch das von dir oben angegebene
paint(Graphics) ?

Meine Interpretaion, mit der Bitte diese nochmals zu korrigieren:
Konkret:
Wenn z.B. ein Fenster verändert wird macht der Eventhandler des EDT intern die Anweisung repaint(). Das gleiche wird veranlasst, wenn das repaint() eines Programmierers ausgeführt wird. 
Dieses repaint() (ohne Parameter) stellt einen Auftrag in die Warteschlange (=Eventqueue) des EDT.

Der EDT arbeitet diesen Auftrag irgendwann ab, indem er u.a. eine Methode die sich auf die  entsprechenden Komponente (Fenster) bezieht, ausführt - z.B. fenster1.paint(Graphics g) - und außerdem ein Graphics-Objekt erzeugt, auf das g dann verweist (intern wird z.B. mit new ein Graphics-Objekt gg erzeugt und g = gg gesetzt, wobei gg auch die Bildschirmkoordinaten des zugehörigen Fensters enthält). 
fenster1.paint(...) ruft dann intern - aber nur bei Swing  - paintComponent(g) auf.
Eine in paintComponent(g) vorkommende Anweisung wie 
g.drawImage(myimg,0,0,null);
kann nun die Pixel von myimg auf dem Bildschirm (das Objekt g kennt ja die Bildschirmkoordinaten des zugehörigen Fensters) ausgeben..
Ist das richtig?

Kurz:
Der EDT arbeitet diesen Auftrag irgendwann ab, indem er die zu dem betreffenden Fenster gehörende Methode paint(g) aufruft, die außerdem ein Graphics-Objekt erzeugt, auf das g verweist (das die Bildschirmkoordinaten von g kennt).
Bei Swing ruft diese noch  paintComponent(g) auf.
Eine in paintComponent(g) vorkommende Anweisung g.drawImage(myimg,0,0,null)
kann nun die Pixel von myimg auf dem Bildschirm (g kennt diese) ausgeben.
Ist das richtig?

mfg
Ernst


----------



## Ebenius (6. Mrz 2009)

Das wird mir grad alles zu kompliziert.


Graphics wird vom ComponentPeer erzeugt und ist die Schnittstelle zum Zeichnen. Graphics kennt keine Bildschirmkoordinaten.

repaint() legt einen PaintEvent in die Event Queue.

Das Betriebssystem verschickt zum Zeichnen ebenfalls Nachrichten, die als PaintEvent in der Queue landen können.

Der Event Dispatcher holt sich nacheinander Events aus der Queue. Ein PaintEvent wird so behandelt, dass letztendlich Component.paint(Graphics) mit dem Graphics-Objekt aus dem ComponentPeer aufgerufen wird.

In Swing ist paint(Graphics) eben noch in mehrere paintXXXX(Graphics)-Methoden unterteilt; das hat aber mit dem Verständnis für's System nix zu tun.

Ebenius


----------



## ernst (6. Mrz 2009)

Du schreibst:
"Graphics kennt keine Bildschirmkoordinaten".

Mit der folgenden Methode 
[HIGHLIGHT="Java"]paintComponent(Graphics g){
  g.drawImage(myimg,0,0,null);
}[/HIGHLIGHT]
wird _letztendlich_ etwas auf den Bildschirm gemalt.
Welche Methode gibt dann letztendlich die Pixel auf dem Bildschirm aus, bzw. ruft dann die Methode auf, die dies veranlasst? 
Passiert dies innerhalb von g.drawImage(myimg,0,0,null), bzw. wird dort intern die entsprechende Methode aufgerufen?

mfg
Ernst


----------



## Ebenius (6. Mrz 2009)

Der Koordinatenursprung des Graphics-Objekts bezüglich der Bildschirm-Koordinaten ist entweder in der konkreten Implementierung von Graphics (Graphics ist ja abstrakt) oder in einer von Graphics benutzten Funktionalität verborgen. Wo genau das passiert interessiert aber nicht. Von Graphics aus gesehen (also aus Sicht der abstrakten Klasse und derer Verwender) ist diese Information uninteressant.



ernst hat gesagt.:


> [...] wird _letztendlich_ etwas auf den Bildschirm gemalt.


Oder auf einen Drucker, einen Plotter, eine Leinwand, in eine andere Komponente, ein Image, ein PDF und ein Bildschirm gleichzeitig... Da gibt's viele Möglichkeiten.

Ebenius


----------



## ernst (6. Mrz 2009)

Bist du mit der folgenden Formulierung einverstanden?

repaint()stellt einen Auftrag (PaintEvent) in die Warteschlange (=Eventqueue) des EDT.
Ein PaintEvent wird so behandelt, dass letztendlich (bei Swing) Component.paint(Graphics g) mit dem erzeugten Graphics-Objekt g aufgerufen wird, wobei sich in g die notwendigen Informationen zur Ausgabe (z.B. aus einen Drucker, einen Plotter, eine Leinwand, in eine andere Komponente, ein Image, ein PDF und ein Bildschirm) befinden.

mfg
Ernst


----------



## Ebenius (6. Mrz 2009)

So wird's. Der Rest wäre Krümelkackerei. 

Ebenius


----------

