BGRABitmap tutorial 13/de
│ 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).
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?
Obere, linke Distanz bei Fließkommakoordinaten
It can be for example the distance from the top-left corner (this is not the case of BGRABitmap). In this case, this coordinate would be the upper-left corner of the first pixel. But if we do so, the behavior is slightely different between integer coordinates and floating point coordinates. Indeed, imagine we draw an horizontal segment on pixels (0,1) to (4,1). In pixel coordinates it would be the line (0,1)-(5,1) and the width would be 1 pixel. Now if we want to define this segment with floating point coordinates. The left side would be at 0.0, the right at 5.0, that's ok. The top side would be at 1.0 and the bottom at 2.0. Here is the problem. The center of the line is at a vertical coordinate of 1.5. So to draw this line, we should supply coordinates (0.0,1.5)-(5.0,1.5).
In fact every 1-pixel wide line with integer coordinates would be drawn between pixels, and with antialiasing, this leads to blurred lines. What appeared to be ok with horizontal coordinates is in fact an illusion, because if the line has caps, the correct coordinates are (0.5,1.5)-(4.5,1.5). If we ignore the last pixel issue, we see that these coordinates are simply 0.5 greater.
Pixel-center floating point coordinates
The coordinates can be the distance from the center of the top-left pixel. In other words, integer values are at the center of the pixels.This is the case of BGRABitmap functions. Using these coordinates, the line that fills pixels (0,1) to (4,1) is simply (0.0,1.0)-(4.0,1.0).
This is strange from a mathematical point of view, but very convenient, because you can use integer coordinates to draw normal 1-pixel wide lines. This way, there is little difference between calls to CanvasBGRA functions and BGRABitmap normal floating point functions.
Create a new project
Create a new project and add a reference to BGRABitmap, the same way as in the first tutorial.
Canvas and BGRACanvas
Let's try to show what happens at the pixel level.
First, we'll use the standard Canvas : <delphi>procedure TForm1.FormPaint(Sender: TObject); var image: TBGRABitmap; begin
image := TBGRABitmap.Create(10,10); with image.Canvas do begin //clear with white brush.color := clWhite; FillRect(0,0,image.width,image.height); //blue ellipse with black border brush.style := bsClear; pen.color := clBlack; Ellipse(0,0,9,9); end; /stretch the image so we can see the pixels BGRAReplace(image,image.Resample(image.Width*10,image.Height*10,rmSimpleStretch)); image.Draw(Canvas,0,0,True); image.free;
end; </delphi>
The resample option rmSimpleStretch prevents to use interpolation filters.
You should obtain this :
The pixels of coordinate 9 are not filled as explained above about rectangle bounds. Now, let's draw this BGRACanvas. Just change the 'with' line : <delphi>with image.CanvasBGRA do </delphi>
Now you should obtain this :
The result is very similar except with antialiasing.
Subtle considerations
Suppose we want to fill an antialiased ellipse that fit exactly in a 9x9 bitmap. We could try with this code : <delphi> 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; </delphi>
We obtain :
As you can see, the border is not completely filled. The ellipse is smaller than could be expected. In fact, the center of the ellipse is (4,4) so the left border is at (0,4). But remember it is the center of the pixel. So if we want the ellipse to go to the border of the pixel, we need to add 0.5 to the radius : <delphi> image.FillEllipseAntialias(4,4, 4.5,4.5, BGRABlack); </delphi>
Filled shapes with canvas
Note that with standard canvas, the result is very surprising. Suppose we want to do the same thing. We could try this : <delphi> brush.color := clWhite;
FillRect(0,0,image.width,image.height); brush.color := clBlue; pen.style := psClear; Ellipse(0,0,9,9); </delphi>
We obtain this :
So in this case, there is yet another pixel substracted. The ellipse is 8 pixel wide where as the supplied rectangle is 9 pixel wide.