Delphi 7的简单抗锯齿功能

时间:2010-08-31 20:49:56

标签: delphi

我需要一个非常简单的函数来绘制一堆具有抗锯齿功能的行。它必须遵循Delphi范式:自包含和SYSTEM INDEPENDENT(没有DLL地狱),快速,简单。 有谁知道这样的图书馆?

到现在为止我已经尝试过:

WuLine
swissdelphicenter.ch/torry/showcode.php?id=1812
我不认为这个代码的作者曾经运行它。画一条线需要一秒钟!这显然只是出于教育目的:)

来自TMetaFile的抗锯齿图纸
链接:blog.synopse.info/post/2010/04/02/Antialiased-drawing-from-TMetaFile
还没有真正试过这个(我可能很快就会这样做)。它仅适用于TMetaFiles。它只加载EMF文件并使用抗锯齿功能绘制它。此外,该网站上的许多代码只是示范/教育。

Image32
非常好的图书馆 - 迄今为止最完整。我可能会用它,但它对于我需要的东西来说太过分了 缺点:
  - 添加到应用程序的足迹非常大   - 真的很难用   - 即使是简单的任务,你也需要深入了解其模糊的文档。   - 提供的演示代码太复杂了   - 越野车!   - 没有最近的更新(以修复错误)

抗粮几何图书馆
该库需要一个体面的安装程序。该库的编写者是Linux / Mac用户。 Windows实现看起来很奇怪。我不能对图书馆本身说些什么。

Xiaolin Wu的基础功能(作者:Andreas Rejbrand)
请看下面的几个帖子。 Andreas Rejbrand提供了一个非常紧凑的解决方案。迄今为止的最佳解决方


看起来我必须解释为什么我不喜欢大型第三方库和VCL:

  • 你必须安装它们
  • 大型库意味着大量的错误,这意味着
  • 您必须检查更新(并重新安装)
  • 当你重新安装Delphi时,你必须再次安装它们(是的,我讨厌安装VCL)
  • 对于VCL,这意味着您必须在已经拥挤的调色板中加载一些额外的图标。
  • (有时)不支持
  • 添加到您的应用程序大小的大量足迹
  • 大型库意味着(并非总是但在大多数情况下)难以使用 - 比您需要的更难。
  • (对于外部DLL和API)您的应用程序变得依赖于系统 - 非常讨厌!

7 个答案:

答案 0 :(得分:35)

在Delphi中实现Xiaolin Wu的抗锯齿线渲染算法并不是很难。当我编写以下过程时,我使用the Wikipedia article作为参考(实际上,我只是将伪代码翻译成Delphi并更正了错误,并添加了对彩色背景的支持):

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) < abs(dy);
  if swapped then
  begin
    swap(x1, y1);
    swap(x2, y2);
    swap(dx, dy);
  end;
  if x2 < 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;

要使用此功能,只需提供要绘制的画布(以类似于需要设备上下文(DC)的Windows GDI函数的方式),并指定行上的初始点和最终点。请注意,上面的代码绘制了黑色行,并且背景必须为白色。将其概括为任何情况并不困难,甚至不是透明的透明图纸。只需调整plot功能,其中c \in [0, 1](x, y)处像素的不透明度

使用示例:

创建一个新的VCL项目并添加

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;

Click to magnify
(Magnify)

的OpenGL

如果您需要2D或3D的高性能和高质量渲染,并且您自己完成所有绘图,那么OpenGL通常是最佳选择。在Delphi中编写OpenGL应用程序非常容易。有关我在十分钟内完成的示例,请参阅http://privat.rejbrand.se/smooth.exe。使用鼠标右键在填充的多边形和轮廓之间切换,然后单击并按住鼠标左键进行拍摄!

更新

我只是让代码在彩色背景上工作(例如,照片)。

Click to magnify
(Magnify)

更新 - 超快速方法

上面的代码相当慢,因为Bitmap.Pixels属性非常慢。当我使用图形时,我总是使用二维颜色值数组来表示位图,这种颜色值要快得多。当我完成图像后,我将其转换为GDI位图。我还有一个从GDI位图创建像素图数组的函数。

我修改了上面的代码来绘制数组而不是GDI位图,结果很有希望:

  • 渲染100行所需的时间
  • GDI位图:2.86 s
  • 像素阵列:0.01 s

如果我们让

type
  TPixmap = array of packed array of RGBQUAD;

并定义

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 < 0) or (y < 0) or (x >= ClientWidth) or (y >= ClientHeight) then
        Exit;
      resclr := Pixmap[round(y), round(x)]
    end
    else
    begin
      if (y < 0) or (x < 0) or (y >= ClientWidth) or (x >= 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) < abs(dy);
  if swapped then
  begin
    swap(x1, y1);
    swap(x2, y2);
    swap(dx, dy);
  end;
  if x2 < 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;

和转换功能

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;

然后我们可以写

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;

将在不到百分之一秒的时间内在表单上呈现100条消除锯齿的线条。

但是,代码中似乎存在一个小错误,可能是在Canvas中 - &gt; Pixmap功能。但是现在我太累了,无法调试(只是下班回家)。

答案 1 :(得分:10)

我相信GDI +会进行抗锯齿绘制(默认情况下),我不知道最近的Delphi版本是否有GdiPlus.pas,但有copies available online

答案 2 :(得分:4)

您可以尝试TAgg2D。它是通过AggPas进行2D绘图的简化API。所以你可以使用简单的函数,如:

  • 行(x1,y1,x2,y2)
  • 矩形(x1,y1,x2,y2)
  • RoundedRect(x1,y1,x2,y2,r)

容易!

答案 3 :(得分:3)

答案 4 :(得分:3)

下载Graphics32。创建一个新的TBitmap32实例。调用TBitmap32.RenderText方法:

procedure TBitmap32.RenderText(X, Y: Integer; const Text: String; AALevel: Integer; Color: TColor32);

if AALevel > -1然后您应该获得抗锯齿文本。

当您在TBitmap32实例上写完字符串后,您可以使用DrawTo方法将此TBitmap32实例绘制到任何Canvas:

procedure TBitmap32.DrawTo(hDst: HDC; DstX, DstY: Integer);

答案 5 :(得分:3)

以下是我所知道的产品:

  1. DtpDocument非常好的矢量编辑器。很多功能。抗锯齿非常好。商业解决方案
  2. ImageEn这对我来说足够快了。商业解决方案
  3. Cairo Graphics它非常快,Mozilla在Firefox中用于渲染。您可以通过this教程轻松地在Delphi中使用Cairo。我强烈建议你一拍。

答案 6 :(得分:1)

有用于Delphi的BGRABitmap https://github.com/bgrabitmap/BGRABitmapDelphi

BGRABitmap最初是Lazarus FPC库,现在可与Delphi VCL一起使用,并且具有抗锯齿和透明的功能。