BGRABitmap tutorial 13/de

From Lazarus wiki
Jump to navigationJump to search

Deutsch (de) English (en)


Home | Tutorial 1 | Tutorial 2 | Tutorial 3 | Tutorial 4 | Tutorial 5 | Tutorial 6 | Tutorial 7 | Tutorial 8 | Tutorial 9 | Tutorial 10 | Tutorial 11 | Tutorial 12 | Tutorial 13 | Tutorial 14 | Tutorial 15 | Tutorial 16 | Edit

Dieses Tutorial beschreibt das Koordinatensystem von BGRABitmap.

Pixelkoordinaten

Die Routinen der Standardleinwand verwenden ganzzahlige Koordinaten. Dies ist ebenfalls so bei der Eigenschaft 'CanvasBGRA', welche die Funktionen der Standardleinwand emuliert, allerdings zusätzlich mit Antialiasing (AntialiasingMode), Alphablending (Eigenschaft 'Opacity' von Pen, Brush und Font) und Gammakorrektur.

Wenn wir uns nur auf die Integerkoordinaten beschränken ohne Antialiasing, dann bestimmen die Koordinaten eine Pixelposition, d.h. ein Quadrat. Wenn wir also eine Linie zeichnen von (0,0) nach (5,5), dann ist das oberste, linke Pixel auch das erste gezeichnete Pixel. Bei der Standardleinwand wird das letzte Pixel nicht gezeichnet, die Linie endet also bei (4,4).

BGRATutorial13a.png

Wenn wir eine Ellipse zeichnen, legt das umgebende Rechteck die Pixel fest, die wir zur Darstellung der Ellipse verwenden. Auch hier gilt, dass bei der Standardleinwand die unteren rechten Koordinaten von der Zeichnung ausgeschlossen sind, somit sind beim Füllen des Rechtecks (0,0)-(5,5) in Wirklichkeit nur die Pixel mit den Koordinaten von 0 bis 4 betroffen.

Fließkommakoordinaten

Wenn wir jetzt mit dezimalen Werte arbeiten, geben die Koordinaten eine Position an, die irgendwo auf den Pixeln liegen kann. Der Wert Breite des Stiftes bedeutet eine wirkliche Distanz, nicht nur eine Anzahl von Pixeln. Was stellt (0.0,0.0) also dar?

Fließkommakoordinaten ab der oberen, linken Ecke

Es könnte beispielsweise die Distanz von der oberen, linken Ecke sein (-dies ist bei BGRABitmap aber nicht der Fall -). Dann würde diese Koordinate die obere, linke Ecke des ersten Pixels sein. Aber wenn wir das so betrachten, ist das Verhalten zwischen ganzzahligen und Fließkommakoordinaten leicht unterschiedlich. Genau, stellen Sie sich vor, wir zeichneten eine waagrechte Linie auf den Pixeln (0,1) bis (4,1). In Pixelkoordinaten wäre das die Linie (0,1)-(5,1) und die Breite wäre 1 Pixel. Nun wollen wir aber dieses Segment mit Fließkommakoordinaten definieren. Die linke Seite wäre bei 0.0, die rechte bei 5.0, das passt. Die obere Seite wäre bei 1.0 und die untere bei 2.0. Hier liegt das Problem. Die Mitte der Linie liegt bei der vertikalen Koordinate 1.5. Um also diese Linie zu zeichnen, sollten wir die Koordinaten (0.0,1.5)-(5.0,1.5) angeben.

BGRATutorial13b.png

Tatsächlich würde jede Linie mit der Breite 1 Pixel bei ganzzahligen Koordinaten zwischen den Pixeln gezeichnet, und dies würde mit Antialiasing zu verschwommenen Linien führen. Was bei horizontalen Koordinaten in Ordnung zu sein schien, ist in Wirklichkeit eine Illusion, denn wenn die Linie abgerundete Enden hat, sind die korrekten Koordinaten (0.5,1.5)-(4.5,1.5). Wenn wir das Problem des letzten Pixels außer Acht lassen, sehen wir, dass diese Koordinaten einfach um 0.5 größer sind.

Fließkommakoordinaten ab der Pixelmitte

