TÓM TẮT

Mở Đầu

Pascal, một trong những ngôn ngữ lập trình cổ điển, vẫn được sử dụng rộng rãi trong giáo dục và các dự án nhỏ vì cú pháp rõ ràng, cấu trúc chặt chẽ và khả năng kiểm soát lỗi tốt. Mặc dù Pascal không phải là ngôn ngữ chuyên dụng cho đồ họa, nhưng với các thư viện mở rộng như Graph, CRT, BGRABitmap, SDL, OpenGL và thậm chí là Lazarus/Free Pascal (được tích hợp sẵn VCL), chúng ta vẫn có thể thực hiện các thao tác vẽ hình 2D, thậm chí 3D một cách khá linh hoạt.

Bài viết này sẽ cung cấp cho bạn một hướng dẫn chi tiết, đầy đủ và thực tiễn về cách vẽ các hình cơ bản (điểm, đường thẳng, hình chữ nhật, hình tròn, đa giác…) trong Pascal, đồng thời mở rộng sang các kỹ thuật nâng cao như đổ màu, đổ gradient, vẽ ảnh bitmap, tạo animation và kết hợp với các thư viện bên ngoài. Ngoài ra, chúng ta sẽ thảo luận về môi trường phát triển (IDE) phù hợp, cách cài đặt thư viện, các lỗi thường gặp và cách khắc phục.

Lưu ý: Bài viết được chia thành nhiều phần, mỗi phần tập trung vào một khía cạnh cụ thể. Bạn có thể đọc toàn bộ để nắm vững kiến thức tổng thể, hoặc chỉ cần tham khảo phần bạn quan tâm.

1. Chuẩn Bị Môi Trường Phát Triển

1.1. Lựa Chọn IDE

IDE Ưu điểm Nhược điểm Hệ điều hành hỗ trợ
Free Pascal + Lazarus Miễn phí, mã nguồn mở, hỗ trợ GUI mạnh (LCL), tích hợp sẵn thư viện Graph Giao diện đôi khi chậm, tài liệu tiếng Anh nhiều Windows, Linux, macOS
Turbo Pascal (DOS) Giao diện cổ điển, rất nhẹ, phù hợp cho học tập Không hỗ trợ Windows 10+ trực tiếp, thiếu tính năng hiện đại DOSBox (điều chế)
Delphi (Object Pascal) IDE mạnh, hỗ trợ VCL, FireMonkey, đa nền tảng Bản trả phí (có bản Community Edition) Windows, macOS (qua FireMonkey)
CodeTyphon Cung cấp nhiều thư viện cho Pascal, hỗ trợ Android/iOS Cài đặt phức tạp, tài liệu hạn chế Windows, Linux

Khuyến nghị: Nếu bạn mới bắt đầu, Free Pascal + Lazarus là lựa chọn tốt nhất vì nó miễn phí, dễ cài đặt và có sẵn thư viện Graph (trong gói Graph.pas). Đối với người muốn phát triển GUI hiện đại, Delphi Community Edition là một lựa chọn mạnh mẽ.

1.2. Cài Đặt Thư Viện Đồ Họa

1.2.1. Thư viện Graph (đối với Free Pascal)

Thư viện Graph là một wrapper cho chế độ đồ họa VGA (mode 0x13) trong môi trường DOS hoặc Windows console. Để sử dụng:

  1. Cài đặt Free Pascal: Tải và cài đặt tại https://www.freepascal.org.
  2. Cài đặt Lazarus: Tải và cài đặt tại https://www.lazarus-ide.org.
  3. Kiểm tra thư mục $(FPCDIR)/units/$(OS)/$(CPU)/: Đảm bảo có file graph.ppu và graph.pas.
  4. Thêm vào dự án: uses Graph;.

1.2.2. Thư viện BGRABitmap (đồ họa 2D nâng cao)

  • Cài đặt: fpc -iTP để cài BGRABitmap qua fpmake. Hoặc tải source tại https://github.com/BGRABitmap.
  • Sử dụng: uses BGRABitmap, BGRABitmapTypes, Graph;.

