2010-08-31 55 views
19

Necesito una función muy simple para dibujar un grupo de líneas con anti-aliasing. Tiene que seguir el paradigma de Delphi: autónomo y SISTEMA INDEPENDIENTE (sin infierno de DLL), rápido, simple. ¿Alguien conoce una biblioteca así?Función anti-aliasing simple para Delphi 7

Hasta ahora me han tratado:

WuLine
swissdelphicenter.ch/torry/showcode.php?id=1812
No creo que el autor de este código nunca lo dirige. ¡Toma un segundo dibujar una sola línea! Obviamente, es sólo con fines educativos :)

anti alias dibujo de TMetaFile
Enlace: blog.synopse.info/post/2010/04/02/Antialiased-drawing-from-TMetaFile
No tenga realmente intenté esto todavía (puedo hacerlo pronto). Funciona solo con TMetaFiles. Solo carga un archivo EMF y lo dibuja usando funciones anti aliasing. Además, muchos códigos en ese sitio web son solo de demostración/educativos.

Image32
Muy buena biblioteca - más completa hasta ahora. Podría usarlo, pero es excesivo para lo que necesito.
Desventajas:
- La huella agregada a la aplicación es bastante grande.
- Realmente difícil de usar.
- Necesita profundizar en su oscura documentación incluso para tareas simples. - El código de demostración proporcionado es demasiado complejo.
- ¡Buggy!
- No hay actualizaciones recientes (a corregir los errores)

biblioteca Geometría Anti-Grain
La biblioteca necesita un instalador decente. Los escritores de la biblioteca son usuarios de Linux/Mac. La implementación de Windows se ve extraña. No puedo decir algo más sobre la biblioteca en sí.

función basada de Xiaolin Wu (por Andreas Rejbrand)
Sólo hay que ver algunos puestos de abajo. Andreas Rejbrand proporcionó una solución muy compacta. La mejor solución hasta ahora.


Parece que tengo que explicar por qué no me gustan las bibliotecas 3 ª parte grandes y VCL:

  • hay que instalarlos
  • gran biblioteca significa gran número de insectos que significa
  • usted tiene que comprobar si hay actualizaciones (e instalarlos de nuevo)
  • cuando vuelva a instalar Delphi, hay que instalarlos una vez más (VCL sí me gusta la instalación)
  • para VCL, significa que tiene que cargar algunos iconos adicionales en su paleta ya atestada.
  • (a veces) no hay soporte
  • huella GRANDE añade al tamaño de su aplicación
  • grandes medios de la biblioteca (bueno, no siempre, pero en la mayoría de los casos) difícil de usar - más difícil de lo necesario.
  • (para DLL externos y API) su aplicación se vuelve dependiente del sistema - ¡realmente desagradable!
+1

El interés de nuestra biblioteca SynGdiPlus es que puede tener su código de dibujo escrito en métodos simples de VCL TCanvas. Dibuje en un TMetaFileCanvas, luego juegue en un mapa de bits usando nuestra biblioteca. Es muy rápido y funciona para mucho más que dibujar líneas. Y la huella añadida al ejecutable de su aplicación es insignificante. Le invitamos a que publique comentarios o preguntas sobre nuestra biblioteca en nuestro sitio web. Y no solo es demostrativo/educativo: se usa en aplicaciones reales, para una gran representación, con mucho código VCL TCanvas (otros programadores pensaron que era un dibujo de puntos :) –

+3

GDI + se ajusta a mis requisitos (D7). – Ampere

+0

SynGdiPlus funcionará con D7. Vea nuestro foro: http://synopse.info/forum/viewtopic.php?pid=498#p498 –

Respuesta

33

No es muy difícil implementar el algoritmo de renderizado de líneas anti-aliasing de Xiaolin Wu en Delphi. Solía ​​the Wikipedia article como referencia cuando escribí el siguiente procedimiento (en realidad, yo sólo traduje el pseudo-código para Delphi y ha corregido un error, y ha añadido soporte para un fondo de color):

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; 

Para utilizar esta función, simplemente proporcione el lienzo para dibujar (de una manera bastante similar a las funciones de Windows GDI que requieren un contexto de dispositivo (DC)) y especifique los puntos iniciales y finales en la línea. Observe que el código anterior dibuja una línea negra, y que el fondo tiene que ser blanco. No es difícil generalizar esto en cualquier situación, ni siquiera dibujos con transparencia alfa. Simplemente ajuste la función plot, en la cual c \in [0, 1] es opacidad del píxel en (x, y).

Ejemplo de uso:

Crear un nuevo proyecto VCL y añadir

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 http://privat.rejbrand.se/aaline.png
(Magnify)

OpenGL

Si necesita alto rendimiento y alta calidad de representación en 2D o 3D , y usted hace todo el dibujo usted mismo, entonces OpenGL es generalmente la mejor opción. Es muy fácil escribir una aplicación OpenGL en Delphi. Ver http://privat.rejbrand.se/smooth.exe para un ejemplo que hice en solo diez minutos. Use el botón derecho del mouse para alternar entre polígonos y contornos rellenos, y haga clic y mantenga presionado el botón izquierdo del mouse para disparar.

actualización

que acaba de hacer el trabajo de código en un fondo de color, por ejemplo, una fotografía.

Click to magnify http://privat.rejbrand.se/aabkg.png
(Magnify)

Actualización - El método ultrarrápido

El código anterior es bastante lento porque la propiedad Bitmap.Pixels es increíblemente lento. Cuando trabajo con gráficos, siempre represento un mapa de bits usando una matriz bidimensional de valores de color, que es mucho, mucho, mucho más rápido. Y cuando termine con la imagen, la convierto en un mapa de bits GDI. También tengo una función que crea una matriz de pixmap a partir de un mapa de bits GDI.

