Lời mở đầu

Trong thế giới lập trình, Pascal không chỉ là một ngôn ngữ dạy học cơ bản mà còn là một công cụ mạnh mẽ để thực hiện các tác vụ đồ họa đơn giản. Khi nói tới “cách vẽ bằng Pascal”, hầu hết mọi người nghĩ ngay đến việc sử dụng các hàm đồ họa trong thư viện Graph (đôi khi còn gọi là BGI – Borland Graphics Interface) để tạo ra các hình ảnh, biểu đồ, hay thậm chí là trò chơi 2D. Bài viết này sẽ cung cấp cho bạn một hướng dẫn chi tiết, từ những kiến thức nền tảng nhất cho đến các kỹ thuật nâng cao, giúp bạn tự tin triển khai các dự án đồ họa bằng Pascal.

Lưu ý: Bài viết hướng tới người dùng sử dụng Free Pascal Compiler (FPC) hoặc Turbo Pascal trên môi trường Windows. Nếu bạn đang dùng các IDE khác (Lazarus, Delphi), phần lớn các hàm vẫn tương tự, chỉ cần điều chỉnh một chút.

1. Tổng quan về thư viện đồ họa Pascal

1.1. Thư viện Graph (BGI)

Thư viện Graph (hay BGI) là một giao diện lập trình ứng dụng (API) cho phép người dùng vẽ các đối tượng đồ họa như điểm, đường thẳng, hình tròn, đa giác, văn bản, và thậm chí là ảnh bitmap. Thư viện này được phát triển bởi Borland và được tích hợp sẵn trong Turbo Pascal và các phiên bản Pascal cũ. Khi sử dụng FPC, chúng ta cần tải thêm graph unit và BGI driver phù hợp.

1.2. Các thành phần chính

Thành phần Mô tả
initgraph Khởi tạo môi trường đồ họa, chọn driver và chế độ hiển thị.
closegraph Đóng môi trường đồ họa, trả lại điều khiển cho chế độ văn bản.
setcolor Đặt màu vẽ.
setbkcolor Đặt màu nền.
line, rectangle, circle, ellipse, arc Vẽ các hình cơ bản.
outtextxy, settextstyle, settextjustify In văn bản lên màn hình.
putimage, getimage Làm việc với ảnh bitmap.
setfillstyle, floodfill Đổ màu vào các vùng kín.

2. Cài đặt môi trường đồ họa Pascal

2.1. Cài đặt Free Pascal Compiler (FPC)

  1. Truy cập trang chủ của FPC: https://www.freepascal.org/
  2. Tải phiên bản phù hợp với hệ điều hành Windows (thường là fpc-<phiên bản>-i386-win32.exe).
  3. Cài đặt theo hướng dẫn, nhớ chọn Add to PATH để có thể chạy fpc từ command line.

2.2. Cài đặt Lazarus (IDE tích hợp)

Nếu bạn muốn làm việc trong môi trường GUI, Lazarus là lựa chọn tốt:

  1. Tải Lazarus từ https://www.lazarus-ide.org/
  2. Cài đặt kèm FPC (nếu chưa cài).
  3. Khi tạo dự án mới, bạn có thể thêm unit graph vào phần uses.

2.3. Chuẩn bị driver BGI

  • BGI driver (ví dụ: BGI\BGI.DRV) và BGI font (BGI\FONT.LBM) thường nằm trong thư mục BGI của Turbo Pascal.
  • Khi sử dụng FPC, bạn có thể tải driver từ: https://github.com/tomasz-makowski/graph
  • Đặt thư mục BGI cùng cấp với file .exe hoặc chỉ định đường dẫn trong hàm initgraph.
uses graph; var gd, gm: Integer;
begin gd := DETECT; { Tự động phát hiện driver } initgraph(gd, gm, 'BGI'); { Thư mục chứa driver } if graphresult <> grOk then begin writeln('Không thể khởi tạo đồ họa: ', grapherrormsg(graphresult)); halt(1); end; { ... vẽ đồ họa ... } readln; closegraph;
end.