1.2.3. SDL + PasSDL2 (đồ họa đa nền tảng)

  • Cài đặt SDL2: Tải thư viện SDL2 (https://www.libsdl.org) và cài đặt pasdl2 (https://github.com/PascalGameDev/pasdl2).
  • Lợi ích: Hỗ trợ Windows, Linux, macOS, Android, iOS; cho phép tạo game, animation mượt mà.

2. Kiến Thức Cơ Bản Về Đồ Họa Trong Pascal

2.1. Khái Niệm “Mode Đồ Họa”

Trong Pascal truyền thống, Graph làm việc dựa trên độ phân giải (resolution) và bộ màu (palette). Một số mode phổ biến:

Mode Độ phân giải Màu Mô tả
0x13 320×200 256 Mode VGA chuẩn, thích hợp cho game cổ điển
0x12 640×480 16 Độ phân giải cao hơn, ít màu
0x10 640×350 16 Độ phân giải trung bình, ít màu
0x03 640×480 16 Text mode (không dùng cho đồ họa)

Lưu ý: Khi chạy trong môi trường Windows hiện đại, các mode này được mô phỏng qua WinGraph (thư viện của Lazarus) hoặc SDL.

2.2. Các Hàm Cơ Bản Trong Graph

Hàm Tham số Mô tả
initgraph(gdriver, gmode, path) gdriver, gmode là biến integer; path là thư mục driver Khởi tạo môi trường đồ họa.
closegraph Không Đóng chế độ đồ họa, trả về text mode.
setcolor(col) col (0..255) Đặt màu vẽ hiện tại.
line(x1, y1, x2, y2) Tọa độ điểm đầu và cuối Vẽ đường thẳng.
rectangle(x1, y1, x2, y2) Hai góc đối diện Vẽ hình chữ nhật (khung).
fillrect(x1, y1, x2, y2) — Vẽ hình chữ nhật đầy màu.
circle(x, y, radius) — Vẽ vòng tròn.
ellipse(x, y, startAngle, endAngle, xRadius, yRadius) — Vẽ elip.
setfillstyle(style, color) style (solid, hatch, etc.) Đặt kiểu đổ màu cho các hình.
floodfill(x, y, borderColor) — Đổ màu từ điểm (x,y) tới khi gặp màu viền.
outtextxy(x, y, text) — Vẽ chuỗi ký tự tại vị trí.
putpixel(x, y, color) — Đặt màu cho một điểm.

2.3. Màu Sắc

Cách Vẽ Hình Trong Pascal
Cách Vẽ Hình Trong Pascal
  • Màu nền (Background) và màu vẽ (Foreground): Được đặt bằng setbkcolor và setcolor.
  • Palette 256 màu: Mỗi màu có mã 0..255. Các màu chuẩn:
  • 0 = Black
  • 1 = Blue
  • 2 = Green
  • 3 = Cyan
  • 4 = Red
  • 5 = Magenta
  • 6 = Brown
  • 7 = Light Gray
  • 8 = Dark Gray
  • 9 = Light Blue
  • 10 = Light Green
  • 11 = Light Cyan
  • 12 = Light Red
  • 13 = Light Magenta
  • 14 = Yellow
  • 15 = White

Bạn có thể thay đổi palette bằng setpalette (trong chế độ VGA) hoặc setrgbpalette (trong môi trường SDL).

3. Vẽ Các Hình Cơ Bản

3.1. Vẽ Điểm

procedure DrawPixelDemo;
var gd, gm: Integer;
begin gd := Detect; { Tự động phát hiện driver } InitGraph(gd, gm, ''); SetColor(Yellow); PutPixel(160, 100, Yellow); { Vẽ điểm tại trung tâm màn hình 320x200 } ReadLn; CloseGraph;
end;

Giải thích: PutPixel là hàm cơ bản nhất, thường dùng trong thuật toán rasterization (điền màu cho các hình phức tạp).

3.2. Vẽ Đường Thẳng

procedure DrawLineDemo;
var gd, gm: Integer;
begin gd := Detect; InitGraph(gd, gm, ''); SetColor(Red); Line(10, 10, 310, 190); { Đường chéo từ góc trên trái tới góc dưới phải } SetColor(Blue); Line(10, 190, 310, 10); { Đường chéo ngược lại } ReadLn; CloseGraph;
end;

Thuật toán Bresenham: Được sử dụng nội bộ bởi Line. Nếu muốn tự cài, bạn có thể viết hàm BresenhamLine để hiểu sâu hơn.

3.3. Vẽ Hình Chữ Nhật

procedure DrawRectangleDemo;
var gd, gm: Integer;
begin gd := Detect; InitGraph(gd, gm, ''); SetColor(Green); Rectangle(50, 50, 270, 150); { Khung hình chữ nhật } SetFillStyle(SolidFill, LightGray); FloodFill(60, 60, Green); { Đổ màu bên trong } ReadLn; CloseGraph;
end;

Lưu ý: FloodFill cần một điểm nằm trong vùng và màu viền để biết khi nào dừng.

3.4. Vẽ Hình Tròn

procedure DrawCircleDemo;
var gd, gm: Integer;
begin gd := Detect; InitGraph(gd, gm, ''); SetColor(Cyan); Circle(160, 100, 80); { Bán kính 80 pixel } SetFillStyle(SolidFill, LightBlue); FloodFill(160, 100, Cyan); ReadLn; CloseGraph;
end;

Thuật toán Midpoint Circle: Được dùng trong hàm Circle. Giúp vẽ nhanh và chính xác.

3.5. Vẽ Đa Giác (Polygon)

procedure DrawPolygonDemo;
var gd, gm: Integer; points: array1..5 of PointType;
begin gd := Detect; InitGraph(gd, gm, ''); SetColor(Magenta); points1.x := 100; points1.y := 50; points2.x := 200; points2.y := 30; points3.x := 250; points3.y := 120; points4.x := 180; points4.y := 180; points5.x := 80; points5.y := 140; Polygon(points, 5); { Vẽ đa giác 5 đỉnh } SetFillStyle(SolidFill, LightMagenta); FloodFill(150, 100, Magenta); { Đổ màu bên trong } ReadLn; CloseGraph;
end;

Chi tiết: Polygon nhận mảng PointType (cấu trúc {x, y: Integer}) và số đỉnh. Đảm bảo các đỉnh được sắp xếp theo chiều kim đồng hồ hoặc ngược chiều kim đồng hồ để FloodFill hoạt động đúng.

4. Kỹ Thuật Đổ Màu và Gradient

4.1. Đổ Màu Đơn (SolidFill)

Sử dụng SetFillStyle(SolidFill, color) và FloodFill. Đây là cách nhanh nhất để tô màu cho các hình đã vẽ.

SetFillStyle(SolidFill, Yellow);
FloodFill(x, y, borderColor);

4.2. Đổ Màu Hạt (Hatch Fill)

SetFillStyle(HatchFill, Red);
FloodFill(x, y, borderColor);

Các kiểu hatch có sẵn: HorizontalLine, VerticalLine, CrossLine, DiagCross, DiagLine1, DiagLine2.

4.3. Gradient (Màu Độ Trơn)

Graph không hỗ trợ gradient trực tiếp. Bạn có thể tự tạo bằng cách vẽ các đường thẳng hoặc hình chữ nhật mỏng với màu thay đổi dần:

procedure DrawVerticalGradient(x1, y1, x2, y2: Integer; startColor, endColor: Byte);
var i, steps: Integer; r1, g1, b1, r2, g2, b2: Byte; r, g, b: Byte;
begin steps := y2 - y1; GetRGBPalette(startColor, r1, g1, b1); GetRGBPalette(endColor, r2, g2, b2); for i := 0 to steps do begin r := r1 + (r2 - r1) i div steps; g := g1 + (g2 - g1) i div steps; b := b1 + (b2 - b1) i div steps; SetRGBPalette(i, r, g, b); SetColor(i); Line(x1, y1 + i, x2, y1 + i); end;
end;

Giải thích: GetRGBPalette và SetRGBPalette là các hàm (có thể tự viết) để truy cập và thay đổi bảng màu. Khi chạy trên SDL hoặc BGRABitmap, bạn sẽ dùng SetPixel với giá trị ARGB trực tiếp.

Cách Vẽ Hình Trong Pascal
Cách Vẽ Hình Trong Pascal

5. Vẽ Ảnh Bitmap

5.1. Đọc và Hiển Thị BMP

procedure DrawBMPDemo;
var gd, gm: Integer; img: Picture;
begin gd := Detect; InitGraph(gd, gm, ''); img := LoadPicture('sample.bmp'); { Thư viện Graph hỗ trợ BMP } PutImage(0, 0, img^.Image, NormalPut); { Vẽ lên góc trái trên } ReadLn; CloseGraph;
end;

Lưu ý: LoadPicture trả về con trỏ Picture. Đừng quên Dispose(img); khi không còn dùng.

5.2. Vẽ Bitmap với BGRABitmap (độ phân giải cao)

uses BGRABitmap, BGRABitmapTypes, Graph; procedure DrawBitmapBGRADemo;
var bmp: TBGRABitmap; gd, gm: Integer;
begin gd := Detect; InitGraph(gd, gm, ''); bmp := TBGRABitmap.Create('sample.png'); { Hỗ trợ PNG, JPEG } bmp.Draw(Canvas, 0, 0, True); { Canvas là đối tượng đồ họa Graph } bmp.Free; ReadLn; CloseGraph;
end;

Ưu điểm: BGRABitmap hỗ trợ alpha blending, anti-aliasing, và nhiều định dạng hình ảnh.

6. Tạo Animation Cơ Bản

6.1. Vòng Lặp Vẽ và Delay

procedure SimpleAnimation;
var gd, gm, i: Integer;
begin gd := Detect; InitGraph(gd, gm, ''); SetColor(White); for i := 0 to 300 do begin ClearDevice; { Xóa màn hình } Circle(160, 100, i div 2); { Tăng bán kính dần } Delay(20); { Nghỉ 20ms } end; ReadLn; CloseGraph;
end;

6.2. Double Buffering (Tránh nhấp nháy)

Graph không có double buffer sẵn, nhưng bạn có thể tạo một off-screen bitmap và vẽ lên đó, sau đó PutImage lên màn hình:

procedure DoubleBufferDemo;
var gd, gm: Integer; buffer: PImage; i: Integer;
begin gd := Detect; InitGraph(gd, gm, ''); GetMem(buffer, SizeOf(ImageRec)); buffer^.Image := AllocImage(320, 200, GetMaxColor); for i := 0 to 100 do begin ClearDevice; SetColor(Black); SetFillStyle(SolidFill, Black); FloodFill(0, 0, Black); { Xóa buffer } SetColor(Yellow); Circle(160, 100, i); PutImage(0, 0, buffer^.Image, NormalPut); Delay(30); end; FreeMem(buffer, SizeOf(ImageRec)); ReadLn; CloseGraph;
end;

Lưu ý: Khi sử dụng AllocImage, bạn cần giải phóng bộ nhớ bằng FreeImage.

6.3. Animation với SDL (mượt hơn)

uses SDL2, SDL2_image; procedure SDLAnimation;
var window: PSDL_Window; renderer: PSDL_Renderer; evt: TSDL_Event; running: Boolean; angle: Single;
begin SDL_Init(SDL_INIT_VIDEO); window := SDL_CreateWindow('SDL Animation', SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_SHOWN); renderer := SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); running := True; angle := 0; while running do begin while SDL_PollEvent(@evt) <> 0 do if evt.type_ = SDL_QUITEV then running := False; SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); SDL_RenderDrawLine(renderer, 320, 240, 320 + Round(100 Cos(DegToRad(angle))), 240 + Round(100 Sin(DegToRad(angle)))); SDL_RenderPresent(renderer); angle := angle + 2; SDL_Delay(16); { ~60 FPS } end; SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit;
end;

