Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>It is not very hard to implement Xiaolin Wu's anti-aliasing line-rendering algorithm in Delphi. I used <a href="http://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm" rel="noreferrer">the Wikipedia article</a> as a reference when I wrote the following procedure (actually, I just translated the pseudo-code to Delphi and corrected a bug, and added support for a coloured background):</p> <pre><code>procedure DrawAntialisedLine(Canvas: TCanvas; const AX1, AY1, AX2, AY2: real; const LineColor: TColor); var swapped: boolean; procedure plot(const x, y, c: real); var resclr: TColor; begin if swapped then resclr := Canvas.Pixels[round(y), round(x)] else resclr := Canvas.Pixels[round(x), round(y)]; resclr := RGB(round(GetRValue(resclr) * (1-c) + GetRValue(LineColor) * c), round(GetGValue(resclr) * (1-c) + GetGValue(LineColor) * c), round(GetBValue(resclr) * (1-c) + GetBValue(LineColor) * c)); if swapped then Canvas.Pixels[round(y), round(x)] := resclr else Canvas.Pixels[round(x), round(y)] := resclr; end; function rfrac(const x: real): real; inline; begin rfrac := 1 - frac(x); end; procedure swap(var a, b: real); var tmp: real; begin tmp := a; a := b; b := tmp; end; var x1, x2, y1, y2, dx, dy, gradient, xend, yend, xgap, xpxl1, ypxl1, xpxl2, ypxl2, intery: real; x: integer; begin x1 := AX1; x2 := AX2; y1 := AY1; y2 := AY2; dx := x2 - x1; dy := y2 - y1; swapped := abs(dx) &lt; abs(dy); if swapped then begin swap(x1, y1); swap(x2, y2); swap(dx, dy); end; if x2 &lt; x1 then begin swap(x1, x2); swap(y1, y2); end; gradient := dy / dx; xend := round(x1); yend := y1 + gradient * (xend - x1); xgap := rfrac(x1 + 0.5); xpxl1 := xend; ypxl1 := floor(yend); plot(xpxl1, ypxl1, rfrac(yend) * xgap); plot(xpxl1, ypxl1 + 1, frac(yend) * xgap); intery := yend + gradient; xend := round(x2); yend := y2 + gradient * (xend - x2); xgap := frac(x2 + 0.5); xpxl2 := xend; ypxl2 := floor(yend); plot(xpxl2, ypxl2, rfrac(yend) * xgap); plot(xpxl2, ypxl2 + 1, frac(yend) * xgap); for x := round(xpxl1) + 1 to round(xpxl2) - 1 do begin plot(x, floor(intery), rfrac(intery)); plot(x, floor(intery) + 1, frac(intery)); intery := intery + gradient; end; end; </code></pre> <p>To use this function, simply provide the canvas to draw to (in a manner rather similar to the Windows GDI functions that require a device context (DC)), and specify the initial and final points on the line. Notice that the code above draws a <em>black</em> line, and that the background <em>has to be white</em>. It is not difficult to generalize this to any situation, not even alpha-transparent drawings. Simply adjust the <code>plot</code> function, in which <code>c \in [0, 1]</code> is the <em>opacity</em> of the pixel at <code>(x, y)</code>.</p> <p>Example usage:</p> <p>Create a new VCL project and add</p> <pre><code>procedure TForm1.FormCreate(Sender: TObject); begin Canvas.Brush.Style := bsSolid; Canvas.Brush.Color := clWhite; end; procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin Canvas.FillRect(ClientRect); DrawAntialisedLine(Canvas, Width div 2, Height div 2, X, Y, clBlack); end; </code></pre> <p><a href="http://privat.rejbrand.se/aaline.png" rel="noreferrer">Click to magnify http://privat.rejbrand.se/aaline.png<br/>(Magnify)</a></p> <h1>OpenGL</h1> <p>If you need high-performance and high-quality rendering in 2D or 3D, and you do all the drawing yourself, then OpenGL is generally the best choice. It is very easy to write an OpenGL application in Delphi. See <a href="http://privat.rejbrand.se/smooth.exe" rel="noreferrer">http://privat.rejbrand.se/smooth.exe</a> for an example I made in just ten minutes. Use the right mouse button to toggle between filled polygons and outlines, and click and hold the left mouse button to shoot!</p> <h1>Update</h1> <p>I just made the code work on a coloured background, for instance, an photograph.</p> <p><a href="http://privat.rejbrand.se/aabkg.png" rel="noreferrer">Click to magnify http://privat.rejbrand.se/aabkg.png<br/>(Magnify)</a></p> <h1>Update - The Ultra-Fast Method</h1> <p>The above code is rather slow because the <code>Bitmap.Pixels</code> property is amazingly slow. When I work with graphics, I always represent a bitmap using a two-dimensional array of colour values, which is much, much, much faster. And when I am done with the image, I convert it to a GDI bitmap. I also have a function that creates a pixmap array from a GDI bitmap.</p> <p>I modified the code above to draw on an array instead of a GDI bitmap, and the result is promising:</p> <ul> <li>Time required to render 100 lines</li> <li>GDI Bitmap: 2.86 s</li> <li>Pixel array: 0.01 s</li> </ul> <p>If we let</p> <pre><code>type TPixmap = array of packed array of RGBQUAD; </code></pre> <p>and define</p> <pre><code>procedure TForm3.DrawAntialisedLineOnPixmap(var Pixmap: TPixmap; const AX1, AY1, AX2, AY2: real; const LineColor: TColor); var swapped: boolean; procedure plot(const x, y, c: real); var resclr: TRGBQuad; begin if swapped then begin if (x &lt; 0) or (y &lt; 0) or (x &gt;= ClientWidth) or (y &gt;= ClientHeight) then Exit; resclr := Pixmap[round(y), round(x)] end else begin if (y &lt; 0) or (x &lt; 0) or (y &gt;= ClientWidth) or (x &gt;= ClientHeight) then Exit; resclr := Pixmap[round(x), round(y)]; end; resclr.rgbRed := round(resclr.rgbRed * (1-c) + GetRValue(LineColor) * c); resclr.rgbGreen := round(resclr.rgbGreen * (1-c) + GetGValue(LineColor) * c); resclr.rgbBlue := round(resclr.rgbBlue * (1-c) + GetBValue(LineColor) * c); if swapped then Pixmap[round(y), round(x)] := resclr else Pixmap[round(x), round(y)] := resclr; end; function rfrac(const x: real): real; inline; begin rfrac := 1 - frac(x); end; procedure swap(var a, b: real); var tmp: real; begin tmp := a; a := b; b := tmp; end; var x1, x2, y1, y2, dx, dy, gradient, xend, yend, xgap, xpxl1, ypxl1, xpxl2, ypxl2, intery: real; x: integer; begin x1 := AX1; x2 := AX2; y1 := AY1; y2 := AY2; dx := x2 - x1; dy := y2 - y1; swapped := abs(dx) &lt; abs(dy); if swapped then begin swap(x1, y1); swap(x2, y2); swap(dx, dy); end; if x2 &lt; x1 then begin swap(x1, x2); swap(y1, y2); end; gradient := dy / dx; xend := round(x1); yend := y1 + gradient * (xend - x1); xgap := rfrac(x1 + 0.5); xpxl1 := xend; ypxl1 := floor(yend); plot(xpxl1, ypxl1, rfrac(yend) * xgap); plot(xpxl1, ypxl1 + 1, frac(yend) * xgap); intery := yend + gradient; xend := round(x2); yend := y2 + gradient * (xend - x2); xgap := frac(x2 + 0.5); xpxl2 := xend; ypxl2 := floor(yend); plot(xpxl2, ypxl2, rfrac(yend) * xgap); plot(xpxl2, ypxl2 + 1, frac(yend) * xgap); for x := round(xpxl1) + 1 to round(xpxl2) - 1 do begin plot(x, floor(intery), rfrac(intery)); plot(x, floor(intery) + 1, frac(intery)); intery := intery + gradient; end; end; </code></pre> <p>and the conversion functions</p> <pre><code>var pixmap: TPixmap; procedure TForm3.CanvasToPixmap; var y: Integer; Bitmap: TBitmap; begin Bitmap := TBitmap.Create; try Bitmap.SetSize(ClientWidth, ClientHeight); Bitmap.PixelFormat := pf32bit; BitBlt(Bitmap.Canvas.Handle, 0, 0, ClientWidth, ClientHeight, Canvas.Handle, 0, 0, SRCCOPY); SetLength(pixmap, ClientHeight, ClientWidth); for y := 0 to ClientHeight - 1 do CopyMemory(@(pixmap[y][0]), Bitmap.ScanLine[y], ClientWidth * sizeof(RGBQUAD)); finally Bitmap.Free; end; end; procedure TForm3.PixmapToCanvas; var y: Integer; Bitmap: TBitmap; begin Bitmap := TBitmap.Create; try Bitmap.PixelFormat := pf32bit; Bitmap.SetSize(ClientWidth, ClientHeight); for y := 0 to Bitmap.Height - 1 do CopyMemory(Bitmap.ScanLine[y], @(Pixmap[y][0]), ClientWidth * sizeof(RGBQUAD)); Canvas.Draw(0, 0, Bitmap); finally Bitmap.Free; end; end; </code></pre> <p>then we can write</p> <pre><code>procedure TForm3.FormPaint(Sender: TObject); begin // Get the canvas as a bitmap, and convert this to a pixmap CanvasToPixmap; // Draw on this pixmap (very fast!) for i := 0 to 99 do DrawAntialisedLineOnPixmap(pixmap, Random(ClientWidth), Random(ClientHeight), Random(ClientWidth), Random(ClientHeight), clRed); // Convert the pixmap to a bitmap, and draw on the canvas PixmapToCanvas; end; </code></pre> <p>which will render 100 anti-aliased lines on the form, in less than one hundredth of a second.</p> <p>There seems to be a small bug in the code, though, probably in the Canvas -> Pixmap function. But right now I am way too tired to debug (just got home from work).</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload