Основные графические объекты и CDI

Графические объекты - это объекты, с помощью которых осуществляется вывод на экран изображений. Их использование в приложениях для создания графических изображений возможно на различных уровнях - в этом раздели будет рассмотрена работа с объектами с использованием функций GDI.

Графических объектов не так уж и много:

  • Точка. Изображение можно рисовать точками. Наиболее простой метод, так как для него достаточно вызова лиш одной функции SetPixel() (ровно как и для чтения GetPixel()). В принципе, точка не является графическим объектом, так как она не существует как объект и рассматривается здесь лишь как одна из возможностей получения изображения.

  • Перо. Изображение можно рисовать перьями (как корандашом на листе бумаги). Перо обладает толщиной, цветом и типом линии (сплошная, прерывистая, точечная и т. п.). Перед использованием перо создается как объект.

  • Кисть. Предопределенный системой или созданный программистом набор пикселов, который, как единое целое, может быть отображен на экране монитора.
        Кисти используются в функциях для заполнения внутренних областей замкнутых фигур и фонов окон (аналогично сплошной закраске). Перед использованием кисть создается как объект.

  • Растровые изображения. Набор байт, содержащий значения цветов и информацию о координатах для отображения на экране пикселов, в совокупности составляющих изображение. Это картинки, фоны, графические элементы (кнопки, меню, иконки) и т. п. Перед использованием растровые изображения могут быть созданы программно или использоваться как заранее созданные и хранящиеся в файлах, ресурсах и т.д.

  • Шрифты. Это либо набор пикселов (растровое изображение) для матричных шрифтов, либо набор кривых, описывающих контур отображаемых букв для векторных шрифтов (например шрифты True Type). Перед использованием шрифты могут быть созданы программно или используются заранее подготовленные и установленные в системе шрифты.

В начало

Получение изображения перерисовкой пикселей

Изменить один контекста устройства дает возможность вызов функции SetPixel():

WINGDIAPI COLORREF WINAPI SetPixel(HDC hdc,int x,int y,COLORREF color);

Первый аргумент хэндл контекста устройства (как и во всех последующих примерах - поэтому далее этот параметр не будет поясняться).

x и y - координаты прорисовываемого пикселя.

color - цвет пикселя, как совокупность трех цветов (red - красного, зеленого - green и синего - blue, или RGB -значение цвета ). Каждая из этих микроточек может иметь значение, соответствующее интенсивности свечения от 0 до 255 (максимальная яркость). Таким образом может быть определено почти 17 миллионов цветов. Число отображаемых цветов может быть меньше и зависит от видиокарты компьютера и установок Windows.

Можно задать цвет как 16 ричное число:

0xx00bbggrr

Макрос RGB, возвращающий цвет пикселя, определен как:

#define RGB(r, g, b) ((COLORREF) (((BYTE)(r) 
                   | ((WORD) ((BYTE)(g)) <8 )) 
                   | (((DWORD) (ВYTЕ) (P)) <16 )))

Пример использования функции SetPixel(). В примере точками рисуется зеленая линия длиной 100 пикселей.

 
HDC  hDc = GetDC(Handle);
for(int i=0; i < 100;i++)
 SetPixel(hDc,100,100,RGB(0,255,0)):
ReleaseDC(Handle,hDc);

В принципе для рисования линий более подходят функции работы с перьями - о чем речь пойдет ниже.

В начало

Перо и его использование для рисования графичиских примитивов

Перо перед использованием создается с помощью функции CreatePen. Эта функция создает логическое перо, которое задает указанный стиль, ширину, и цвет пера. Перо перед использованием выбирается в контекст устройства.

HPEN CreatePen 
(
 int       PenStyle, 	 // стиль пера
 int       nWidth, 	 // ширина  
 COLORREF  crColor 	 // цвет 
); 	

Стиль пера задают константы:

PS_SOLID       - сплошная линия.
PS_DASH        - штриховая линия. 
PS_DOT         - пунктирная линия. 
PS_DASHDOT     - штрих пунктирная линия.
PS_DASHDOTDOT  - чередующиеся черточки и двойные точки.
PS_NULL        - невидимое перо.
PS_INSIDEFRAME - перо для линий, выводимых внутри рамки закрытых форм 
(например, Ellipse, Rectangle, RoundRect, Pie, и Chord функции). 

Перо может быть создано также функцией CreatePenIndirect().

HPEN CreatePenIndirect 
(
 CONST LOGPEN *lplgpn  // указатель на структуру LOGPEN 
); 	

Функция SelectObject выбирает объукт (в данном случае перо) в указанный контекст устройства. Новый объект заменяет предыдущий объект того же самого типа и возвращает HDC заменяемого объекта.

HGDIOBJ SelectObject 
(
 HDC hdc,         // дескриптор контекста устройства 
 HGDIOBJ hgdiobj  // дескриптор объекта который вибирается  
); 	

Для рисования перьями используются функции:

  • bool MoveToEx(HDC hdc,int x1,int y1,LPPOINT lpPoint). Перемещает точку начала рисования линии в указанные координаты. LPPOINT lpPoint - адрес старой текущей позиции

  • bool LineTo(int x2,int y2). Рисует линию начиная с текущей позиции, заданной функцией MoveTo до указанных координат.

  • bool Rectangle(HDC hdc,int x1,int y1,int x2,int y2). Рисует прямоугольник, размер которого определяется координатами верхнего (x1,y1) и нижнего (x2,y2) угла. Используется текущее перо, а для заполнения текущая кисть.

  • bool Ellipse(HDC hdc,int x1,int y1,int x2,int y2).Рисует элипс, вписанный в прямоугольник, размер которого определяется координатами верхнего (x1,y1) и нижнего (x2,y2) угла. элипс заполнен белым цветом и обведен линией пера контекста устройства. Используется текущее перо, а для заполнения текущая кисть.

  • bool RoundRect(HDC hdc,int x1,int y1,int x2,int y2,int x3,int y3). Рисует прямоугольник с закругленными краями, размер которого определяется координатами верхнего (x1,y1), нижнего (x2,y2) угла и координатами скругления (x3,y3). Используя текущее перо, а для заполнения текущая кисть.

  • bool Arc(HDC hdc,int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4); Рисует элиптическую дугу, логически ограниченную прямоугольником, размер которого определяется координатами верхнего (x1,y1) и нижнего (x2,y2) угла. Непосредственно дуга определяется дополнительными двумя точками (x3,y3,x4,y4). Первая - начало дуги - находится на пересечении эллипса, частью которого является дуга, и прямой, проходящей через центр прямоугольника и точку начала дуги. Вторая - конец дуги - определяется аналогично. Дуга прорисовывается против часовой стрелки. Огpаничивающий пpямоугольник должен быть не длиннее и не шиpе 32767.

  • bool ArcTo(HDC hdc,int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4). Полностью аналогична функции ArcTo, за исключением того, что запоминается как текущая позиция пера последняя точка дуги.

  • bool Pie(HDC hdc,int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4); Рисует сектор элипса, логически вписываемый в прямоугольник, размер которого определяется координатами верхнего (x1,y1) и нижнего (x2,y2) угла, а координаты начальной и конечной точек (x3,y3,x4,y4), аналогично функции Arc(). Огpаничивающий пpямоугольник должен быть не длиннее и не шиpе 32767. Используется текущее перо, а для заполнения текущая кисть.

  • bool Chord(HDC hdc,int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4). Рисует сегмент элипса (область, ограниченную пересечением эллипса и линии), логически вписываемый в прямоугольник, размер которого определяется координатами верхнего (x1,y1) и нижнего (x2,y2) угла, а координаты ограничительной линии (x3,y3,x4,y4). Используется текущее перо, а для заполнения текущая кисть.

  • bool Polyline(HDC hdc,CONST POINT *lppt,int cPoints); Функция рисует ломаную линию по массиву точек, на который указывает lppt и число точек из этого массива равно cPoints.

    Отрезки прямых рисуются текущим пером. Фигуры, образованные сегментами, не закрашиваются.

  • bool Polygon(HDC hdc,CONST POINT *lppt,int cPoints); Функция рисует многоугольник, состоящий из двух или больше вершины, связанных прямыми линиями по массиву точек, на который указывает lppt и число точек из этого массива равно cPoints.. Используется текущее перо и заполнение текущей кистью.

