Difference between revisions of "Developing with Graphics/es"
Line 173: | Line 173: | ||
Este es un ejemplo de control personalizado: | Este es un ejemplo de control personalizado: | ||
− | < | + | <delphi> type |
− | |||
TMyDrawingControl = class(TCustomControl) | TMyDrawingControl = class(TCustomControl) | ||
public | public | ||
Line 223: | Line 222: | ||
MyDrawingControl.Parent := Self; | MyDrawingControl.Parent := Self; | ||
MyDrawingControl.DoubleBuffered := True; | MyDrawingControl.DoubleBuffered := True; | ||
− | end; | + | end;</delphi> |
− | </ | ||
no olvide destruirlo: | no olvide destruirlo: | ||
− | < | + | <delphi> procedure TMyForm.FormDestroy(Sender: TObject); |
− | |||
begin | begin | ||
MyDrawingControl.Free; | MyDrawingControl.Free; | ||
− | end; | + | end;</delphi> |
− | </ | ||
No es necesario asignar cero a Top y Left, ya que ésta es la posición predefinida, pero se hace así para reforzar la posición donde se colocará el control. | No es necesario asignar cero a Top y Left, ya que ésta es la posición predefinida, pero se hace así para reforzar la posición donde se colocará el control. |
Revision as of 23:44, 1 June 2009
│
Deutsch (de) │
English (en) │
español (es) │
français (fr) │
italiano (it) │
日本語 (ja) │
한국어 (ko) │
Nederlands (nl) │
português (pt) │
русский (ru) │
slovenčina (sk) │
中文(中国大陆) (zh_CN) │
中文(台灣) (zh_TW) │
Esta página dará inicio a una serie de tutoriales que traten sobre la manipulación de mapa de bits y otros tipos de gráficos. Como no soy un experto programador de gráficos, ¡invito a todos a compartir sus experiencias! Basta con añadir un enlace a la siguiente sección, añadir una página y crear su propio antículo para el Wiki.
En esta página se dará alguna información general.
Otros artículos sobre gráficos
- GLScene - Una adaptación de la librería visual de gráficos OpenGL GLScene
- TAChart - Componente gráfico para Lazarus
- PascalMagick - Una forma fácil de usar la API para interactuar con ImageMagick, un paquete software libre multiplataforma para crear, editar y componer imágenes de mapa de bit.
- PlotPanel - Un componente para trazar y realizar gráficos animados
Trabajo con TBitmap
Lo primero que hay que recordar es que Lazarus quiere decir plataforma independiente, así que todos los métodos que usan funcionalidades de la API de Windows se salen de la cuestión. Así que un método como ScanLIne no es soportado por Lazarus, porque está destinado a Device Independent Bitmap y usa funciones de GDI32.dll.
Tenga cuidado porque si no especifica la anchura y altura de su TBitmap tendrá la predefinida, que es muy pequeña.
Un ejemplo de desvanecimiento
Supongamos que quiere hacer una imagen que se desvanece. En Delphi podría hacer algo así: <delphi> type
PRGBTripleArray = ^TRGBTripleArray; TRGBTripleArray = array[0..32767] of TRGBTriple; procedure TForm1.FadeIn(aBitMap: TBitMap); var Bitmap, BaseBitmap: TBitmap; Row, BaseRow: PRGBTripleArray; x, y, step: integer; begin Bitmap := TBitmap.Create; try Bitmap.PixelFormat := pf32bit; // or pf24bit Bitmap.Assign(aBitMap); BaseBitmap := TBitmap.Create; try BaseBitmap.PixelFormat := pf32bit; BaseBitmap.Assign(Bitmap); for step := 0 to 32 do begin for y := 0 to (Bitmap.Height - 1) do begin BaseRow := BaseBitmap.Scanline[y]; Row := Bitmap.Scanline[y]; for x := 0 to (Bitmap.Width - 1) do begin Row[x].rgbtRed := (step * BaseRow[x].rgbtRed) shr 5; Row[x].rgbtGreen := (step * BaseRow[x].rgbtGreen) shr 5; // Fading Row[x].rgbtBlue := (step * BaseRow[x].rgbtBlue) shr 5; end; end; Form1.Canvas.Draw(0, 0, Bitmap); InvalidateRect(Form1.Handle, nil, False); RedrawWindow(Form1.Handle, nil, 0, RDW_UPDATENOW); end; finally BaseBitmap.Free; end; finally Bitmap.Free; end; end;</delphi>
Está función en Lazarus sería así: <delphi> procedure TForm1.FadeIn(ABitMap: TBitMap);
var SrcIntfImg, TempIntfImg: TLazIntfImage; ImgHandle,ImgMaskHandle: HBitmap; FadeStep: Integer; px, py: Integer; CurColor: TFPColor; TempBitmap: TBitmap; begin SrcIntfImg:=TLazIntfImage.Create(0,0); SrcIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle); TempIntfImg:=TLazIntfImage.Create(0,0); TempIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle); TempBitmap:=TBitmap.Create; for FadeStep:=1 to 32 do begin for py:=0 to SrcIntfImg.Height-1 do begin for px:=0 to SrcIntfImg.Width-1 do begin CurColor:=SrcIntfImg.Colors[px,py]; CurColor.Red:=(CurColor.Red*FadeStep) shr 5; CurColor.Green:=(CurColor.Green*FadeStep) shr 5; CurColor.Blue:=(CurColor.Blue*FadeStep) shr 5; TempIntfImg.Colors[px,py]:=CurColor; end; end; TempIntfImg.CreateBitmap(ImgHandle,ImgMaskHandle,false); TempBitmap.Handle:=ImgHandle; TempBitmap.MaskHandle:=ImgMaskHandle; Canvas.Draw(0,0,TempBitmap); end; SrcIntfImg.Free; TempIntfImg.Free; TempBitmap.Free; end;</delphi>
El código Lazarus de esta página se ha tomado del proyecto $LazarusPath/examples/lazintfimage/fadein1.lpi . Así que si desea iniciarse en la programación de gráficos eche una mirada a este ejemplo.
Dibujando mapa de bits de color transparente
Una nueva característica, incluida en Lazarus 0.9.11, son los mapa de bits de color transparente. Los archivos Bitmap (*.BMP) no pueden almacenar cualquier información sobre transparencia, pero pueden trabajar como si la tuviera si selecciona un color en ellos para representar la zona transparente. Este es un truco habitual usado en aplicaciones Win32.
El siguiente ejemplo carga un mapa de bit desde un recurso Windows, selecciona un color para que sea transparente (clFuchsia) y después lo dibuja.
<delphi> procedure MyForm.MyButtonOnClick(Sender: TObject); var
buffer: THandle; bmp: TBitmap; memstream: TMemoryStream;
begin
bmp := TBitmap.Create;
buffer := Windows.LoadBitmap(hInstance, MAKEINTRESOURCE(ResourceID));
if (buffer = 0) then exit; // Error loading the bitmap
bmp.Handle := buffer; memstream := TMemoryStream.create; try bmp.SaveToStream(memstream); memstream.position := 0; bmp.LoadFromStream(memstream); finally memstream.free; end;
bmp.Transparent := True; bmp.TransparentColor := clFuchsia;
MyCanvas.Draw(0, 0, bmp);
bmp.Free; // Release allocated resource
end;</delphi>
Observe las operaciones de memoria realizadas con TMemoryStream. Son necesarios para garantizar la correcta carga de la imagen.
Gráficos en movimiento - Cómo evitar el parpadeo
Muchos programas dibujan su salida al GUI como gráficos 2D. Si esos gráficos necesitan cambiar rápidamente, pronto tendrá un problema: los gráficos que cambian rápidamente a menudo parpadean en la pantalla. Esto ocurre cuando los usuarios algunas veces ven las imágenes ocultas y algunas veces las ven cuando se dibujan sólo parcialmente. Esto ocurre porque el proceso de pintar requiere tiempo.
Pero ¿cómo puedo evitar el parpadeo y conseguir la mejor velocidad de trazado? Claro, podría trabajar con aceleración de hardware utilizando OpenGL, pero es verdaderamente pesado para pequeños programas u ordenadores antiguos. Este tutorial esta enfocado hacia el dibujo con TCanvas. Si necesita ayuda con OpenGL, eche una mirada al ejemplo que viene con Lazarus. Puede también usar el paquete para juegos de A. J. Venter, que proporciona un canvas (lienzo) de doble memoria intermedia y un componente sprite (objeto móvil).
Ahora examinaremos las opciones que tenemos para dibujar en un Canvas (lienzo):
- Dibujar en un TImage
- Dibujar en el envento OnPaint del formulario, un TPaintBox u otro control
- Crear un control personalizado que se dibuja a sí mismo
- Utilizando el paquete para juegos de A.J. Venter
Dibujar en un TImage
Nunca use el evento OnPaint para dibujar en un TImage. Un TImage es almacenado en la memoria intermedia por lo que todo lo que necesita hacer es dibujarlo desde cualquier lugar y se cambia para siempre. Sin embargo, si constantemente redibuja, la imagen parpadeará. En este caso puede intentar otras opciones. Dibujar en un TImage se considera más lento que otras posibilidades.
<delphi> procedure TForm1.BitBtn1Click(Sender: TObject);
var x, y: Integer; begin // Draws the backgroung MyImage.Canvas.Pen.Color := clWhite; MyImage.Canvas.Rectangle(0, 0, Image.Width, Image.Height); // Draws squares MyImage.Canvas.Pen.Color := clBlack; for x := 1 to 8 do for y := 1 to 8 do MyImage.Canvas.Rectangle(Round((x - 1) * Image.Width / 8), Round((y - 1) * Image.Height / 8), Round(x * Image.Width / 8), Round(y * Image.Height / 8)); end;</delphi>
Dibujar en el evento OnPaint
En este caso todo tiene que dibujarlo en el evento OnPaint del formulario. No se mantiene en la memoria intermedia, como en el TImage.
Crear un control personalizado que se dibuja a sí mismo
Crear un control personalizado tiene la ventaja de estructurar su código y poder reutilizar el control. Está opción es muy rápida, pero puede todavía generar parpadeo si no dibuja en un TBitmap primero y después dibuja en el canvas (lienzo). En este caso no necesita utilizar el evento OnPaint del control.
Este es un ejemplo de control personalizado:
<delphi> type
TMyDrawingControl = class(TCustomControl) public procedure Paint; override; end; implementation procedure TMyDrawingControl.Paint; var x, y: Integer; Bitmap: TBitmap; begin Bitmap := TBitmap.Create; try // Initializes the Bitmap Size Bitmap.Height := Height; Bitmap.Width := Width; // Draws the background Bitmap.Canvas.Pen.Color := clWhite; Bitmap.Canvas.Rectangle(0, 0, Width, Height); // Draws squares Bitmap.Canvas.Pen.Color := clBlack; for x := 1 to 8 do for y := 1 to 8 do Bitmap.Canvas.Rectangle(Round((x - 1) * Width / 8), Round((y - 1) * Height / 8), Round(x * Width / 8), Round(y * Height / 8)); Canvas.Draw(0, 0, Bitmap); finally Bitmap.Free; end; inherited Paint; end;
y cómo lo creamos en el formulario:
procedure TMyForm.FormCreate(Sender: TObject);
begin
MyDrawingControl:= TMyDrawingControl.Create(Self);
MyDrawingControl.Height := 400;
MyDrawingControl.Width := 500;
MyDrawingControl.Top := 0;
MyDrawingControl.Left := 0;
MyDrawingControl.Parent := Self;
MyDrawingControl.DoubleBuffered := True;
end;</delphi>
no olvide destruirlo:
<delphi> procedure TMyForm.FormDestroy(Sender: TObject);
begin
MyDrawingControl.Free;
end;</delphi>
No es necesario asignar cero a Top y Left, ya que ésta es la posición predefinida, pero se hace así para reforzar la posición donde se colocará el control.
"MyDrawingControl.Parent := Self;" es muy importante y no verá su control si no lo hace así.
"MyDrawingControl.DoubleBuffered := True;" se requiere para evitar el parpadeo en Windows. No tiene ningún efecto en gtk.
Utilizando el paquete para juegos de A.J. Venter
El paquete para juegos está enfocado al dibujo de cualquier cosa en un canvas (lienzo) de doble memoria intermedia, que sólo se actualiza cuando usted está preparado. Esto supone verdaderamente bastante código, pero tiene la ventaja de ser capaz de hacer muy rápidamente cambios de escenas con múltiples sprites (objetos móviles) en ellos. Si desea utilizar esta opción, puede interesarle el paquete para juegos de A. J. Venter, un conjunto de componentes para desarrollar juegos en Lazarus, que proporciona un componente de pantalla de doble memoria intermedia así como un componente sprite (ojeto móvil), diseñado para integrarse bien con cualquier otro. Puede obtener el paquete para juegos via subversion:
svn co svn://silentcoder.co.za/lazarus/gamepack