3. Các hàm vẽ cơ bản

3.1. Vẽ điểm và đường thẳng

  • putpixel(x, y, color): Đặt một điểm tại tọa độ (x, y) với màu color.
  • line(x1, y1, x2, y2): Vẽ một đoạn thẳng từ (x1, y1) tới (x2, y2).
setcolor(Yellow);
putpixel(100, 100, Yellow);
line(120, 120, 200, 200);

3.2. Vẽ hình chữ nhật và hình vuông

  • rectangle(left, top, right, bottom): Vẽ khung hình chữ nhật.
  • Đối với hình vuông, chỉ cần right-left = bottom-top.
setcolor(Red);
rectangle(50, 50, 150, 150); { Hình vuông }

3.3. Vẽ hình tròn và elip

  • circle(x, y, radius): Vẽ vòng tròn.
  • ellipse(x, y, startAngle, endAngle, xradius, yradius): Vẽ elip hoặc cung.
setcolor(Blue);
circle(200, 200, 80);
ellipse(200, 200, 0, 360, 120, 60); { Elip ngang }

3.4. Vẽ đa giác

Cách Vẽ Bằng Pascal
Cách Vẽ Bằng Pascal
  • polygon(n, points): Vẽ đa giác với n đỉnh, points là mảng các tọa độ.
type PointArray = array1..5 of pointtype;
var poly: PointArray;
begin poly1.x := 100; poly1.y := 100; poly2.x := 150; poly2.y := 80; poly3.x := 200; poly3.y := 120; poly4.x := 180; poly4.y := 170; poly5.x := 120; poly5.y := 160; setcolor(Green); polygon(5, poly);
end;

3.5. Đổ màu (Fill)

  • setfillstyle(pattern, color): Đặt kiểu mẫu và màu cho việc đổ.
  • floodfill(x, y, borderColor): Bắt đầu đổ màu từ (x, y) cho tới khi gặp borderColor.
setfillstyle(SolidFill, LightBlue);
setcolor(Black);
rectangle(250, 250, 350, 350);
floodfill(260, 260, Black);

4. Xử lý văn bản trong đồ họa

4.1. Đặt kiểu chữ

  • settextstyle(font, direction, size): Chọn font (DEFAULT_FONT, TRIPLEX_FONT, etc.), hướng (HORIZ_DIR, VERT_DIR) và kích thước.
settextstyle(DEFAULT_FONT, HORIZ_DIR, 2);
setcolor(White);
outtextxy(100, 400, 'Xin chào Pascal!');

4.2. Canh lề và căn chỉnh

  • settextjustify(horiz, vert): horiz có thể là LEFT_TEXT, CENTER_TEXT, RIGHT_TEXT. vert là TOP_TEXT, BOTTOM_TEXT, CENTER_TEXT.
settextjustify(CENTER_TEXT, CENTER_TEXT);
outtextxy(getmaxx div 2, getmaxy div 2, 'Trung tâm');

4.3. In ký tự đặc biệt

Đối với ký tự Unicode, Pascal truyền thống không hỗ trợ trực tiếp. Tuy nhiên, nếu bạn dùng Lazarus với Graph unit mở rộng, có thể vẽ bitmap chứa ký tự Unicode hoặc dùng thư viện SDL2/ OpenGL để hiển thị văn bản Unicode.

5. Làm việc với ảnh bitmap

5.1. Đọc và ghi ảnh

  • getimage(x, y, w, h, buffer): Lấy một vùng ảnh vào bộ nhớ.
  • putimage(x, y, buffer, mode): Đặt ảnh từ bộ nhớ lên màn hình. mode có thể là NORMAL_PUT, XOR_PUT, AND_PUT, OR_PUT.
type ImagePtr = ^ImageRec; ImageRec = record size: Word; data: array0..0 of Byte; end; var img: ImagePtr; imgSize: Word;
begin imgSize := imagesize(0, 0, 200, 200); GetMem(img, imgSize); getimage(0, 0, 200, 200, img); putimage(250, 0, img, NORMAL_PUT); FreeMem(img, imgSize);
end;