Эти функции работают только в Windows NT:

  • bool AngleArc(HDC hdc,int x1,int y1,int r,float fStartAngle, float fSweepAngle); Рисует линию сегмента и дугу с центром радиуса дуги в точке x1,y1 и радиусом r (радиус круга в логических модулях, всегда положителен). fStartAngle - стартовый угол в градусах относительно оси X, fSweepAngle - определяет конечный угол в градусах относительно стартового угла. Фигура не заполнена.

  • bool PolyPolyline(HDC hdc,CONST POINT *lppt, CONST DWORD *lpdwPolyPoints,DWORD cCount); lppt - указатель на массив структур типа POINT. Каждая структура в массиве идентифицирует точку в логическом пространстве. lpdwPolyPoints - указывает на массив переменных, определяющих число точек в массиве lppt для соответствующей полилинии. Значение каждого элемента должно быть больше или равно двум. cCount - определяет количество элементов в массиве lpdwPolyPo. Отрезки прямых рисуются текущим пером. Фигуры, образованные сегментами, не закрашиваются.

  • bool PolyPolylineTo(HDC hdc,CONST POINT *lppt, CONST DWORD *lpdwPolyPoints,DWORD cCount); Полностью аналогична функции PolyPolyline, за исключением того, что запоминается как текущая позиция пера последняя точка линии.

  • bool PolyPolygon(HDC hdc,CONST POINT *lppt, CONST DWORD *lpdwPolyPoints,DWORD cCount); Параметры аналогичны параметрам функции PolyPolyline. Функция рисует ряд замкнутых многоугольников. Каждый многоугольник рисуются текущим пером и закрашен текущей кистью. Многоугольники могут накладываться друг на друга.

  • bool PolyBlezier(HDC hdc,CONST POINT *lppt,DWORD cPoints); Выводит одну или большее Bezier сплайнов (кривых Блейзера). Эти кривые задаются началом, концом линии и промежуточными точками, определяющие изгиб. В массиве точек первая и четвертая используются как конечные, вторая и третья как промежуточные. Для следущей линии необходимо еще три точки - четвертая точка первой линии является начальной для второй. Рисунок не заполнен. Эта функция выводит линии, используя текущее перо

  • bool PolyBlezierTo(HDC hdc,CONST POINT *lppt,DWORD cPoints); Полностью аналогична функции PolyBezier, за исключением того, что запоминается как текущая позиция пера последняя точка линии.

  • bool PolyDraw(const POINT* lpPoints,const BYTE* lpTypes,int nCount); Функция составляет множество сегментов строки и Кривых Безье. lpPoints - указатель на массив структур данных POINT, который содержит оконечные точки для каждого сегмента линии и контрольных точек для каждого Bezier сплайна.lpTypes - указатель на массив, который определяет, как каждая точка в lpPoints массиве используется. Значения могут быть одним из следующего:

    • PT_MOVETO - Определяет, что эта точка начинает новый рисунок. Эта точка становится новой текущей позицией.

    • PT_LINETO - Определяет, что линия должна быть выведена от текущей позиции до этой точки, которая затем становится новой текущей позицией.

    • PT_BEZIERTO - Определяет, что эта точка - контрольная точка или последняя точка для Bezier сплайна.

    • PT_BEZIERTO - точек всегда должно быть три. Текущая позиция определяет отправную точку для Bezier сплайна. Первые две PT_BEZIERTO точки - контрольные точки, а третья PT_BEZIERTO точка - точка окончания. Точка окончания становится новой текущей позицией. Если не имеется трех последовательных точек PT_BEZIERTO, результат ошибка. PT_LINETO или тип PT_BEZIERTO может быть объединен с другими константами, используя поразрядный оператор OR указывая, что соответствующая точка - последняя точка в рисунке, и рисунок закрыт.

    • PT_CLOSEFIGURE - Определяет, что рисунок автоматически закрыт после того, как PT_LINETO или тип PT_BEZIERTO для этой точки выполнен. Линия выведена от этой точки до самой последней точки MoveTo или PT_MOVETO. Этот флажок объединен с типом PT_LINETO для строки, или с типом PT_BEZIERTO для Bezier сплайна, используя поразрядный OR оператора. Текущая позиция установлена к отметке окончания заключительной строки.

  • nCount - Определяет общее число точек в lpPoints массиве и число байтов в lpTypes массиве.

    Функция модифицирует текущую позицию. Нарисованные замкнутые фигуры не заполняются.

