Có thể bạn quan tâm: Cách Vẽ Hình Lục Lăng: Hướng Dẫn Chi Tiết Từ Cơ Bản Đến Nâng Cao
TÓM TẮT
- 1 Mở Đầu
- 2 1. Chuẩn Bị Môi Trường Phát Triển
- 3 2. Kiến Thức Cơ Bản Về Đồ Họa Trong Pascal
- 4 3. Vẽ Các Hình Cơ Bản
- 5 4. Kỹ Thuật Đổ Màu và Gradient
- 6 5. Vẽ Ảnh Bitmap
- 7 6. Tạo Animation Cơ Bản
- 8 7. Vẽ 3D Cơ Bản (Wireframe)
- 9 8. Xử Lý Sự Kiện và Tương Tác Người Dùng
- 10 9. Tích Hợp Thư Viện BGRABitmap – Đồ Họa Hiện Đại
- 11 10. Debugging và Xử Lý Lỗi Thường Gặp
- 12 11. Dự Án Thực Tế – Tạo Ứng Dụng Vẽ Đơn Giản
- 13 12. Tổng Kế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:
- Cài đặt Free Pascal: Tải và cài đặt tại https://www.freepascal.org.
- Cài đặt Lazarus: Tải và cài đặt tại https://www.lazarus-ide.org.
- Kiểm tra thư mục
$(FPCDIR)/units/$(OS)/$(CPU)/: Đảm bảo có filegraph.ppuvàgraph.pas. - 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 quafpmake. 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ó thể bạn quan tâm: Cách Vẽ Hình Trong Minecraft: Hướng Dẫn Chi Tiết Từ Cơ Bản Đến Nâng Cao
- Màu nền (Background) và màu vẽ (Foreground): Được đặt bằng
setbkcolorvà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ó thể bạn quan tâm: Cách Vẽ Hình Người Nhện: Hướng Dẫn Chi Tiết Từ Cơ Bản Đến Nâng Cao
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ó thể bạn quan tâm: Cách Vẽ Hình Mặt Cười: Hướng Dẫn Chi Tiết Từ Cơ Bản Đến Nâng Cao
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:
TBGRABitmapcung cấp các hàmDrawLineAntialias,RectangleAntialias,EllipseAntialias,FloodFill.- Các sự kiện
MouseDown/Move/Upđiều khiển công cụ. PaintBox1.Invalidatebuộ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
CanvasBmpvàoTList<TBGRABitmap>mỗi khi hoàn thành một thao tác. - Layer: Tạo nhiều
TBGRABitmapvà vẽ chồng lên nhau. - Export sang PNG:
CanvasBmp.SaveToFile('output.png');(đòi hỏiBGRABitmaphỗ trợ PNG).
12. Tổng Kết
Trong suốt bài viết, chúng ta đã:
- Thiết lập môi trường (Free Pascal, Lazarus, Turbo Pascal, SDL, BGRABitmap).
- Hiểu rõ cách khởi tạo đồ họa với
initgraph, chế độ VGA, và các hàm cơ bản. - Vẽ các hình 2D: điểm, đường thẳng, hình chữ nhật, vòng tròn, đa giác.
- Áp dụng kỹ thuật đổ màu, gradient, và sử dụng
BGRABitmapđể có màu sắc phong phú, anti‑alias. - Xử lý ảnh bitmap, hiển thị, và thao tác trên chúng.
- Tạo animation bằng vòng lặp, double buffering, và SDL cho hiệu suất cao.
- Thực hiện chiếu 3D (wireframe) bằng công thức chiếu perspective.
- 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.
- Giải quyết lỗi thường gặp và cung cấp công cụ debug.
- 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.
