Задание:
При выполнении работы необходимо сделать следующее.
- Изучить основы программирования для Windows на WinAPI. (книга Петцольда «Программирование для Windows 95», глава 2).
- Изучить основы программирования двумерной графики с помощью библиотеки GDI. (книга Петцольда «Программирование для Windows 95», глава 4, нелишне изучить 3 и 5).
- Ознакомиться с заготовкой программы – файл gdia.cpp.
- В заготовке программы приведен пример простого построения горизонтальных линий.
- Обратите внимание на функцию OnGDILines. В ней показан вывод прямой линии с помощью функций GDI. Измените функцию (включая, возможно, название), чтобы она рисовала графические примитивы вашего варианта. При этом используйте библиотечные функции GDI рисования линий, окружностей, эллипсов и т. д.
- Обратите внимание на функцию OnMyGDILines. В ней показан вывод прямой линии по точкам. Измените функцию, реализовав в ней заданный вам алгоритм (Брезенхема или средней точки) для рисования необходимых примитивов. Для вывода точки на экран будет использоваться функция SetPixel. Вывод через SetPixel – очень неэффективный способ вывода на экран. Поэтому ваша реализация алгоритма в этой функции будет самой медленной.
- Изучите основы программирования двумерной графики с помощью GDI+ (MSDN, книга Петцольда «Программирование для Windows на C#» — где-то в \\SERVER\For All\!Документация\!Книги).
- Обратите внимание на функцию OnGDIPlusLines. Она похожа на 1 OnGDILines, только для вывода линий используются функции GDI+. Измените функцию так, чтобы она рисовала заданные вам графические примитивы с помощью библиотечных функций GDI+.
- Обратите внимание на функцию OnGDIPlusBMPLines. В ней используется другой подход к рисованию на экране. Сначала в памяти создается битовый образ, затем рисование графических примитивов происходит не на экран, а в битовый образ, после чего полученный битовый образ выводится на экран. Этот подход более эффективен с точки зрения производительности, и при использовании GDI+ он очень легко реализуется. Измените функцию так, чтобы она рисовала заданные вам графические примитивы.
- Обратите внимание на функцию OnGDIBMPLines. В ней предыдущий подход (из пункта 9) реализован с помощью функций библиотеки GDI. Пожалуй, это самый эффективный по быстродействию способ. Измените функцию так, чтобы она рисовала заданные вам графические примитивы.
- Обратите внимание на функцию OnGDIPlusBMPOwnDrawLines. В ней тоже производится промежуточный вывод в битовый образ, только на сей раз не с помощью библиотечных функций рисования графических примитивов, а путем прямого доступа к массиву цветов пикселей битового образа. Измените функцию, чтобы в ней реализовывался заданный вам алгоритм, как и в пункте 6, только вместо вывода на экран с помощью SetPixel, вывод будет производиться в битовый образ путем прямого доступа к цветам пикселей. Постарайтесь сделать свою реализацию как можно более эффективной, чтобы, по возможности, она давала лучшее быстродействие, чем функция из предыдущего пункта.
- 12.Во всех функциях должны строиться графические примитивы со случайными координатами и случайными цветами.
- Сравните реализованные методы по быстродействию. Сделайте выводы.
Контекст устройства
Вспомните, описатель — это просто число, которое Windows использует для внутренней ссылки на объект. Вы получаете описатель от Windows и затем используете этот описатель в разных функциях. Описатель контекста устройства — это паспорт вашего окна для функций GDI. Этот описатель дает вам полную свободу при рисовании в рабочей области вашего окна, и вы можете сделать ее такой, как пожелаете.
Контекст устройства фактически является структурой данных, которая внутренне поддерживается GDI. Контекст устройства связан с конкретным устройством вывода информации, таким как принтер, плоттер или дисплей. Что касается дисплея, то в данном случае контекст устройства обычно связан с конкретным окном на экране.
Некоторые значения в контексте устройства являются графическими «атрибутами» (attributes). Эти атрибуты определяют некоторые особенности работы функций рисования GDI. Например, для функции TextOut эти атрибуты контекста устройства задают цвет текста, цвет фона для текста, процедуру преобразования координат x и y, передаваемых функции TextOut в координаты рабочей области, а также шрифт, используемый для вывода текста.
Когда программе необходимо начать рисование, она должна получить описатель контекста устройства. После окончания рисования программа должна освободить описатель. Когда программа освободит описатель, он становится недействительным и не должен далее использоваться. Во время обработки каждого отдельного сообщения программа должна получить и освободить описатель. За исключением описателя контекста устройства, созданного функцией CreateDC (эта функция не будет рассматриваться в данной главе), вам не следует хранить описатель контекста устройства в промежутке между обработкой различных сообщений.
В приложениях для Windows при подготовке процесса рисования на экране, обычно используются два метода получения описателя контекста устройства.
СМЫСЛ ФУНКЦИИ SelectObject
Смысл функции SelectObject в том, что она выбирает соответствующий объект (PEN, например) и в дальнейшем графические функции будут рисовать с использованием этого объекта
ОТЛИЧИЕ ПОДХОДА В GDI+ ОТ GDI
Отличие в том, что в GDI+ в функциях не нужно передавать описатель контекста устройства. Также в GDI+ функции организованы более понятно для человека.
ДЕЙСТВИЯ, НЕОБХОДИМЫЕ ДЛЯ ИСПОЛЬЗОВАНИЯ GDI+
Для использования GDI+ в программе необходимо подключить заголовочный файл “GDIPLUS.H” и в настройках прописать библиотеку “GDIPLUS.LIB”.
ОТЛИЧИЯ НЕПОСРЕДСТВЕННОГО ВЫВОДА НА ЭКРАН ОТ ИСПОЛЬЗОВАНИЯ В КАЧЕСТВЕ БУФЕРА БИТОВОГО ОБРАЗА
При использовании битового образа в качестве буфера цвета непосредственно заносятся на своё место в буфере. Прорисовка на экран осуществляется только после заполнения всего буфера, что ускоряет процесс.
РЕАЛИЗАЦИЯ АЛГОРИТМА ПОСТРОЕНИЯ СЕГМЕНТА
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
VOID _fastcall MySegment(HDC hdc, int xc, int yc, int r,float a1, float a2,COLORREF color) { int register x=0; int register y=r; int register F=1-r; int register dFs=3; int register dFd=5-2*r; static float p1=0.78539816339744830961566084581988f, p2=1.5707963267948966192313216916398f, p3=2.3561944901923449288469825374596f, p4=3.1415926535897932384626433832795f, p5=3.9269908169872415480783042290994f, p6=4.7123889803846898576939650749193f, p7=5.4977871437821381673096259207391f, p8=6.283185307179586476925286766559f; int xs,xe,ys,ye; if (((a1>p1)&&(a1<p3)) || ((a1>p5)&&(a1<p7))) { ys=-round(cos(a1)*r); xs=-round(sqrt(r*r-ys*ys)); if (a1>p4) xs=-xs; } else { xs=round(sin(a1)*r); ys=-round(sqrt(r*r-xs*xs)); if ((a1>p2)&&(a1<p6)) ys=-ys; } if (((a2>p1)&&(a2<p3)) || ((a2>p5)&&(a2<p7))) { ye=-round(cos(a2)*r); xe=-round(sqrt(r*r-ye*ye)); if (a2>p4) xe=-xe; } else { xe=round(sin(a2)*r); ye=-round(sqrt(r*r-xe*xe)); if ((a2>p2)&&(a2<p6)) ye=-ye; } //MoveToEx(hdc,xc,yc,NULL); //LineTo(hdc,xc+xs,yc-round(cosf(a1)*r)); //MoveToEx(hdc,xc,yc,NULL); //LineTo(hdc,xc+xe,yc-round(cosf(a2)*r)); LineBres(hdc,xs+xc,ys+yc,xc,yc,color); LineBres(hdc,xe+xc,ye+yc,xc,yc,color); xs=abs(xs); ys=abs(ys); xe=abs(xe); ye=abs(ye); BOOL *ss,*es; BOOL s[9]={TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE}; float fa1,fa2; if (a1<a2) { fa1=a1; fa2=a2; } else { fa1=a2; fa2=a1; } int n=0; n=int (fa1/p1)+1; for (int m=1; m<n;m++) s[m]=FALSE; s[n]=!(n%2); ss=&(s[n]); n=int(fa2/p1)+1; for (m=8; m>n;m--) s[m]=FALSE; s[n]=(n%2); es=&(s[n]); if (a1>a2) for (n=1; n<=8; n++) s[n]=!(s[n]); while (x<=y) { if ( ((x==xs)&&(y==ys))||((x==ys)&&(y==xs)) ) { if (*ss) (*ss)=FALSE; else (*ss)=TRUE; } if ( ((x==xe)&&(y==ye))||((x==ye)&&(y==xe)) ) { if (*es) (*es)=FALSE; else (*es)=TRUE; } if (s[4]) SetPixel(hdc,xc+x,yc+y,color); if (s[5]) SetPixel(hdc,xc-x,yc+y,color); if (s[1]) SetPixel(hdc,xc+x,yc-y,color); if (s[8]) SetPixel(hdc,xc-x,yc-y,color); if (s[3]) SetPixel(hdc,xc+y,yc+x,color); if (s[6]) SetPixel(hdc,xc-y,yc+x,color); if (s[2]) SetPixel(hdc,xc+y,yc-x,color); if (s[7]) SetPixel(hdc,xc-y,yc-x,color); if (F>=0) { F+=dFd; x++; y--; dFs+=2; dFd+=4; } else { F+=dFs; x++; dFs+=2; dFd+=2; } } } |
Для ускорения некоторые переменные были описаны как register и сама функция как _fastcall.
СРАВНИТЕЛЬНАЯ ТАБЛИЦА РАЗЛИЧНЫХ РЕАЛИЗАЦИЙ ВЫВОДА ГРАФИЧЕСКИХ ПРИМИТИВОВ
Реализация | Сравнительное время (мс) |
GDI+ | 263,4400 |
GDI | 555,6300 |
Вывод с помощью функции GDI SetPixel и реализованным алгоритмом | 1156,0000 |
Прорисовка на BMP в GDI+ функцией GDI+ | 187,3400 |
Прорисовка на BMP с прямым доступом к пикселям в GDI+ реализованным алгоритмом | 100,310 |
Прорисовка на BMP в GDI функцией GDI | 140,4700 |
ВЫВОДЫ О БИБЛИОТЕКАХ GDI и GDI+
По скорости работы и понятности GDI+ заметно превосходит GDI.