Следующие примеры показывают использование некоторых описанных функций.

void __fastcall 
TForm1::Button1Click(TObject *Sender)
{
 HPEN       hPen,hPenOld;
 //Получаем контекст окна приложения
 HDC        hDc = GetDC(Handle);
 //Создаем перо сплошное, толщиной 2,красное
 hPen=CreatePen(PS_SOLID,2,RGB(255,0,0));
 //Выбираем перо в контекст устройства и запоминаем старое
 hPenOld = SelectObject(hDc,hPen);
 //Рисуем линию
 MoveToEx(hDc,0,0,NULL);
 LineTo(hDc,Width,Height);
 //Рисуем  прямоугольник
 Rectangle(hDc,50,50,100,100);
 //Рисуем  прямоугольник со скругленными углами
 RoundRect(hDc,150,150,400,400,50,50);
 //Рисуем элипс
 Ellipse(hDc,100,100,300,300);
 //Рисуем  сектор элипса
 Pie(hDc,0,0,200,200,0,0,0,100);
 //Рисуем дугу
 Arc(hDc,0,0,250,250,125,0,50,0);
 //Рисуем  сегмент, замкнутый дугой 
 AngleArc(hDc,100,150,50,0,180);
 //Рисуем  сегмент элипса
 Chord(hDc,0,0,200,200,0,0,10,200);
 //Рисуем ломаную линию
 TPoint      tPoint[3];
 tPoint[0] = Point(100,500);
 tPoint[1] = Point(150,400);
 tPoint[2] = Point(100,300);
 Polyline(hDc,tPoint,3);
 //Рисуем  кривые Блейзера
 TPoint tPoints[7];
 tPoints[0]=TPoint(0,0);
 tPoints[1]=TPoint(800,30);
 tPoints[2]=TPoint(0,40);
 tPoints[3]=TPoint(550,400);
 tPoints[4]=TPoint(350,200);
 tPoints[5]=TPoint(550,400);
 tPoints[6]=TPoint(0,500);
 PolyBezier(hDc,tPoints,6);
 //Возвращаем все на свои места
 SelectObject(hDc,hPenOld);
 DeleteObject(hPen);
 DeleteObject(hPenOld);
 ReleaseDC(Handle,hDc);
 //Или
 DeleteDC(hDc);
}

Для того, чтобы рисовать не в форме приложения,а на экране дисплея, достаточно в предыдущем примере получить контекст экрана монитора.

HDC   hDc=CreateDC("DISPLAY",NULL,NULL,NULL);

Несмотря на то, что многие функции работают только в Windows NT, но в BorlandC++ Builder доступ к ним имеется через свойство Canvas компонентов.

В начало

Кисти, их создание и применение

Кисти используются в Windows в основном для заливки внутренних областей.

Чтобы использовать кисть ее необходимо создать с помощью функций CreateSolidBrush, CreateDIBPatternBrush, CreateHatchBrush, CreatePatternBrush... и затем, как и для пера, выбрать их в контекст отображения, используя функцию SelectObject. После применения средств рисования их можно удалить с помощью функции DeleteObject.

Основные функции для создания кисти, следующие:

Функция CreateSolidBrush создает логическую кисть, которая имеет указанный цвет.

HBRUSH CreateSolidBrush 
(
 COLORREF crColor // цвет кисти  
); 	

Функция CreateHatchBrush создает логическую кисть, которая имеет указанный образец штриховки и цвет.

HBRUSH CreateHatchBrush 
(
 Int fnStyle,    // стиль штриховки 
 COLORREF clrref // цвет  
); 	

FnStyle определяет стиль штриховки кисти. Этот параметр может иметь одно из следующих значений:

HS_BDIAGONAL 	 штриховка 45 градусов слева направо;
HS_CROSS 	 горизонтальная и вертикальная штриховка(квадратиками);
HS_DIAGCROSS 	 Штриховка сеточкой 45 градусов;
HS_FDIAGONAL 	 С 45 градусами справа на лево;
HS_HORIZONTAL 	 горизонтальная штриховка;
HS_VERTICAL 	 вертикальная штриховка.

Функция CreatePatternBrush создает логическую кисть с указанным растровым изображением.

HBRUSH CreatePatternBrush
(
 HBITMAP hbmp // Хэндл изображения 
);	

В Win9x кисти, созданные как точечные рисунки в формате растрового изображения больше чем 8x8 пикселы не поддерживаются. Если точечный рисунок больше, то используется только его часть.

Цвет однобитных кистей при значении бита 1 соответствует цвету текста и фона контекста устройства, 0 текущему цвету контекста.

Исполльзования кистей

Мы уже использовали кисти при рассмотрении фигур, созданных перьями. Это все функции, которые предназначены для рисования предопределенно замкнутых фигур ( Rectangle(), Ellipse(), RoundRect(), Pie(), Chord(), PolyGon(), PolyPolyGon()). Здесь используются совместно кисти и перья и если их параметры не переопределить, то используются атрибуты по умолчанию как для пера так и для кисти.

Атрибуты контекста графических устройств

Атрибут Значение по умолчанию Назначение
Цвет фона Белый Цвет заполнения фона
Прозрачность фона OPAQUE Если TRANSPARENT, то через фон видна подложка окна, если OPAQUE - то подложки окна не видно
Логический номер кисти Белая кисть Заливка замкнутых фигур
Начало координат кисти 0,0 Смещение верхнего левого угла кисти от начала координат окна
Текущая позиция пера
0,0 Точка, в которой находится перо перед перемешением и после перемещения для функций, не изменяющих текущую позицию
Режим графического вывода R2_COPYPEN Логическая операция по смешиванию цветов пера и фона
Логический номер шрифта System Текущий шрифт, устаноыленный как системный для Windows
Тип геометрических координат MM_TEXT Задает единицы измерения координат и направление осей отсчета
Логический номер пера Черное перо Текущее перо для рисования
Интервал между символами 0 Дополнительный интервал между символами
Режим закрашивания многоугольников Альтернативный Задает правила закрашивания многоугольников, нарисованных функцией Polygon()
Режим растяжения Черный по белому Задает режим отображения при использовании функции StretchBlt()

Примеры работы с кистями

Использование одноцветной и штриховых кистей:

void __fastcall 
TForm1::Button1Click(TObject *Sender)
{
 HPEN       hPen,hPenOld;
 HBRUSH     hBrush,hBrushOld;
 //Если будем ртсовать в приложении
 HDC        hDc = GetDC(Handle);
 //Если рисуем на экране монитора
 HDC        hDc=CreateDC("DISPLAY",NULL,NULL,NULL);
 //Если использовать одноцветную кисть
 hBrush=CreateSolidBrush(RGB(0,0,255));
 //Если использовать штриховую кисть кисть
 hBrush=CreateHatchBrush(HS_HORIZONTAL,RGB(0,0,255));
 //Без этой функции штриховка будет по белому квадрату
 //с этой функцией под штриховкой то, что было на экране
 //монитора или в окне приложения
 SetBkMode(hDc,TRANSPARENT);
 //Как и в примерах выше создаем перо для контура
 hPen=CreatePen(PS_SOLID,2,RGB(255,0,0));
 //Выбираем созданные объекты в контекст устройства
 hPenOld = SelectObject(hDc,hPen);
 hBrushOld=SelectObject(hDc,hBrush);
 //Рисуем прямоугольник
 Rectangle(hDc,0,0,200,200);
 //Освобождаем ресурсы
 SelectObject(hDc,hPenOld);
 DeleteObject(hPen);
 DeleteObject(hPenOld);
 DeleteObject(hBrush);
 DeleteObject(hBrushOld);
 ReleaseDC(Handle,hDc);
 DeleteDC(hDc);
}

Использование кисти с созданным растровым изображением:

void __fastcall
TForm1::Button2Click(TObject *Sender)
{
 HDC    hDc;
 HBRUSH hBrush,hBrushOld;
 //  Массив для пикеселей изображения  10*10 пикселей. При 24 
 //цветах на каждый пиксель требуется 3 байта или 1.5 unsigned 
 //short числа, для 10*10 пикселей требуется 15*15=225 чисел. 
 //  Создание кистей из битовых образов размером более 8*8
 //пикселей в Win 9x не поддерживается. Если указан битовый
 //образ большего размера, используется его часть.
 unsigned short usrgMassPix[225];
 //Зная как в памяти располагаются биты, например для 24 цветовой
 //палитры синий цвет ff0000, можем задать его как цвет кисти
 if(GetDeviceCaps(CreateDC("DISPLAY",NULL,NULL,NULL),BITSPIXEL) == 24)
 {
  for(int i=0; i < 75; i++)
  {
   usrgMassPix[i*3]=255;
   usrgMassPix[i*3+1]=0;
   usrgMassPix[i*3+2]=0;
  }
 }
 else //Здесь для других значений или если не знаем как
  for(int i=0; i < 225; i++) usrgMassPix[i] =random(255);
 //Создаем  изображение кисти в памяти 10*10 пикселов  и получаем его хэндл
 //Функция CreateBitmap будет описана позже
 HBITMAP
  hBmp = 
   CreateBitmap(10,10,GetDeviceCaps(CreateDC("DISPLAY",
                                       NULL,NULL,NULL),PLANES),
     GetDeviceCaps(CreateDC("DISPLAY",NULL,NULL,NULL),BITSPIXEL),
      &usrgMassPix[0]);
 //Получаем хэндл устройства
 hDc=GetDC(Handle);
 //Создаем кисть
 hBrush = CreatePatternBrush(hBmp);
 //Выбираем кисть в контекст устройства
 hBrushOld=SelectObject(hDc,hBrush);
 //Здесь можно было выбрать перо для обводки фигуры как и 
 //в примере выше, но решили воспользоваться пером по умолчанию
 //Рисуем кистью, например элипс
 Ellipse(hDc,200,200,600,400);
 //Возвращаем все в исходное состояние
 SelectObject(hDc,hBrushOld);
 DeleteObject(hBmp);
 DeleteObject(hBrush);
 DeleteObject(hBrushOld);
 ReleaseDC(Handle,hDc);
 DeleteDC(hDc);
}

В данном примере использованы не только перо и кисть, но и объект DC - растровое изображение, о котором речь пойдет в следующем параграфе.

Продолжение. Растровые изображения

В начало главы

В начало раздела

Домой