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


api_pen.png

содержание | назад | вперед