API и FreeBasic. (Рисование примитивов с помощью GDI часть первая)
В прошлой статье рассмотрев контексты, я не написал ни одного примера. Да и какой пример я и мог написать, если еще не описал ни одной функции рисования :) Ничего , в этой и следующих статьях нагоним :) В данной статье будут рассмотрены функции для рисования таких примитивов как точки, линии и сопутствующие им. Замечу , что функции рисования используют логическую систему координат. Для наших примеров достаточно того, чтобы единицей измерения был один пиксель. То есть система отображения логических координат полностью соответствует физическим координатам. Для каких-то специфичных задач (например построение графиков), возможно будет удобнее будет прибегнуть к изменениям этих атрибутов. Так можно единицами измерения назначить миллиметры или дюймы, а оси координат сместить. Если вам интересно это, вы можете изучить функцию SetMapMode
Начнем с рисования точки. Для этого существует функция SetPixel:
SetPixel( _ hdc As HDC,_ ' контекст X As Integer,_ 'x-координата пиксела Y As Integer,_ 'y-координата пиксела crColor As COLORREF _ ' цвет пиксела (от &h0 до &hFFFFFF) ) As COLORREF
Тут по моему все просто, замечу лишь, что тип COLORREF соответствует типу
INTEGER. Вообще все цвета в GDI идут в перевернутом порядке BGR (BLUE,
GREEN, RED). Хотя во многих других графических подсистемах используется RGB
(RED, GREEN, BLUE).
Пример использования SetPixel:
#INCLUDE "windows.bi" Dim msg As MSG Dim As WNDCLASSEX wc Dim As String NameClass="MyClass" Dim As HINSTANCE Hinst=GetModuleHandle(0) Function wndproc(hwnd As HWND, msg As Uinteger,_ wparam As WPARAM, lparam As LPARAM) As Integer Static canvas As HWND Select Case msg Case WM_CREATE canvas = CreateWindowEx(0,"Static","",WS_VISIBLE Or WS_CHILD,10,10,300,220,hwnd,Cast(HMENU,1),0,0) SetTimer(hwnd,1,50,0) Case WM_PAINT Dim ps As PAINTSTRUCT BeginPaint(canvas,@ps) For i As Integer = 1 To 300 SetPixel(ps.hdc,i,Rnd*220,&hff0000) Next EndPaint(canvas,@ps) Case WM_TIMER Static As Integer i,j MoveWindow(hwnd,100,100,i+320,i+240,TRUE) If i>10 Then j = TRUE Elseif i=0 Then j = FALSE Endif If j = TRUE Then i-=1 Else i+=1 Endif Case WM_DESTROY KillTimer(hwnd,1) PostQuitMessage(0) End Select Return DefWindowProc(hwnd,msg,wparam,lparam) End Function With wc .cbSize=SizeOf(WNDCLASSEX) .style=CS_HREDRAW Or CS_VREDRAW .lpfnWndProc=@wndproc .hInstance=Hinst .hIcon=LoadIcon(0,IDI_QUESTION) .hCursor=LoadCursor(0,IDC_ARROW) .hbrBackground=Cast(HBRUSH,COLOR_WINDOW) .lpszClassName=StrPtr(NameClass) .hIconSm=.hIcon End With If RegisterClassEx(@wc)=0 Then Print "Register error, press any key" Sleep End Endif CreateWindowEx(0,NameClass,"Рисование точек",_ WS_VISIBLE Or WS_OVERLAPPEDWINDOW,100,100,320,240,0,0,Hinst,0) While GetMessage(@msg,0,0,0) TranslateMessage(@msg) DispatchMessage(@msg) Wend
В этом примере, при изменении координат окна, мы заставляем его
перерисовываться , вынуждая систему посылать сообщение WM_PAINT. А в сообщении
идет рандомное рисование точек в цикле.
У функции SetPixel есть обратная функция GetPixel, которая не устанавливает точку, а получает цвет пиксела по определенным координатам.
GetPixel( _ hdc As HDC,_ ' контекст X As Integer,_ 'x-координата получаемого пиксела Y As Integer,_ 'y-координата получаемого пиксела ) As COLORREF
Ниже пример этакой примитивной программы для получения цвета в любой
точке экрана. Значения выводятся в консоль.
#INCLUDE "windows.bi" Dim As HDC hdc Dim As Point pt Do hdc = GetDC(0) GetCursorPos(@pt) Print Hex(GetPixel(hdc,pt.x,pt.y)) ReleaseDC(0,hdc) Loop Until Multikey(1) ' Esc для выхода
А теперь перейдем к рисованию линий. Вообще вывод каких либо фигур
точками возможен, но из-за низкой производительности это не практикуется. В GDI
есть хорошо оптимизированные функции для этого, скорость работы которых довольно
прилична.
Однако прежде чем рассмотреть рисование линий, наверно стоит разобрать как установить цвет линий и ее толщину. Для таких изменений нужно создать перо и выбрать его в контекст. По умолчанию в контексте системой установлено перо с черным цветом и толщиной один пиксель. Как вы понимаете , рисовать графику таким скудным инструментом "не камильфо". Для создания пера можно конечно взять GetStockObject, но она может возвратить только перо белого, черного и бесцветного цвета, опять же с однопиксельной толщиной. Этот вариант то же не радует...
Очень хорошо , что для создания перьев в GDI есть специальные функции CreatePen и CreatePenIndirect. По сути они одинаковы. Их разница в том, что у одной настройки пера записываются в параметры функции, а у другой в поля структуры LOGPEN.
Декларация CreatePen:
CreatePen( _ fnPenStyle As Integer,_ ' стиль пера nWidth As Integer,_ ' толщина пера crColor As COLORREF _ ' цвет пера ) As HPEN
С толщиной и цветом пера должно быть все понятно. А стиль пера может быть:
- PS_SOLID - Сплошная линия
- PS_DASH - Штриховая линия
- PS_DOT - Пунктирная
- PS_DASHDOT - Штрих-пунктирная линия, одна точка на одну линию
- PS_DASHDOTDOT - Штрих-пунктирная линия, две точки на одну линию
- PS_NULL - Невидимая линия
- PS_INSIDEFRAME - Линия, предназначенная для обводки замкнутых фигур
Декларация CreatePenIndirect:
CreatePenIndirect( _ lplgpn As LOGPEN Ptr _ ' указатель на структуру LOGPEN ) As HPEN
Сама структура LOGPEN:
Type LOGPEN lopnStyle As Integer ' стиль пера lopnWidth As Integer ' толщина пера lopnColor As COLORREF ' цвет пера End Type
Есть еще функция ExtCreatePen для создания косметических или геометрических перьев. Все ее тонкости можете посмотреть ЗДЕСЬ. В примере ниже ее использование я покажу.
И так после того, как мы создали свое перо, его надо отправить в контекст перед использованием функции для рисования линий. Этим заведует функция SelectObject. На самом деле данная функция отправляет в контекст не только перья, но и кисти, изображения, шрифты, области регионов. Ее декларация выглядит так:
SelectObject( _ hdc As HDC ,_ ' контекст hgdiobj As HGDIOBJ ' хендл объекта кисти, пера и пр. ) As HGDIOBJ ' возвращает предыдущий объект
Когда объект (перо кисть и пр.) более не требуется, его следует освободить функцией DeleteObject . Ее единственный параметр - указатель на объект. Многими авторами книг рекомендуется перед удалением объектов, отправить в контекст объект, который создан системой по умолчанию. Его при первом изменении, возвращает функция SelectObject. Но на практике я почти никогда так не делал и это не вызывало никаких эксцессов.
Вот мы дошли до рисования самой линии. Этим заведует функция LineTo:
LineTo(_ hdc As HDC ,_ ' контекст nXEnd As Integer ,_ ' X - конечная координата nYEnd As Integer _ ' Y - конечная координата ) As BOOL
Как можно видеть, данная функция имеет конечные координаты линии, но нет
начальных. То есть она рисует линию из текущей позиции пера, которую можно
устанавливать\менять функцией MoveToEx:
MoveToEx(_ hdc As HDC,_ ' контекст X As Integer ,_ ' новая X позиция пера Y As Integer ,_ ' новая Y позиция пера lpPoint As LPPOINT _ ' указатель на структуру POINT, 'в которой будут старые координаты. 'Если не требуется, можно указать сюда 0 ) As BOOL
Кстати для получения текущей позиции пера, есть функция
GetCurrentPositionEx:
GetCurrentPositionEx(_ hdc As HDC ,_ ' контекст lpPoint As LPPOINT _ ' указатель на структуру POINT ' в которую будут возвращены координаты ) As BOOL
Вроде как теперь есть все необходимое для создания примера рисования
линий:
#INCLUDE "windows.bi" Dim msg As MSG Dim As WNDCLASSEX wc Dim As String NameClass="MyClass" Dim As HINSTANCE Hinst=GetModuleHandle(0) Sub DrawLines(hdc As HDC) Dim As HPEN hPenSolid = CreatePen(PS_SOLID,4,&hFF) Dim As HPEN hPenDashDot = CreatePen(PS_DASHDOT,1,&hFF0000) Dim As LOGBRUSH lBrush lBrush.lbStyle = BS_HATCHED lBrush.lbColor = &hff00 lBrush.lbHatch = HS_DIAGCROSS Dim As HPEN hPenGeometric = ExtCreatePen(_ PS_GEOMETRIC,20,_ @lBrush,0,0) For i As Integer = -2 To 2 SelectObject(hdc,hPenSolid) MoveToEx(hdc,50,10,0) LineTo(hdc,50+i*20,150) SelectObject(hdc,hPenDashDot) MoveToEx(hdc,150,150,0) LineTo(hdc,150+i*20,100) Next SelectObject(hdc,hPenGeometric) MoveToEx(hdc,250,20,0) LineTo(hdc,250,150) DeleteObject(hPenSolid) DeleteObject(hPenDashDot) DeleteObject(hPenGeometric) End Sub Function wndproc(hwnd As HWND, msg As Uinteger,_ wparam As WPARAM, lparam As LPARAM) As Integer Static canvas As HWND Select Case msg Case WM_CREATE canvas = CreateWindowEx(0,"Static","",WS_VISIBLE Or WS_CHILD,10,10,310,210,hwnd,Cast(HMENU,1),0,0) Case WM_PAINT Dim ps As PAINTSTRUCT BeginPaint(canvas,@ps) DrawLines(ps.hDC) EndPaint(canvas,@ps) Case WM_DESTROY PostQuitMessage(0) End Select Return DefWindowProc(hwnd,msg,wparam,lparam) End Function With wc .cbSize=SizeOf(WNDCLASSEX) .style=CS_HREDRAW Or CS_VREDRAW .lpfnWndProc=@wndproc .hInstance=Hinst .hIcon=LoadIcon(0,IDI_QUESTION) .hCursor=LoadCursor(0,IDC_ARROW) .hbrBackground=Cast(HBRUSH,COLOR_WINDOW) .lpszClassName=StrPtr(NameClass) .hIconSm=.hIcon End With If RegisterClassEx(@wc)=0 Then Print "Register error, press any key" Sleep End Endif CreateWindowEx(0,NameClass,"Рисование линий",_ WS_VISIBLE Or WS_OVERLAPPEDWINDOW,100,100,330,230,0,0,Hinst,0) While GetMessage(@msg,0,0,0) TranslateMessage(@msg) DispatchMessage(@msg) Wend
содержание | назад | вперед