he modificado el código anterior para dibujar en una matriz en lugar de un mapa de bits GDI, y el resultado es prometedor:

  • Tiempo para rendir 100 líneas
  • GDI Bitmap: 2,86 s
  • Pixel matriz: 0,01 s

Si dejamos que

type 
    TPixmap = array of packed array of RGBQUAD; 

y definir

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; 

y la conversión funciones

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; 

entonces podemos escribir

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; 

que rinda 100 líneas anti-alias en el formulario, en menos de una centésima de un segundo

Parece que hay un pequeño error en el código, probablemente, en la función Canvas -> Pixmap. Pero ahora mismo estoy demasiado cansado para depurarme (llegué a casa del trabajo).

+0

Esto es realmente lento, y es una prueba de concepto más que una función útil. Jugar con metarchivos necesita muchos comandos, mucho más que solo el dibujo lineal. Debería ir y ver http://www.crossgl.com/aggpas por ejemplo o http://graphics32.org/wiki para obtener una biblioteca de dibujo más completa. –

+0

O, por supuesto, use GdiPlus, que se envía con cada Windows desde XP. –

+0

@ A.Buez: No entiendo de qué estás hablando. No juego con metarchivos. Pero es bien sabido que la propiedad 'Pixels' es lenta. Si uno dibujara la línea en una matriz bidimensional de píxeles y luego usara la propiedad 'scanline' para convertir a/desde el lienzo, probablemente podría mejorar el rendimiento significativamente. Pero si no necesita dibujar más de (digamos) un centenar de líneas, esto es "lo suficientemente rápido" tal como está. –

10

creo GDI + dibujo anti-aliasing (por defecto), no sé si las últimas versiones de Delphi tienen un GdiPlus.pas, pero hay copies available online.

+1

GDI + no hace anti-aliasing del contenido GDI EMF/TMetaFile/Canvas. Solo renderiza el anti-aliasing con metarchivos GDI +/EMF +/Canvas. Entonces, para usar anti-aliasing, tendrás que reescribir todos tus dibujos usando métodos GDI +. Nuestra biblioteca GdiPlus permite dibujos anti-alias usando comandos de lienzo VCL "normales": dibuja en un archivo TMeta, luego escríbelo usando nuestra biblioteca. Es rápido y fácil. –

+2

Leo demasiado rápido. Para dibujar líneas, puede usar métodos GdiPlus. Pero tendrá que aprender sobre el dibujo de GdiPlus, mientras que con nuestra biblioteca usará métodos clásicos de VCL TCanvas, como siempre. Si desea utilizar el dibujo de GdiPlus, le recomiendo usar la biblioteca http://www.bilsen.com/gdiplus/index.shtml, que es mucho más fácil de usar que las conversiones anteriores, pero requiere Delphi 2009/2010/XE. –

+1

@Bouchez: ¿funciona en sistemas anteriores a WinXP? ¿Funciona en TODOS los sistemas operativos WinXP o el usuario tiene que instalar algunas actualizaciones? – Ampere

3

darle una oportunidad a Anti-Grain Geometry library

+1

Esta biblioteca parece compleja. ¿Sabes si puedo usarlo tan simple como DrawAntiAliasLine()? ¿O tengo que realizar acrobacias complejas como tengo que hacer con Graphics32? – Ampere

3

Descargar Graphics32. Crea una nueva instancia de TBitmap32. Llamar al método TBitmap32.RenderText:

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

if AALevel > -1 entonces usted debe conseguir el texto suavizado.

Cuando haya terminado de escribir la cadena (s) en su instancia TBitmap32, a continuación, se puede dibujar este caso TBitmap32 a cualquier Canvas utilizando el método DrawTo:

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

Por favor, lea mi publicación de nuevo. Ya estoy haciendo esto. Lo que necesito es "Necesito una biblioteca muy simple para dibujar un montón de líneas con anti-aliasing. Tiene que seguir el paradigma Delphi: independiente y SYSTEM INDEPENDENT (sin DLL hell), rápido, simple". – Ampere

+0

Creo que Graphics32 cumple la mayoría (si no todas) de sus solicitudes. Es autónomo, independiente de la plataforma (Free Pascal y Lazarus en Windows y OSX), y bastante rápido. Si es o no lo suficientemente simple, bueno, ese es el ojo del espectador. – Stefan

+0

La mayoría, pero no todas. Es por eso que lo cambio. Si hubiera cumplido todos mis requisitos, no habría estado publicando aquí. ¿Derecha? - Para hacerlo simple: quiero algo mejor que Graphics32. – Ampere

4

Usted puede tratar de TAgg2D. Es una API simplificada para el dibujo 2D sobre AggPas. Para que pueda utilizar las funciones simples como:

  • línea (x1, y1, x2, y2)
  • Rectángulo (x1, y1, x2, y2)
  • RoundedRect (x1, y1, x2, y2, r)

¡Fácil!

+0

Suena bien. ¡Gracias! – Ampere

3

Éstos son los productos que yo sepa:

  1. DtpDocument Muy buen editor de vectores. Muchas características Anti-Aliasing soportado muy bien. Solución comercial
  2. ImageEn Es lo suficientemente rápido para mí. Solución comercial
  3. Cairo Graphics Es muy rápido y utilizado por Mozilla en Firefox para renderizar. Puede usar fácilmente Cairo en Delphi por el tutorial this. Te recomiendo que tomes una foto.