Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Книга о KOL.doc
Скачиваний:
29
Добавлен:
30.04.2019
Размер:
1.77 Mб
Скачать

2.18. Элементы графики. Графические инструменты (tGraphicTool) и канва для рисования (tCanvas)

При создании проектов в среде Windows есть, по крайней мере, одна задача, чрезвычайно сложная для программирования с использованием чистого API. Это - рисование на DC (DC - Device Context) окна или на временном изображении в памяти (bitmap’е - битовой карте, т.е. точечном изображении, распространенное наименование для растрового изображения) с использованием инструментов для рисования - шрифтов, карандашей и кистей. Без объектного программирования эта задача становится весьма сложной, код запутанным и громоздким, в нем чрезвычайно легко сделать ошибки, или просто "забыть" разрушить какой-либо GDI инструмент (GDI - Graphic Device Interface). В результате в программе может возникнуть так называемая "утечка ресурсов" – resource leak (которая может привести к тому, что все ресурсы в системе, число которых ограничено, закончатся, и компьютер придется перезагружать).

Для этого и в VCL, и в KOL создается объект TCanvas (только в KOL это объектный тип, а в VCL - класс), а к нему набор инструментов, инкапсулирующих шрифт (font), кисть (brush) и карандаш (pen). Для этих трех графических инструментов в VCL используется стандартный подход: есть базовый класс TGraphicsObject, от него наследуются классы TFont, TBrush и TPen. В библиотеке KOL наследники экономятся, и все три вида графических инструментов представлены одним и тем же объектным типом TGraphicTool. Разумеется, у них различаются конструкторы, и функциональность объекта и набор поддерживаемых свойств различен. По этой причине, не следует пытаться, к примеру, изменять свойство FontName для кисти - это все равно ничего не даст.

Конструкторы:

NewCanvas( DC ) - создает объект канвы (если DC указан, то эта канва привязывается к существующему контексту устройства, обычно для изображения в памяти). В реальном программировании, создавать канву самостоятельно практически никогда нет необходимости, вместо этого следует использовать свойство Canvas соответствующего объекта, чтобы рисовать на битмапе или в окне, то же относится и к нижеприведенным конструкторам графических инструментов: обычно следует использовать свойства Font, Brush и Pen самой канвы или визуального объекта;

NewFont - создает шрифт (возвращает PGraphicTool);

NewBrush - создает кисть (возвращает PGraphicTool) ;

NewPen - создает карандаш (возвращает PGraphicTool).

Теперь о свойствах и методах канвы. Они, в основном, совпадают с тем, что есть в VCL. Но есть и отличия. Самое важное отличие в том, каким образом предотвращается слишком большой рост используемых приложением ресурсов GDI. Разбираясь с тонким и непростым механизмом, реализующим эту задачу в VCL, я в свое время потратил не один вечер на то, чтобы разобраться, как оно там работает. Это нечто похожее на автоматическую сборку мусора, которая используется иногда в менеджерах памяти. Я принял решение для KOL использовать более простой алгоритм. Все это внутренние детали реализации, и я не могу останавливаться на них подробнее сейчас, но отмечу, что в результате канва в библиотеке KOL имеет чуть больше ограничений на свое применение.

Например, не следует брать канву оконного объекта в произвольный момент времени, и начинать на ней что-либо рисовать. Рисовать следует именно тогда, когда начинается обработка сообщения OnPaint (т.е. когда система разрешает это делать). Как же следует поступать, если необходимо изобразить некоторую анимацию, и обновлять изображение в окне через некоторые интервалы времени? Правильное решение заключается в том, чтобы по истечении очередного интервала времени (например, по событию OnTimer в объекте "часы") "сообщить" системе, что окно "испорчено" и требует перерисовки (например, вызвать метод Invalidate соответствующего оконного объекта). После чего система сама пошлет окну сообщения WM_ERASEBKGND (стереть задний план) и WM_PAINT (нарисовать содержимое), при этом будет вызван обработчик события OnPaint, и вот тут-то и следует нарисовать очередной "кадр" своей анимации.

Объекты графических инструментов имеют несколько общих свойств:

Handle - дескриптор графического объекта;

HandleAllocated - проверяет, что дескриптор создан (если просто обратиться к свойству Handle, то дескриптор будет создан, так что проверять его на равенство нулю смысла не имеет для этих целей);