Ưu điểm: SDL cho phép xử lý sự kiện, âm thanh, và hỗ trợ đa nền tảng.

7. Vẽ 3D Cơ Bản (Wireframe)

Mặc dù Pascal không có thư viện 3D tích hợp, chúng ta có thể tự cài thuật toán Projection và Z-buffer để vẽ mô hình dạng lưới.

7.1. Thuật Toán Chiếu Phép (Projection)

type TVector3 = record x, y, z: Double; end; TVector2 = record x, y: Integer; end; function Project(const v: TVector3; const d: Double): TVector2;
begin Result.x := Round(v.x d / (v.z + d)); Result.y := Round(v.y d / (v.z + d));
end;

7.2. Vẽ Khung Lưới

procedure DrawCube;
var gd, gm: Integer; pts3D: array0..7 of TVector3; pts2D: array0..7 of TVector2; i: Integer; edges: array0..11,2 of Integer = ( (0,1),(1,2),(2,3),(3,0), (4,5),(5,6),(6,7),(7,4), (0,4),(1,5),(2,6),(3,7));
begin gd := Detect; InitGraph(gd, gm, ''); { Định nghĩa các đỉnh của hình lập phương } pts3D0 := TVector3.Create(-50,-50,-50); pts3D1 := TVector3.Create( 50,-50,-50); pts3D2 := TVector3.Create( 50, 50,-50); pts3D3 := TVector3.Create(-50, 50,-50); pts3D4 := TVector3.Create(-50,-50, 50); pts3D5 := TVector3.Create( 50,-50, 50); pts3D6 := TVector3.Create( 50, 50, 50); pts3D7 := TVector3.Create(-50, 50, 50); { Chiếu các điểm lên 2D } for i := 0 to 7 do pts2Di := Project(pts3Di, 200); SetColor(White); for i := 0 to 11 do Line(160+pts2Dedgesi,0.x, 100+pts2Dedgesi,0.y, 160+pts2Dedgesi,1.x, 100+pts2Dedgesi,1.y); ReadLn; CloseGraph;
end;

