Bài giảng Lập trình hướng đối tượng C++ - Chương 8: Đồ họa

doc 24 trang hoanguyen 3760
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng Lập trình hướng đối tượng C++ - Chương 8: Đồ họa", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên

Tài liệu đính kèm:

  • docbai_giang_lap_trinh_huong_doi_tuong_c_chuong_8_do_hoa.doc

Nội dung text: Bài giảng Lập trình hướng đối tượng C++ - Chương 8: Đồ họa

  1. Chương 8 Tên tệp tin Kiểu màn hình đồ họa Đồ họa ATT.BGI ATT & T6300 (400 dòng) Trong chương này sẽ giới thiệu các hàm để vẽ các đường và hình CGA.BGI IBMCGA, MCGA và các máy tương thích cơ bản như đường tròn, cung elip, hình quạt, đường gẫy khúc, hình EGAVGA.BGI IBM EGA, VGA và các máy tương thích đa giác, đường thẳng, đường chữ nhật, hình chữ nhật, hình hộp chữ HERC.BGI Hercules monochrome và các máy tương thích nhật, Ngoài ra còn đề cập tới các vấn đề rất lý thú khác như: xử lý văn bản trên màn hình đồ họa, cửa sổ và kỹ thuật tạo ảnh di động. IBM8514.BGI IBM 8514 và các máy tương thích Các hàm đồ họa được khai báo trong tệp graphics.h. PC3270.BGI IBM 3270 PC Màn hình đồ họa gồm nhiều điểm ảnh được sắp xếp trên các § 1. Khái niệm đồ họa đường thẳng ngang và dọc. Điều này đúng cho tất cả các kiểu màn Để hiểu kỹ thuật lập trình đồ họa, đầu tiên phải hiểu các yếu tố cơ hình đồ họa của máy tính. Khác biệt chủ yếu giữa chúng là kích bản của đồ họa. Từ trước đến nay chúng ta chủ yếu làm việc với kiểu thước và số các điểm ảnh. Trong kiểu CGA (độ phân giải thấp), điểm văn bản. Nghĩa là màn hình được thiết lập để hiển thị 25 dòng, mỗi ảnh có kích thước lớn, chiều ngang có 320 điểm ảnh, còn theo chiều dòng có thể chứa 80 ký tự. Trong kiểu văn bản, các ký tự hiển thị dọc có 200 điểm ảnh. Màn hình VGA có độ phân giải cao hơn: điểm trên màn hình đã được phần cứng của máy PC ấn định trước và ta ảnh nhỏ hơn, trên mỗi hàng có 640 điểm ảnh và trên mỗi cột có 480 không thể nào thay đổi được kích thước, kiểu chữ. điểm ảnh. Điểm ảnh càng nhỏ thì số điểm ảnh trên màn hình càng nhiều và chất lượng đồ họa càng cao. ở màn hình đồ họa, ta có thể xử lý đến từng chấm điểm (pixel) Mỗi kiểu đồ họa dùng một hệ tọa độ riêng. Hệ tọa độ cho màn trên màn hình và do vậy muốn vẽ bất kỳ thứ gì cũng được. Sự bài trí hình VGA là 640 x 480 như sau : và số pixel trên màn hình được gọi là độ phân giải (resolution). Do mỗi kiểu màn hình đồ họa có một cách xử lý đồ họa riêng nên (0,0) (639,0) TURBO C cung cấp một tệp tin điều khiển riêng cho từng kiểu đồ họa. Bảng 8-1 cho thấy các kiểu đồ họa và các tệp tin điều khiển chúng. Ngoài các tệp có đuôi BGI chứa chương trình điều khiển đồ họa, TURBO C còn cung cấp các tệp tin đuôi CHR chứa các Font chữ để vẽ các kiểu chữ khác nhau trên màn hình đồ họa. Đó là các tệp: GOTH.CHR LITT.CHR SANS.CHR (0,479) (639,479) TRIP.CHR Hình 8.1. Hệ tọa độ VGA Bảng 8-1. Các tệp tin điều khiển đồ họa của TURBO C++ 446 447
  2. Nhờ hệ tọa độ này, ta có thể tác động hay tham chiếu đến bất kỳ initgraph(&mh, &mode, "C:\TC"); điểm ảnh nào trên màn hình đồ họa. . . . Nếu dùng màn hình CGA thì góc dưới phải có tọa độ (319, 199). } Độc lập với kiểu đồ họa đang sử dụng, các hàm getmaxx và getmaxy Bảng 8-2. Các giá trị khả dĩ của graphdriver, graphmode bao giờ cũng cho tọa độ x và y lớn nhất trong kiểu đồ họa đang 448 449 dùng. graphdriver graphmode Độ phân giải Một chương trình đồ họa thường gồm các phần sau: Detect (0) - Khởi động hệ thống đồ họa. CGA (1) CGAC0 (0) 320 x 200 - Xác định mầu nền (mầu màn hình), mầu đường vẽ, mầu tô và CGAC1 (1) 320 x 200 kiểu (mẫu) tô. CGAC2 (2) 320 x 200 - Vẽ, tô mầu các hình mà ta mong muốn. CGAC3 (3) 320 x 200 CGAHi (4) 640 x 200 - Các thao tác đồ họa khác như cho hiện các dòng chữ MCGA (2) MCGA0 (0) 320 x 200 - Đóng hệ thống đồ họa để trở về mode văn bản. MCGA1 (1) 320 x 200 MCGA2 (2) 320 x 200 § 2. Khởi động hệ đồ họa MCGA3 (3) 320 x 200 Mục đích của việc khởi động hệ thống đồ họa là xác định thiết bị MCGAMed (4) 640 x 200 đồ họa (màn hình) và mốt đồ họa sẽ sử dụng trong chương trình. Để MCGAHi (5) 640 x 480 làm điều này ta dùng hàm: EGA (3) EGALO (0) 640 x 200 void initgraph(int *graphdriver, int *graphmode,char EGAHi (1) 640 x 350 *driverpath); EGA64 (4) EGA64LO (0) 640 x 200 trong đó: driverpath là đường dẫn của thư mục chứa các tệp tin điều EGA64Hi (1) 640 x 350 khiển đồ họa, graphdriver, graphmode cho biết màn hình và mốt đồ EGAMONO (5) EGAMONOHi (0) 640 x 350 họa sẽ sử dụng trong chương trình. Bảng 8-2 cho thấy các giá trị khả VGA (9) VGALO (0) 640 x 200 dĩ của graphdriver và graphmode. VGAMED (1) 640 x 350 Ví dụ 1. Giả sử máy tính của ta có màn hình EGA, các tệp tin đồ VGAHI (2) 640 x 480 họa chứa trong thư mục C: \TC, khi đó ta có thể khởi động hệ thống HERCMONO (7) HERCMONOHI 720 x 348 đồ họa như sau: ATT400 (8) ATT400C0 (0) 320 x 200 #include "graphics.h" ATT400C1 (1) 320 x 200 main() ATT400C2 (2) 320 x 200 { ATT400C3 (3) 320 x 200 int mh=EGA, mode= EGALO; ATT400MED (4) 640 x 400 ATT400HI (5) 640 x 400
  3. PC3270 (10) PC3270HI (0) 720 x 350 #include "graphics.h" IBM8514 (6) IBM8514LO (0) 640 x 480, 256 mầu #include "stdio.h" IBM8514HI (1) 1024 x 768, 256 mầu main() { Chú ý 1. Bảng 8-2 cho các tên hằng và giá trị của chúng mà các int mh=0, mode= 0; biến graphdriver, graphmode có thể nhận. Chẳng hạn hằng DETECT initgraph(&mh, &mode, ""); có giá trị 0, hằng VGA có giá trị 9, hằng VGALO có giá trị 0 Khi lập trình ta có thể dùng tên hằng hoặc giá trị tương ứng của chúng. 450 printf("\n Giá trị số của màn hình là: %d", mh); 451 Chẳng hạn các phép gán trong ví dụ 1 có thể viết theo một cách khác closegraph(); tương đương như sau: } mh=3; Nếu chương trình cho kết quả: mode=0; Giá trị số của màn hình là: 3 Chú ý 2. Bảng 8.2 cho thấy độ phân giải phụ thuộc cả vào màn hình và mode. Ví dụ trong màn hình EGA nếu dùng mode EGALO thì ta có thể khẳng định loại màn hình đang dùng là EGA. thì độ phân giải là 640 x 200, hàm getmaxx cho giá trị 639, hàm Chú ý 4. Nếu chuỗi dùng để xác định driverpath là một chuỗi getmaxy cho giá trị 199 . Nếu cũng màn hình EGA mà dùng mode rỗng (như trong ví dụ 2) thì chương trình dịch sẽ tìm các tệp điều EGAHI thì độ phân giải là 640x 350, hàm getmaxx cho giá trị 639, khiển đồ họa trên thư mục chủ. hàm getmaxy cho giá trị 349. Chú ý 3. Nếu không biết chính xác kiểu màn hình đang sử dụng § 3. Lỗi đồ họa thì ta gán cho biến graphdriver hằng DETECT hay giá trị 0. Khi đó kết quả của hàm initgraph sẽ là: Khi khởi động hệ thống đồ họa nếu máy không tìm thấy các chương trình điều khiển đồ họa thì sẽ phát sinh lỗi đồ họa và việc - Kiểu của màn hình đang sử dụng được phát hiện, giá trị số của khởi động coi như không thành. Lỗi đồ họa còn phát sinh khi dùng nó được gán cho biến graphdriver. các hàm đồ hoạ. Trong mọi trường hợp, hàm graphresult cho biết có - Mode đồ họa ở độ phân giải cao nhất ứng với màn hình đang sử lỗi hay không lỗi và đó là lỗi gì. Bảng 8-3 cho các mã lỗi mà hàm dụng cũng được phát hiện và giá trị số của nó được gán cho biến này phát hiện được. Ta có thể dùng hàm grapherrormsg với mã lỗi graphmode. do hàm graphresult trả về để biết được đó là lỗi gì, ví dụ: Như vậy việc dùng hằng số DETECT chẳng những có thể khởi int maloi; động được hệ thống đồ họa của màn hình hiện có theo mode có độ maloi = graphresult(); phân giải cao nhất, mà còn giúp ta xác định chính xác kiểu màn hình đang sử dụng. printf("\nLỗi đồ họa là: %d", grapherrormsg(maloi)); Ví dụ 2. Chương trình dưới đây xác định kiểu màn hình đang sử Bảng 8-3. Các mã lỗi của Graphresult dụng: Hằng Trị Lỗi phát hiện
  4. grOk 0 Không có lỗi 4. Chọn giải mầu grNoInitGraph -1 Chưa khởi động hệ đồ họa Để thay đổi giải mầu đã được định nghĩa trong bảng 8.4 ta dùng grNotDetected -2 Không có phần cứng đồ họa hàm grFileNotFound -3 Không tìm thấy trình điều khiển đồ họa void setpalette(int colornum, int color); grInvalidDriver -4 Trình điều khiển không hợp lệ Ví dụ câu lệnh grNoLoadMem -5 Không đủ RAM cho đồ họa setpalette(0, Lightcyan); grNoScanMem -6 Vượt vùng RAM trong Scan fill biến mầu đầu tiên trong bảng mầu thành xanh lơ nhạt. Các mầu khác grNoFloodMem -7 Vượt vùng RAM trong flood fill không452 bị ảnh hưởng. 453 grFontNoFound -8 Không tìm thấy tập tin Font Bảng 8-4. Các giá trị khả dĩ của color grNoFontMem -9 Không đủ RAM để nạp Font Tên hằng Giá trị số Mầu hiển thị grInvalidMode -10 Kiểu đồ họa không hợp lệ cho trình điều khiển BLACK 0 Đen grError -11 Lỗi đồ họa tổng quát BLUE 1 Xanh da trời grIOerror -12 Lỗi đồ họa vào ra GREEN 2 Xanh lá cây grInvalidFont -13 Tập tin Font không hợp lệ CYAN 3 Xanh lơ grInvalidFontNum -14 Số hiệu Font không hợp lệ RED 4 Đỏ MAGENTA 5 Tím § 4. Mầu và mẫu BROWN 6 Nâu 1. Để chọn mầu nền ta sử dụng hàm LIHGTGRAY 7 Xám nhạt DARKGRAY 8 Xám sẫm void setbkcolor(int color); LIGHTBLUE 9 Xanh da trời nhạt 2. Để chọn mầu đường vẽ ta dùng hàm LIGHTGREEN 10 Xanh lá cây nhạt void setcolor(int color); LIGHTCYAN 11 Xanh lơ nhạt 3. Để chọn mẫu (kiểu) tô và mầu tô ta dùng hàm LIGHTRED 12 Đỏ nhạt void setfillstyle(int pattern, int color); LIGHTMAGENTA 13 Tím nhạt Trong cả 3 trường hợp color xác định mã của mầu. Các giá trị khả YELLOW 14 Vàng dĩ của color cho trong bảng 8-4, pattern xác định mã của mẫu tô WHITE 15 Trắng (xem bảng 8-5). Mẫu tô và mầu tô sẽ được sử dụng trong các hàm pieslice, 5. Để nhận giải mầu hiện hành ta dùng hàm fillpoly, bar, bar3d và floodfill (xem §5 dưới đây). void getpalette (struct palettetype *palette);
  5. ở đây palettetype là kiểu đã định nghĩa trước như sau: 8. Hàm getmaxcolor trả về mã mầu cực đại thuộc giải mầu hiện #define MAXCOLORS 15 đang có hiệu lực. Trên 256 K EGA, hàm getmaxcolor luôn cho giá trị 15. struct palettetype { § 5. Vẽ và tô mầu unsigned char size; Có thể chia các đường và hình thành bốn nhóm chính: unsigned char colors[MAXCOLORS+1]; - Đường tròn và ellipse }; - Đường gấp khúc và hình đa giác ở đây: size là số lượng mầu trong palette, colors là mảng chứa mầu - Đường thẳng với chỉ số mảng chạy từ 0 đến size - 1 - Hình chữ nhật Bảng 8-5. Các giá trị khả dĩ của pattern Tên hằng Giá trị số Mô tả kiểu tô 454A. Đường tròn và hình tròn 455 EMPTY_FILL 0 Tô bằng mầu nền Nhóm này gồm cung tròn, đường tròn, cung ellipse và hình quạt. SOLID_FILL 1 Tô bằng đường nét liền 1. Cung tròn. Để vẽ một cung tròn ta dùng hàm LINE_FILL 2 Tô bằng - - - void arc(int x, int y, int gd, int gc, int r); LTSLASH_FILL 3 Tô bằng /// ở đây: SLASH_FILL 4 Tô bằng /// in đậm (x, y) là tọa độ của tâm cung tròn, BKSLASH_FILL 5 Tô bằng \\\ in đậm r là bán kính LTBKSLASH_FILL 6 Tô bằng \\\ gd là góc đầu HATCH_FILL 7 Tô bằng đường gạch bóng nhạt gc là góc cuối XHATCH_FILL 8 Tô bằng đường gạch bóng chữ thập Chú ý: Trong tất cả các hàm dưới đây, góc tính theo độ và có giá INTERLEAVE_FILL 9 Tô bằng đường đứt quãng trị từ 0 đến 360. WIDE_DOT_FILL 10 Tô bằng dấu chấm thưa 2. Đường tròn. Để vẽ một đường tròn ta dùng hàm CLOSE_DOT_FILL 11 Tô bằng dấu chấm mau void circle(int x, int y, int r); 6. Hàm getcolor trả về mầu đã xác định trước đó bằng hàm setcolor. ở đây: (x, y) là tọa độ của tâm; 7. Hàm getbkcolor trả về mầu đã xác định trước đó bằng hàm setbkcolor. r là bán kính đường tròn. 3. Cung ellipse. Để vẽ một cung Ellipse ta dùng hàm void ellipse(int x,int y,int gd,int gc,int xr,int yr);
  6. ở đây: setfillstyle (SLASH_FILL, RED); (x, y) là tọa độ của tâm cung Ellipse // Vẽ: một cung tròn ở góc phần tư thứ nhất, gd là góc đầu // một cung Ellipse ở góc phần tư thứ ba, gc là góc cuối // một đường tròn, một quạt tròn xr là bán trục ngang arc(160, 50, 0, 90, 45); yr là bán trục đứng. ellipse(480, 50, 180, 270, 150, 45); 4. Hình quạt. Để vẽ và tô màu một hình quạt ta dùng hàm circle(160, 150, 45); void pieslice(int x,int y,int gd,int gc,int r); pieslice(480, 150, 90, 360, 45); ở đây: // Kết thúc chế độ đồ họa (x,y) là tọa độ tâm hình quạt closegraph(); gd là góc đầu gc là góc cuối } r là bán kính B.456 Đường gấp khúc và đa giác 457 Ví dụ 1. Chương trình dưới đây sẽ vẽ: một cung tròn ở góc phần 5. Muốn vẽ một đường gấp khúc đi qua n điểm: (x1,y1), , tư thứ nhất, một cung ellipse ở góc phần tư thứ ba, một đường tròn (xn,yn) thì trước hết ta phải đưa các tọa độ vào một mảng a nào đó và một hình quạt quét từ 90 đến 360 độ. kiểu int. Nói một cách chính xác hơn, cần gán x1 cho a[0], y1 cho #include a[1], x2 cho a[2], y2 cho a[3], Sau đó ta viết lời gọi hàm: main() drawpoly(n, a); { Khi điểm cuối (xn, yn) trùng với điểm đầu (x1, y1) ta nhận được một đường gấp khúc khép kín. int mh, mode; // Khởi động đồ họa, màn hình EGA, mode EGALO 6. Giả sử a là mảng đã nói trong điểm 5, khi đó lời gọi hàm mh=EGA; fillpoly(n, a); sẽ vẽ và tô mầu một đa giác có đỉnh là các điểm mode=EGALO; (x1, y1), ,(xn, yn). initgraph(&mh, &mode,""); Ví dụ 2. Chương trình dưới đây sẽ vẽ một đường gấp khúc và hai // Mầu nền Green, mầu đường vẽ hình tam giác. //White, mầu tô Red, kiểu tô SlashFill #include setbkcolor (GREEN); // Xây dựng các mảng chứa tọa độ các đỉnh setcolor (WHITE); int poly1[]={5,200,190,5,100,300};
  7. int poly2[]={205,200,390,5,300,300}; vẽ một đường thẳng từ vị trí hiện tại (x, y) của con chạy đến điểm int poly3[]={405,200,590,5,500,300,405,200}; (x + dx,y + dy). Con chạy được di chuyển đến vị trí mới. main() 10. Hàm { void moveto(int x,int y); int mh=0, mode=0; sẽ di chuyển con chạy tới vị trí (x, y). initgraph(&mh, &mode, ""); Ví dụ 3. Chương trình dưới đây tạo lên một đường gấp khúc bằng // Mầu nền CYAN, mầu đường vẽ các đoạn thẳng. Đường gấp khúc đi qua các đỉnh: (20, 20), (620, 20), (620, 180), (20, 180) và (320, 100). // YELLOW, mầu tô MAGENTA, mẫu tô SolidFill #include setbkcolor (CYAN); Setcolor (YELLOW); main() setfillstyle (SOLID_FILL, MAGENTA); { drawpoly (3, poly1); // Đường gấp khúc int mh=0, mode=0; fillpoly (3, poly2); // Hình đa giác initgraph(&mh, &mode, ""); fillpoly(4, poly3); // Hình đa giác setbkcolor(GREEN); closegraph(); setcolor(YELLOW); } 458 moveto(320,100); 459 C. Đường thẳng line(20,20,620,20); linerel(-300,80); 7. Hàm lineto(620,180); void line(int x1,int y1,int x2,int y2); lineto(620,20); vẽ đường thẳng nối hai điểm (x1, y1) và (x2, y2) nhưng không làm closegraph(); thay đổi vị trí con chạy. } 8. Hàm D. Hình chữ nhật void lineto(int x,int y); 11. Hàm vẽ đường thẳng từ điểm hiện tại tới điểm (x, y) và chuyển con chạy đến điểm (x, y). void rectangle(int x1,int y1,int x2,int y2); sẽ vẽ một đường chữ nhật có các cạnh song song với các cạnh của 9. Hàm màn hình. Tọa độ đỉnh trên bên trái của hình chữ nhật là (x1,y1) và void linerel(int dx,int dy); điểm dưới bên phải là (x2,y2).
  8. 12. Hàm closegraph(); void bar(int x1,int y1,int x2,int y2); } sẽ vẽ và tô mầu một hình chữ nhật. Các giá trị x1,y1,x2 và y2 có ý nghĩa như đã nói trong điểm 11. § 6. Chọn kiểu đường 13. Hàm 1. Hàm void bar3d(int x1,int y1,int x2,int y2,int depth,int top); void setlinestyle(int linestyle,int pattern,int thickness); sẽ vẽ một khối hộp chữ nhật, mặt ngoài của nó là hình chữ nhật xác tác động đến nét vẽ của các thủ tục line, lineto, rectange, drawpoly, định bởi các tọa độ x1,y1,x2,y2 (như đã nói trong điểm 12). Hình circle, Hàm này cho phép ta ấn định 3 yếu tố của đường thẳng là chữ nhật này được tô mầu. Tham số depth ấn định số điểm ảnh trên dạng, bề dầy và mẫu tự tạo. bề sâu của khối 3 chiều. Tham số top có thể nhận trị 1 (TOPON) hay + Dạng đường do tham số linestyle khống chế. Sau đây là các giá 0 (TOPOFF) và khối 3 chiều sẽ có nắp hay không nắp (xem hình vẽ). trị khả dĩ của linestyle và dạng đường thẳng tương ứng. SOLID_LINE = 0 Nét liền DOTTED_LINE = 1 Nét chấm CENTER_LINE = 2 Nét chấm gạch DASHED_LINE = 3 Nét gạch TOPON TOPOFF USERBIT_LINE = 4 Mẫu tự tạo Ví dụ 4. Chương trình dưới đây sẽ vẽ một đường chữ nhật, một + Bề dầy do tham số thickness khống chế. Giá trị này có thể là: hình chữ nhật và một khối hộp chữ nhật có nắp. 460 NORM_WIDTH = 1 Bề dầy bình thường 461 #include THICK_WIDTH = 3 Bề dầy gấp ba main() + Mẫu tự tạo: Nếu tham số thứ nhất là USERBIT_LINE thì ta có { thể tạo ra mẫu đường thẳng bằng tham số pattern. Ví dụ xét đoạn int mh=0, mode=0; chương trình: initgraph(&mh, &mode, ""); int pattern= 0x1010; setbkcolor(GREEN); setlinestyle(USERBIT_LINE, pattern, NORM_WIDTH); setcolor(RED); line(0,0,100,200); setfillstyle(CLOSE_DOT_FILL,YELLOW); Giá trị của pattern trong hệ 16 là 0x1010 hay trong hệ 2 là rectangle(5,5,300,160); 0001 0000 0001 0000 bar(5,175,300,340); Chỗ nào có bit 1 điểm ảnh sẽ sáng, bit 0 làm tắt điểm ảnh. bar3d(320,100,500,340,100,1);
  9. 2. Để nhận các giá trị hiện hành của 3 yếu tố trên ta dùng hàm: kieu_cu.upattern, kieu_cu.thickness); void getlinesettings(struct linesettingstype *lineinfo); Line(0,20,100,20); với kiểu linesettingstype đã được định nghĩa trước như sau: getch(); struct linesettingstype closegraph(); { } int linestyle; 3. Hàm unsigned int upattern; void setwritemode( int writemode); int thickness; sẽ thiết lập kiểu thể hiện đường thẳng cho các hàm line, drawpoly, }; linerel, lineto, rectangle. Kiểu thể hiện do tham số writemode khống chế: Ví dụ 1. Chương trình dưới đây minh họa cách dùng các hàm setlinestyle và getlinesettings để vẽ đường thẳng. - Nếu writemode bằng COPY_PUT = 0, thì đường thẳng được viết đè lên dòng đang có trên màn hình. // kiểu đường - Nếu writemode bằng XOR_PUT = 1, thì mầu của đường thẳng #include định vẽ sẽ kết hợp với mầu của từng chấm điểm của đường hiện có #include trên màn hình theo phép toán XOR (chương 3, §3) để tạo lên một main() đường thẳng mới. { Một ứng dụng của XOR_PUT là: Khi thiết lập kiểu writemode struct linesettingstype kieu_cu; bằng XOR_PUT rồi vẽ lại đường thẳng cùng mầu thì sẽ xóa đường int mh=0, mode=0; thẳng cũ và khôi phục trạng thái của màn hình. initgraph(&mh, &mode, ""); Chương trình dưới đây minh họa cách dùng hàm setwritemode. 462Khi thực hiện ta sẽ thấy hình chữ nhật thu nhỏ dần vào tâm màn 463 if (graphresult!= grOk) exit(1); hình. setbkcolor(GREEN); setcolor(RED); line(0,0,100,0); Ví dụ 2: // Lưu lại kiểu hiện tại // Thu hình; getlinesettings(kieu_cu); #include // Thiết lập kiểu mới #include setlinestyle(DOTTED_LINE,0,THICK_WIDTH); main() line(0,0,100,10); { // Phục hồi kiểu cũ struct linesettingstype kieu_cu; setlinestyle(kieu_cu.linestyle, int mh=0, mode=0, x1, y1, x2, y2;
  10. initgraph(&mh, &mode, ""); clip = 1 không cho phép vẽ ra ngoài viewport if (graphresult!= grOk) exit(1); clip = 0 Cho phép vẽ ra ngoài viewport. setbkcolor(GREEN); Ví dụ câu lệnh setcolor(RED); setviewport(100,50,200,150, 1); setfillstyle(CLOSE_DOT_FILL, YELLOW); sẽ thiết lập viewport. Sau khi lập viewport ta có hệ tọa độ mới mà x1=0; y1=0; góc trên bên trái của viewport sẽ có tọa độ (0,0). x2=getmaxx(); y2=getmaxy(); 2. Để nhận viewport hiện hành ta dùng hàm setwritemode(XOR_PUT); tt: rectangle(x1,y1,x2,y2); // Vẽ hình chữ nhật void getviewsettings(struct viewporttype *vp); if ( (x1+1)<(x2-1) && (y1+1)<(y2-1) ) ở đây kiểu viewporttype đã được định nghĩa như sau: { struct viewporttype rectangle(x1,y1,x2,y2); // xóa hình chữ nhật { x1=x1+1; y1=y1+1; co hình chữ nhật int left, top, right, bottom; int clip; x2=x2-1; y2=y2-1; }; goto tt; } 3. Để xóa viewport ta dùng hàm setwritemode(COPY_PUT); // Trở về overwrite mode void clearviewport(void); closegraph(); 4. Để xóa màn hình và đưa con chạy về tọa độ (0,0) của màn hình } ta dùng hàm void cleardevice(void); Chú ý: Câu lệnh này sẽ xóa mọi thứ trên màn hình. § 7. Cửa sổ (Viewport) 1. Viewport là một vùng chữ nhật trên màn hình đồ họa tựa như 5.464 Tọa độ âm dương 465 window trong textmode. Để thiết lập viewport ta dùng hàm Nhờ sử dụng Viewport có thể viết các chương trình đồ họa theo void setviewport(int x1,int y1,int x2,int y2,int clip); tọa độ âm dương. Muốn vậy ta thiết lập viewport sao cho tâm tuyệt trong đó (x1,y1) là tọa độ góc trên bên trái và (x2,y2) là tọa độ góc đối của màn hình là góc trên bên trái của viewport và cho clip = 0 để dưới bên phải. Bốn giá trị này phải thỏa mãn: có thể vẽ ra ngoài giới hạn của viewport. Sau đây là đoạn chương trình thực hiện công việc trên 0 <= x1 <= x2 int xc, yc; 0 <= y1 <= y2 xc= getmaxx()/2; yc= getmaxy()/2; Tham số clip có thể nhận một trong hai giá trị:
  11. setviewport(xc, yc, getmaxx(), getmaxy(), 0); line(0,-(getmaxy()/2),0,getmaxy()/2); Như thế màn hình sẽ được chia làm 4 phần với tọa độ âm dương settextjustify(1,1); setcolor(RED); như sau: outtextxy(0,0,"(0,0)"); Phần tư trái trên: x âm, y âm for (i=-400;i 1 đôi chút: giữ nguyên từ đầu đến outtextxy, thay phần cuối bởi đoạn chương trình dưới đây. Ta sẽ được đồ thị từ các đoạn thẳng rất ngắn #include ghép lại. #include Ví dụ 2: #define SCALEX 20 // Phần đầu giống ví dụ 1 #define SCALEY 60 setcolor(YELLOW); main() for (i=-400;i<=400;++i) { { int mh=0, mode=0, x, y, i; x=round(2*M_PI*i*SCALEX/200); initgraph(&mh, &mode, ""); y=round(sin(2*M_PI*i/200)*SCALEY); if (graphresult!= grOk) exit(1); if(i= -400) moveto(x,y); setviewport(getmaxx()/,getmaxy()/2, else lineto(x,y); getmaxx(),getmaxy(), 0); 466 467 // Kẻ hệ trục tọa độ } setcolor(BLUE); getch(); line(-(getmaxx()/2),0,getmaxx()/2,0); }
  12. § 8. Tô điểm, tô miền b) Khi trên màn hình không có một đường nào như vậy, thì cả màn hình được tô màu. 1. Hàm Ví dụ 1. Chương trình dưới đây sẽ vẽ một đường tròn đỏ trên màn void putpixel(int x, int y, int color); hình xanh. Tọa độ (x,y) của điểm gieo được nạp vào từ bàn phím. sẽ tô điểm (x,y) theo mầu xác định bởi color. Tùy thuộc vào giá trị cụ thể của x,y, chương trình sẽ tô mầu vàng cho hình tròn hoặc phần màn hình bên ngoài hình tròn. 2. Hàm #include unsigned getpixel(int x, int y); #include sẽ trả về số hiệu mầu của điểm ảnh ở vị trí (x,y). Chú ý: nếu điểm này chưa được tô mầu bởi các hàm vẽ hoặc putpixel (mà chỉ mới main() được tạo mầu nền bởi setbkcolor) thì hàm cho giá trị bằng 0. Vì vậy { có thể dùng hàm này theo mẫu dưới đây để xác định các nét vẽ trên int mh=0, mode=0, x, y; màn hình đồ hoạ và vẽ ra giấy. if (getpixel(x,y)!=0) initgraph(&mh, &mode, ""); { if (graphresult!= grOk) exit(1); // Điểm (x,y) được vẽ , đặt một chấm điểm ra giấy setbkcolor(GREEN); } setcolor(RED); setfillstyle(11,YELLOW); 3. Tô miền circle(320,100,50); Để tô mầu cho một miền nào đó trên màn hình ta dùng hàm moveto(1,150); void floodfill(int x, int y, int border); outtext(" Toa do diem gieo x,y "); ở đây: (x,y) là tọa độ của một điểm nào đó gọi là điểm gieo. scanf("%d%d",&x,&y); flooddfill(x,y,RED); tham số border chứa mã của một mầu. } Sự hoạt động của hàm floodfill phụ thuộc vào giá trị của x,y, border và trạng thái màn hình. Ví dụ 2. Minh họa cách dùng hàm Putpixel và hàm getpixel để vẽ a) Khi trên màn hình có một đường (cong hoặc gấp khúc) khép các điểm ảnh và sau đó xóa các điểm ảnh. Muốn kết thúc chương trình bấm ESC. kín mà mã mầu của nó bằng giá trị của border thì: 468 469 + Miền giới hạn bởi đường kín sẽ được tô mầu nếu điểm gieo #include (x,y) nằm bên trong miền này. #include + Nếu điểm gieo (x,y) nằm bên ngoài thì phần màn hình bên #include ngoài miền đóng nói trên được tô màu.
  13. #include ++i; int seed = 1962; // Nhân cho bộ tạo số ngẫu nhiên x=random(xmax)+1;y=random(ymax)+1; int numpts = 2000; // Vẽ 2000 chấm điểm color=random(maxcolor); int ESC = 27; putpixel(x,y,color); void putpixelplay(void); } main() // Xóa các điểm ảnh { srand(seed); int mh=0, mode=0; i=0; initgraph(&mh, &mode, ""); while(i<=numpts) if (graphresult()!= grOk) exit(1); { putpixelplay(); ++i; closegraph(); x= random(xmax) + 1; y= random(ymax) + 1; } color=random(maxcolor); void putpixelplay(void) putpixel(x,y,0); { } int i,x,y,color,xmax,ymax,maxcolor,ch; if(kbhit()) struct viewporttype v; { getviewsettings(&v); xmax=(v.right - v.left -1); ymax=(v.bottom - v.top -1); ch=getch(); maxcolor=getmaxcolor(); if (ch==ESC) break; while (!kbhit()) } { } //Vẽ các chấm điểm một cách ngẫu nhiên } // Kết thúc hàm putpixelplay srand(seed); i=0; while(i<=numpts) 470 § 9. Xử lý văn bản trên màn hình đồ hoạ 471 { 1. Hiển thị văn bản trên màn hình đồ hoạ
  14. Hàm - Nếu charsize = 2, ký tự được thể hiện trong hình chữ nhật 16*16 void outtext (char *s); pixel. sẽ hiện chuỗi ký tự (do s trỏ tới) tại vị trí hiện tại của con trỏ. . . . Hàm - Nếu charsize = 10, ký tự được thể hiện trong hình chữ nhật 80*80 pixel. void outtextxy(int x,int y,char *s); Cuối cùng là tham số font để chọn kiểu chữ và nhận một trong sẽ hiện chuỗi ký tự (do s trỏ tới) tại ví trí (x,y). các hằng sau: Ví dụ 1: Hai cách sau đây sẽ cho cùng kết quả DEFAULT_FONT = 0 outtextxy (100,100," chao ban "); TRIPLEX_FONT = 1 và SMALL_FONT = 2 moveto (100,100); outtext (" chao ban "); SANS_SERIF_FONT = 3 Chú ý: Trong mốt đồ họa vẫn cho phép dùng hàm nhập dữ liệu GOTHIC_FONT = 4 scanf và các hàm bắt phím getch, kbhit. Các giá trị do settextstyle thiết lập sẽ dữ nguyên cho đến khi gọi một settextstyle mới. 2. Fonts Ví dụ 2: Như đã nói ở trên: Các Fonts nằm trong các tệp tin .CHR trên đĩa. Các Font này cho các kích thước và kiểu chữ khác nhau sẽ hiện thị settextstyle (3,VERT_DIR,2); trên màn hình đồ hoạ bằng outtext hay outtextxy. Để chọn và nạp outtextxy (50,50," HELLO "); Font chúng ta dùng hàm: void settextstyle(int font,int direction,int charsize); 3. Vị trí hiển thị (Chú ý: hàm chỉ có tác dụng nếu tồn tại các tệp .CHR) Hàm settextjustify cho phép ấn định nơi hiển thị văn bản của outtext theo quan hệ với vị trí hiện tại của con chạy hay của Với direction là một trong hai hằng số: outtextxy theo quan hệ với toạ độ (x,y). HORIZ_DIR = 0 Hàm này có dạng VERT_DIR = 1 void settextjustify(int horiz, int vert); Nếu direction là HORIZ_DIR, văn bản sẽ hiển thị theo hướng Tham số horiz có thể là một trong các hằng số sau: nằm ngang từ trái sang phải. Nếu direction là VERT_DIR, văn bản sẽ hiển thị theo chiều đứng từ dưới lên trên. LEFT_TEXT = 0 (Văn bản xuất hiện bên phải con chạy) Đối charsize là hệ số phóng to ký tự và có giá trị trong khoảng từ CENTER_TEXT = 1 (Chỉnh tâm văn bản theo vị trí con chạy) 1 đến 10. RIGHT_TEXT = 2 (Văn bản xuất hiện bên trái con chạy) - Nếu charsize = 1, ký tự được thể hiện trong hình chữ nhật 8*8 Tham số Vert có thể là một trong các hằng số sau: pixel. 472 473
  15. BOTTOM_TEXT = 0 (Văn bản xuất hiện phía trên con chạy) getch(); CENTER_TEXT = 1 (Chỉnh tâm văn bản theo vị trí con chạy) closegraph(); TOP_TEXT = 2 (Văn bản xuất hiện phía dưới con chạy) } Ví dụ 3: Hàm settextjustify(1,1); void textwidth(char *s); outtextxy(100,100,"ABC"); sẽ dựa vào chiều dài của chuỗi, kích thước Font chữ, hệ số khuyếch đại chữ để trả về bề rộng (theo pixel) của chuỗi do s trỏ tới. Kết quả là điểm (100,100) sẽ nằm giữa chữ B. Ví dụ 5: Trong chương trình dưới đây sẽ lập các hàm vào ra trên 4. Bề rộng và bề cao của văn bản màn hình đồ hoạ. Hàm #include void textheight (char *s); #include trả về chiều cao (theo pixel) của chuỗi do s trỏ tới. Ví dụ nếu ký tự #define Enter 13 có kích thước 8*8 thì #define Lmargin 10 textheight ("H") = 8 void text_write(int *x,int *y,char *s); Ví dụ 4: Đoạn chương trình dưới đây sẽ cho hiện 5 dòng chữ. void text_writeln(int *x,int *y,char *s); #include void text_read(int *x,int *y,char *s); main() void text_write(int *x,int *y,char *s) { { int mh=0,mode=0,y,size; outtextxy(*x,*y,s); *x += textwidth(s); initgraph(&mh,&mode,""); } y=10; void text_writeln(int *x,int *y,char *s) settextjustify(0,0); { for (size=1; size<=5; ++size) outtextxy(*x,*y,s); { *x=Lmargin; settextstyle(0,0,size); *y += textheight(s)+5; outtextxy(0,y,"GRAPHICS"); } y += textheight("GRAPHICS") + 10; void text_read(int *x,int *y,char *s) } {
  16. int i=0; char ch[2]; ch[1]=0; 474 while(1) § 10. Cắt hình, Dán hình và Tạo ảnh chuyển động 475 { 1. Hàm ch[0]=getch(); unsigned imagesize(int x1,int y1,int x2,int y2) if(ch[0]==Enter) break; trả về số byte cần thiết để lưu trữ ảnh trong phạm vi hình chữ nhật text_write(x,y,ch); (x1,y1,x2,y2). s[i]=ch[0]; ++i; 2. Hàm } #include s[i]=0; void *malloc(unsigned n); } trả về con trỏ trỏ tới một vùng nhớ n byte mới được cấp phát. main() 3. Hàm { void getimage(int x1,int y1,int x2,int y2,void *bitmap); int mh=0,mode=0,x,y,xmax,ymax; sẽ chép các điểm ảnh của hình chữ nhật (x1,y1,x2,y2) và các thông char name[25]; tin về bề rộng, cao của hình chữ nhật vào vùng nhớ do bitmap trỏ tới. initgraph(&mh,&mode,""); Vùng nhớ và biến bitmap cho bởi hàm malloc. Độ lớn của vùng nhớ được xác định bằng hàm imagesize. settextstyle(TRIPLEX_FONT,HORIZ_DIR,3); x=Lmargin; y=100; 4. Hàm text_write (&x,&y,"cho ten cua ban: "); void putimage(int x,int y,void *bitmap,int copymode); text_read (&x,&y,name); dùng để sao ảnh lưu trong vùng nhớ bitmap ra màn hình tại vị trí (x,y). Tham số copymode xác định kiểu sao chép ảnh, nó có thể nhận text_writeln (&x,&y,"" ); các giá trị sau: text_write(&x,&y,"chao ban "); COPY_PUT = 0 Sao chép nguyên xi. text_write(&x,&y,name); XOR_PUT = 1 Các điểm ảnh trong bitmap kết hợp với getch(); các điểm ảnh trên màn hình bằng phép XOR closegraph(); OR_PUT = 2 Các điểm ảnh trong bitmap kết hợp với } các
  17. điểm ảnh trên màn hình bằng phép OR Nguyên tắc tạo ảnh di động giống như phim hoạt hình: AND_PUT = 3 Các điểm ảnh trong bitmap kết hợp với - Vẽ một hình (trong chuỗi hình mô tả chuyển động) các - Delay điểm ảnh trên màn hình bằng phép AND - Xoá hình đó NOT_PUT = 4 ảnh xuất hiện trên màn hình theo dạng đảo - Vẽ hình kế theo ngược (phép NOT) với ảnh trong bitmap. - Delay Nhận xét: Nếu dùng mode XOR_PUT để chép hình, rồi lặp lại . . . 476đúng câu lệnh đó thì hình sẽ bị xoá và màn hình trở lại như cũ. Kỹ 477 thuật này dùng để tạo lên các hình ảnh chuyển động. A) Vẽ hình Ví dụ 1: Chương trình dưới đây minh hoạ cách dùng imagesize, Cách 1: Vẽ lại một ảnh nhưng tại các vị trí khác nhau. malloc, getimage và putimage. Cách 2: Lưu ảnh vào một vùng nhớ rối đưa ảnh ra màn hình tại #include các vị trí khác nhau. #include B) Xóa ảnh main() Cách 1: Dùng hàm cleardevice { int mh=0,mode=0; Cách 2: Dùng hàm putimage (mode XOR_PUT) để xếp chồng lên ảnh cần xoá. char *p; unsigend size; Cách 3: Lưu trạng thái màn hình vào một chỗ nào đó. Vẽ một hình ảnh. Đưa trạng thái cũ màn hình ra xếp đè lên ảnh vừa vẽ. initgraph (&mh,&mode,""); bar(0,0,getmaxx(),getmaxy()); Kỹ thuật tạo ảnh di động được minh hoạ trong các chương trình của §11. size = imagesize(10,20,30,40); p=(char*)malloc(size); // p trỏ tới vùng nhớ size byte // mới được cấp phát § 11. Một số chương trình đồ hoạ getimage (10,20,30,40,p); Chương trình 1: Đầu tiên vẽ bầu trời đầu sao. Sau đó từng chùm getch(); pháo hoa được bắn lên bầu trời. Khi bấm phím Enter thì việc bắn cleardevice(); pháo hoa kết thúc, ta nhận lại bầu trời đầy sao. Bấm tiếp Enter thì kết putimage (100,100,p,COPY_PUT); thúc chương trình. getch(); // Bắn pháo hoa trên bầu trời đầy sao closegraph(); #include } #include 5. Tảo ảnh di động #include
  18. #include do main() { { // Đưa 100 quả pháo lên màn hình tại các vị trí quy định int x[101],y[101]; for (i=1;i =1; i) { putimage(x[i],y[i],p[i],COPY_PUT); putpixel(random(getmaxx()), delay(500); random(getmaxy()),random(getmaxcolor())); } while(!kbhit()); } getch(); // Lưu hiện trạng 100 hình chữ nhật trên màn hình để khôi getch(); phục closegraph(); for (i=1;i n=imagesize(x[i],y[i],x[i]+10,y[i]+10); #include p[i]=(char*)malloc(n); #include getimage(x[i],y[i],x[i]+10,y[i]+10,p[i]); #include } // Hàm kẻ đoạn thẳng từ tâm đồng hồ theo độ, chiều dài, // Chu trình bắn pháo hoa // độ dầy và mầu
  19. void ke(int ddo, unsigned dai, unsigned day,unsigned mau); void ke_phut(unsigned phut) // Kẻ kim giây khi biết số giây { void ke_giay(unsigned giay); int ddo; // Kẻ kim phút khi biết số phút ddo= (90-6*phut); void ke_phut(unsigned phut); ke(ddo,rphut,3,mphut); } // Kẻ kim giờ khi biết số giờ // Hàm ke kim gio void ke_gio(unsigned gio, unsigned phut); void ke_gio(unsigned gio, unsigned phut) void chay_kim_giay(void); void chay_kim_phut(void); { void chay_kim_gio(void); int ddo; int x0,y0,rgio,rphut,rgiay,mgio,mphut,mgiay; ddo = 360 + 90 - 30*(gio%12) - (phut+1)/2; unsigned phutgioht,gioht,phutht,giayht; ke(ddo,rgio,3,mgio); void ke(int ddo, unsigned dai, unsigned day,unsigned mau) } 480 481 { // Hàm chỉnh giây hiện tại và làm chuyển động kim giây unsigned x,y; float goc; void chay_kim_giay(void) while (ddo>=360) ddo=ddo-360; { goc=(M_PI/180)*ddo; unsigned giay; struct time t; x=x0+ (int)(dai*cos(goc)+0.5); gettime(&t); y=y0- (int)(dai*sin(goc)+0.5); giay=t.ti_sec; setcolor(mau); setlinestyle(0,0,day); line(x0,y0,x,y); if (giay!=giayht) } { // Hàm ke kim giay ke_giay(giayht); void ke_giay(unsigned giay) giayht=giay; { ke_giay(giayht); int ddo; } ddo = (90 - 6*giay); } ke(ddo,rgiay,1,mgiay); // Hàm chỉnh phút hiện tại và làm chuyển động kim phút } // Hàm ke kim phut void chay_kim_phut(void)
  20. { gioht=gio; unsigned phut; ke_gio(gioht,phutgioht); struct time t; } gettime(&t); } phut=t.ti_min; main() if (phut!=phutht) { { struct time t; char *dso[]={"", "12", "1", "2", "3", "4", "5", "6", "7", "8", "9", ke_phut(phutht); "10", "11"}; phutht=phut; int i,mh=0,mode=0,r,x,y; ke_phut(phutht); float goc; } initgraph(&mh,&mode,""); } x0=(getmaxx()/2)-1; y0=(getmaxy()/2)-1; // Hàm chỉnh giờ phút hiện tại và làm chuyển động kim giờ r=y0-2; 482void chay_kim_gio(void) rgiay = r-10; rphut=r-50; rgio=r-90; 483 { mgiay= BROWN; mphut=RED; // mgio:=magenta; unsigned h,gio,phut,sophut,sophutht; mgio=YELLOW; struct time t; // Vẽ chu vi đồng hồ gettime(&t); setcolor(BLUE); setlinestyle(0,0,3); circle(x0,y0,r); gio=t.ti_hour; phut=t.ti_min; setfillstyle(1,YELLOW); sophut = gio*60+phut; floodfill(0,0,BLUE); sophutht = gioht*60+phutgioht; setfillstyle(1,WHITE); floodfill(x0,y0,BLUE); if ( sophut =12) setfillstyle(1,GREEN); floodfill(x0,y0,BLUE); { settextjustify(1,1); setcolor(MAGENTA); ke_gio(gioht,phutgioht); outtextxy(x0,y0+120,"IBM-JIMIKO"); phutgioht=phut; // Ghi chữ số
  21. settextstyle(3,0,3); settextjustify(1,1); setcolor(BLUE); Chương trình 3: Vẽ một con tầu vũ trụ bay trên bầu trời đầy sao for (i=1;i x = x0+ (int)(rphut*cos(goc)+0.5); #include y = y0- (int)(rphut*sin(goc)+0.5); #include outtextxy(x,y,dso[i]); #include } #include // Xác định thời điểm đầu #include gettime(&t); // Khai báo các hàm trong chương trình gioht=t.ti_hour; phutht=t.ti_min; giayht=t.ti_sec; void tau_cd(void); // tầu chuyển động phutgioht=phutht; void nhap_nhay_bt(void); // nhấp nháy bầu trời setwritemode(XOR_PUT); void main(void); // hàm main // Ve kim gio,phut,giay // Khai báo các biến mảng ngoài ke_gio(gioht,phutgioht); int a,b,x,y,x0,y0,mh=0,mode=0,n,i; ke_phut(phutht); 484 float goc,xt,yt; 485 ke_giay(giayht); char *p; // Làm chuyển động các kim int xx[1001],yy[1001]; do // Hàm main { void main(void) chay_kim_giay(); chay_kim_phut(); { chay_kim_gio(); initgraph(&mh,&mode,""); } if (graphresult()!=0) exit(1); while(!kbhit()); // Vẽ tầu vũ trụ closegraph(); setcolor(RED); } ellipse(100,50,0,360,20,8); ellipse (100,46,190,357,20,6);
  22. line(107,44,110,38); tau_cd(); circle(110,38,2); nhap_nhay_bt(); line(93,44,90,38); } while(!kbhit()); circle(90,38,2); getch(); setfillstyle(SOLID_FILL,BLUE); closegraph(); floodfill(101,54,RED); } setfillstyle(SOLID_FILL,MAGENTA); void tau_cd(void) floodfill(94,45,RED); { // Lưu ảnh của tầu vũ trụ vào bộ nhớ xt=a*cos(goc)+x0; n=imagesize(79,36,121,59); yt=-b*sin(goc)+y0; p=(char*)malloc(n); x=(int)(xt+0.5); y=(int)(yt+0.5); getimage(79,36,121,59,p); // Vẽ bầu trời đầy sao và lưu vị trí của chúng // Đặt tầu vũ trụ lên màn hình // vào các mảng xx, yy để phục vụ hàm nhap_nhay_bt putimage(x,y,p,XOR_PUT); cleardevice(); delay(500); for (i=1;i<=1000;++i) // Xóa { putimage(x,y,p,XOR_PUT); xx[i]=random(getmaxx()); yy[i]=random(getmaxy()); // Thay đổi góc để làm cho tầu chuyển động putpixel(xx[i],yy[i],random(getmaxcolor())); goc -= M_PI/30; } if (goc<M_PI/2) goc=2*M_PI+M_PI/2; // Xác định giá trị ban đầu cho các biến } 486 // dùng để điều khiển chuyển động tầu void nhap_nhay_bt(void) 487 goc= 2*M_PI + M_PI/2; { x0= (getmaxx() - 42)/2; static i=1; // Lệnh này thực hiện một lần khi dịch y0= (getmaxy() - 25)/2; int j; a=x0; b=y0; // Cho nhấp nháy bằng cách đổi mầu 50 ngôi sao // chu trình tầu vũ trụ chuyển động và các ngôi sao nhấp nháy for (j=1;j<=50;++j) do { { putpixel(xx[i],yy[i],random(getmaxcolor()));
  23. ++i; int c=0,i; if (i>1000) i=1; for (i=0;i >i); } return c; } § 12. In ảnh từ màn hình đồ hoạ void in_anh(int dd,int xt,int yt,int xd,int yd) Hàm in_anh dưới đây sẽ in ảnh trong miền chữ nhật (xt, yt, xd, { yd) của màn hình đồ hoạ ra giấy trên các máy in LQ1070, LQ1170 và FX1050. //dd - so lan in lai mot dong void in_anh(int dd,int xt,int yt,int xd,int yd); char c,ch1; Tham số dd là độ đậm của nét in. Thực chất dd là số lần in lại. int scot,m,mm,k,dong,cot,i,j,n1,n2; Bình thường chon dd=1. Nếu muốn in rõ hơn ta chọn dd bằng 2 dong=(yd-yt+1)/6; mm=(yd-yt+1) % 6; hay 3. cot=xd-xt+1; Trong hàm in_anh có dùng hàm tao_mau, nó được mô tả như sau: for (i=0;i 0) và 0 (ứng với điểm chưa tô mầu). { Hàm in_anh sẽ dùng hàm tao_mau để duyệt trên miền chữ nhật scot=0; (xt,yt,xd,yd). Mỗi lần duyệt sẽ nhận được một mẫu các chấm điểm (giá trị nguyên) và mẫu này được in ra giấy. for (j=0;j < cot;++j) Dưới đây là nội dung của 2 hàm nói trên. if (tao_mau(m,xt+j,yt+i*6)) scot=j+1; 488// in ảnh if (scot) 489 #include "stdio.h" { #include "graphics.h" n1=scot % 256; n2= scot/256; int tao_mau(int k,int x,int y); for (k=0;k<dd;++k) void in_anh(int dd,int xt,int yt,int xd,int yd); { int tao_mau(int k,int x,int y) fprintf(stdprn,"%c%c%c%c%c%c",13,27,'*', { 0,n1,n2); //LQ
  24. for (j=0;j < scot;++j) { if (kbhit())//bat phim { if ((ch1=getch())==0) getch(); if (ch1==27) goto ket; } c=tao_mau(m,xt+j,yt+i*6); fprintf(stdprn,"%c",c); } } } fprintf(stdprn,"%c%c%c",27,'A',m); fprintf(stdprn,"\n"); } } ket: fprintf(stdprn,"%c%c",27,'@'); } 490