Шаг 53.
Библиотека OWL.
Более подробно о классе TDC. Рисование GDI-контуров

    На этом шаге мы рассмотрим рисование различных фигур.

    Как видно из текста предыдущей программы, GDI обладает возможностью изображения фигур различных типов, включая прямоугольники, сегменты (круговые диаграммы), овалы, линии, пунктиры и скругленные прямоугольники. В GDI также содержатся функции для отображения растровых изображений и пиктограмм. Ниже вы увидите, каким образом приведенная программа обрабатывает все типы фигур, список которых имеется в меню Shape.

Окрашивание пикселя

    Текущий объект в действительности рисуется с помощью DrawObject(). В этой функции внутри оператора switch используется режим рисования (Shape mode), задающий форму вычерчиваемой фигуры. Если режимом является mpixel, то данная функция рисует точку в месте с заданными координатами:

    case mPixel   : // Окрасить пиксель в данной точке. 
      clientDC->SetPixel (point, TColor::LtRed); break;

    Рассматриваемая функция делает это, вызывая функцию контекста устройства объекта SetPixel(), унаследованную из OWL-класса TDC. Функция SetPixel(), которая устанавливает пиксель в заданном месте и с заданным цветом, является перегружаемой функцией с двумя версиями. В первой версии, которая приведена в рассмотренном выше фрагменте текста программы, в качестве аргументов используются объект TPoint, содержащий координаты пикселя, и объект TColor, содержащий цвет пикселя. Во второй версии этой функции в качестве аргументов используются два целых числа, представляющих координаты х и у для точки, и объект TColor.

Рисование линии

    Если режим рисования установлен как режим mLine, то функция DrawObject() должна начертить линию:

	 case mLine    : // Провести линию к точке.
                clientDC->LineTo (point); break;

    Линия чертится от текущей стартовой точки до той точки, координаты которой заданы в единственном аргументе TPoint. Так как LineTo() является перегруженной функцией, вы также можете задать эту точку в виде двух целых чисел, представляющих собой координаты х и у. Текущей стартовой точкой является окончание последней вычерченной линии. Вы можете поменять эту стартовую точку в любой момент, вызвав перегруженную функцию контекста устройства MoveTo():

    MoveTo (int X, int Y);
    MoveTo (const TPoint  &point);
    MoveTo (const  TPoint &point, TPoint &oldPoint);

    Функция в первых двух версиях перемещает текущую стартовую точку в положение с заданными координатами. Функция в третьей версии не только задает стартовую позицию для точки (point), но также возвращает старую начальную позицию в положение oldPoint.

Рисование секторов

    Когда программа установлена в режим рисования mPie, функция DrawObject() должна вычертить "кусок пирога" (не тот, который едят, а который используется для построения круговых диаграмм):

    case mPie     : // Рисовать в данной точке круговую диаграмму. 
      clientDC->Pie (point.x, point.y, point.x+40, point.y+40, 
              point.x+40, point.y+20, point.x+20, point.y+40); break;

    Секторы в виде "кусков пирога" являются довольно сложными фигурами. Для функции Pie(), приведенной в показанном выше фрагменте текста, в качестве аргументов требуются четыре целых числа, представляющих координаты х и у ограничивающего прямоугольника, и четыре целых числа, задающих начальную точку и конечную точку сектора. Чтобы облегчить понимание, представим сначала, что имеется сектор в виде круга (или эллипса). Имея это в виду, можно без труда вообразить, что ограничивающий прямоугольник, который представлен четырьмя первыми аргументами функции Pie(), есть прямоугольник, в который вписан эллипс, как показано на рисунке 1.


Рис.1. Прямоугольник, ограничивающий эллипс

    Сектор ("кусок пирога") является некоторой частью этого эллипса, который рисуется против часовой стрелки, начиная от радиуса, пересекающего стартовую точку, до радиуса, пересекающего конечную точку, как показано на рисунке 2.


Рис.2. Координаты сектора в виде "куска пирога"

    Pie() является перегруженной функцией, которую вы можете вызвать или вместе с аргументами, которые даны в приведенном выше фрагменте текста программы, или со ссылкой на объект TRect для ограничивающего прямоугольника и ссылками на два объекта TPoint для стартовой и конечной точек соответственно:

    Pie   (const  TRect  &rect, const TPoint &start, const TPoint  &end);

Рисование эллипса

    При установке в режим mEllipse программа должна чертить эллипс:

    case mEllipse : // Рисовать в данной точке эллипс. 
      clientDC->Ellipse (point.x, point.y, point.x+40, point.y+30); break;

    Функция Ellipse() класса TDC является перегруженной функцией, позволяющей чертить эллипс, используя координаты различных типов, как показано в приведенном ниже фрагменте программы:

    Ellipse (int x1,   int y1,   int x2,   int  y2);
    Ellipse (const TPoint   &point1,   const TPoint &point2);
    Ellipse (const TPoint   &point,   const TSize &size);
    Ellipse (const TRect   &rect);

    В любом из этих случаев аргументы представляют координаты ограничивающего эллипс прямоугольника, показанного на рисунке 2. В примере предыдущей программы функция Ellipse() вызывается, когда программа переключается в режим рисования mEllipse:

    case mEllipse : // Рисовать в данной точке эллипс. 
      clientDC->Ellipse (point.x, point.y, point.x+40, point.y+30); break;

Рисование скругленного прямоугольника

    Скругленный прямоугольник, так же как и сектор, является сложным объектом. Программа чертит скругленные прямоугольники, когда установлен режим mRndRect:

    case mRndRect : // Рисовать в данной точке
                    // прямоугольник со скругленными углами. 
	clientDC->RoundRect (point.x, point.y, point.x+40, 
                  point.y+30, 20, 20); break;

    В функции RoundRect(), которая создает прямоугольник со скругленными углами, в качестве аргументов используются координаты ограничивающего прямоугольника и радиусы эллипса, которые определяют степень скругления углов. Эта перегруженная функция подразделяется на четыре разновидности:

    RoundRect (int x1, int y1, int x2, int y2, int x3, int y3); 
    RoundRect (const TPoint  &point1, const TPoint  &point2, TPoint  &rad); 
    RoundRect (const  TPoint &point, const  TSize &size, TPoint  &rad); 
    RoundRect (const TRect &rect, const TPoint &size);


    Замечание. При рисовании таких объектов, как прямоугольники, секторы и эллипсы, Windows для рисования контуров этих объектов использует текущее выбранное перо или кисть для заливки. Подбирая перья и кисти в контексте устройства, вы можете изменить стиль рисования этих объектов в Windows.

Заливка области цветом

    Если пользователь имеет дело с программой в режиме mFill и нажимает клавишу мыши в каком-либо месте внутри окна, то программа должна сначала отыскать цвет в этом выбранном месте, а затем залить все так же окрашенные окружающие пикселы цветом, соответствующим текущему цвету кисти:

    case mFill    :   //  Получить цвет в данной точке.
      color  = clientDC->GetPixel (point); 
                      // Закрасить область вокруг точки. 
		clientDC->ExtFloodFill (point, color, FLOODFILLSURFACE); break;

    Функция GetPixel() обеспечивает возврат объекта TColor, содержащего цвет в заданной позиции, которая может быть представлена либо как ссылка на объект TPoint, либо в виде двух целых чисел, представляющих координаты х и у этой позиции. После получения цвета программа вызывает функцию ExtFloodFill() для заливки всех пикселей заданным цветом, который соответствует выбранному для кисти. В этой функции в качестве аргументов используются точка, в которой должна начаться заливка, цвет границы или заливаемой области, и флаг, указывающий, как должен интерпретироваться второй аргумент. Если флагом является FLOODFILLSURFACE, то производится заливка всех окружающих пикселов с заданным цветом. Если флагом является FLOODFILLBORDER, то производится заливка всех пикселей любого цвета до тех пор, пока не будет достигнута граница, имеющая заданный цвет. Если вы используете флаг FLOODFILLBORDER, то такой же результат можно получить и от функции FloodFill(), имеющей следующий вид:

    FloodFill(const TPoint &point, TColor  color);

Рисование пиктограммы

    Если программа находится в режиме mIcon, она должна рисовать пиктограмму приложения в точке с выбранными координатами:

    case mIcon    :   //  Рисовать  пиктограмму в данной точке.
      clientDC->DrawIcon(point, *GDIIcon); break;

    В функции DrawIcon() в качестве аргументов используются координаты пиктограммы (которые могут быть представлены в виде ссылки на объект TPoint или в виде двух целых чисел, представляющих координаты х и у и ссылки на объект TIcon.

Рисование растрового изображения

    Наконец, когда поступает запрос нарисовать растровое изображение, выполняется наиболее сложный случай оператора switch:

    case mBitmap  :  // Прочитать контекст устройства 
                     // для памяти, основанный на
                     // контексте окна пользователя. 
      TMemoryDC memDC (*clientDC); 
                     // Выбрать растровое изображение 
                     // в контекст устройства памяти. 
      memDC.SelectObject (*bitmapl); 
                     // Получить атрибуты растрового изображения. 
      bitmapl->GetObject (bm); 
                     // Отобразить растровое 
                     // изображение в данной точке. 
      clientDC->BitBlt(point.x, point.y, bm.bmWidth, 
                bm.bmHeight, memDC, 0,0, SRCCOPY);

    Программа сначала вызывает конструктор TMemoryDC для конструирования совместного контекста памяти на основе существующего контекста окна пользователя. Затем программа вызывает функцию объекта контекста памяти SelectObject() для выбора растрового изображения в контекст устройства памяти и функцию объекта растрового изображения GetObject() для заполнения структуры BITMAP bm. Затем информация, хранящаяся в bm, совместно с другими требуемыми аргументами, используется при обращении к функции BitBlt(), которая рисует растровое изображение (или, на самом деле, копирует его из памяти) на экране.


    Замечание. Хотя вызов функции SelectObject() для заполнения структуры BITMAP является стандартным способом получения информации о растровом изображении, однако OWL-класс TBitmap содержит такие функции как Width() и Height(), которые обеспечивают быстрое получение информации о растровом изображении со структурой BITMAP.

    В приведенном выше примере можно заменить обращение к bitmapl->Width() и bitmap2->Height() на bm.bmWidth и bm.bmHeight в обращении к функции BitBlt():

      clientDC->BitBlt(point.x, point.y, bm.bmWidth, 
                bm.bmHeight, memDC, 0,0, SRCCOPY);

    Аргументами функции BitBlt() являются значения координат х и у прямоугольника, ширины и высоты итогового прямоугольника, ссылка на исходный контекст устройства (DC), координаты вершины правого угла исходного прямоугольника и флаг, указывающий на растровую операцию, которая используется при объединении исходного прямоугольника с итоговым прямоугольником.


    Замечание. Растровые операции копируют области памяти из одного места в другое, обычно из ОЗУ на экран. Имеется множество способов, с помощью которых могут быть объединены прямоугольник-источник и прямоугольник-адресат; в Windows описан целый ряд констант для наиболее широкоупотребимых операций, включая SRCCPY, которая обеспечивает копирование источника в адресат, BLACKNESS, которая окрашивает объект-адресат в черный цвет, DSTINVERT, которая инвертирует объект-адресат, SRCINVERT, которая устанавливает исключающее ИЛИ (XOR) между источником и адресатом, и SRCAND, устанавливающая логическое И (AND) между источником и адресатом.

    На следующем шаге мы рассмотрим переключатели доступности команд.




Предыдущий шаг Содержание Следующий шаг