5.2. Lưu ảnh ra file

Thư viện Graph không có hàm lưu ảnh trực tiếp. Bạn có thể:

  1. Sử dụng writeimagefile (có trong một số bản mở rộng).
  2. Ghi dữ liệu raw sang file .bmp bằng cách tự tạo header BMP và ghi buffer.
procedure SaveBMP(filename: string; x, y, w, h: Integer);
var img: ImagePtr; imgSize: Word; f: file;
begin imgSize := imagesize(x, y, x + w - 1, y + h - 1); GetMem(img, imgSize); getimage(x, y, x + w - 1, y + h - 1, img); AssignFile(f, filename); Rewrite(f, 1); BlockWrite(f, img^, imgSize); CloseFile(f); FreeMem(img, imgSize);
end;

Lưu ý: File được lưu ở dạng raw (không có header BMP), nên để mở trong các phần mềm ảnh cần chuyển đổi hoặc thêm header.

6. Các kỹ thuật nâng cao

6.1. Vẽ hoạt ảnh (Animation)

Sử dụng vòng lặp và delay để tạo chuyển động. Đảm bảo xóa hình cũ bằng cleardevice hoặc setfillstyle + floodfill.

var i: Integer;
begin for i := 0 to getmaxx do begin cleardevice; setcolor(Random(15) + 1); circle(i, getmaxy div 2, 30); delay(10); end;
end;

6.2. Double Buffering

Để tránh flicker (nhấp nháy), vẽ lên một off-screen buffer rồi sao chép sang màn hình.

var buffer: ImagePtr; bufSize: Word;
begin bufSize := imagesize(0, 0, getmaxx, getmaxy); GetMem(buffer, bufSize); { Vẽ vào buffer } setfillstyle(SolidFill, Black); floodfill(0, 0, White); { ... các lệnh vẽ ... } putimage(0, 0, buffer, NORMAL_PUT); FreeMem(buffer, bufSize);
end;

6.3. Xử lý sự kiện chuột

Thư viện Graph cung cấp mouse qua unit mouse. Cần khởi tạo và kiểm tra trạng thái.

uses graph, mouse; begin initgraph(gd, gm, ''); initmouse; showmouse; repeat if mousex <> oldx then begin setcolor(White); line(oldx, 0, oldx, getmaxy); setcolor(Red); line(mousex, 0, mousex, getmaxy); oldx := mousex; end; until mousebuttons and leftbutton <> 0; hidemouse; closegraph;
end.

6.4. Vẽ các biểu đồ thống kê

Cách Vẽ Bằng Pascal
Cách Vẽ Bằng Pascal

Bạn có thể dùng các hàm vẽ hình chữ nhật và outtextxy để tạo bar chart, line chart, hoặc pie chart.

6.4.1. Bar Chart (Biểu đồ cột)

procedure DrawBarChart(values: array of Integer; maxVal: Integer);
var i, barWidth, gap, height, x, y: Integer; scale: Real;
begin barWidth := 40; gap := 20; scale := (getmaxy - 100) / maxVal; { 100 pixel margin trên và dưới } for i := 0 to High(values) do begin height := Round(valuesi scale); x := 50 + i (barWidth + gap); y := getmaxy - 50; setfillstyle(SolidFill, i + 1); bar(x, y, x + barWidth, y - height); outtextxy(x, y + 10, IntToStr(valuesi)); end;
end;

6.4.2. Pie Chart (Biểu đồ tròn)

procedure DrawPieChart(values: array of Integer);
var i, total, angleStart, angleEnd, cx, cy, radius: Integer; colorIdx: Integer;
begin total := 0; for i := 0 to High(values) do total := total + valuesi; cx := getmaxx div 2; cy := getmaxy div 2; radius := Min(cx, cy) - 50; angleStart := 0; colorIdx := 1; for i := 0 to High(values) do begin angleEnd := angleStart + Round(360 valuesi / total); setfillstyle(SolidFill, colorIdx); pieslice(cx, cy, angleStart, angleEnd, radius); angleStart := angleEnd; Inc(colorIdx); end;
end;

