47952 (597372), страница 8
Текст из файла (страница 8)
Ширина геометрической линии может быть любой, она задается параметром dwWidth в логических единицах. Параметр lpLogBrush описывает кисть, которая задает цвет и узор линии; кисть может быть однотонной, смешанной, штрихованной или даже созданной по образцу — зависимому от устройства битмапу. Использование независимых от устройства битмапов в качестве образцов при описании пера недопустимо. О кистях и битмапах см. разделы “Кисть” и “Растровые изображения”.
Как и в случае косметических перьев, параметры dwStyleCount и lpdwStyle определяют стиль штриховки, если задан стиль линии PS_USERSTYLE, иначе они не используются. Однако в отличие от косметического пера, ширина штрихов и промежутков задается в логических единицах.
После использования созданного пера вы должны его уничтожить, причем перед этим он должен быть освобожден из контекста устройства (или весь контекст должен быть освобожден). Перо, как и все объекты GDI, уничтожается с помощью функции:
BOOL DeleteObject (hGDIobj);
BOOL DeletePen (hPen);
Текущая позиция пера
Следующий атрибут контекста устройства, используемый при рисовании линии, называется текущей позицией пера. Этот атрибут используется очень небольшим числом функций, поэтому мы вкратце рассмотрим их всех, даже если они не связаны с рисованием линий.
С первыми тремя функциями мы уже познакомились — они–то и используются для рисования линий.
DWORD MoveTo (hDC, nX, nY); 0
BOOL MoveToEx (hDC, nX, nY, lpPoint);
BOOL LineTo (hDC, nX, nY);
Помимо них с текущей позицией пера имеют дело функции GetCurrentPosition и GetCurrentPositionEx а также, в некоторых случаях, функции TextOut и ExtTextOut.
DWORD GetCurrentPosition (hDC); 0
BOOL GetCurrentPositionEx (hDC, lpPoint);
Эти функции возвращают позицию пера. Как обычно — GetCurrentPosition возвращает ее в двойном слове и применятся только в 16ти разрядном Windows API, тогда как функция GetCurrentPositionEx возвращает результат в структуре типа POINT и может применяться как в Windows API, так и в Win32 API.
Две другие функции — TextOut и ExtTextOut обычно не используют текущую позицию пера, вы должны им отдельно указывать позицию для вывода текста. Однако, иногда может быть удобно перейти в специальный режим привязки текста TA_UPDATECP (см. функцию SetTextAlign), при котором текст будет выводиться от текущей позиции, а после вывода текста текущая позиция переместится в конец выведенного фрагмента.
Цвет фона и режим заполнения фона
Еще один используемый атрибут контекста устройства — цвет фона. Это не тот цвет, которым закрашивается фон окна, хотя он может и совпадать с ним. Фон окна закрашивается не текущим цветом фона, а кистью, назначенной окну при регистрации класса. Цвет фона и режим заполнения фона используются:
функциями для рисования линий — для заполнения промежутков между штрихами прерывистой линии
функциями вывода текста — для заполнения пространства под символами текста.
функциями, применяющими кисть — для заполнения фона между линиями штрихованных кистей.
Рисунок 5. Рисование прерывистых линий в разных режимах заполнения фона.
Для задания цвета фона и для выяснения текущего цвета вы можете воспользоваться функциями
COLORREF SetBkColor (hDC, crColor);
COLORREF GetBkColor (hDC);
Функции возвращают используемый ранее цвет фона. Аналогично перу, GDI будет использовать ближайший чистый цвет в качестве цвета фона.
В некоторых случаях бывает удобно проводить пунктирные и штрих–пунктирные линии так, что бы в промежутках был виден прежний фон (прежнее изображение), а не закрашивать их цветом фона. Для этого вы должны изменить режим заполнения фона. Для это предназначены функции:
int SetBkMode (hDC, nBkMode);
int GetBkMode (hDC);
GDI поддерживает два разных режима заполнения фона; один из них называется OPAQUE — это режим по умолчанию. В режиме OPAQUE промежутки заполняются текущим цветом фона, а в другом режиме, называемом TRANSPARENT, фон в промежутках не изменяется.
Режим рисования
Атрибут контекста устройства, называемый режим рисования, влияет на сам процесс рисования. Дело в том, что рисование на устройстве легко может быть представлено как операции над битовыми последовательностями, содержащими те или иные данные. Пока мы говорили о рисовании линии, подразумевая простейший перенос битового образа рисуемого объекта (линии) на другой битовый образ (уже имеющееся изображение).
Атрибут режим рисования задает правила, по которым GDI переносит информацию из одной битовой последовательности на другую. Так, помимо самого очевидного копирования возможны операции инверсии как уже имеющегося изображения, так и рисуемого, объединение с помощью различных битовых операций И (and), ИЛИ (or), ИСКЛЮЧАЮЩЕЕ ИЛИ (xor).
При рассмотрении процесса переноса изображения мы будем исходить из предположения монохромного устройства вывода, так как анализ процесса рисования на цветном устройстве выглядит громоздко. Говоря о монохромном устройстве мы будем обозначать светлую точку будем битом со значением 1, а для темную точку — 0.
При рисовании мы можем условно рассматривать три разные битовые последовательности:
исходное изображение, содержащее рисуемый объект, в документации называется pen (почему–то считается, что рисуется именно линия текущим карандашом — отсюда название — но, вообще говоря, режим рисования применяется для всех операций вывода графических примитивов — линий, эллипсов, многоугольников, текста и пр.);
контекст устройства, содержащий нарисованный ранее образ (хотя бы просто закрашенный фон), в документации называется destination;
результат — то изображение, которое будет находиться на контексте устройства после рисования;
Применительно к этим трем битовым последовательностям говорят о двоичной растровой операции (binary raster operation, ROP2), так как в формировании результата участвуют две исходных битовых последовательности.
Режим рисования задает операцию над битами обеих последовательностей, выполняемую в процессе переноса изображения. Всего возможно 16 различных режимов рисования:
| Перо (Pen) P | 1 1 0 0 | Выполняемая | Режим рисования |
| Имеющееся изображение (Destination) D | 1 0 1 0 | операция | |
| 0 0 0 0 | 0 | 0 R2_BLACK | |
| 0 0 0 1 | ~ (P|D) | 1 R2_NOTMERGEPEN | |
| 0 0 1 0 | (~P)&D | 2 R2_MASKNOTPEN | |
| 0 0 1 1 | ~P | 3 R2_NOTCOPYPEN | |
| 0 1 0 0 | P& (~D) | 4 R2_MASKPENNOT | |
| 0 1 0 1 | ~D | 5 R2_NOT | |
| Повторное рисование восстанавливает фон | 0 1 1 0 | P^D | 6 R2_XORPEN |
| 0 1 1 1 | ~ (P&D) | 7 R2_NOTMASKPEN | |
| 1 0 0 0 | P&D | 8 R2_MASKPEN | |
| 1 0 0 1 | ~ (P^D) | 9 R2_NOTXORPEN | |
| Прежнее изображение не меняется | 1 0 1 0 | D | 10 R2_NOP |
| 1 0 1 1 | (~P)|D | 11 R2_MERGENOTPEN | |
| Режим рисования по умолчанию | 1 1 0 0 | P | 12 R2_COPYPEN |
| 1 1 0 1 | P| (~D) | 13 R2_MERGEPENNOT | |
| 1 1 1 0 | P|D | 14 R2_MERGEPEN | |
| 1 1 1 1 | 1 | 15 R2_WHITE |
Так, например, по умолчанию используется операция копирования исходного изображения на контекст устройства (называемая R2_COPYPEN), довольно часто применяется операция исключающее или (R2_XORPEN) между пером и существующим изображением на контексте.
Если монохроматические переносимое изображение и уже имеющееся рассматривать как битовые последовательности, то возможны 4 случая:
оба изображения, переносимое (pen) и имеющееся (destination), содержат светлые точки
переносимое изображение (pen) содержит светлую точку, а имеющееся (destination) — темную
переносимое изображение (pen) содержит темную точку, а имеющееся (destination) — светлую
оба изображения, переносимое (pen) и имеющееся (destination), содержат темные точки
Говоря в терминах битов может понадобиться комбинировать 1 с 1, 1 с 0, 0 с 1 и 0 с 0. Эти четыре варианта перечислены во втором столбце таблицы в заголовке. В строчках ниже дается ожидаемый результат во всех четырех случаях.
Например, если в результате комбинирования светлой со светлой должна получиться светлая точка (1 и 1 дает 1), а в остальных случаях — темная (1 и 0, 0 и 1, а также 0 и 0 дают 0), то в таблице эта операция будет обозначена как 1 0 0 0 — R2_MASKPEN.
Вы можете установить желаемый режим рисования, или узнать текущий с помощью функций
int SetROP2 (hDC, nIndex);
int GetROP2 (hDC);
В описании этих функций можно также найти символические имена всех 16 режимов и их краткое описание. Однако, следуя приводимому в документации описанию, найти нужную операцию может быть затруднительно (обычно там не приводятся результаты выполнения операций, только названия и обозначения). В этом случае вы можете получать нужный номер растровой операции из этой таблички, даже вы вообще можете обойтись без символических имен, просто рассматривая битовую последовательность в табличке как номер режима. Этим правилом можно пользоваться, если порядок четырех возможных комбинаций соответствует приведенному в таблице и в примере.
Например, вы хотите выбрать такой режим, что бы изображение получало светлую точку только в том случае, когда и рисуемая точка и точка имеющегося фона имеют темный цвет, или когда точка линии — светлая, а фона — темная; во всех остальных случаях должна получиться темная точка.
Рисунок 6. Получение номера растровой операции.
Направление рисования дуг
Для задания дуги, которую вы хотите нарисовать необходимо определить эллипс, дугу которого вы собираетесь нарисовать, а также начальную и конечную точки дуги. Но вот проблема — от начальной до конечной точки можно провести дугу в двух разных направлениях, так что приходится дополнительно определять, в каком именно направлении дуга будет нарисована. Обычно принято, что дуга рисуется против хода часовой стрелки.
Вы можете при необходимости узнать или изменить направление рисования дуг с помощью функций:
int GetArcDirection (hDC);
int SetArcDirection (hDC, nIndex);
Допустимыми являются AD_COUNTERCLOCKWISE — рисование против хода часовой стрелки (принято по умолчанию) и AD_CLOCKWISE — по ходу часовой стрелки.
Внимание! Эти две функции не работают в случае расширенного режима задания координат (см. функцию SetGraphicsMode 1, GM_ADVANCED) — в этом случае дуги всегда рисуются против хода часовой стрелки.
Дополнительные средства для рисования линий
Windows содержит еще одну функцию, которая применяется для рисования линий. Она предназначена для последовательного вычисления координат точек, принадлежащих линии, и выполнения над этими точками какой-либо операции, определенной пользователем. Фактически эта функция для каждой точки рисуемой линии вызывает указанную пользователем процедуру. Любопытно, что сама эта функция никак не взаимодействует с контекстом устройства, она выполняет только математические операции, вычисляя все промежуточные точки линии.
void LineDDA (nXStart, nYStart, nXEnd, nYEnd, lpfnDdaPrc, lParam);
nXStart, nYStart — определяют первую точку линии
nXEnd, nYEnd — определяют последнюю точку линии