OnChange - событие, которое срабатывает при изменении любых свойств графического инструмента;

ReleaseHandle - отбирает владение дескриптором у инструмента, возвращая прежний дескриптор (Handle);

Assign( GT ) - присваивает все свойства заданного инструмента данному (разновидность инструмента должна быть той же, т.е. кисть можно присваивать кисти, и т.д.);

Color - цвет.

Все прочие свойства различаются, в том числе по именам.

Свойства для кисти (Brush):

BrushBitmap - битмап (т.е. картинка), которая используется для заливки, когда применяется кисть для заливки;

BrushStyle - стиль кисти (особенно интересны стиль bsSolid - основной стиль для заливки, и bsClear - когда кисть не работает, этот стиль "прозрачной" кисти позволяет во всех операциях рисования на канве сохранить подложку в неприкосновенности);

BrushLineColor - цвет линий для кисти со стилями (BrushStyle) штриховки линиями.

Свойства для карандаша (Pen):

PenWidth - ширина карандаша в пикселях;

PenStyle - стиль карандаша (степень штрихованности линии, так же есть psClear, позволяющий игнорировать карандаш при отрисовке);

PenMode - режим рисования карандаша (черный, белый, цветной, инверсный и т.п.);

GeometricPen - устанавливает так называемый "геометрический" карандаш (в отличие от не-геометрического, позволяет задать вид начертания оконечностей линий, см. PenJoin и PenEndCap);

PenBrushStyle - стиль кисти для геометрического карандаша со штриховкой;

PenBrushBitmap - битмап для заливки при рисовании геометрическим карандашом;

PenEndCap - форма окончания линии для геометрического карандаша (круглое, квадратное, плоское);

PenJoin - способ соединения линий (круглый, граничный, серединный).

Свойства для шрифта (Font):

FontHeight - высота шрифта в пикселях (исключение для rich edit - объекта: для него высота шрифта задается в специальных единицах измерения, называемых twips (дословно: двадцатая), и составляющих 1/20 высоты точки на принтере, или 1/1440 дюйма, или 1/10 пикселя - приблизительно, в зависимости от разрешения дисплея);

FontWidth - ширина шрифта в пикселях, если 0, то используется стандартная ширина шрифта для текущей высоты, в KOL это можно изменить, зауживая или утолщая шрифты по своему вкусу;

FontPitch - стиль шрифта (моноширинный, пропорциональный или по умолчанию);

FontStyle - набор стилей шрифта (жирный - см. так же FontWeight, наклонный, подчеркнутый, перечеркнутый);

FontCharset - набор символов (принудительный выбор того или иного национального набора символов);

FontQuality - качество рисования шрифта;

FontOrientation - угол поворота шрифта в градах, т.е. в 1/10 градуса. Значение 900 соответствует повороту шрифта на 90 градусов против часовой стрелки. Работает это свойство только для TrueType - шрифтов (например, Arial или Times);

FontWeight - задает точное значение для утолщения шрифта. Если задано ненулевое значение, то указание fsBold, т.е. "жирный" в стилях шрифта игнорируется. Значение 700 соответствует стилю fsBold, значение 400 - стилю fsNormal, прочие в соответствии с полученной шкалой калибровки;

FontName - имя шрифта;

IsFontTrueType - проверяет, является шрифт TrueType-шрифтом (т.е. "истинно масштабируемым").

В дополнение, чуть более подробная информация об использовании шрифтов в визуальных объектах. В KOL-приложении (в том числе, в MCK-проектах) имеется возможность не задавать шрифт вообще, в этом случае будет использован системный шрифт (чрезвычайно большой, FixedSys). Вообще же, в MCK-проектах в качестве шрифта по умолчанию используется шрифт "по умолчанию", характеристики которого записаны в глобальной структуре DefFont. Изначально это MS Sans Serif с высотой 0, т.е. высота зависит от настроек по умолчанию для рабочего стола (и цвет шрифта по умолчанию берется из глобальной переменной DefFontColor, изначально clWindowText). Если эти переменные изменить до создания первого визуального объекта, то все шрифты (присваиваемые по умолчанию) в приложении изменятся.