Kết quả: Một khung hình lập phương 3D dạng wireframe xuất hiện ở trung tâm màn hình.

8. Xử Lý Sự Kiện và Tương Tác Người Dùng

8.1. Bắt Phím Nhấn (Keyboard)

procedure KeyboardDemo;
var gd, gm: Integer; ch: Char;
begin gd := Detect; InitGraph(gd, gm, ''); SetColor(White); OutTextXY(10, 10, 'Nhấn phím bất kỳ, ESC để thoát'); repeat if KeyPressed then begin ch := ReadKey; OutTextXY(10, 30, 'Bạn vừa nhấn: '+ch); end; until ch = #27; { ESC } CloseGraph;
end;

8.2. Bắt Chuột (Mouse)

Cách Vẽ Hình Trong Pascal
Cách Vẽ Hình Trong Pascal
procedure MouseDemo;
var gd, gm: Integer; mx, my, mb: Integer;
begin gd := Detect; InitGraph(gd, gm, ''); ShowMouseCursor(True); repeat GetMousePos(mx, my, mb); ClearDevice; SetColor(Yellow); Circle(mx, my, 20); OutTextXY(10, 10, Format('Mouse: X=%d Y=%d Button=%d', mx, my, mb)); Delay(30); until mb = 1; { Nhấn nút trái để thoát } CloseGraph;
end;