7. Tối ưu hoá và khắc phục lỗi thường gặp

7.1. Lỗi “Graphics driver not found”

  • Kiểm tra đường dẫn truyền vào initgraph. Đảm bảo thư mục BGI tồn tại và chứa BGI.DRV.
  • Nếu dùng FPC, có thể cần chỉ định driver VESA hoặc SVGA tùy vào độ phân giải.
initgraph(gd, gm, 'C:\Pascal\BGI');

7.2. Màn hình nhấp nháy (Flicker)

  • Áp dụng double buffering (phần 6.2).
  • Tránh sử dụng cleardevice quá thường xuyên; chỉ xóa vùng cần thiết.

7.3. Độ phân giải không phù hợp

  • getmaxx và getmaxy trả về kích thước hiện tại. Nếu muốn độ phân giải cao hơn, thay đổi gm (graphics mode) trong initgraph.
  • Ví dụ: gm := 640480; (độ phân giải VGA). Đối với độ phân giải lớn hơn, cần driver VESA.

7.4. Không hiển thị màu đúng

  • Đảm bảo sử dụng palette đúng: setcolor(1..15) cho chế độ 16 màu. Đối với chế độ 256 màu, sử dụng setcolor(0..255).
  • Kiểm tra graphresult sau mỗi lệnh vẽ để bắt lỗi.

7.5. Vấn đề tương thích với Windows 10/11

  • Thư viện BGI được thiết kế cho môi trường DOS, vì vậy trên Windows hiện đại có thể gặp compatibility mode.
  • Giải pháp: chạy chương trình trong DOSBox hoặc chuyển sang thư viện đồ họa hiện đại như SDL2, SFML, hoặc OpenGL.

8. Ví dụ thực tế: Vẽ một trò chơi đơn giản – “Snake”

Dưới đây là một dự án nhỏ minh họa cách kết hợp các hàm vẽ, xử lý bàn phím và timer để tạo ra trò chơi Snake.

program SnakeGame;
uses graph, crt, dos; const MaxX = 640; MaxY = 480; BlockSize = 20; MaxLen = 100; type TPoint = record x, y: Integer; end; var gd, gm: Integer; snake: array1..MaxLen of TPoint; len, dir: Integer; food: TPoint; ch: Char; gameOver: Boolean; procedure InitGame;
var i: Integer;
begin len := 5; dir := 0; { 0=up,1=right,2=down,3=left } for i := 1 to len do begin snakei.x := MaxX div 2; snakei.y := MaxY div 2 + i BlockSize; end; Randomize; food.x := Random(MaxX div BlockSize) BlockSize; food.y := Random(MaxY div BlockSize) BlockSize; gameOver := False;
end; procedure DrawBlock(p: TPoint; col: Integer);
begin setfillstyle(SolidFill, col); bar(p.x, p.y, p.x + BlockSize, p.y + BlockSize);
end; procedure DrawSnake;
var i: Integer;
begin setcolor(Black); cleardevice; for i := 1 to len do DrawBlock(snakei, LightGreen); DrawBlock(food, Red);
end; procedure MoveSnake;
var i: Integer;
begin { Di chuyển thân } for i := len downto 2 do begin snakei := snakei-1; end; { Di chuyển đầu } case dir of 0: Dec(snake1.y, BlockSize); 1: Inc(snake1.x, BlockSize); 2: Inc(snake1.y, BlockSize); 3: Dec(snake1.x, BlockSize); end; { Kiểm tra va chạm tường } if (snake1.x < 0) or (snake1.x >= MaxX) or (snake1.y < 0) or (snake1.y >= MaxY) then gameOver := True; { Kiểm tra ăn thức ăn } if (snake1.x = food.x) and (snake1.y = food.y) then begin if len < MaxLen then Inc(len); food.x := Random(MaxX div BlockSize) BlockSize; food.y := Random(MaxY div BlockSize) BlockSize; end; { Kiểm tra tự cắn đuôi } for i := 2 to len do if (snake1.x = snakei.x) and (snake1.y = snakei.y) then gameOver := True;
end; begin gd := DETECT; initgraph(gd, gm, ''); if graphresult <> grOk then begin writeln('Không thể khởi tạo đồ họa: ', grapherrormsg(graphresult)); halt(1); end; InitGame; repeat if keypressed then begin ch := readkey; case ch of #75: if dir <> 1 then dir := 3; { trái } #77: if dir <> 3 then dir := 1; { phải } #72: if dir <> 2 then dir := 0; { lên } #80: if dir <> 0 then dir := 2; { xuống } #27: gameOver := True; { ESC } end; end; MoveSnake; DrawSnake; delay(100); until gameOver; setcolor(White); outtextxy(getmaxx div 2 - 50, getmaxy div 2, 'Game Over!'); readln; closegraph;
end.