Что касается KOL-приложений (т.е. написанных без использования MCK, то если их вообще не менять и не обращаться к свойству Font, то как раз будет использован системный шрифт FixedSys, а если обратиться хотя бы к одному свойству шрифта визуального объекта, то и у этого визуального объекта, и у всех его дочерних объектов сначала применится DefFont, а затем будет модифицировано указанное свойство (если оно модифицируется).

И, наконец, приведу перечень свойств и методов канвы, которые могут понадобиться при программировании рисования:

Handle - дескриптор контекста устройства (тот самый DC, с которым связан объект канвы). Он предоставляется для того, чтобы оставалась возможность выполнить какие-либо низкоуровневые операции, используя функции API, если для них нет аналога в объекте TCanvas. Так же, дескриптор канвы всегда может быть передан в качестве параметра функциям, которые ориентированы на работу с контекстом устройства DC, и могут ничего не знать о канве, и не использовать ее методов;

PenPos - позиция карандаша, запоминает последнюю координату, использованную в методах MoveTo и LineTo;

Pen - свойство, предоставляющее объект "карандаш";

Brush - свойство, предоставляющее объект "кисть";

Font - свойство, предоставляющее объект "шрифт";

Arc( X1, Y1, X2, Y2, X3, Y3, X4, Y4 ) - рисует арку эллиптической формы - вдоль кривой эллипса, ограничивая эллипс точками (X1,Y1) и (X2,Y2) и изображая кривую против часовой стрелки начиная точкой (X3,Y3) и до точки (X4,Y4), для рисования используется карандаш (Pen);

Chord( X1, Y1, X2, Y2, X3, Y3, X4, Y4 ) - рисует форму, ограниченную аркой и хордой, соединяющей концы арки. Ограничивающая линия рисуется используя карандаш (Pen), внутренность полученной фигуры заливается с помощью кисти (Brush);

DrawFocusRect( R ) - рисует фокусную рамку вдоль заданного прямоугольника (используется Pen в режиме XOR, т.е. повторный вызов того же метода возвращает изображение в исходное состояние);

Ellipse(X1, Y1, X2, Y2) - рисует эллипс, ограниченный прямоугольником, заданным двумя вершинами в точках (X1, Y1) и (X2, Y2). Для изображения границы используется карандаш (Pen), внутренность эллипса заливается кистью (Brush);

FillRect( R ) - заливает прямоугольник R используя кисть (Brush);

FillRgn(rgn) - заливает заданный регион используя кисть (Brush);

FloodFill( X, Y, Color, FillStyle ) - заливка области либо залитой цветом Color, либо наоборот, до границы цвета Color, в зависимости от FillStyle, кистью (Brush);

FrameRect( R ) - рисует границу заданного прямоугольника используя кисть (Brush);

MoveTo( X, Y ) - перемещает карандаш в точку (X, Y);

LineTo( X, Y ) - рисует прямую линию от текущей позиции карандаша до точки (X, Y) инструментом карандаша (Pen);

Pie( X1, Y1, X2, Y2, X3, Y3, X4, Y4 ) - рисует сектор, опирающийся на эллиптическую дугу и с центром в центре эллипса, вписанного в прямоугольник (X1, Y1), (X2, Y2), дуга при этом рисуется против часовой стрелки от точки (X3, Y3) до точки (X4, Y4), граница полученной фигуры рисуется карандашом (Pen), а внутренняя часть сектора закрашивается кистью (Brush);

Polygon( pts ) - рисует многоугольник по массиву заданных точек (последняя точка в массиве соединяется с первой), граница рисуется карандашом, а внутренняя часть заливается кистью;

Polyline( pts ) - рисует ломаную линию по заданному массиву точек, используя карандаш;

Rectangle( X1, Y1, X2, Y2 ) - рисует прямоугольник с вершинами в точках (X1, Y1), (X2, Y2), граница изображается карандашом, внутренность заливается кистью;

RoundRect( X1, Y1, X2, Y2, X3, Y3 ) - рисует прямоугольник со скругленными углами, используя для скругления эллипс высоты Y3 и ширины X3;

TextOut( X, Y, s ) - рисует текст s с точки (X, Y) текущим шрифтом (Font) и заливая подложку кистью (Brush);

ExtTextOut( X, Y, options, s, spacing ) - рисует текст в заданном прямоугольнике, используя дополнительные опции и массив дистанций между буквами (spacing), подробнее следует смотреть описание API-функции ExtTextOut, которую и вызывает данный метод;

DrawText( s, R, flags ) - рисует текст в прямоугольнике, используя API-функцию DrawText и позволяя выполнить форматирование текста в соответствии с заданными флажками;

TextRect( R, X, Y, s ) - рисует текст, ограничивая область отрисовки прямоугольником R;

TextExtent( s ) - вычисляет размер текста в пикселях;

TextArea( s, sz, pt ) - вычисляет для заданного текста и текущего шрифта (учитывая в том числе его ориентацию, т.е. угол поворота, и другие свойства) размер прямоугольника и начальную точку для задания в методах отрисовки текста;

TextWidth( s ) - вычисляет ширину текста;

TextHeight( s ) - вычисляет высоту текста;

ClipRect - возвращает текущий прямоугольник, ограничивающий область вывода;

ModeCopy - текущий режим копирования (для метода CopyRect);

CopyRect( Rdst, srcDC, Rsrc ) - копирует прямоугольник из другой (или этой же) канвы, возможно, выполняя по дороге растягивание / сжатие или даже зеркальное отражение по горизонтали или вертикали, в зависимости от заданных координат исходного и целевого прямоугольника;

OnChange - событие, которое срабатывает, как только изменяется содержимое канвы;

Assign( srcCanvas ) - присваивает данной канве содержимое и параметры заданной канвы, в том числе копирует графические инструменты;

RequiredState( i ) - метод в основном для внутреннего применения, обеспечивает готовность дескрипторов требуемых графических инструментов к рисованию, входной параметр может быть комбинацией (объединением по OR) флажков HandleValid, FontValid, BrushValid, PenValid и ChangingCanvas;

DeselectHandles - отсоединяет от канвы все инструменты. Имеет смысл использовать данный метод, если выполнялись прямые изменения параметров шрифта, кисти или карандаша – через функции API, и требуется перед дальнейшим рисованием обеспечить пересоздание дескрипторов для этих инструментов и их присоединение к канве с уже исправленными дескрипторами. Эта функция так же в основном предназначена для внутреннего употребления;

Pixels[ X, Y ] - медленный доступ к пикселям канвы (для быстрого попиксельного рисования на битмапе в памяти рекомендуется использовать свойство Scanline[ ] объекта TBitmap).

Как видите, основной набор свойств и методов канвы не менее богат, чем в VCL. Кроме того, имеется ряд дополнений, позволяющих работать с Unicode-текстом из не-UNICODE приложения (в UNICODE-приложении, все обычные функции для работы с текстом автоматически транслируются в свои аналоги, совместимые с UNICODE):

WTextOut( X, Y, s ) - выводит строку Unicode по заданным координатам (аналог TextOut );

WExtTextOut( X, Y, options, s, spacing ) - аналогично ExtTextOut, но для Unicode-текста;

WDrawText( s, R, flags ) - аналог DrawText для Unicode;

WTextRect( R, X, Y, s ) - аналог TextRec для Unicode;

WTextExtent( s ) - аналогично TextExtent, вычисляет размер текста в пикселях;

WTextWidth( s ) - ширина Unicode-текста в пикселях;

WTextHeight( s ) - высота Unicode-текста в пикселях.

И, завершая обсуждение канвы и инструментов для рисования на канве, перечислю здесь функции KOL, предназначенные для работы с цветом. Цвет, так же как и в VCL, хранится в 32-разрядной целочисленной переменной (типа TColor), в которой знак ( меньше нуля ) означает, что это системная константа цвета, соответствующая одному из системных элементов в настройке рабочего стола, а младшие три байта - во всех остальных случаях хранят значения красного, зеленого и синего цветовых каналов в обычной системе кодирования цвета R, G, B (R - младший байт, B - старший байт в тройке). Существует ряд функций для преобразования цвета:

Color2RGB( C ) - преобразует системный цвет в RGB-кодировку (если цвет уже задан RGB-кодом, то он же и возвращается в качестве результата);

ColorsMix( C1, C2 ) - смешивает два цвета (среднее арифметическое по каждому из каналов R, G, B), оба цвета предварительно приводятся в кодировку RGB;

Color2RGBQuad( C ) - для заданного цвета возвращает структуру TRGBQuad (используется в 32-разрядных битмапах для хранения отдельных пикселей, например);

Color2Color16( C ) - возвращает цвет в представлении для 16-разрядной цветовой палитры на 64К цветов;

Color2Color15( C ) - аналогично, но для палитры 32К цветов.