Lưu ý: ShowMouseCursor và GetMousePos chỉ hoạt động trong chế độ đồ họa Graph (đôi khi cần Mouse unit).

9. Tích Hợp Thư Viện BGRABitmap – Đồ Họa Hiện Đại

9.1. Cài Đặt BGRABitmap

fpc -iTP # Kiểm tra các gói đã cài
fpc -iTP BGRABitmap

Hoặc tải source từ GitHub và biên dịch:

git clone https://github.com/BGRABitmap/BGRABitmap.git
cd BGRABitmap
make install

9.2. Vẽ Đồ Họa Anti-Aliasing

uses BGRABitmap, BGRABitmapTypes, Graph; procedure AntiAliasingDemo;
var bmp: TBGRABitmap; gd, gm: Integer;
begin gd := Detect; InitGraph(gd, gm, ''); bmp := TBGRABitmap.Create(320,200, clBlack); bmp.EllipseAntialias(160,100, 120,80, BGRA(255,255,0,255), 2); bmp.FontHeight := 14; bmp.TextOut(120, 180, 'Anti‑Aliasing Demo', BGRA(255,255,255,255)); bmp.Draw(Canvas,0,0); bmp.Free; ReadLn; CloseGraph;
end;

Kết quả: Đường viền tròn mượt, chữ rõ nét – điều không thể làm được bằng Graph thuần.

10. Debugging và Xử Lý Lỗi Thường Gặp

Lỗi Nguyên nhân Cách khắc phục
Error: Can't find unit Graph Thư viện không được cài hoặc không trong PATH Kiểm tra $(FPCDIR)/units/$(OS)/$(CPU)/ hoặc cài lại Free Pascal.
Graphics mode not supported Độ phân giải không khả dụng trên máy hiện tại Thử mode 0x13 hoặc chuyển sang SDL.
Access violation khi PutImage Con trỏ Image không được khởi tạo hoặc đã giải phóng Đảm bảo LoadPicture thành công, không free trước khi dùng.
Màn hình nhấp nháy Vẽ trực tiếp lên màn hình, không double buffer Sử dụng off‑screen bitmap hoặc SDL.
Màu không đúng Palette chưa được thiết lập hoặc sử dụng màu ngoài phạm vi Dùng SetRGBPalette để tùy chỉnh, hoặc dùng BGRABitmap.

Công cụ Debug: Free Pascal tích hợp gdb (Linux) hoặc Turbo Debugger (Windows). Khi làm việc với SDL, bạn có thể dùng printf hoặc SDL_Log để in thông tin.

11. Dự Án Thực Tế – Tạo Ứng Dụng Vẽ Đơn Giản

11.1. Yêu Cầu

  • Giao diện vẽ tự do (công cụ bút, tẩy).
  • Lưu/Load ảnh BMP.
  • Đổ màu bằng bucket fill.
  • Hỗ trợ undo/redo (đơn giản với stack).

11.2. Kiến Trúc

+-------------------+
| MainForm (Lazarus)|
+-------------------+
| Canvas (TBGRABitmap) |
| Toolbar (Button, ComboBox) |
+-------------------+
| Event Handlers (MouseDown, MouseMove, MouseUp) |
+-------------------+
| Commands: DrawLine, DrawRect, DrawEllipse, Fill, Eraser |
+-------------------+
| UndoStack: TList<TBGRABitmap> |
+-------------------+

11.3. Mã Nguồn Cơ Bản (Lazarus)

unit Unit1; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, BGRABitmap, BGRABitmapTypes, StdCtrls, Menus; type TTool = (tkPen, tkEraser, tkFill, tkRect, tkEllipse); { TForm1 } TForm1 = class(TForm) PaintBox1: TPaintBox; MainMenu1: TMainMenu; FileMenu: TMenuItem; OpenItem: TMenuItem; SaveItem: TMenuItem; ExitItem: TMenuItem; ToolMenu: TMenuItem; PenItem: TMenuItem; EraserItem: TMenuItem; FillItem: TMenuItem; procedure FormCreate(Sender: TObject); procedure PaintBox1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); procedure PaintBox1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure PaintBox1Paint(Sender: TObject); procedure OpenItemClick(Sender: TObject); procedure SaveItemClick(Sender: TObject); procedure ExitItemClick(Sender: TObject); procedure PenItemClick(Sender: TObject); procedure EraserItemClick(Sender: TObject); procedure FillItemClick(Sender: TObject); private CanvasBmp: TBGRABitmap; CurrentTool: TTool; IsDrawing: Boolean; StartPt: TPoint; public end; var Form1: TForm1; implementation {$R .lfm} procedure TForm1.FormCreate(Sender: TObject);
begin CanvasBmp := TBGRABitmap.Create(PaintBox1.Width, PaintBox1.Height, clWhite); CurrentTool := tkPen; IsDrawing := False;
end; procedure TForm1.PaintBox1Paint(Sender: TObject);
begin CanvasBmp.Draw(PaintBox1.Canvas, 0, 0);
end; procedure TForm1.PaintBox1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin IsDrawing := True; StartPt := Point(X, Y); if CurrentTool = tkFill then begin CanvasBmp.FloodFill(X, Y, CanvasBmp.GetPixel(X, Y), csFiller); PaintBox1.Invalidate; IsDrawing := False; end;
end; procedure TForm1.PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin if not IsDrawing then Exit; case CurrentTool of tkPen: begin CanvasBmp.DrawLineAntialias(StartPt.X, StartPt.Y, X, Y, BGRA(0,0,0,255), 1); StartPt := Point(X, Y); end; tkEraser: begin CanvasBmp.EraseRect(StartPt.X-5, StartPt.Y-5, StartPt.X+5, StartPt.Y+5); StartPt := Point(X, Y); end; // tkRect và tkEllipse sẽ vẽ preview trên canvas tạm thời end; PaintBox1.Invalidate;
end; procedure TForm1.PaintBox1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin if not IsDrawing then Exit; case CurrentTool of tkRect: CanvasBmp.RectangleAntialias(StartPt.X, StartPt.Y, X, Y, BGRA(0,0,0,255)); tkEllipse: CanvasBmp.EllipseAntialias((StartPt.X+X) div 2, (StartPt.Y+Y) div 2, Abs(X-StartPt.X) div 2, Abs(Y-StartPt.Y) div 2, BGRA(0,0,0,255)); end; IsDrawing := False; PaintBox1.Invalidate;
end; procedure TForm1.OpenItemClick(Sender: TObject);
begin if OpenDialog1.Execute then CanvasBmp.LoadFromFile(OpenDialog1.FileName); PaintBox1.Invalidate;
end; procedure TForm1.SaveItemClick(Sender: TObject);
begin if SaveDialog1.Execute then CanvasBmp.SaveToFile(SaveDialog1.FileName);
end; procedure TForm1.ExitItemClick(Sender: TObject);
begin Close;
end; procedure TForm1.PenItemClick(Sender: TObject);
begin CurrentTool := tkPen;
end; procedure TForm1.EraserItemClick(Sender: TObject);
begin CurrentTool := tkEraser;
end; procedure TForm1.FillItemClick(Sender: TObject);
begin CurrentTool := tkFill;
end; end.