Giải thích nhanh:

  • Sử dụng array để lưu vị trí các đoạn thân.
  • MoveSnake cập nhật vị trí, kiểm tra va chạm và ăn thức ăn.
  • DrawSnake vẽ lại toàn bộ màn hình mỗi khung.
  • Điều khiển bằng các phím mũi tên.

9. Thay thế BGI: Sử dụng SDL2 trong Free Pascal

Nếu bạn muốn phát triển ứng dụng đồ họa hiện đại, SDL2 là lựa chọn tốt. Dưới đây là ví dụ ngắn gọn tích hợp SDL2 để vẽ một vòng tròn.

program CircleSDL2;
uses sdl2, sdl2_gfx; var window: PSDL_Window; renderer: PSDL_Renderer; quit: Boolean; event: SDL_Event;
begin SDL_Init(SDL_INIT_VIDEO); window := SDL_CreateWindow('Circle with SDL2', SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN); renderer := SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); quit := False; while not quit do begin while SDL_PollEvent(@event) <> 0 do if event.type_ = SDL_QUITEV then quit := True; SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); circleColor(renderer, 400, 300, 100, $FFFF0000); { Vòng tròn đỏ } SDL_RenderPresent(renderer); SDL_Delay(16); end; SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit;
end.

Ưu điểm của SDL2:

  • Hỗ trợ đa nền tảng (Windows, Linux, macOS).
  • Đồ họa phần cứng (GPU) nhanh hơn.
  • Hỗ trợ âm thanh, nhập liệu, mạng.

Bạn có thể chuyển từ BGI sang SDL2 khi dự án cần mở rộng hoặc muốn chạy trên hệ điều hành mới.

10. Kết luận

Việc vẽ bằng Pascal không chỉ là một bài tập lập trình mà còn là cơ hội để hiểu sâu hơn về cách hoạt động của đồ họa máy tính ở mức độ thấp. Từ việc khởi tạo môi trường đồ họa, vẽ các hình cơ bản, xử lý văn bản, làm việc với bitmap, tới các kỹ thuật nâng cao như double buffering và animation, bạn đã có một bộ công cụ hoàn chỉnh để tạo ra các ứng dụng đồ họa 2D.

Mặc dù thư viện BGI đã cổ điển và có những hạn chế (độ phân giải, màu sắc, tương thích), nó vẫn là một nền tảng học tập tuyệt vời. Khi đã thành thạo, bạn có thể dần chuyển sang các thư viện hiện đại hơn như SDL2, SFML, hoặc OpenGL để phát triển các dự án đồ họa phức tạp hơn.

Hy vọng bài viết này đã cung cấp cho bạn đủ kiến thức và ví dụ thực tiễn để bắt đầu hoặc nâng cao kỹ năng vẽ bằng Pascal. Hãy thực hành, thử nghiệm các ý tưởng sáng tạo của mình và chia sẻ kết quả với cộng đồng lập trình Pascal! Chúc bạn thành cô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 *