• Không có kết quả nào được tìm thấy

LẬP TRÌNH WINDOWS

N/A
N/A
Protected

Academic year: 2022

Chia sẻ "LẬP TRÌNH WINDOWS "

Copied!
281
0
0

Loading.... (view fulltext now)

Văn bản

(1)
(2)

ĐẠI HỌC TÔN ĐỨC THẮNG

TÓM TẮT BÀI GIẢNG

LẬP TRÌNH WINDOWS

(3)

1. Tổng quan... 1

1.1 Windows 95 và lập trình trong môi trường Windows... 1

1.2 Chương trình Windows đầu tiên ... 3

1.3 Thủ tục cửa sổ...11

1.4 Môi trường Visual C++...13

2. Tổng quan về Giao Diện Thiết Bị Đồ Hoạ (GDI) ...15

2.1 Giới thiệu ...15

2.2 Giao diện người dùng...15

2.3 Ngữ cảnh thiết bị (device context)...16

2.4 Quy trình vẽ ...18

2.5 Vẽ từng điểm ...19

2.6 Tạo Marker...21

2.7 Vẽ và vẽ lại ...23

2.8 BeginPaint và EndPaint ...23

2.9 Vùng hợp lệ và không hợp lệ...24

2.10 InvertPixel và DrawMarker...25

2.11 GetDC và ReleaseDC...26

3. Vẽ đường ...28

3.1 Đường ...28

3.2 Các hàm vẽ đường ...28

3.3 Thuộc tính vẽ đường ...33

3.4 Bút vẽ (pen)...34

3.5 Các chế độ vẽ (Drawing Modes) ảnh hưởng đến vẽ đường...37

(4)

5. Xuất Văn bản...60

5.1 Tổng quan ...60

5.2 Các hàm xuất văn bản ...60

TextOut ...60

ExtTextOut...61

TabbedTextOut ...61

DrawText ...64

GrayString ...66

5.3 Các thuộc tính xuất văn bản...67

5.4 Fonts ...70

CreateFont...72

LOGFONT ...72

CreateFontIndirect ...74

5.5 Các thông số văn bản...75

SIZE ...75

GetTextExtentPoint32...75

TEXTMETRIC...75

GetTextMetrics ...76

5.6 Font và Path ...79

6. Bộ định thời ...81

6.1 Giới thiệu ...81

6.2 Các hàm thao tác bộ định thời ...81

6.3 Hoạt động của bộ định thời...82

(5)

ShowCursor...115

SetCursor...115

7.3 Phím tắt (Keyboard Accelerators) ...115

7.4 Menu...117

7.5 Bitmap ...124

LoadBitmap...125

CreateCompatibleBitmap...126

BitBlt ...126

StretchBlt...126

8. Custom Menu ...134

8.1 Các hàm thao tác trên menu ...134

LoadMenu ...135

SetMenu ...136

DrawMenuBar...136

AppendMenu...136

InsertMenuItem...137

8.2 Thay đổi menu trong chương trình...138

8.3 Menu do người sử dụng tự vẽ (Owner Draw Menu)...143

8.4 Bimap Menu ...174

9. Cửa Sổ Và Nút Điều Khiển ...183

9.1 Đăng ký lớp cửa sổ (tư tưởng OOP) ...183

9.2 Tạo cửa sổ...184

9.3 Tạo cửa sổ con ...185

(6)

10.5 Một số ví dụ về sử dụng hộp hội thoại modal ...218

11. Tương tác với người sử dụng...94

11.1 SetCapture, GetCapture và ReleaseCapture...94

SetCapture...94

ReleaseCapture...94

GetCapture...95

11.2 Kéo lên đối tượng bằng cách xoá và vẽ lại...95

11.3 Kéo lên đối tượng bằng chọn chế độ vẽ R2_NOTXORPEN ...98

SetROP2...98

12. Biến đổi toạ độ ...232

12.1 Các thuộc tính GDI ...232

12.2 Các chế độ toạ độ GDI ...232

SetMapMode...233

GetMapMode ...233

12.3 Tịnh tiến và nhân tỉ lệ...234

DPtoLP ...234

LPtoDP ...234

SetViewportOrgEx ...235

GetViewportOrgEx ...235

SetWindowOrgEx ...235

GetWindowOrgEx...235

12.4 Chế độ thiết bị (Device Mapping Mode)...236

12.5 Chế độ ràng buộc (Fully Constraint Mapping Mode)...237

(7)

13.4 Các thao tác trên cửa sổ trượt ...252

SetScrollRange...252

SetScrollPos ...253

ScrollWindow...253

SCROLLINFO...253

SetScrollInfo ...254

GetScrollInfo...254

ShowScrollBar...255

13.5 Các ví dụ sử dụng cửa sổ trượt ...255

Chương trình Scroll V1 ...255

(8)

Lời nói đầu

Hệ điều hành Windows với giao diện theo hướng đồ hoạ đã trở thành một môi trường làm việc chuẩn cho những người sử dụng máy vi tính cá nhân. Việc xây dựng các chương trình hoạt động trong môi trường Windows đã trở thành một yêu cầu thiết yếu cho những người xây dựng phần mềm khi môi trường DOS đã hoàn thành nhiệm vụ lịch sử của nó.

Để theo kịp với trào lưu thế giới, khoa Công nghệ thông tin và Toán ứng dụng trường Đại học Tôn Đức Thắng đã cập nhật và bổ sung môn Lập trình Windows vào chương trình đào tạo của khoa.

Tài liệu này được biên soạn nhằm phục vụ cho các sinh viên chuyên ngành Công nghệ thông Tin của trường. Nội dung bao gồm các kỹ thuật cốt yếu nhất về lập trình trong môi trường Windows sử dụng ngôn ngữ C/C++.

Windows cung cấp hơn một ngàn hàm để các lập trình viên tạo các ứng dụng khác nhau, từ soạn thảo văn bản, vẽ các đối tượng đồ hoạ, hiển thị hình ảnh, phim video, tạo âm thanh, truyền dữ liệu…. Phạm vi tài liệu chỉ giới hạn trong các nội dung chính bao gồm vẽ hình, xuất văn bản, xây dựng giao diện, tạo hộp hội thoại… Từ những kiến thức căn bản trên, sinh viên có thể tham khảo thêm các thư viện do Windows cung cấp để có thể xây dựng các ứng dụng lớn theo yêu cầu như một luận văn tốt nghiệp hoặc một phần mềm thực sự.

Tài liệu mang tính chất tóm tắt kiến thức và không thể tránh khỏi sai sót. Rất mong nhận được sự phê bình, đóng góp ý kiến của các bạn đồng nghiệp và các sinh viên.

Tháng 11/2004

Tác giả

(9)

1. Tổng quan

1.1 Windows 95 và lập trình trong môi trường Windows

• Hệ điều hành 32 bit

• Giao diện người dùng kiểu đồ hoạ (GUI)

− Giao diện trực quan (Visual Interface).

− WYSIWYG, người sử dụng tương tác với chương trình

− Các ứng dụng Windows có giao diện thống nhất với cùng dáng vẻ : Một cửa sổ hình chữ nhật, có thanh tiêu đề, menu, hộp hội thoại, thanh trượt …

• Đa nhiệm

− Mỗi chương trình chiếm một phần tài nguyên của hệ thống và có một phần bộ nhớ thường trú.

− Nhiều chương trình có thể được kích hoạt và chạy cùng một lúc

− Các phiên bản Windows 16 bít hoạt động đa nhiệm theo cơ chế nonpreemtive.

− Windows 32 bít hoạt động theo cơ chế preemtive. Mỗi chương trình có thể được tách thành các thread thực hiện cùng lúc.

• • Quản lý bộ nhớ

− Mã chương trình và dữ liệu có thể dịch chuyển trong bộ nhớ vật lý.

− Khả năng sử dụng bộ nhớ phụ.

− Các thư viện liên kết động (.DLL).

(10)

− Chương trình viết cho Windows không điều khiển trực tiếp thiết bị xuất như màn hình hay máy in mà thông qua một ngôn ngữ lập trình đồ hoạ gọi là Giao Diện Thiết Bị Đồ Hoạ (Graphics Device Interface: GDI).

− Các phần mềm MS-DOS cần có các tập tin điều khiển thiết bị đi kèm (màn hình, máy in). Chương trình viết cho Windows không cần quan tâm đến vấn đề này.

• Kiến trúc hướng thông điệp (message driven)

− Windows và các ứng dụng Windows hoạt động theo cơ chế truyền, nhận thông điệp, các hoạt động của chương trình thay đổi tuỳ theo thông điệp mà nó nhận được, thông điệp được gởi qua lại giữa ứng dụng và Windows, giữa các ứng dụng với nhau.

− Cửa sổ ứng dụng tự động được vẽ lại mỗi khi có sự thay đổi kích thước hay vùng bị che. Điều này được thực hiện nhờ hệ điều hành gởi thông điệp cho chương trình.