Giải thích nhanh:

  • TBGRABitmap cung cấp các hàm DrawLineAntialias, RectangleAntialias, EllipseAntialias, FloodFill.
  • Các sự kiện MouseDown/Move/Up điều khiển công cụ.
  • PaintBox1.Invalidate buộc PaintBox vẽ lại, hiển thị nội dung bitmap.

11.4. Mở Rộng

  • Undo/Redo: Lưu bản sao của CanvasBmp vào TList<TBGRABitmap> mỗi khi hoàn thành một thao tác.
  • Layer: Tạo nhiều TBGRABitmap và vẽ chồng lên nhau.
  • Export sang PNG: CanvasBmp.SaveToFile('output.png'); (đòi hỏi BGRABitmap hỗ trợ PNG).

12. Tổng Kết

Trong suốt bài viết, chúng ta đã:

  1. Thiết lập môi trường (Free Pascal, Lazarus, Turbo Pascal, SDL, BGRABitmap).
  2. Hiểu rõ cách khởi tạo đồ họa với initgraph, chế độ VGA, và các hàm cơ bản.
  3. Vẽ các hình 2D: điểm, đường thẳng, hình chữ nhật, vòng tròn, đa giác.
  4. Áp dụng kỹ thuật đổ màu, gradient, và sử dụng BGRABitmap để có màu sắc phong phú, anti‑alias.
  5. Xử lý ảnh bitmap, hiển thị, và thao tác trên chúng.
  6. Tạo animation bằng vòng lặp, double buffering, và SDL cho hiệu suất cao.
  7. Thực hiện chiếu 3D (wireframe) bằng công thức chiếu perspective.
  8. Xử lý sự kiện bàn phím, chuột, và xây dựng giao diện vẽ cơ bản trong Lazarus.
  9. Giải quyết lỗi thường gặp và cung cấp công cụ debug.
  10. Triển khai dự án thực tế: một ứng dụng vẽ đơn giản với các công cụ và lưu/đọc file.

Với kiến thức này, bạn đã sẵn sàng khám phá sâu hơn: tạo trò chơi 2D, mô phỏng vật lý, vẽ biểu đồ khoa học, hoặc thậm chí xây dựng trình biên dịch đồ họa cho các ngôn ngữ riêng. Pascal, dù không còn là “ngôn ngữ hot” nhưng vẫn là nền tảng vững chắc để học lập trình đồ họa, giúp bạn nắm vững các khái niệm cơ bản như pixel rasterization, double buffering, event handling, và mô hình 3D cơ bản. Khi bạn đã thành thạo các kỹ thuật trên, việc chuyển sang các ngôn ngữ hiện đại như C++, C#, hoặc Python (với Pygame, OpenGL) sẽ trở nên dễ dàng hơn rất nhiều.

Chúc bạn thành công trong hành trình vẽ hình bằng Pascal! Nếu còn thắc mắc, hãy để lại bình luận hoặc tham gia các cộng đồng như Free Pascal Forum, Lazarus IDE Discord, và Stack Overflow để nhận được hỗ trợ nhanh chóng.

Rate this post
Mục nhập này đã được đăng trong Blog. Đánh dấu trang permalink.

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *