# Java zeichnet nicht pixelgenau



## mkdrive2 (29. Jun 2018)

Ich habe ein Problem mit dem Zeichnen in Java. Java zeichnet nicht pixelgenau, und deshalb sind Formen, die ich zeichnen will größer als sie sein sollen, und wenn ich eine gerade Linie zeichnen will, indem ich einzelne Pixel aneinander reihe, dann geht das nicht. Es bleiben einige Pixel aus. Siehe folgendes Beispielprogramm:

```
import javax.swing.*;
import java.awt.*;

public class Programm1
{
    public static void main(String[] args)
    {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new Programm1Component());
        frame.pack();
        frame.setVisible(true);
    }
}

class Programm1Component extends JComponent
{
    public void paintComponent(Graphics g)
    {
        g.fillRect(1,1,20,20);
        g.drawRect(1,25,0,0);
        g.drawRect(2,25,0,0);
        g.drawRect(3,25,0,0);
        g.drawRect(4,25,0,0);
        g.drawRect(5,25,0,0);
        g.drawRect(6,25,0,0);
        g.drawRect(7,25,0,0);
        g.drawRect(8,25,0,0);
        g.drawRect(9,25,0,0);
    }
  
    public Dimension getPreferredSize()
    {
        return new Dimension(300,300);
    }
}
```

So sieht das bei mir aus:






Das Quadrat soll eigentlich nur 20 Pixel breit und hoch sein, aber es ist in Wirklichkeit 25 Pixel breit und hoch. Darunter soll eine 9 Pixel lange, durchgezogene Linie sein, aber stattdessen werden drei Striche gezeichnet.

Ist das nur bei mir so? Habe ich vielleicht eine fehlerhafte Java-Version? Ich benutze javac.exe und java.exe Version 10.0.1

Wenn das so normal ist, gibt es vielleicht eine Alternative in Java, wo ich pixelgenau zeichnen kann?


----------



## Robat (29. Jun 2018)

Also ich kann das Problem nicht reproduzieren .. bei mir werden die "einzelnen Pixel" als Linie dargestellt.
Und wie kommst du darauf, dass dein Quadrat 25 Pixel breit / hoch ist? Du lässt die einzelnen Rechtecke doch bei 25px beginnen wie kann dein großes Quadrat dann auch 25px groß sein?


----------



## mkdrive2 (29. Jun 2018)

Robat hat gesagt.:


> Und wie kommst du darauf, dass dein Quadrat 25 Pixel breit / hoch ist? Du lässt die einzelnen Rechtecke doch bei 25px beginnen wie kann dein großes Quadrat dann auch 25px groß sein?


Ich habe ein "fillRect" mit 20 Pixel Höhe und Breite. Darunter habe ich um 9 Pixel zu zeichnen "drawRect" mit Höhe und Breite 0 gemalt.



> Also ich kann das Problem nicht reproduzieren .. bei mir werden die "einzelnen Pixel" als Linie dargestellt.


Das ist sehr komisch. Bei dir ist es also nicht wie im Bild, das ich hochgeladen habe? Ist das Quadrat bei dir 20 Pixel hoch und breit? Die Linie darunter ist durchgezogen?


----------



## Robat (29. Jun 2018)

So richtig kann ich immer noch nicht nachvollziehen wie du auf die angeblichen 25px kommst.
In der Doku steht ganz klar 


> Fills the specified rectangle. The left and right edges of the rectangle are at x and x + width - 1. The top and bottom edges are at y and y + height - 1. The resulting rectangle covers an area width pixels wide by height pixels tall. The rectangle is filled using the graphics context's current color.



Und das ist auch in dem Fall so. 
Du hast ein Rechteck dessen obere linke Ecke die Koordinaten (1,1) und die untere rechte Ecke (20,20) besitzt. 
Die einzelnen Pixel deiner Linie lässt du alle bei (x, 25) zeichnen .. das bedeutet *wenn* dein Rechteck 25px hoch *wäre *würde es ja mit deiner "Linie" in Verbindung kommen .. tut es aber nicht


----------



## Blender3D (29. Jun 2018)

mkdrive2 hat gesagt.:


> g.drawRect(1,25,0,0);
> g.drawRect(2,25,0,0);
> g.drawRect(3,25,0,0);
> g.drawRect(4,25,0,0);
> ...


Diese Befehle zeichnen gar nichts. Weil du bei Weite und Höhe 0 übergibst.

```
g.drawRect(x, y, width, height)
```


----------



## Robat (29. Jun 2018)

Blender3D hat gesagt.:


> Diese Befehle zeichnen gar nichts. Weil du bei Weite und Höhe 0 übergibst.
> 
> ```
> g.drawRect(x, y, width, height)
> ```


Doch.

```
public void drawRect(int var1, int var2, int var3, int var4) {
    if (var3 >= 0 && var4 >= 0) {
        if (var4 != 0 && var3 != 0) {
            this.drawLine(var1, var2, var1 + var3 - 1, var2);
            this.drawLine(var1 + var3, var2, var1 + var3, var2 + var4 - 1);
            this.drawLine(var1 + var3, var2 + var4, var1 + 1, var2 + var4);
            this.drawLine(var1, var2 + var4, var1, var2 + 1);
        } else {
            this.drawLine(var1, var2, var1 + var3, var2 + var4);
        }

    }
}
```


----------



## mkdrive2 (29. Jun 2018)

Robat hat gesagt.:


> Du hast ein Rechteck dessen obere linke Ecke die Koordinaten (1,1) und die untere rechte Ecke (20,20) besitzt.
> Die einzelnen Pixel deiner Linie lässt du alle bei (x, 25) zeichnen .. das bedeutet *wenn* dein Rechteck 25px hoch *wäre *würde es ja mit deiner "Linie" in Verbindung kommen .. tut es aber nicht


Könntest du bitte das Bild ansehen, das ich in meinem ersten Beitrag eingefügt habe? In dem Bild ist ganz klar zu sehen, dass das Quadrat, das ich mit "fillRect" gezeichnet habe 25 Pixel breit und hoch ist.

Der Grund, warum es nicht über die Linie geht ist, weil *das ganze Fenster *langezogen ist, nicht nur das Quadrat.

Und du hast mir noch nicht geantwortet, ob bei dir die Linie, die ich mit 9 "drawRect"s gezeichnet habe, durchgezogen ist oder wie bei mir im obigen Bild (!!) zwei mal unterbrochen ist.


Blender3D hat gesagt.:


> Diese Befehle zeichnen gar nichts. Weil du bei Weite und Höhe 0 übergibst.


Ich glaube das war ein Scherz von dir. Danke.


----------



## Robat (29. Jun 2018)

mkdrive2 hat gesagt.:


> Und du hast mir noch nicht geantwortet, ob bei dir die Linie, die ich mit 9 "drawRect"s gezeichnet habe, durchgezogen ist oder wie bei mir im obigen Bild (!!) zwei mal unterbrochen ist.


Doch hab ich in meinem ersten Post gemacht. Aber gerne noch mal .. ja die Pixel werden als ganze, durchgezogene Linie dargestellt.



mkdrive2 hat gesagt.:


> In dem Bild ist ganz klar zu sehen, dass das Quadrat, das ich mit "fillRect" gezeichnet habe 25 Pixel breit und hoch ist.


Nö für mich nicht. Wo soll das da zusehen sein?


----------



## mkdrive2 (29. Jun 2018)

Robat hat gesagt.:


> Nö für mich nicht. Wo soll das da zusehen sein?


Ich habe mit dem Windows-Programm "Paint" die Pixel gezählt. Es waren 25 Pixel für Höhe und Breite. Hast du kein Programm, mit dem du Pixel sehen kannst?

Aber danke schonmal, wahrscheinlich habe ich doch ein fehlerhaftes Java. Ich werde es mal mit einer älteren Version versuchen.


----------



## Robat (29. Jun 2018)

Welche Java-Version nutzt du denn? Hab es gerade noch mal mit der JDK8 und JDK10 ausprobiert .. funktioniert wunderbar.



mkdrive2 hat gesagt.:


> Hast du kein Programm, mit dem du Pixel sehen kannst?


Hab ich schon  Und nur für dich habe ich es gerade mal gemacht und es kamen 20x20px raus


----------



## mkdrive2 (29. Jun 2018)

Robat hat gesagt.:


> Hab ich schon  Und nur für dich habe ich es gerade mal gemacht und es kamen 20x20px raus


Du meinst bei dem Bild, das ich oben eingefügt habe? Das verwirrt mich jetzt sehr. Außerdem ist in meinem Bild auch zu sehen, dass die Linie _*nicht*_ durchgezogen ist. Also stimmt bei meinem Computer irgendwas nicht.


----------



## Robat (29. Jun 2018)

Nein hab das ganze mit meinem Demoprojekt gemacht


----------



## mkdrive2 (29. Jun 2018)

Robat hat gesagt.:


> Nein hab das ganze mit meinem Demoprojekt gemacht


Ich weiß, dass bei dir anscheinend alles pixelgenau gezeichnet wird. Ich habe ein Bild oben eingefügt, das zeigt, dass es bei mir nicht so ist! Ich habe das Gefühl, wir reden einander vorbei.


----------



## Robat (29. Jun 2018)

Nein ich hab dich schon verstanden und ich weiß jetzt auch wie du darauf kommst, dass dein Quadrat 25x25 px groß ist.
Wollte dir mit den letzten 2 Posts nur versichern, dass es bei mir nicht so ist und mich es immer noch wundert das bei dir zu viele Px gezeichnet werden.

Welche Java-Version nutzt du?


----------



## mkdrive2 (29. Jun 2018)

*JA!* Mit javac und java Version 1.8.0_171 klappt wieder alles so, wie es sein soll!!! Davor habe ich (wie schon im ersten Beitrag geschrieben) mit Version 10.0.1 gearbeitet. Vielen Dank für alles!


----------



## mihe7 (29. Jun 2018)

Robat hat gesagt.:


> ```
> public void drawRect(int var1, int var2, int var3, int var4) {
> if (var3 >= 0 && var4 >= 0) {
> if (var4 != 0 && var3 != 0) {
> ...


Für den Code im else-Zweig gehört ja jemand gestraft


----------



## Robat (29. Jun 2018)

@mihe7 Diesmal war ichs aber nicht!


----------



## mihe7 (29. Jun 2018)

Robat hat gesagt.:


> @mihe7 Diesmal war ichs aber nicht!



Ich hab mir den diesbezüglichen Kommentar auch sehr verkneifen müssen


----------



## mrBrown (29. Jun 2018)

mkdrive2 hat gesagt.:


> Das Quadrat soll eigentlich nur 20 Pixel breit und hoch sein, aber es ist in Wirklichkeit 25 Pixel breit und hoch. Darunter soll eine 9 Pixel lange, durchgezogene Linie sein, aber stattdessen werden drei Striche gezeichnet.


Hast du die Größe während das Programm lief mit einem Bildschirmlineal oder auf dem Screenshot gemessen? Und sind die Lücken in der Linie auf zur Laufzeit sichtbar?

Zwischen beiden gibt es Unterschiede, je nach eingestellter Auflösung, OS, etc...

Auf'nem Screenshot ist es bei mir 40px breit, zur Laufzeit wie es soll nur 20px und die Linie ist von Java 1.8 bis 11 immer durchgezogen


----------



## mrBrown (29. Jun 2018)

mihe7 hat gesagt.:


> Für den Code im else-Zweig gehört ja jemand gestraft


Wie würdest du es denn schöner lösen? 

Also Abseits von der Benennung, da sollte @Robat sich mal die Sourcen laden


----------



## mihe7 (29. Jun 2018)

mrBrown hat gesagt.:


> Wie würdest du es denn schöner lösen?


So, dass man sofort weiß, was passiert:

```
this.drawLine(var1, var2, var1, var2);
```


----------



## mrBrown (29. Jun 2018)

mihe7 hat gesagt.:


> So, dass man sofort weiß, was passiert:
> 
> ```
> this.drawLine(var1, var2, var1, var2);
> ```


Das macht aber jetzt was anderes als das ursprüngliche


----------



## Robat (29. Jun 2018)

Sicher? Im else-Zweig können var3 und var4 doch eigentlich nur noch 0 sein .. deshalb könnte man die Addition auch getrost ignorieren.


----------



## mrBrown (29. Jun 2018)

Robat hat gesagt.:


> Sicher? Im else-Zweig können var3 und var4 doch eigentlich nur noch 0 sein .. deshalb könnte man die Addition auch getrost ignorieren.


Nö, nur mindestens var3 *oder* var4 sind 0


----------



## mihe7 (29. Jun 2018)

Asche auf mein Haupt: mihe7 hats versaut.


----------



## Robat (29. Jun 2018)

Stimmt .. ich schiebe es mal auf die Uhrzeit.


mihe7 hat gesagt.:


> mihe7 hats versaut.


Das entscheiden andere!


----------



## mrBrown (29. Jun 2018)

Aber wer kommt denn bei sowas auf die Idee, die Reihenfolge zu ändern?!  

```
if (var3 >= 0 && var4 >= 0) {
if (var4 != 0 && var3 != 0) {
```


----------



## mihe7 (29. Jun 2018)

@mrBrown das war Absicht! Irgendwer nach 20 Jahren wird den Code im java-forum schon lesen...

@Robat Auch wenn De Morgan und nicht De Abend grüßen lässt: nein, ich übernehme die volle Verantwortung.


----------



## Robat (29. Jun 2018)

mihe7 hat gesagt.:


> Auch wenn De Morgan und nicht De Abend grüßen lässt:


Made my day


----------



## mkdrive2 (30. Jun 2018)

mrBrown hat gesagt.:


> Hast du die Größe während das Programm lief mit einem Bildschirmlineal oder auf dem Screenshot gemessen? Und sind die Lücken in der Linie auf zur Laufzeit sichtbar?


Ich habe die Größe mit einem Screenshot gemessen. Die Lücken waren aber auch zur Laufzeit da. Ich hatte außerdem noch einige andere Fehler in der Version 10.0.1 entdeckt, wie z.B. dass bei "drawRect(0,0,99,99);" die obere Kante des Quadrats nicht gezeichnet wird und bei "g.drawRect(0,0,99,99); g.drawRect(100,100,99,99); g.drawRect(200,200,99,99);" waren die Quadrate nicht genau diagonal zueinander, sondern um ein Pixel horizontal versetzt. All diese Fehler waren nicht bei der JDK-Version 1.8.0_171 da.



> Zwischen beiden gibt es Unterschiede, je nach eingestellter Auflösung, OS, etc...


Ich benutze Windows 10, und dort scheinen die Screenshots pixelgenau zu sein. Jedenfalls war mein Quadrat auch auf dem Screenshot 20 Pixel groß, wenn ich JDK-Version 1.8.0_171 benutzte.



> Auf'nem Screenshot ist es bei mir 40px breit, zur Laufzeit wie es soll nur 20px und die Linie ist von Java 1.8 bis 11 immer durchgezogen


Darf ich fragen, welchen OS du benutzt?


----------



## mrBrown (30. Jun 2018)

mkdrive2 hat gesagt.:


> Ich habe die Größe mit einem Screenshot gemessen. Die Lücken waren aber auch zur Laufzeit da. Ich hatte außerdem noch einige andere Fehler in der Version 10.0.1 entdeckt, wie z.B. dass bei "drawRect(0,0,99,99);" die obere Kante des Quadrats nicht gezeichnet wird und bei "g.drawRect(0,0,99,99); g.drawRect(100,100,99,99); g.drawRect(200,200,99,99);" waren die Quadrate nicht genau diagonal zueinander, sondern um ein Pixel horizontal versetzt. All diese Fehler waren nicht bei der JDK-Version 1.8.0_171 da.
> 
> Ich benutze Windows 10, und dort scheinen die Screenshots pixelgenau zu sein. Jedenfalls war mein Quadrat auch auf dem Screenshot 20 Pixel groß, wenn ich JDK-Version 1.8.0_171 benutzte.


Tritt das ganze auch auf, wenn du auf ein BufferedImage zeichnest und das speicherst?
Von 8 zu 10 gab es afaik Änderungen bzgl. Displays mit hoher Auflösung. Das _kann_ damit zusammenhängen.
Wie ist deine Auflösung und die HiDPI-Einstellungen (kann man da bei Windows überhaupt was einstellen?)

Die Lücken sollten allerdings nicht auftreten, das kann durchaus ein Bug im JRE sein...



mkdrive2 hat gesagt.:


> Darf ich fragen, welchen OS du benutzt?


macOS


----------



## mihe7 (30. Jun 2018)

Wenn auf dem selben System zwei JDK/JRE-Versionen unterschiedliche Ergebnisse liefern, dann würde ich einen Bug melden (https://bugreport.java.com). An der API hat sich diesbezüglich mW nichts geändert.


----------



## mrBrown (30. Jun 2018)

mihe7 hat gesagt.:


> Wenn auf dem selben System zwei JDK/JRE-Versionen unterschiedliche Ergebnisse liefern, dann würde ich einen Bug melden (https://bugreport.java.com). An der API hat sich diesbezüglich mW nichts geändert.


Umgang mit HiDPI-Grafik hat sich mit 9 geändert: http://openjdk.java.net/jeps/263, da würde ich einen Bug vermuten.

(Zumindest würde eine Skalierung von 1,25 zum Fehler passen:
aus 20 wird 25,
im skalierten an Pixel 4 eine Lücke: 3+1,25=3, 4*1,25=5
im skalierten an Pixel 9 eine Lücke: 7*1,25=8, 8*1,25=10)


----------



## mkdrive2 (30. Jun 2018)

mrBrown hat gesagt.:


> Tritt das ganze auch auf, wenn du auf ein BufferedImage zeichnest und das speicherst?


Ich habe JDK 10 nochmal runtergeladen, installiert und auf ein BufferedImage gezeichnet und als PNG-Datei gespeichert. Auf den Bildern sind die Fehler, die ich oben erwähnt habe, nicht zu sehen.


```
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import javax.imageio.ImageIO;

public class Programm3
{
    public static void main(String[] args)
    {
        BufferedImage img = new BufferedImage(300,300,BufferedImage.TYPE_INT_ARGB);
        Graphics big = img.createGraphics();
        big.setColor(Color.white);
        big.fillRect(0,0,300,300);
        big.setColor(Color.black);
        big.fillRect(1,1,20,20);
        big.drawRect(1,25,0,0);
        big.drawRect(2,25,0,0);
        big.drawRect(3,25,0,0);
        big.drawRect(4,25,0,0);
        big.drawRect(5,25,0,0);
        big.drawRect(6,25,0,0);
        big.drawRect(7,25,0,0);
        big.drawRect(8,25,0,0);
        big.drawRect(9,25,0,0);
      
        try {
            File outputfile = new File("bild1.png");
            ImageIO.write(img, "png", outputfile);
        } catch (IOException e) {
        }
      
        big.setColor(Color.white);
        big.fillRect(0,0,300,300);
        big.setColor(Color.black);
        big.drawRect(0,0,99,99);
        big.drawRect(100,100,99,99);
        big.drawRect(200,200,99,99);
      
        try {
            File outputfile = new File("bild2.png");
            ImageIO.write(img, "png", outputfile);
        } catch (IOException e) {
        }
    }
}
```

Ich habe aber dann nochmal wieder das Programm laufen lassen, das ich in meinem ersten Beitrag eingefügt habe, und wieder der gleiche Fehler. (Unterbrochene Striche.) Auch der gleiche Fehler bei den drei drawRects "g.drawRect(0,0,99,99); g.drawRect(100,100,99,99); g.drawRect(200,200,99,99);". Wieder horizontal versetzte Quadrate.



> Von 8 zu 10 gab es afaik Änderungen bzgl. Displays mit hoher Auflösung. Das _kann_ damit zusammenhängen.
> Wie ist deine Auflösung und die HiDPI-Einstellungen (kann man da bei Windows überhaupt was einstellen?)


Ich weiß nicht, was HiDPI ist. Ich habe den Dell P2416D als Monitor. Der hat 23.8 Zoll Größe und eine Auflösung von 2560 x 1440.



> Die Lücken sollten allerdings nicht auftreten, das kann durchaus ein Bug im JRE sein...


Ich benutze nicht den JRE um die Programme auszuführen, sondern nur die Dateien, die im bin-Ordner der JDK sind.



mihe7 hat gesagt.:


> Wenn auf dem selben System zwei JDK/JRE-Versionen unterschiedliche Ergebnisse liefern, dann würde ich einen Bug melden (https://bugreport.java.com).


Ich habe mir die Seite mal angeguckt und das sieht etwas zu kompliziert aus. Es sieht außerdem auch so aus, als ob sich der Fehler schlecht reproduzieren lassen.


----------



## mrBrown (30. Jun 2018)

mkdrive2 hat gesagt.:


> Ich weiß nicht, was HiDPI ist. Ich habe den Dell P2416D als Monitor. Der hat 23.8 Zoll Größe und eine Auflösung von 2560 x 1440.


Unter "All settings > System > Display" müsste sich ein Slider mit einer Prozentangabe finden lassen, das ist der relevante Wert (und bei dir würde ich raten, er steht auf 125%?)



mkdrive2 hat gesagt.:


> Ich benutze nicht den JRE um die Programme auszuführen, sondern nur die Dateien, die im bin-Ordner der JDK sind.


Das ist im wesentlichen das JRE.



mkdrive2 hat gesagt.:


> Ich habe mir die Seite mal angeguckt und das sieht etwas zu kompliziert aus. Es sieht außerdem auch so aus, als ob sich der Fehler schlecht reproduzieren lässt.


Ich kann es grad mangels Windows nicht reproduzieren, aber generell sollte der reproduzierbar sein, wenn es mit der Skalierung zusammenhängt.
Entweder auf einem System mit Skalierung 125% oder indem man beim Start `-Dsun.java2d.uiScale=1.25`(?) übergibt.


----------



## mihe7 (30. Jun 2018)

mrBrown hat gesagt.:


> Umgang mit HiDPI-Grafik hat sich mit 9 geändert: http://openjdk.java.net/jeps/263



Hm... das darf aber m. E. nicht dazu führen, dass sich zwischen zwei nebeneinander gesetzten Pixel weitere, nicht gesetzte Pixel befinden. Andererseits: "Coordinates are infinitely thin and lie between the pixels of the output device." Blöd ist es so oder so


----------



## mrBrown (30. Jun 2018)

mihe7 hat gesagt.:


> Hm... das darf aber m. E. nicht dazu führen, dass sich zwischen zwei nebeneinander gesetzten Pixel weitere, nicht gesetzte Pixel befinden. Andererseits: "Coordinates are infinitely thin and lie between the pixels of the output device." Blöd ist es so oder so


Ja, die Lücken sollten eigentlich nicht da sein.

Aber zumindest die "Vergrößerung" von 20px auf 25px dürften damit erklärbar sein.


----------



## mkdrive2 (30. Jun 2018)

mrBrown hat gesagt.:


> Unter "All settings > System > Display" müsste sich ein Slider mit einer Prozentangabe finden lassen, das ist der relevante Wert (und bei dir würde ich raten, er steht auf 125%?


Ja, genau! Ich habe gemerkt, dass die Fehler verschwinden, wenn ich hier die Skalierung auf 100% ändere!

Darf ich fragen, was ich in der Seite des Bug-Reports (https://bugreport.java.com/bugreport/start_form.do) bei "Component" und evtl. auch "Subcomponent" schreiben muss? Reicht es, wenn ich bei "Component" "Core Libraries" wähle und "Subcomponent" leer lasse? Das ist sehr unbequem, dass man dort kein Bild einfügen kann...


----------



## mihe7 (30. Jun 2018)

Ich würde mal sagen Component: client-libs, Subcomponent: java.awt


----------



## mkdrive2 (30. Jun 2018)

mihe7 hat gesagt.:


> Ich würde mal sagen Component: client-libs, Subcomponent: java.awt


Danke. Ich habe nun ein Bug report abgeschickt. Bei dem Formular war aber noch ein Pflichtfeld mit dem Label "Company". Da habe ich einfach "private person" hingeschrieben. Ich hoffe, dass das OK sein wird...


----------



## JavaNuub2018 (6. Jul 2018)

Kennt man sich ? ps: Software-Engineering ?? xP


----------