• Thủ tục cửa sổ

− Hệ điều hành ra lệnh cho ứng dụng nhờ thủ tục cửa sổ (Window Procedure). Hàm cửa sổ cho biết phản ứng của chương trình với các tác động bên ngoài như user input…

− Hệ điều hành Windows gởi thông điệp cho một chương trình ứng dụng bằng cách gọi hàm cửa sổ của ứng dụng đó, với tham số là thông điệp.

• Tài nguyên.

− Window cho phép người sử dụng đưa vào chương trình các tài nguyên thuộc các loại biểu tượng (icons), con trỏ (cursors), bitmap, bảng các chuỗi hằng (string tables), bảng phím tắt (Accelerator), hộp hội thoại (Dialog), menu, thanh công cụ (toolbar) và siêu văn bản (văn bản loại HTML).

− Một số tài nguyên thuộc các loại này có thể được Windows cung cấp sẵn gọi là tài

nguyên chuẩn. Người sử dụng có thể tự tạo các tài nguyên khác thông qua một tập

tin tài nguyên có phần đuôi .RC (Resource Script).

(11)

Hình 1-1 – Biểu tượng chuẩn trong Windows

1.2 Chương trình Windows đầu tiên 1.2.1 Các tập tin chương trình nguồn

sdafx.h, stdafx.cpp, hello.cpp

stdafx.h

#include <windows.h>

// C RunTime Header Files

#include <stdlib.h>

#include <malloc.h>

#include <memory.h>

#include <tchar.h>

stdafx.cpp

//stdafx.cpp : Hello.pch will be the pre-compiled header

#include "stdafx.h"

(12)

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {

MSG msg;

// Initialize global strings MyRegisterClass(hInstance);

// Perform application initialization:

if (!InitInstance (hInstance, nCmdShow)) {

return FALSE;

}

// Main message loop:

while (GetMessage(&msg, NULL, 0, 0)) {

TranslateMessage(&msg);

DispatchMessage(&msg);

}

return msg.wParam;

}

// FUNCTION: MyRegisterClass()

// PURPOSE: Registers the window class.

ATOM MyRegisterClass(HINSTANCE hInstance) {

WNDCLASSEX wcex;

wcex.cbSize = sizeof(WNDCLASSEX);

wcex.style = CS_HREDRAW | CS_VREDRAW;

wcex.lpfnWndProc = (WNDPROC)WndProc;

wcex.cbClsExtra = 0;

wcex.cbWndExtra = 0;

wcex.hInstance = hInstance;

wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);

wcex.hCursor = LoadCursor(NULL, IDC_ARROW);

(13)

hInst = hInstance; // Store instance handle in our global variable hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

if (!hWnd)

return FALSE;

ShowWindow(hWnd, nCmdShow);

UpdateWindow(hWnd);

return TRUE;

}

// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) // WM_PAINT - Paint the main window

// WM_DESTROY - post a quit message and return

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

PAINTSTRUCT ps;

HDC hdc;

LPSTR szHello = "Hello, world";

switch (message) {

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

// TODO: Add any drawing code here...

RECT rt;

GetClientRect(hWnd, &rt);

DrawText(hdc, szHello, strlen(szHello), &rt, DT_SINGLELINE

| DT_CENTER | DT_VCENTER);

EndPaint(hWnd, &ps);

break;

case WM_DESTROY:

PostQuitMessage(0);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

}

return 0;

}

1.2.2 Qui ước đặt tên các hằng

(14)

CW Create Window option

DT DrawText option

Ví dụ các hằng có sử dụng trong chương trình

CS_HREDRAW DT_SINGLELINE WM_CREATE CS_VREDRAW IDC_ARROW WM_DESTROY CW_USEDEFAULT DT_VCENTER WM_PAINT

DT_CENTER IDI_APPLICATION WS_OVERLAPPEDWINDOW 1.2.3 Kiểu dữ liệu

• Đơn

WORD BYTE DWORD INT PSTR LPSTR UINT

WPARAM LPARAM LRESULT APIENTRY CALLBACK (__stdcall)

• Cấu trúc

MSG Cấu trúc thông điệp

WNDCLASSEX Cấu trúc lớp cửa sổ PAINSTRUCT Cấu trúc paint

RECT Cấu trúc hình chữ nhật

• Handle (chỉ điểm)

HINSTANCE Handle đến thể hiện của chương trình

HWND Handle đến một cửa sổ

HDC Handle đến ngữ cảnh thiết bị (device context) HICON Handle đến một biểu tượng

HCURSOR Handle đến một cursor

(15)

hdc handle to device context hwnd handle to window

i index

l LONG (long int)

lp long pointer

n int

pt POINT

by BYTE

i int

b hoặc f : BOOL

w WORD

sz chuỗi kết thúc bởi ‘\0’

1.2.5 Hoạt động của chương trình

• Chương trình có hai hàm chính WinMain, WndProc và hai hàm phục vụ là MyRegisterClass và InitInstance.

• Trình tự thực hiện một chương trình Windows là

− Đăng ký lớp cửa sổ,

− Tạo cửa sổ hiển thị cửa sổ

− Xử lý các thông điệp nhờ vòng lặp message và thủ tục cửa sổ.

− WinMain không trực tiếp gọi hàm WndProc, hàm được gọi bởi Windows

• Các hàm trong Windows

− Windows cung cấp hơn một ngàn hàm thư viện

− Các hàm sử dụng trong chương trình trên bao gồm

LoadIcon – nạp biểu tượng cho chương trình.

(16)

TranslateMesage – Chuyển đổi thông điệp bàn phím.

DispatchMesage – Gởi thông điệp đến thủ tục cửa sổ.

BeginPaint, EndPaint – Khởi động và kt việc vẽ cửa sổ.

GetClientRect – Lấy kích thước của vùng client của cửa sổ.

DrawText – Hiển thị chuỗi ký tự.

PostQuitMessage – Đưa thông điệp “quit : thoát khỏi chương trình” vào hàng đợi thông điệp.

DefWindowProc – Thực hiện xử lý mặc nhiên các thông điệp không được chương trình xử lý.

1.2.6 Hàm chính (WinMain)

Gồm các phần chính đăng ký lớp, tạo cửa sổ, hiển thị cửa sổ, xử lý thông điệp int APIENTRY WinMain(HINSTANCE hInstance,

HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)

hInstance : Handle đến chương trình hiện hành

hPrevInstance : Handle đến chương trình trước đó (Windows 95 : luôn luôn là NULL).

Trong Windows 32 bit, hPrevInstance luôn luôn NULL, để chỉ có tối đa một phiên bản, ta dùng hàm FindWindow.

hwndPrev = FindWindow(“HelloAppClass”, 0);

if (hwndPrev) {

// switch to prev ver }

lpCmdLine : Tham số dòng lệnh

nCmdShow : tuỳ chọn cách hiển thị cửa sổ

SW_SHOWNORMAL hoặc SW_SHOWMINNOACTIVE

(17)

int cbWndExtra;

HANDLE hInstance;

HICON hIcon;

HCURSOR hCursor;

HBRUSH hbrBackground;

LPCTSTR lpszMenuName;

LPCTSTR lpszClassName;

HICON hIconSm;

} WNDCLASSEX;

Đăng ký

Ta đăng ký lớp cửa sổ bằng cách đưa thông tin vào cấu trúc cửa sổ và gọi hàm RegisterClassEx.

WNDCLASSEX wcex;

wcex.cbSize = sizeof(WNDCLASSEX);

wcex.style = CS_HREDRAW | CS_VREDRAW;

wcex.lpfnWndProc = (WNDPROC)WndProc;

wcex.cbClsExtra = 0;

wcex.cbWndExtra = 0;

wcex.hInstance = hInstance;

wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);

wcex.hCursor = LoadCursor(NULL, IDC_ARROW);

wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);

wcex.lpszMenuName = NULL;

wcex.lpszClassName = szWindowClass;

wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);

RegisterClassEx(&wcex);

1.2.8 Tạo cửa sổ

Để tạo cửa sổ ta dùng hàm

CreateWindow

hoặc

CreateWindowEx
(18)

HMENU hMenu, // handle to menu or child-window identifier HANDLE hInstance, // handle to application instance

LPVOID lpParam // pointer to window-creation data );

HWND CreateWindowEx(

DWORD dwExStyle, // extended window style

LPCTSTR lpClassName, // pointer to registered class name LPCTSTR lpWindowName, // pointer to window name

DWORD dwStyle, // window style

int x, // horizontal position of window int y, // vertical position of window int nWidth, // window width

int nHeight, // window height

HWND hWndParent, // handle to parent or owner window

HMENU hMenu, // handle to menu, or child-window identifier HINSTANCE hInstance, // handle to application instance

LPVOID lpParam // pointer to window-creation data );

Ví dụ

hWnd = CreateWindow(

szWindowClass, // Window class name

“Hello program”, // Window name WS_OVERLAPPEDWINDOW, // window style

CW_USEDEFAULT, // horizontal position of window 0, // vertical position of window CW_USEDEFAULT, // window width

0, // window height

NULL, // parent window handle

NULL, // handle to menu, or child-window identifier hInstance, // handle to application instance

NULL); // pointer to window-creation data

1.2.9 Hiển thị cửa sổ

(19)

while (GetMessage(&msg, NULL, 0, 0)) {

TranslateMessage(&msg);

DispatchMessage(&msg);

}

msg là biến có kiểu MSG typedef struct tagMSG {

HWND hwnd;

UINT message;

WPARAM wParam;

LPARAM lParam;

DWORD time;

POINT pt;

}MSG;

1.3 Thủ tục cửa sổ 1.3.1 Xử lý các thông điệp

Thủ tục cửa sổ là hàm xử lý thông điệp tương ứng một lớp cửa sổ. Thủ tục cửa sổ chỉ rỏ phản ứng của của chương trình với biến cố nhập, qui định cách vẽ lại phần cửa sổ trước đó bị che khuất.

switch (message) {

case WM_CREATE:

// Xu ly thong diep WM_CREATE break;

case WM_PAINT:

(20)

WM_PAINT : Thông điệp được gởi mỗi khi cần vẽ lại cửa sổ.

WM_CHAR : Thông điệp ký tự được bấm.

WM_TIMER : Thông điệp của bộ định thời.

WM_LBUTTONDOWN : Thông điệp được gởi mỗi khi mắt trái chuột được bấm.

WM_DESTROY: Thông điệp chỉ rõ Windows đang huỷ cửa sổ sau khi nhận lệnh của người dùng. Các ứng dụng xử lý thông điệp này theo cách chuẩn là gọi PostQuitMessage(0);

1.3.2 Thông điệp WM_DESTROY và cách kết thúc chương trình

• Chương trình kết thúc bình thường phải bảo đảm dọn dẹp và giải phóng lại bộ nhớ cũng như các tài nguyên khác mà chương trình đã dùng. Chương trình kết thúc khi vòng lặp thông điệp nhận được thông điệp WM_QUIT.

• Chương trình có thể được kết thúc bằng nhiều cách: Bằng [System menu] Close, bấm Alt-F4 hoặc biểu tượng X ở góc trên bên phải. Mỗi cách trên đều dẫn đến một loạt các thông điệp được gởi cho hàm cửa sổ. Hàm cửa sổ có thể xử lý các thông điệp này hoặc gởi cho hàm xử lý cửa sổ mặc nhiên.

Một trong số các thông điệp được hàm cửa sổ quan tâm xử lý là WM_DESTROY. Xử lý chuẩn cho thông điệp này là gọi PostQuitMessage(0). Hàm này chèn WM_QUIT vào hàng đợi thông điệp. GetMessage sẽ trả về 0 khi nhận WM_QUIT và kết thúc vòng lặp thông điệp.

• Dãy thông điệp sau đây được gởi cho hàm cửa sổ khi người sử dụng bấm tổ hợp phím tắt Alt-F4

WM_SYSKEYDOWN [Alt] được bấm WM_SYSKEYDOWN [F4] được bấm

WM_SYSCOMMAND Phát sinh system command

(21)

1.3.3 Xử lý mặc nhiên các thông điệp

− Các ứng dụng Windows đều có giao diện chung khá giống nhau. Điều này được thực hiện nhờ hàm xử lý thông điệp mặc nhiên.

− Thông điệp không được WndProc quan tâm xử lý sẽ được gởi cho DefWindowProc xử lý. DefWindowProc là hàm xử lý mặc nhiên các thông điệp, nó hoạt động giống nhau cho tất cả các ứng dụng Windows.

− WndProc có thể gởi thông điệp cho DefWindowProc và ngược lại. Ví dụ, khi người sử dụng nhấn tổ hợp phím Alt-F4 để tắt ứng dụng, một loạt các thông điệp được gởi cho WinProc.

− Thông điệp bao giờ cũng được gởi cho hàm cửa sổ trước. Hàm cửa sổ sẽ gọi DefWindowProc để xử lý các thông điệp nó không quan tâm.

− Người sử dụng có thể thay đổi một vài hoạt động mặc nhiên bằng cách tự xử lý một số thông điệp nào đó.

1.4 Môi trường Visual C++

Môi trường Visual C++ 6.0 trở lên cho phép người sử dụng dễ dàng tạo ứng dụng Windows.

Ta có thể dễ dang tạo project, tạo và soạn thảo các tập tin tài nguyên, các tập tin chương trình

nguồn và đưa các tập tin này vào project. Ta cũng có thể tạo project với chương trình mẫu và

tài nguyên mẫu được cung cấp sẵn. Từ các tập tin mẫu này, người lập trình có thể thêm mã

chương trình, thay đổi tài nguyên để có được ứng dụng như ý.

(22)

Hình 1-2 – Tạo project trong VC++ 6.0

Hình 1-3 Tạo ứng dụng mẫu trong môi trường VC++ 6.0

(23)

2. Tổng quan về Giao Diện Thiết Bị Đồ Hoạ (GDI)

Giao diện đồ hoạ là một trong các đặc tính nổi bật của hệ điều hành Windows. Trong chương này ta sẽ trình bày tổng quan các vấn đề liên quan đến các thao tác tạo tạo xuất liệu đồ hoạ, các thao tác vẽ có thể dùng GDI của Windows.

2.1 Giới thiệu

• GDI là thư viện đồ hoạ của Windows, nó xử lý các thao tác xuất kết quả dạng đồ hoạ lên màn hình cũng như lên các thiết bị xuất khác như máy in, máy vẽ. GDI có thể tạo ra các điểm vẽ, đường, chữ… Windows cũng dùng chính GDI để tạo giao diện người dùng như các cửa sổ, biểu tượng, menu, hộp hội thoại.

• GDI còn cho phép vẽ trên thiết bị logic hay còn gọi là thiết bị ảo như bitmaps và metafiles.

• GDI tạo khả năng đồ hoạ độc lập thiết bị, nó cung cấp cùng một thủ tục vẽ cho tất cả các loại thiết bị thay vì 4 cho 4 loại thiết bị khác nhau là màn hình, máy in, bitmaps và metafile.

Các thiết bị GDI

• GDI có thể vẽ trên các thiết bị khác nhau nhờ các bộ điều khiển thiết bị.

• Bộ điều khiển thiết bị cho GDI biết khả năng vẽ có sẵn của thiết bị (nhờ device capability bits). Có 5 cờ cho biết khả năng của thiết bị: vẽ cung, đường thẳng, đa giác, bitmaps và text.

• Mỗi bộ điều khiển thiết bị phải cung cấp ít nhất hai thủ tục, vẽ từng điểm và vẽ đường.

• Bitmap luôn luôn có hình chữ nhật, nó là hình ảnh được lưu trữ trong bộ nhớ giống như ảnh trên màn hình được lưu trữ trong bộ nhớ của card điều khiển màn hình.

2.2 Giao diện người dùng

(24)

2.2.2 Các đối tượng vẽ logic

GDI dùng đối tượng vẽ logic để có khả năng độc lập thiết bị. Đối tượng vẽ logic mô tả xuất liệu ở mức cao, độc lập với thiết bị. Các đối tượng vẽ bao gồm pens (bút), brushes (chổi vẽ), fonts (kiểu chữ) và logical colors (màu luận lý).

2.3 Ngữ cảnh thiết bị (device context)

• Ngữ cảnh thiết bị là tập hợp các thuộc tính vẽ trong đó có một pen để vẽ đường, một brush để tô màu và một font để hiển thị văn bản.

• Nhờ ngữ cảnh thiết bị, các hàm vẽ trở nên đơn giản hơn với các tham số được giảm đến mức tối thiểu vì các thuộc tính vẽ được lấy mặc nhiên từ ngữ cảnh thiết bị. Ta tham chiếu đến ngữ cảnh thiết bị nhờ một handle đến ngữ cảnh thiết bị (hdc).

Ví dụ :

hdc = BeginPaint(hWnd, &ps);

RECT rt;

GetClientRect(hWnd, &rt);

DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);

EndPaint(hWnd, &ps);

• Ngữ cảnh thiết bị còn có vai trò liên kết một chương trình với một một vùng vẽ nhất

định. Chương trình muốn vẽ lên màn hình (máy in) phải nắm được ngữ cảnh thiết bị

của màn hình (máy in).

(25)

Hình 2-1 – Hình vẽ ở cửa sổ này không lấn vào cửa sổ khác

(26)

Hình 2-2 – Các vùng xén tạo nên các hàng rào không cho phép ứng dụng này vẽ lên cửa sổ ứng dụng khác

• Vai trò thứ ba của ngữ cảnh thiết bị là ngăn cách xuất liệu của nhiều chương trình truy xuất thiết bị cùng lúc (vai trò permission slip). Đối với máy in dùng cơ chế spooling và đối với các cửa sổ ứng dùng, dùng clipping.

• Khi một ứng dụng muốn vẽ, nó phải mượn DC từ Windows Borrowing routines Returning routines Description BeginPaint

GetDC

EndPaint ReleaseDC

Clip to invalid part of client area

Clip to entire client area

(27)

• Thực hiện các thao tác vẽ

• Trả lại DC cho Windows

Hai hàm mượn và trả DC thông dụng là BeginPaint và EndPaint, BeginPaint cho handle đến ngữ cảnh thiết bị của phần đã được xén trong vùng client.

Việc vẽ trong Windows thường theo khuôn mẫu sau:

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

// Các thao tác vẽ

EndPaint(hWnd, &ps);

break;

Có thể dùng GetDC và RealeaseDC để thực hiện thao tác vẽ ngay khi có biến cố nhập như chuột hay bàn phím được bấm. GetDC trả về handle đến toàn bộ vùng client của cửa sổ ứng dụng.

case WM_CHAR:

hdc = GetDC(hWnd);

// Thao tác vẽ

ReleaseDC(hWnd, hdc);

break;

Ta cũng có thể dùng GetWindowDC để vẽ lên cả phần non-client của cửa sổ ứng dụng.

2.5 Vẽ từng điểm

Trong phần này ta sẽ tìm hiểu chi tiết qui trình vẽ trên vùng client của một cửa sổ ứng dụng, và các thao tác vẽ bằng cách trực tiếp bật tắt một pixel nào đó trên màn hình. Các chương tiếp theo sẽ trình bày các thao tác vẽ đường, hình có tô màu, vẽ chữ…

2.5.1 Chương trình Pixel

Chương trình pixel sau đây khi được thực hiện sẽ hiển thị một chấm đen ở giữa vùng client của cửa sổ ứng dụng. Chương trình pixel gồm các tập tin chương trình nguồn sau:

stdafx.h stdafx.cpp pixel.cpp

// Trich doan pixel.cpp

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

(28)

return DefWindowProc(hWnd, message, wParam, lParam);

}

return 0;

}

2.5.2 SetPixel và GetPixel

Để vẽ từng điểm, ta dùng hàm SetPixel, để lấy giá trị của một pixel xác định trên cửa sổ ứng dụng, ta dùng hàm GetPixel. Trong chương trình trên, ta còn dùng hàm GetClientRect. Khai báo của các hàm trên như sau:

Hàm SetPixel đặt giá trị đỏ, xanh lá cây, xanh dương (RGB) cho một pixel tại một toạ độ qui định trước.

COLORREF SetPixel(

HDC hdc, // handle of device context int X, // x-coordinate of pixel int Y, // y-coordinate of pixel COLORREF crColor // pixel color );

Hàm GetPixel trả về giá trị đỏ, xanh lá cây, xanh dương của một pixel tại một vị trí qui định trước.

COLORREF GetPixel(

HDC hdc, // handle of device context int XPos, // x-coordinate of pixel int nYPos // y-coordinate of pixel );

COLORREF là một số DWORD lưu trữ 3 thành phần R,G,B biểu diễn màu. Ta có thể dùng macro RGB để tạo ra màu mong muốn.

Tổ hợp RGB Màu

(29)

BOOL GetClientRect(

HWND hWnd, // handle of window

LPRECT lpRect // address of structure for client coordinates );

LPRECT là con trỏ đến cấu trúc RECT

typedef struct _RECT { // rc LONG left;

LONG top;

LONG right;

LONG bottom;

} RECT;

2.6 Tạo Marker

Chương trình Marker sử dụng SetPixel và GetPixel để đánh dấu hình chữ thập nhỏ (+) mỗi khi người sử dụng bấm mắt chuột trái. Các tập tin chương trình nguồn gồm.

stdafx.h stdafx.cpp marker.cpp

(30)

#define MAX_MARKERS 100

#define MARKER_SIZE 5

void DrawMarker(HDC hdc, POINT pt, int size);

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

PAINTSTRUCT ps;

HDC hdc;

int i;

static POINT apt[MAX_MARKERS];

static int nMarkers = 0;

switch (message) {

case WM_LBUTTONDOWN:

if (nMarkers <= MAX_MARKERS) {

apt[nMarkers].x = LOWORD(lParam);

apt[nMarkers].y = HIWORD(lParam);

nMarkers++;

InvalidateRect(hWnd, NULL, TRUE);

} break;

case WM_RBUTTONDOWN:

nMarkers = 0;

InvalidateRect(hWnd, NULL, TRUE);

break;

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

for (i = 0; i < nMarkers; i++)

DrawMarker(hdc, apt[i], MARKER_SIZE);

EndPaint(hWnd, &ps);

break;

case WM_DESTROY:

PostQuitMessage(0);

break;

default:

return DefWindowProc(hWnd, message, wParam,

(31)

for (i = 1; i <= size; i++) {

InvertPixel(hdc, x-i, y);

InvertPixel(hdc, x+i, y);

InvertPixel(hdc, x, y-i);

InvertPixel(hdc, x, y+i);

} }

2.6.2 Hoạt động của chương trình

Hàm cửa sổ của chương trình xử lý bốn thông điệp WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_PAINT và WM_DESTROY. WM_LBUTTONDOWN và WM_RBUTTONDOWN là thông điệp mắt chuột, nó được gởi tới cho hàm cửa sổ mỗi khi mắt trái (hoặc phải) của chuột được bấm. Trong Marker, ta xử lý thông điệp WM_LBUTTONDOWN bằng cách thêm một điểm vào mảng lưu trữ các điểm và gởi thông báo cần vẽ lại nhờ hàm InvalidateRect.

Tham số lParam gởi kèm theo thông điệp cho biết vị trí của con trỏ chuột tại thời điểm bấm (hoặc nhả).

2.7 Vẽ và vẽ lại

Cửa sổ trong Windows thường bị che một phần bởi hộp hội thoại, menu, con trỏ chuột hay một cửa sổ khác. Khi không còn bị che, phần này cần được phục hồi. Trong một số trường hợp đặc biệt khi phần bị che tương đối nhỏ như con trỏ chuột di chuyển hay một hộp hội thoại được mở ra, Windows tìm cách lưu phần bị che để dễ phục hồi sau này.

Trong đa số các trường hợp còn lại, cơ chế phục hồi phần cửa sổ bị che là bản thân cửa sổ tự nó vẽ lại phần hư. Điều này được thực hiện mỗi khi cửa sổ nhận được thông điệp WM_PAINT.

Windows gởi thông điệp WM_PAINT này đến cửa sổ bị che trong các trường hợp sau:

• Cửa sổ che đóng lại hoặc di chuyển.

• Cửa sổ thay đổi kích thước (lớp cửa sổ phải được đăng ký với cờ CS_HREDRAW và CS_VREDRAW được bật).

• Chương trình gián tiếp gởi WM_PAINT thông qua hàm InvalidateRect.

• Cửa sổ được cuốn.

• Menu popup mở ra rồi đóng lại

(32)

Hàm mượn ngữ cảnh thiết bị là BeginPaint, hàm trả là EndPaint. Hai hàm này sử dụng cấu trúc PAINTSTRUCT. Khai báo cấu trúc PAINSTRUCT và các hàm trên như sau.

typedef struct tagPAINTSTRUCT { // ps

HDC hdc;

BOOL fErase;

RECT rcPaint;

BOOL fRestore;

BOOL fIncUpdate;

BYTE rgbReserved[32];

} PAINTSTRUCT;

HDC BeginPaint(

HWND hwnd, // handle to window

LPPAINTSTRUCT lpPaint // pointer to structure for paint information

);

BOOL EndPaint(

HWND hWnd, // handle to window

CONST PAINTSTRUCT *lpPaint // pointer to structure for paint data );

BeginPaint mượn Windows ngữ cảnh thiết bị (DC) và đưa các thông tin vẽ vào cấu trúc PAINSTRUCT. BeginPaint được sử dụng để hoạt động cùng với thông điệp WM_PAINT.

BeginPaint cung cấp thông tin về các vùng xén, vùng không hợp lệ.

Hàm EndPaint trả lại DC cho Windows. Khi hàm này được gọi Windows khôi phục lại các thuộc tính vẽ sẵn sàng cho chương trình cần vẽ ở lần kế tiếp. Đồng thời EndPaint cũng thông báo rằng vùng không hợp lệ đã được sửa chữa.

BeginPaint và EndPaint luôn luôn phải đi đôi với nhau.

(33)

);

Trong marker ta gọi với các tham số

InvalidateRect(hWnd, NULL, TRUE);

Tham số thứ ba là cờ xoá, giá trị TRUE cho biết sẽ xoá phần nền trước khi vẽ. Tham số thứ hai là vùng cửa sổ cần vẽ lại. Giá trị NULL cho biết cần phải vẽ lại toàn bộ vùng client.

Vẽ lại toàn bộ vùng client như trên là không hiệu quả. Thông thường, ta chỉ cần vẽ lại một vùng nhỏ bị hư hay cần cập nhật, ta gọi vùng này là vùng không hợp lệ. Trong marker, mỗi lần thêm một dấu thập mới, ta chỉ cần vẽ lại vùng đủ chứa marker này.

Ví dụ sau minh hoạ việc vẽ lại toàn bộ đúng dấu thập tại vị trí mắt trái chuột được bấm.

// Marker ver 1.1 : Chi ve lai phan hinh chu nhat vua du chua marker case WM_LBUTTONDOWN:

if (nMarkers <= MAX_MARKERS) {

apt[nMarkers].x = LOWORD(lParam);

apt[nMarkers].y = HIWORD(lParam);

nMarkers++;

r.bottom = HIWORD(lParam) + MARKER_SIZE + 1;

r.top = HIWORD(lParam) - MARKER_SIZE - 1;

r.left = LOWORD(lParam) - MARKER_SIZE - 1;

r.right = LOWORD(lParam) + MARKER_SIZE + 1;

InvalidateRect(hWnd, &r, TRUE);

} break;

2.10 InvertPixel và DrawMarker

Sử dụng SetPixel để vẽ dấu thập có thể không có tác dụng (vô hình) nếu màu nền trùng với màu của dấu thập. Để bảo đảm việc đánh dấu trên mọi nền, ta phủ định giá trị pixel của màu nền. Điều này được thực hiện nhờ GetPixel theo sau bởi SetPixel với giá trị đã được phủ định từng bit.

void InvertPixel(HDC hdc, int x, int y) {

COLORREF crColor = GetPixel(hdc, x, y);

SetPixel(hdc, x, y, ~crColor);

(34)

}

Để có thể đánh dấu trên nền có màu khác nhau, DrawMarker phải bảo đảm vẽ dấu chữ thập bằng gọi InvertPixel, InvertPixel đảo ngược giá trị pixel trên nền cách phủ định giá trị pixel đang có.

2.11 GetDC và ReleaseDC

Mỗi khi mắt chuột được bấm, thay vì gián tiếp ra lệnh việc vẽ lại thông qua InvalidateRect để gởi WM_PAINT. Ta có thể vẽ trực tiếp dấu thập mỗi khi nhận được thông điệp mắt trái chuột được bấm. Điều này có thể được thực hiện bằng GetDC và ReleaseDC. Hàm GetDC cho mượn ngữ cảnh thiết bị đến toàn bộ vùng client, ReleaseDC trả lại ngữ cảnh này. Giữa hai thao tác mượn và trả này, ta có thể thực hiện các thao tác vẽ.

// Marker.cpp : Defines the entry point for the application.

// Version 1.2: Dung GetDC de ve ngay khi bam mat trai chuot

// Trich doan phan WndProc

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

#define MAX_MARKERS 100

#define MARKER_SIZE 5 HDC hdc;

TCHAR szHello[MAX_LOADSTRING];

int i;

static POINT apt[MAX_MARKERS];

static int nMarkers = 0;

switch (message) {

case WM_LBUTTONDOWN:

if (nMarkers <= MAX_MARKERS) {

apt[nMarkers].x = LOWORD(lParam);

apt[nMarkers].y = HIWORD(lParam);

nMarkers++;

hdc = GetDC(hWnd);

DrawMarker(hdc, apt[nMarkers-1], MARKER_SIZE);

(35)

EndPaint(hWnd, &ps);

break;

case WM_DESTROY:

PostQuitMessage(0);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

}

return 0;

}

(36)

3. Vẽ đường

3.1 Đường

• Đường là một đối tượng hình học có được bằng cách nối liên tiếp các điểm.

• Đường là loại đối tượng hình học mở. Mỗi đường đều có điểm đầu và điểm cuối

• Đường được vẽ theo giải thuật inclusive/exclusive, nghĩa là điểm đầu thuộc đường, nhưng điểm cuối không thuộc đường.

3.2 Các hàm vẽ đường

GDI cung cấp hàm vẽ các đường thẳng, cung, đường cong bezier, polyline.

Một số hàm vẽ đường sử dụng cấu trúc POINT

typedef struct tagPOINT {

LONG x;

LONG y;

} POINT;

3.2.1 MoveToEx và LineTo

BOOL MoveToEx(

HDC hdc, int X, int Y, LPPOINT lpPoint );

BOOL LineTo(

HDC hdc, int nXEnd, int nYEnd );

(37)

CONST POINT *lppt, // pointer to array containing endpoints int cPoints // number of points in the array

);

BOOL PolylineTo(

HDC hdc, // handle to device context

CONST POINT *lppt, // pointer to array of points DWORD cCount // number of points in array );

BOOL PolyPolyline(

HDC hdc, // handle of a device context

CONST POINT *lppt, // address of an array of points CONST DWORD *lpdwPolyPoints, // address of an array of values

DWORD cCount // number of counts in the second array );

Linedrag: Ví dụ minh hoạ MoveTo và LineTo

LineDrag cho phép vẽ các đường bằng cách bầm mắt trái chuột rồi kéo lê.

(38)

#include "stdafx.h"

#include "resource.h"

#define MAX_SEGS 100

#define MAX_POINTS 10000

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

int wmId, wmEvent;

PAINTSTRUCT ps;

HDC hdc;

TCHAR szPrompt[MAX_LOADSTRING];

LoadString(hInst, IDS_PROMPT, szPrompt, MAX_LOADSTRING);

int i,j;

static int nSegs, nPoints;

static int anCounts[MAX_SEGS+1];

static BOOL bMouseDown;

static POINT oldPoint, apt[MAX_POINTS];

switch (message) {

case WM_LBUTTONDOWN:

if (nSegs < MAX_SEGS && nPoints < MAX_POINTS) {

bMouseDown = TRUE;

SetCapture(hWnd);

anCounts[nSegs] = nPoints;

apt[nPoints].x = LOWORD(lParam);

apt[nPoints++].y = HIWORD(lParam);

nSegs++;

} break;

case WM_MOUSEMOVE:

if (bMouseDown) {

if (nPoints < MAX_POINTS) {

apt[nPoints].x = LOWORD(lParam);

apt[nPoints++].y = HIWORD(lParam);

anCounts[nSegs] = nPoints;

(39)

case WM_RBUTTONDOWN:

nSegs = nPoints = 0;

InvalidateRect(hWnd, NULL, TRUE);

break;

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

RECT rt;

GetClientRect(hWnd, &rt);

DrawText(hdc, szPrompt, strlen(szPrompt), &rt, DT_CENTER);

for (i = 0; i < nSegs; i++) {

j = anCounts[i];

MoveToEx(ps.hdc, apt[j].x, apt[j].y, &oldPoint);

for (; j < anCounts[i+1]; j++)

LineTo(ps.hdc, apt[j].x, apt[j].y);

}

EndPaint(hWnd, &ps);

break;

case WM_DESTROY:

PostQuitMessage(0);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

}

return 0;

}

Chương trình trên có thể cải tiến dùng Polyline hoặc PolyPolyline 3.2.3 Arc

Hàm Arc vẽ một cung là một phần của hình Ellipse

BOOL Arc(

HDC hdc, // handle to device context int nLeftRect, // x-coord : UPL

int nTopRect, // y-coord : UPL int nRightRect, // x-coord : LWR int nBottomRect, // y-coord : LWR

(40)

BOOL ArcTo(

HDC hdc, // handle to device context int nLeftRect, // x-coord : UPL

int nTopRect, // y-coord : UPL int nRightRect, // x-coord : LWR int nBottomRect, // y-coord : LWR

int nXRadial1, // x-coord of the first radial ending point int nYRadial1, // y-coord of the first radial ending point int nXRadial2, // x-coord of the second radial ending point int nYRadial2 // y-coord of the second radial ending point );

3.2.5 AngleArc

BOOL AngleArc(

HDC hdc, // handle to device context int X, // x-coordinate of circle's center int Y, // y-coordinate of circle's center DWORD dwRadius, // circle's radius

FLOAT eStartAngle, // arc's start angle FLOAT eSweepAngle // arc's sweep angle );

3.2.6 PolyBezier và PolyBezierTo

Hàm PolyBezier vẽ một hay nhiều đường cong Bézier.

BOOL PolyBezier(

HDC hdc, // handle to device context

CONST POINT *lppt, // pointer to endpoints and control points DWORD cPoints // count of endpoints and control points );

BOOL PolyBezierTo(

(41)

Hình 3-1- Bezier

(42)

Thuộc tính Ý nghĩa Màu nền (background color)

Chế độ nền (background mode) Vị trí hiện hành (curent position) Chế độ vẽ (drawing mode) Bút vẽ (pen)

Màu thứ hai khi không phải bút vẽ PS_SOLID Tắt/mở màu nền

Điểm hiện hành cho các thao tác LineTo, ArcTo…

Thao tác vẽ boolean giữa điểm vẽ và nền Màu, độ rộng và kiểu vẽ đường

Hàm để thay đổi màu nền và chế độ nền là

COLORREF SetBkColor(HDC hdc, COLORREF crColor);

int SetBkMode(HDC hdc, int iBkMode);

iBkMode có thể là OPAQUE hoặc TRANSPARENT

3.4 Bút vẽ (pen)

• Bút vẽ là thuộc tính chỉ rõ cách vẽ đường, bút vẽ có ba thành phần là màu, độ rộng và kiểu.

• Bút vẽ có thể được chia sẻ giữa các các chương trình và giữa các thiết bị.

• GDI cung cấp sẵn một số bút vẽ. Người sử dụng có thể tự tạo thêm các bút vẽ khác.

• Bút vẽ có thể được dùng độc lập thiết bị vì thực chất là bút vẽ luận lý. Khi GDI vẽ, nó yêu cầu thiết bị nhận biết bút vẽ và bộ điều khiển thiết bị sẽ vẽ đường theo chất lượng yêu cầu. Nhờ vậy bút vẽ có thể được chia sẻ trên các thiết bị khác nhau.

3.4.1 Tạo và dùng bút vẽ có sẵn

Windows cung cấp sẵn một tập các bút vẽ (stock pens). Đó là các bút vẽ đen, trắng và rỗng.

Hàm GetStockObject

Bút vẽ được tham chiếu đến bằng handle. Hàm GetStockObject cho phép lấy handle của một bút vẽ có sẵn.

HPEN hpen;

(43)

hpenOld = SelectObject(hdc, hPenNew);

…// Các thao tác vẽ đường

SelectObject(hdc, hPenOld);

3.4.2 Tạo mới các bút vẽ

Người sử dụng có thể tự tạo các bút vẽ khác hơn các bút vẽ do Windows cung cấp, có hai cách để tạo bút vẽ.

Hàm CreatePen

HPEN hpen;

hpen = CreatePen (nPenStyle, nWidth, crColor);

Để tạo bút vẽ đặc, độ rộng 4, màu đỏ, dùng

hpen = CreatePen (PS_SOLID, R, RGB(255,0,0));

nPenStype có thể có các giá trị được định nghĩa sẵn như sau:

PS_SOLID Pen is solid.

PS_DASH Pen is dashed.

PS_DOT Pen is dotted.

PS_DASHDOT Pen has alternating dashes and dots.

PS_DASHDOTDOT Pen has dashes and double dots.

PS_NULL Pen is invisible.

PS_INSIDEFRAME Pen is solid, to be drawn inside a filled figure

Hàm CreatePenIndirect

Dùng cấu trúc LOGPEN để tạo bút vẽ

typedef struct tagLOGPEN { // lgpn UINT lopnStyle;

POINT lopnWidth;

COLORREF lopnColor;

} LOGPEN;

Để tạo bút vẽ, ta khởi động các thành phần của LOGPEN và gọi hàm

CreatePenIndirect
(44)

3.4.3 Ví dụ minh hoạ các thuộc tính bút vẽ

Hình 3-3 – Pen

// Pen.cpp : Defines the entry point for the application.

// Trich doan ham WndProc

#define dim(a) sizeof(a)/sizeof(a[0]) struct PenData

{

char *Name;

int nWidth, nStyle;

COLORREF crColor;

};

PenData aPenTab[] = {

"PS_SOLID Width 1", 1, PS_SOLID, RGB(0,0,0),

"PS_SOLID Width 2", 2, PS_SOLID, RGB(0,0,0),

"PS_SOLID Width 4", 4, PS_SOLID, RGB(0,0,0),

(45)

int i;

HPEN hpen

switch (message) {

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

for (i = 0; i < dim(aPenTab); i++) {

TextOut(hdc, 10, i*30, aPenTab[i].Name, lstrlen(aPenTab[i].Name));

hpen = CreatePen(aPenTab[i].nStyle, aPenTab[i].nWidth,

aPenTab[i].crColor);

SelectObject(hdc, hpen);

MoveToEx(hdc, 150, i*30+10, NULL);

LineTo(hdc, 500, i*30+10);

DeleteObject(hpen);

}

EndPaint(hWnd, &ps);

break;

case WM_DESTROY:

PostQuitMessage(0);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

}

return 0;

}

3.5 Các chế độ vẽ (Drawing Modes) ảnh hưởng đến vẽ đường

Chế độ vẽ là thao tác Boolean điểu khiển cách GDI vẽ điểm, đường, hình. Chế độ vẽ còn được gọi là chế độ trộn vì nó trộn logic với giá trị có sẵn trên nền. Hàm SetRop2 cho phép thay đổi chế độ vẽ. Hàm GetRop2 cho biết chế độ vẽ hiện hành.

int SetROP2(

HDC hdc, // handle of device context int fnDrawMode // drawing mode

);

int GetROP2(

(46)

R2_MASKPEN Pixel is a combination of the colors common to both the pen and the screen.

R2_MASKPENNOT Pixel is a combination of the colors common to both the pen and the inverse of the screen.

R2_MERGENOTPEN Pixel is a combination of the screen color and the inverse of the pen color.

R2_MERGEPEN Pixel is a combination of the pen color and the screen color.

R2_MERGEPENNOT Pixel is a combination of the pen color and the inverse of the screen color.

R2_NOP Pixel remains unchanged.

R2_NOT Pixel is the inverse of the screen color.

R2_NOTCOPYPEN Pixel is the inverse of the pen color.

R2_NOTMASKPEN Pixel is the inverse of the R2_MASKPEN color.

R2_NOTMERGEPEN Pixel is the inverse of the R2_MERGEPEN color.

R2_NOTXORPEN Pixel is the inverse of the R2_XORPEN color.

R2_WHITE Pixel is always 1.

R2_XORPEN Pixel is a combination of the colors in the pen and in the screen, but not in both.

(47)

4. Vẽ miền

4.1 Miền

• Miền hay hình kín là đối tượng hình học có hai phần: miền trong và đường biên bao quanh miền đó. Ta còn gọi các hình này là hình có tô màu.

• GDI dùng hai cách diễn dịch toạ độ là tâm điểm (pixel-centered) và giao điểm lưới (grid- intersection) để vẽ các miền. Toạ độ tâm điểm được dùng để vẽ đường còn toạ độ giao điểm lưới được dùng để xén (clipping). Một trong những ảnh hưởng của toạ độ giao điểm lưới là hình vẽ có kích thước nhỏ hơn một pixel so với toạ độ tâm điểm.

• Toạ độ tâm điểm được dùng với các hàm vẽ đa giác, toạ độ giao điểm lưới được dùng để vẽ hình chữ nhật, ellipse, chord, pie.

• GDI cung cấp các hàm vẽ hình với cách diễn dịch toạ độ tương ứng như sau:

Hàm Toạ độ Ý nghĩa

Polygon tâm điểm đa giác

PolyPolygon tâm điểm nhiều đa giác

Chord giao điểm lưới một phần cung được khép kín bằng đường thẳng

Ellipse giao điểm lưới ellipse

Pie giao điểm lưới hình cánh quạt

Rectangle giao điểm lưới hình chữ nhật

RoundRect giao điểm lưới hình chữ nhật có góc tròn 4.2 Các hàm vẽ miền

Người sử dụng có thể dễ dàng vẽ các miền thông dụng bằng các hàm thư viện do GDI cung

cấp. Một số hàm vẽ miền sử dụng cấu trúc RECT

(48)

4.2.1 Vẽ đa giác – Polygon và PolyPolygon

Hàm Polygon vẽ một đa giác bằng cách nối các điểm dùng bút vẽ sẵn có trong ngữ cảnh thiết bị. Nếu điểm đầu và điểm cuối không trùng nhau, một đoạn thẳng sẽ được thêm vào để nối.

Miền bên trong được tô bằng chổi vẽ có sẵn trong ngữ cảnh thiết bị.

BOOL Polygon(

HDC hdc, // handle to device context

CONST POINT *lpPoints, // pointer to polygon's vertices int nCount // count of polygon's vertices

);

Hàm PolyPolygon vẽ nhiều đa giác với chỉ một lệnh gọi. Chương trình vẽ nhiều đa giác dùng hàm này sẽ chạy nhanh hơn gọi nhiều lần hàm vẽ đa giác.

BOOL PolyPolygon(

HDC hdc, // handle to device context

CONST POINT *lpPoints, // pointer to array of vertices for polygons CONST INT *lpPolyCounts, // pointer to array with count of vertices int nCount // count of polygons

);

4.2.2 Vẽ hình chữ nhật – Rectangle, RoundRect, FrameRect và InvertRect

Vẽ hình chữ nhật

BOOL Rectangle(

HDC hdc, // handle to device context

int nLeftRect, // x-coord of bounding rectangle's upper-left corner int nTopRect, // y-coord of bounding rectangle's upper-left corner int nRightRect, // x-coord of bounding rectangle's lower-right corner int nBottomRect // y-coord of bounding rectangle's lower-right corner );

(49)

Hình 4-1 Rectangle

Vẽ hình chữ nhật có góc tròn

BOOL RoundRect(

HDC hdc, // handle to device context

int nLeftRect, // x-coord of bounding rectangle's upper-left corner int nTopRect, // y-coord of bounding rectangle's upper-left corner int nRightRect, // x-coord of bounding rectangle's lower-right corner int nBottomRect, // y-coord of bounding rectangle's lower-right corner int nWidth, // width of ellipse used to draw rounded corners int nHeight // height of ellipse used to draw rounded corners );

(50)

Hình 4-2 RoundRect

Vẽ khung chữ nhật

int FrameRect(

HDC hDC, // handle to device context

CONST RECT *lprc, // pointer to rectangle coordinates HBRUSH hbr // handle to brush

);

Tô chữ nhật

int FillRect(

HDC hDC, // handle to device context

CONST RECT *lprc, // pointer to structure with rectangle HBRUSH hbr // handle to brush

);

Đảo màu hình chữ nhật

BOOL InvertRect(

HDC hDC, // handle to device context

CONST RECT *lprc // pointer to structure with rectangle );

4.2.3 Ellipse, Pie và Chord

BOOL Ellipse(

HDC hdc, // handle to device context

int nLeftRect, // x-coord of bounding rectangle's upper-left corner int nTopRect, // y-coord of bounding rectangle's upper-left corner int nRightRect, // x-coord of bounding rectangle's lower-right corner int nBottomRect // y-coord of bounding rectangle's lower-right corner );

(51)

Hình 4-3 Ellipse BOOL Pie(

HDC hdc, // handle to device context

int nLeftRect, // x-coord of bounding rectangle's upper-left corner int nTopRect, // y-coord of bounding rectangle's upper-left corner int nRightRect, // x-coord of bounding rectangle's lower-right corner int nBottomRect, // y-coord of bounding rectangle's lower-right corner int nXRadial1, // x-coord of first radial's endpoint

int nYRadial1, // y-coord of first radial's endpoint int nXRadial2, // x-coord of second radial's endpoint int nYRadial2 // y-coord of second radial's endpoint );

(52)

Hình 4-4 Pie BOOL Chord(

HDC hdc, // handle to device context

int nLeftRect, // x-coord of the upper-left corner of bounding rectangle int nTopRect, // y-coord of the upper-left corner of bounding rectangle int nRightRect, // x-coord of the lower-right corner of bounding rectangle int nBottomRect, // y-coord of the lower-right corner of bounding rectangle int nXRadial1, // x-coord of the first radial's endpoint

int nYRadial1, // y-coord of the first radial's endpoint int nXRadial2, // x-coord of the second radial's endpoint int nYRadial2 // y-coord of the second radial's endpoint );

Hình 4-5 Chord

4.3 Các thuộc tính tô màu

Ngữ cảnh thiết bị có 7 thuộc tính ảnh hưởng đến việc tô màu:

(53)

Thuộc tính vẽ Ý nghĩa

Màu nền (Background Color): Màu thứ hai của bút vẽ và chổi vẽ đường gạch Chế độ nền (Background Color): Bật / Tắt màu nền

Chổi vẽ (Brush): Màu để tô miền trong của hình Gốc của chổi vẽ (Brush Origin): Vị trí gốc để cho chổi vẽ đường gạch.

Chế độ vẽ(Drawing Mode) Thao tác Boolean với nền

Bút vẽ (Pen) Màu, độ rộng và kiểu của đườngbiên Chế độ tô đa giác (Polygon Fill Mode) Để tô màu đa giác

Chế độ tô màu có thể là ALTERNATE hoặc WINDING.

Để thay đổi chế độ tô màu, ta dùng hàm PolyFillMode

int SetPolyFillMode(

HDC hdc, // handle of device context int iPolyFillMode // polygon fill mode );

Hàm để thay đổi màu nền là SetBkColor

COLORREF SetBkColor(

HDC hdc,

COLORREF crColor );

Và hàm để thay đổi chế độ nền là SetBkMode

int SetBkMode(

HDC hdc, int iBkMode );

iBkMode có thể là OPAQUE hoặc TRANSPARENT

(54)

Hình 4-6 – Chế độ tô màu ALTERNATE và WINDING

4.4 Chổi vẽ ( Brushes )

Chổi vẽ là thuộc tính để tô. Chổi vẽ được tạo bởi 3 thành phần: Kiểu (Style), màu (color) và mẫu (Pattern). Kích thước của chỗi vẽ tính là 8 pixel x 8 pixel. Chổi vẽ có thể được chia sẻ giữa các chương trình và các thiết bị. Windows cung cấp sẵn một số chổi vẽ (stock brushes).

Ngoài ra, người sử dụng có thể tự tạo các chổi vẽ riêng. Ta quản lý chổi vẽ bằng một handle đến chổi vẽ gọi là HBRUSH.

4.4.1 Tạo và sử dụng chổi vẽ có sẵn

Để sử dụng chổi vẽ do Windows cung cấp sẵn, ta dùng hàm GetStockObject.

HGDIOBJ GetStockObject(

(55)

GRAY_BRUSH HOLLOW_BRUSH LTGRAY_BRUSH NULL_BRUSH WHITE_BRUSH

Hollow brush (equivalent to NULL_BRUSH).

Light gray brush.

Null brush (equivalent to HOLLOW_BRUSH).

White brush.

Ví duï

HBRUSH hbr;

hbr = GetStockObject(GRAY_BRUSH);

SelectObject(hdc, hbr);

Ellipse(hdc, x1, y1, x2, y2);

(56)

SelectObject để chọn chổi vẽ vừa tạo. Sau khi dùng xong dùng DeleteObject để giải phóng tài nguyên Windows cấp cho chổi vẽ đó.

Đoạn chương trình sau tạo một chổi vẽ gạch chéo 45 độ có màu xanh dương và vẽ hình ellipse bằng chổi vẽ đó.

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

RECT rt;

HBRUSH hbr;

GetClientRect(hWnd, &rt);

hbr = CreateHatchBrush(HS_BDIAGONAL, RGB(0,0,0xff));

SelectObject(hdc, hbr);

Ellipse(hdc, rt.right/8, rt.bottom/8, 7*rt.right/8, 7*rt.bottom/8);

EndPaint(hWnd, &ps);

DeleteObject(hbr);

break;

Chổi vẽ có thể được tạo sẵn khi cửa sổ vừa được tạo và được giải phóng khi cửa sổ bị huỷ.

case WM_CREATE:

hbr = CreateHatchBrush(HS_BDIAGONAL, RGB(0,0,0xff));

break;

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

RECT rt;

GetClientRect(hWnd, &rt);

SelectObject(hdc, hbr);

Ellipse(hdc, rt.right/8, rt.bottom/8, 7*rt.right/8, 7*rt.bottom/8);

EndPaint(hWnd, &ps);

break;

case WM_DESTROY:

DeleteObject(hbr);

PostQuitMessage(0);

break;

(57)

4.4.3 Tạo trực tiếp các chổi vẽ Hàm CreateSolidBrush

Hàm CreateSolidBrush tạo một chổi vẽ luận lý đặc với màu được quy định bởi tham số crColor

HBRUSH CreateSolidBrush(

COLORREF crColor // brush color value );

Hàm CreateHatchBrush

Hàm CreateSolidBrush tạo một chổi vẽ luận lý là những đường gạch được quy định bởi tham số fnStyle và có màu clrref.

HBRUSH CreateHatchBrush(

int fnStyle, // hatch style COLORREF clrref // color value );

Xem hàm CreateBrushIndirect ở bên dưới để biết các giá trị hợp lệ cho fnStyle.

Hàm CreatePatternBrush

Hàm CreatePatternBrush tạo một chổi vẽ từ mẫu xác định bởi một bitmap trong bộ nhớ.

HBRUSH CreatePatternBrush(

HBITMAP hbmp // handle to bitmap );

4.4.4 Tạo gián tiếp chổi vẽ Hàm CreateBrushIndirect

Hàm CreateBrushIndirect kết hợp tất cả các hàm tạo chổi vẽ kể trên, nó cho phép tạo chổi vẽ với kiểu, màu, mẫu từ một cấu trúc chổi vẽ luận lý cho trước.

HBRUSH CreateBrushIndirect(

(58)

LONG lbHatch;

} LOGBRUSH;

lbStyle

Kiểu của brush. Thành phần lbStyle có các giá trị sau:

Giá trị Ý nghĩa

BS_DIBPATTERN Mẫu chổi vẽ định nghĩa bằng bitmap có kích thước 8x8 pixels thuộc loại DIB.

BS_DIBPATTERN8X8 Tương tự BS_DIBPATTERN.

BS_HATCHED Chổi vẽ Hatched.

BS_HOLLOW Chổi vẽ rỗng.

BS_NULL Chổi vẽ rỗng, giống như BS_HOLLOW.

BS_PATTERN Chỗi vẽ có mẫu định nghĩa bởi bitmap trong bộ nhớ.

BS_PATTERN8X8 Tương tự BS_PATTERN.

BS_SOLID Chổi vẽ đặc.

lbColor

Màu của chổi vẽ. Nếu lbStyle là BS_HOLLOW hay BS_PATTERN, lbColor không có ý nghĩa.

lbHatch

Khi lbStyle là BS_HATCHED, lbHatch chỉ rõ cách thức gạch, nó quy định hướng của đường gạch.

Giá trị Ý nghĩa

HS_BDIAGONAL Gạch theo góc 45 độ từ dưới lên và từ trái sang phải

HS_CROSS Gạch ngang và dọc

(59)

4.5 Ví dụ về chổi vẽ

Chương trình minh hoạ Brush tạo các chổi vẽ do người sử dụng định nghĩa.

Tập tin chương trình nguồn brush.cpp như sau:

#define MAX_BRUSHES 30

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

int wmId, wmEvent;

PAINTSTRUCT ps;

HDC hdc;

int i;

static nBrushes, nCurBrush;

static HBRUSH ahbr[MAX_BRUSHES];

static TCHAR *aszName[MAX_BRUSHES];

static BYTE acPattern1[] = { 0xff, 0,

0xe7, 0, 0xc3, 0, 0x99, 0, 0x3c, 0, 0x7e, 0, 0xff, 0, 0xff, 0, };

static BYTE acPattern2[] = { 0x81, 0,

0x82, 0, 0x84, 0, 0xf8, 0, 0x0, 0, 0x0, 0, 0x0, 0, 0x0, 0, };

switch (message) {

case WM_CREATE:

HBITMAP hBitmap;

(60)

aszName[nBrushes++] = "Hatch HS_FDIAGONAL";

ahbr[nBrushes] = CreateHatchBrush(HS_HORIZONTAL, RGB(0,0,0xff));

aszName[nBrushes++] = "Hatch HS_HORIZONTAL";

ahbr[nBrushes] = CreateHatchBrush(HS_VERTICAL, RGB(0,0,0xff));

aszName[nBrushes++] = "Hatch HS_VERTICAL";

hBitmap = CreateBitmap(8,8,1,1, acPattern1);

ahbr[nBrushes] = CreatePatternBrush(hBitmap);

aszName[nBrushes++] = "Pattern Brush 1";

DeleteObject(hBitmap);

hBitmap = CreateBitmap(8,8,1,1, acPattern2);

ahbr[nBrushes] = CreatePatternBrush(hBitmap);

aszName[nBrushes++] = "Pattern Brush 2";

DeleteObject(hBitmap);

hBitmap = LoadBitmap(hInst, (LPCTSTR)IDB_BITMAP1);

ahbr[nBrushes] = CreatePatternBrush(hBitmap);

aszName[nBrushes++] = "RC Pattern Brush 1";

DeleteObject(hBitmap);

hBitmap = LoadBitmap(hInst, (LPCTSTR)IDB_BITMAP2);

ahbr[nBrushes] = CreatePatternBrush(hBitmap);

aszName[nBrushes++] = "RC Pattern Brush 2";

DeleteObject(hBitmap);

break;

case WM_COMMAND:

wmId = LOWORD(wParam);

wmEvent = HIWORD(wParam);

// Parse the menu selections:

switch (wmId) {

case IDM_ABOUT:

DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);

break;

case IDM_EXIT:

DestroyWindow(hWnd);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

}

(61)

Rectangle(hdc, 180, i*35+10, 600, i*35+39);

}

EndPaint(hWnd, &ps);

break;

case WM_DESTROY:

for (i = 0; i < nBrushes; i++) DeleteObject(ahbr[i]);

PostQuitMessage(0);

break;

default:

return DefWindowProc(hWnd, message, wParam, lParam);

}

return 0;

}

(62)

4.6 Miền và thuộc tính bút vẽ PS_INSIDEFRAME

Bút vẽ có độ rộng và thuộc tính PS_INSIDEFRAME khi kết hợp với vẽ miền cho biết bút vẽ sẽđược tô đậm vào phần trong của miền. Hình vẽ minh hoạ bút vẽ có thuộc tính trên. Đoạn chương trình tương ứng như sau:

case WM_PAINT:

hdc = BeginPaint(hWnd, &ps);

GetClientRect(hWnd, &rt);

hpen = CreatePen(PS_SOLID, 16, RGB(0,255,0));

SelectObject(hdc, hpen);

Ellipse(hdc, 0, 0, 4*rt.right/7, 4*rt.bottom/7);

DeleteObject(hpen);

hpen = CreatePen(PS_INSIDEFRAME, 16, RGB(0,255,0));

SelectObject(hdc, hpen);

Ellipse(hdc, 3*rt.right/7, 3*rt.bottom/7, rt.right, rt.bottom);

DeleteObject(hpen);

SelectObject(hdc, GetStockObject(HOLLOW_BRUSH));

hpen = CreatePen(PS_SOLID, 1, RGB(255,0,0));

SelectObject(hdc, hpen);

Ellipse(hdc, 0, 0, 4*rt.right/7, 4*rt.bottom/7);

Ellipse(hdc, 3*rt.right/7, 3*rt.bottom/7, rt.right, rt.bottom);

DeleteObject(hpen);

rt.left = 3*rt.right/7;

rt.top = 3*rt.bottom/7;

DrawText(hdc, szPrompt, strlen(szPrompt), &rt, DT_SINGLELINE | DT_CENTER | DT_VCENTER);

EndPaint(hWnd, &ps);

break;

}

(63)

Hình 4-10 – PS_INSIDEFRAME style

4.7 Con đường (Paths)

Con đường là tập hợp các đường thẳng và cung được GDI lưu trữ nội bộ. Con đường có thể được xem là mở rộng của miền. Sau khi tạo con đường có thể vẽ đường viền và tô màu.

4.7.1 BeginPath và EndPath

Để định nghĩa một con đường ta theo trình tự: Mở đường, thực hiện các thao tác vẽ, đóng đường.

Hàm BeginPath dùng để mở đường. Sau khi gọi BeginPath, các thao tác vẽ đường không

được gởi ra ngữ cảnh thiết bị mà được lưu trữ nội bởi GDI. Khởi đầu con đường đang định

(64)

4.7.2 Các hàm thao tác con đường.

Các hàm thao tác trên con đường bao gồm StrokePath, FillPath và StrokeAndFillPath.

Tài liệu tham khảo

Tài liệu liên quan

+ Lôøi daãn giaùn tieáp: khoâng ñöôïc ñaët trong daáu ngoaëc keùp hay sau daáu gaïch ngang ñaàu doøng, nhöng tröôùc noù coù theå coù hoaëc coù theå theâm

Hoûi trong hai tuaàn ñoù, trung bình moãi ngaøy cöûa haøng baùn ñöôïc bao nhieâu meùt vaûi, bieát raèng cöûa haøng môû cöûa taát caû caùc ngaøy

Khoaûng ñoùng [a,b] hay môû (a,b) treân ñoù toàn taïi duy nhaát nghieäm cuûa phöông trình goïi laø khoaûng caùch ly nghieäm.. Ñònh

Khoaûng ñoùng [a,b] hay môû (a,b) treân ñoù toàn taïi duy nhaát nghieäm cuûa phöông trình goïi laø khoaûng caùch ly nghieäm.. Ñònh

Trong tuø khoâng röôïu cuõng khoâng hoa, Caûnh ñeïp hoâm nay khoù höõng hôø. Ngöôøi ngaém traêng soi ngoøai cöûa soå, Traêng nhoøm khe cöûa ngaém

Heä tuaàn hoaøn hôû ôû arthropods vaø mollusks, maùu ñöôïc bôm bôûi moät tim hình oáng vaø tröïc tieáp ñi ñeán caùc vuøng khaùc nhau cuûa cô theå thoâng qua

Gioù luøa keõ laù Laù kheõ ñu ñöa Gioù qua cöûa soå Beù vöøa nguû tröa..?. Chuù Boùi Caù nghó

c)Söï di chuyeån nhanh cuûa phöông tieän giao thoâng. d)Söï di chuyeån nhanh baèng chaân.. b)Söï vaän ñoäng nhanh. c)Di chuyeån baèng chaân. Doøng naøo döôùi ñaây