Die Koordinaten könnten auch der Abstand von der Mitte des oberen, linken Pixels sein. Mit anderen Worten sind ganzzahlige Werte in der Mitte der Pixel. Dies ist bei den Funktionen von BGRABitmap der Fall. Nimmt man diese Koordinaten, dann ergibt die Linie, die die Pixel (0,1) bis (4,1) füllt, einfach (0.0,1.0)-(4.0,1.0).

BGRATutorial13c.png

Dies ist aus mathematischer Sicht seltsam, aber sehr praktisch, weil Sie ganzzahlige Koordinaten verwenden können, um normale, 1 Pixel breite Linien zu zeichnen. Auf diese Weise gibt es allerdings einen leichten Unterschied im Aufruf zwischen den Funktionen von CanvasBGRA und normalen Fließkommafunktionen von BGRABitmap.

Erzeugen Sie ein neues Projekt

Erzeugen Sie ein neues Projekt und fügen Sie eine Referenz auf BGRABitmap hinzu, genau so wie im ersten Tutorial.

Canvas und BGRACanvas

Versuchen wir zu zeigen, was auf Pixelebene passiert.

Zuerst verwenden wir die Standardleinwand (Canvas):

procedure TForm1.FormPaint(Sender: TObject);
var image: TBGRABitmap;
begin
  image := TBGRABitmap.Create(10,10);
  with image.Canvas do
  begin
    //einfach weiß
    brush.color := clWhite;
    FillRect(0,0,image.width,image.height);
    //blaue Ellipse mit schwarzem Rand
    brush.style := bsClear;
    pen.color := clBlack;  
    Ellipse(0,0,9,9);
  end;
  /strecke das Bild, damit wir die Pixel sehen können
  BGRAReplace(image,image.Resample(image.Width*10,image.Height*10,rmSimpleStretch));
  image.Draw(Canvas,0,0,True);
  image.free;
end;

Die Resample-Option 'rmSimpleStretch' bewahrt uns vor Interpolationsfiltern.

Sie sollten dies erhalten:

BGRATutorial13d.png

Die Pixel der Koordinate 9 sind nicht gefüllt (wie bereits oben unter Umgebungsrechteck erklärt). Jetzt zeichnen wir auf die BGRACanvas. Ändern Sie einfach die 'with'-Zeile:

with image.CanvasBGRA do

Jetzt sollten Sie dies erhalten:

BGRATutorial13e.png

Das Ergebnis ist recht ähnlich, abgesehen vom Antialiasing.

Tiefere Überlegungen

Angenommen, wir wollten eine gefüllte, antialiasierte Ellipse zeichnen, die genau in eine 9x9 Bitmap passt. Wir könnten diesen Code versuchen:

procedure TForm1.FormPaint(Sender: TObject);
var image: TBGRABitmap;
begin
  image := TBGRABitmap.Create(9,9,BGRAWhite);
  image.FillEllipseAntialias(4,4, 4,4, BGRABlack);  
  BGRAReplace(image,image.Resample(image.Width*10,image.Height*10,rmSimpleStretch));
  image.Draw(Canvas,0,0,True);
  image.free;
end;

Wir erhalten:

BGRATutorial13f.png

Wie Sie sehen, ist der Rand nicht vollständig gefüllt. Die Ellipse ist kleiner als erwartet. Tatsächlich liegt der Mittelpunkt der Ellipse bei (4,4) und der linke Rand bei (0,4). Aber aufgepasst, das ist die Mitte des Pixels. Wenn wir also wollen, dass die Ellipse bis zum Rand des Pixels reicht, müssen wir den Radius um 0.5 vergrößern:

  image.FillEllipseAntialias(4,4, 4.5,4.5, BGRABlack);

BGRATutorial13g.png

Gefüllte Formen mit Canvas

Beachten Sie, dass mit der Standardleinwand das Ergebnis sehr überraschend ausfällt. Angenommen, wir wollten das selbe wie oben machen. Wir könnten folgendes versuchen:

    brush.color := clWhite;
    FillRect(0,0,image.width,image.height);
    brush.color := clBlue;
    pen.style := psClear;
    Ellipse(0,0,9,9);

Wir erhalten dies:

BGRATutorial13h.png

In diesem Fall wurde noch ein weiteres Pixel abgezogen. Die Ellipse ist 8 Pixel breit, wohingegen das bereitgestellte Rechteck 9 Pixel breit ist.

Voriges Tutorial (Textfunktionen) | Nächstes Tutorial (Canvas2D)