Gdi графика в delphi


Gdi: графика в delphi

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

В работе с графикой в Delphi в распоряжении программиста находятся канва (холст, полотно — свойство Canvas Delphi компонентов), карандаш (свойство Pen), кисть (свойство Brush) того компонента или объекта, на котором предполагается рисовать. У карандаша Pen и кисти Brush можно менять цвет (свойство Color) и стиль (свойство Style). Доступ к шрифтам предоставляет свойство канвы Font. Эти инструменты позволяют отображать как текст, так и достаточно сложные графики математического и инженерного содержания, а также рисунки. Кроме этого, работа с графикой позволяет использовать в Delphi такие ресурсы Windows как графические и видеофайлы.

Конечно, не все компоненты в Delphi имеют эти свойства. На вкладке Additional расположен специализированный компонент TImage, специально предназначенный для рисования, но также свойство Canvas имеют, например, такие компоненты как ListBox, ComboBox, StringGrid, а также и сама Форма, которая размещает наши компоненты! Кроме того, для печати документов Delphi обращается к свойству Canvas такого объекта как принтер.

Основное свойство такого объекта как Canvas Delphi — Pixels[i, j] типа TColor, то есть это двумерный массив точек (пикселов), задаваемых своим цветом. Рисование на канве происходит в момент присвоения какой-либо точке канвы заданного цвета. Каждому пикселу может быть присвоен любой доступный для Windows цвет. Например, выполнение оператора

приведёт к рисованию красной точки с координатами [100, 100]. Узнать цвет пиксела можно обратным присвоением:

Тип TColor определён как длинное целое (LongInt). Его четыре байта содержат информацию о долях синего (B), зелёного (G), и красного (R) цветов. В 16-ричной системе это выглядит так: $00BBGGRR. Доля каждого цвета может меняться от 0 до 255. Поэтому чтобы отобразить максимально красную точку, ей нужно присвоить цвет $000000FF.
Для стандартных цветов в Delphi определён набор текстовых констант. Увидеть его можно, открыв в Инспекторе Объектов свойство Color, например, той же Формы.

Следующая таблица содержит некоторые свойства и методы канвы:

Процедура TextOut(X, Y: Integer; const Text: WideString);
Производит вывод строки Text начиная с (X, Y) — левого верхнего пиксела текста.
Свойство TextWidth(var Text: String): Integer;
Содержит длину строки Text в пикселах.
Свойство TextHeight(var Text: String): Integer;
Содержит высоту строки Text в пикселах.
Процедура MoveTo(X, Y: Integer);
Производит перемещение позиции к пикселу с адресом (X, Y).
Процедура LineTo(X, Y: Integer);
Производит рисование прямой линии из точки текущей позиции к пикселу с адресом (X, Y). Адрес (X, Y) становится точкой текущей позиции.
Процедура FillRect(const Rect: TRect);
Заполняет прямоугольник Rect на холсте, используя текущую кисть. Может использоваться, в том числе, для стирания части изображения на холсте.

Напишем, используя только эти методы канвы, приложение для изображения на канве компонента Image текста, который вводится в компонент Memo:

Расположите на форме компоненты Memo, Image (находится на странице Additional), Edit, UpDown (находится на странице Win32). Свойство Associate компонента UpDown приравняйте в выпадающем списке нашему компоненту Edit. Этим компонентом мы будем изменять размер шрифта. Поскольку и Memo и Image находятся у нас на одном устройстве вывода — на нашем экране, размер пиксела у них одинаков, и поэтому размеры изображения будут равны.

Первое, что мы сделаем, это инициализацию переменных при старте программы. Необходимо определить размеры области рисования (создадим для этого глобальную переменную Rect типа TRect) и сделать цвет фона Image белым:

procedure TForm1.FormCreate(Sender: TObject);
begin
Rect.Left:=0;
Rect.Top:=0;
Rect.Right:=Image1.Width;
Rect.Bottom:=Image1.Height;
Image1.Canvas.Brush.Color:=clWhite;
end;

Затем нарисуем рамку по сторонам Image:

procedure TForm1.page;
begin
with Image1.Canvas do
begin
MoveTo(0, 0);
LineTo(Image1.Width-1, 0);
LineTo(Image1.Width-1, Image1.Height-1);
LineTo(0, Image1.Height-1);
LineTo(0, 0);
end;
end;

Попробуем, что получилось. Всё работает, но рамка пока не выводится. Поэтому добавим процедуру page в в процедуру FormCreate. Теперь красиво. Далее напишем простую процедуру стирания, очищения Image. Её нужно будет вызывать перед любым обновлением изображения, иначе предыдущее и последующее изображения будут перекрываться.

procedure TForm1.clearing;
begin
Image1.Canvas.FillRect(Rect); //Прямоугольник Rect заполняется белым цветом, изображение стирается.
end;

Теперь пришла очередь непосредственно процедуры вывода текста. Начнём выводить текст от точки (3, 3) — верхнего левого угла листа, с небольшим отступом в 3 пиксела. Каждую последующую строку будем смещать на высоту строки:

procedure TForm1.prn;
var i: Integer;
begin
with Image1.Canvas do
for i:=1 to Memo1.Lines.Count do
TextOut(3, 3+(i-1)*TextHeight(‘A’), Memo1.Lines[i-1]);
end;

Теперь всё готово для вывода текста. Делать это будем по событию OnChange:

procedure TForm1.Memo1Change(Sender: TObject);
begin
clearing;
prn;
page;
end;

Ну и напоследок процедура изменения размера шрифта:

procedure TForm1.Edit1Change(Sender: TObject);
begin
Memo1.Font.Size:=UpDown1.Position;
Image1.Canvas.Font.Size:=UpDown1.Position;
Memo1Change(Sender);
end;

Можно модифицировать эту программу для вывода текста на печать. Для работы с принтером нужно подключить модуль Printers:

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Printers;

При работе с принтером как с полотном для начала печати вызывается метод BeginDoc, затем производится вывод документа, завершается печать вызовом метода EndDoc:

Delphi GDI+

Написал программку загрузки изображения в формате gpg средствами GDI + и вывел его в PaintBox1. Как сделать чтобы при нажатии кнопки изображение смещалось например влево. И как сделать буферизацию.

10.11.2010, 00:18

Объекты GDI
Вообщем возникла такая проблема, есть массив png картинок разного размера и время от времени нужно.

Буфер GDI+
Рисую с помощью GDI+ графики функций. Как рисовать график в буфере, а затем выводить его на экран?

использование GDI+
Добавлено через 1 час 57 минут Бенджамин Липчак в его суперкниге отзывается о GDI+ как о наиболее.

Тернарные операции GDI
Ребята.нужна помощь.только как можно быстрее. наведите на правильный путь, подскажите метод.

как нарисовать стрелку в GDI+?
Кто-нибудь подскажите как нарисовать стрелку.я нарисовал линию, теперь чтобы получилась стрелка мне.

Gdi: графика в delphi

Смотреть отзывы [0] Поделись знаниями и добавь свой отзыв!

Издательство: Символ-Плюс, 2008 г.
Страниц 648, мягкая обложка
ISBN 5-93286-134-7

ИНФОРМАЦИЯ ОТ ИЗДАТЕЛЯ:

В первой части раскрываются особенности графического механизма Windows и рассматриваются функции прикладного интерфейса программирования GDI (Graphics Device Interface). Во второй части акцент переносится на особенности работы с графикой средствами визуальной библиотеки Delphi. Заключительная треть книги освещает тонкости современной графической библиотеки Windows GDI+.

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

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

Часть I. Graphics Device Interface (GDI)
Глава 1. Программирование на Windows API
Глава 2. Контекст графического устройства
Глава 3. Управление устройствами видеовывода
Глава 4. Кисти
Глава 5. Перья
Глава 6. Линии и кривые
Глава 7. Простейшие геометрические фигуры и заливка областей
Глава 8. Траектории
Глава 9. Регионы
Глава 10. Отсечение и регионы контекста устройства
Глава 11. Системы координат и режимы отображения
Глава 12. Мировые координаты и аффинные преобразования
Глава 13. Представление цвета в RGB-модели
Глава 14. Цветовые палитры
Глава 15. Аппаратно-зависимые растры
Глава 16. Аппаратно-независимые растры
Глава 17. Растровые операции
Глава 18. Расширенный формат метафайла (EMF)
Глава 19. Шрифты
Глава 20. Вывод текста
Глава 21. Альфа-наложение и градиентная заливка

Часть II. Графика в VCL
Глава 22. Визуальная библиотека компонентов Delphi
Глава 23. Холст VCL — класс TCanvas
Глава 24. Растровые и векторные изображения в VCL
Глава 25. Коллекционируем изображения
Глава 26. Графические элементы управления VCL
Глава 27. Организация работы с принтером

Часть III. GDI+
Глава 28. Введение в GDI+
Глава 29. Кисти GDI+
Глава 30. Перья GDI+
Глава 31. Траектории GDI+
Глава 32. Регионы GDI+
Глава 33. Графические примитивы и заливка областей в GDI+
Глава 34. Координатные системы и преобразования в GDI+
Глава 35. Изображения в GDI+
Глава 36. Метаданные EXIF
Глава 37. Особенности работы с битовыми образами и метафайлами
Глава 38. Работа со шрифтами в GDI+
Глава 39. Операции с текстом в GDI+
Глава 40. Качество вывода и коррекция цвета

Графика в Delphi

Delphi позволяет использовать графические возможности операционной системы Windows. В операционной системе Windows графические возможности предоставляет графический интерфейс Win32. в частности интерфейс известный как GDI. Интерфейс GDI используется в системе Win32 для рисования и раскраски изображений. До появления Delphi для программирования графики в среде Windows необходимо было напрямую работать с функциями и процедурами GDI. В Delphi существует объект TCanvas (холст), который инкапсулирует и упрощает использование функций и инструментов (методов) GDI.

Представление рисунка в Delphi

TImage — компонент с таким же названием доступен на палитре компонентов Inditional. С помощью компонента TImage можно загрузить и отобразить на экране любой рисованный файл (*.bmp, *.wmf (16-и разрядный метафайл Windows), *.emf (32-х разрядный расширенный формат метафайла), *.ico, *.jpeg, а так же файлы других графических форматов поддерживаемых надстройками класса TGraphic).

Графические данные хранятся в свойстве picture объекта TImage.Picture.


Определение размеров графического файла:

1. размер (количество пикселей) ? координата (x) ? координата (y);

2. размер (длинна по (x)) ? размер (длинна по (y)) ? количество пикселей ? цвет.

Позволяет хранить изображение в виде матрицы пикселей.

Основной недостаток — плохая масштабируемость (Paint, Photoshop…).

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

Delphi работает только с растровой графикой.

Существует два типа растров:

1). Зависимые от устройства (DDB);

2). Не зависимые от устройства (DIB).

Delphi работает только с независимыми от устройств растрами, т.к. они работают со всеми устройствами.

Загрузка изображения на форму.

Для этого используется класс TPicture. Он представляет собой контейнерный класс для инкапсуляции абстрактного класса TGraphic.

Контейнерный означает, что класс TPicture может хранить ссылки на объекты: TBitmap, TMetafile, TIcon.

Пример Изображение на форме какой-либо картинки:

Procedure TForm1.Button1.Click (sender: object);

Для сохранения используется метод SaveToFile:

Для работы с файлами формата JPEG необходимо в строке используемых модулей записать модуль JPEG.

Класс TBitmap

Класс TBitmap предназначен для хранения растрового изображения. Он инкапсулирует объект растров и палитры системы Win32.GPI.

b:=nil // или b.Destray;

Методы объекта TBitmap:

1. .LoadFromFile — загрузить в объект;

2. .SaveToFile — сохранить;

3. .Assign — позволяет скопировать один растр в другой.

При использование метода *.Assign копирование происходит следующим образом: в объект (b2) записывается ссылка на объект (b1), что позволяет сэкономить память; в случае внесения изменений в объект (b2) происходит автоматическое копирование объекта (b1).

4. .CopyRect — позволяет скопировать прямоугольную часть растрового изображения из одного объекта Bitmap в другой или на форму.

Delphi. GDI+ vs StretchDraw. Как уменьшить много картинок в потоке и сохранить их на диске?

Встала задача – создать thumbnails для полноформатных изображений. В сети уйма информации, не знал за что хвататься, решил пробовать по очереди. В сущности, попробовал 2 способа более менее подробно, а остальные по верхам. Остановился на стандартном StretchDraw из Delphi, из за того, что GDI+ почему-то в дополнительном потоке рисовала белые прямоугольники. Я не стал это глубоко раскапывать, так как нужно было решать задачу.

В результате получим кучу уменьшенный фотографий практически из любых форматов. Я тестил с Jpg, PNG, Tiff, с другими думаю тоже будет работать, так как для загрузки используется twicimage.

Коды приведенные ниже иллюстрируют уменьшение большого числа изображений. Как это работает?

Запускается отдельный поток, в котором в цикле уменьшается большое число изображений

Всё складывается в папочку \thumbnaiPics

Я использовал формат twicimage, который позволяет загружать большое число форматов, ну и в программе уже в ручную ставил фильтр на сами форматы.

Построение графиков функций в Delphi

Компонент TChart может строить самые различные графики и диаграммы.

Рассмотрим задачу построения графика на примере синусоиды (y=sin(x)).

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

Местоположение промежуточных точек рассчитывается как положение предыдущей точки плюс некоторый интервал (шаг). Для первой промежуточной точки предыдущей точкой является граница диапазона.

Построим график синусоиды для диапазона от 0 до 2π.

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

Илон Маск рекомендует:  Введение в графические функции организация вывода в виртуальное окно mfc

1.1.11. Графика в Windows API

1.1.11. Графика в Windows API

Та часть Windows API, которая служит для работы с графикой, обычно называется GDI (Graphic Device Interface). Ключевое понятие в GDI — контекст устройства (Device Context, DC). Контекст устройства — это специфический объект, хранящий информацию о возможностях устройства, о способе работы с ним и о разрешенной для изменения области. В Delphi контекст устройства представлен классом TCanvas , свойство Handle которого содержит дескриптор контекста устройства. TCanvas универсален в том смысле, что с его помощью рисование в окне, на принтере или в метафайле выглядит одинаково. То же самое справедливо и для контекста устройства. Разница заключается только в том, как получить в разных случаях дескриптор контекста.

Большинство методов класса TCanvas являются «калькой» с соответствующих (в большинстве случаев одноименных) функций GDI. Но в некоторых случаях (прежде всего в методах вывода текста и рисования многоугольников) параметры методов TCanvas имеют более удобный тип, чем функции GDI. Например, метод TCanvas.Polygon требует в качестве параметра открытый массив элементов типа TPoint , а соответствующая функция GDI — указатель на область памяти, содержащую координаты точек, и число точек. Это означает, что до вызова функции следует выделить память, а потом — освободить ее. Еще нужен код, который заполнит эту область памяти требуемыми значениями. И ни в коем случае нельзя ошибаться в количестве элементов массива. Если зарезервировать память для одного числа точек, а при вызове функции указать другое, программа будет работать неправильно. Но для простых функций работа через GDI ничуть не сложнее, чем через TCanvas . Для получения дескриптора контекста устройства существует много функций. Только для того, чтобы получить дескриптор контекста обычного окна, существуют четыре функции: BeginPaint , GetDC , GetWindowDC и GetDCEx . Первая из них возвращает контекст клиентской области окна при обработке сообщения WM_PAINT . Вторая дает контекст клиентской области окна, который можно использовать в любой момент времени, а не только при обработке WM_PAINT . Третья позволяет получить контекст всего окна, вместе с неклиентской частью. Последняя же дает возможность получить контекст определенной области клиентской части окна.

После того как дескриптор контекста получен, можно воспользоваться преимуществами класса TCanvas . Для этого необходимо создать экземпляр такого класса и присвоить его свойству Handle полученный дескриптор. Освобождение ресурсов нужно проводить в следующем порядке сначала свойству Handle присваивается нулевое значение, затем уничтожается экземпляр класса TCanvas , после этого с помощью подходящей функции GDI освобождается контекст устройства. Пример такого использования класса TCanvas демонстрируется листингом 1.17.

Листинг 1.17. Использование класса TCanvas для работы с произвольным контекстом устройства

DC:= GetDC(…); // Здесь возможны другие способы получения DC


Canvas.Handle:= DC; // Здесь рисуем, используя Canvas

// Освобождение объекта Canvas не означает освобождения контекста DC

// DC необходимо удалить вручную

Использование класса TCanvas для рисования на контексте устройства, для которого имеется дескриптор, показано в примере PanelMsg на прилагающемся компакт-диске.

Разумеется, можно вызывать функции GDI при работе через TCanvas . Для этого им просто нужно передать в качестве дескриптора контекста значение свойства Canvas.Handle . Коротко перечислим те возможности GDI, которые разработчики VCL почему-то не сочли нужным включать в TCanvas : работа с регионами и траекториями; выравнивание текста по любому углу или по центру; установка собственной координатной системы; получение детальной информации об устройстве; использование геометрических перьев; вывод текста под углом к горизонтали; расширенные возможности вывода текста; ряд возможностей по рисованию нескольких кривых и многоугольников одной функцией; поддержка режимов заливки. Доступ ко всем этим возможностям может быть осуществлен только через API. Отметим также, что Windows NT/2000/XP поддерживает больше графических функций, чем 9x/МЕ. Функции, которые не поддерживаются в 9x/ME, также не имеют аналогов среди методов TCanvas, иначе программы, написанные с использованием данного класса, нельзя было бы запустить в этих версиях Windows.

GDI предоставляет очень интересные возможности по преобразованию координат, но только в Windows NT/2000/XP; в Windows 9x/ME они не поддерживаются. С помощью функции SetWorldTransform можно задать произвольную матрицу преобразования координат, и все дальнейшие графические операции будут работать в новой координатной системе. Матрица позволяет описать такие преобразования координат, как поворот, сдвиг начала координат и масштабирование, т. е. возможности открываются очень широкие. Также существует менее гибкая, но тоже полезная функция преобразования координат — SetMapMode , которая поддерживается во всех версиях Windows. С ее помощью можно установить такую систему координат, чтобы во всех функциях задавать координаты, например, в миллиметрах, а не пикселах. Это позволяет использовать один и тот же код для вывода на устройства с разными разрешениями.

Некоторые возможности GDI, которым нет аналогов в TCanvas , демонстрируются примером GDI Draw.

Для задания цвета в GDI предусмотрен тип COLORREF (в модуле Windows определен также его синоним для Delphi — TColorRef ). Это 4-байтное беззнаковое целое, старший байт которого определяет формат представления цвета. Если этот байт равен нулю (будем называть этот формат нулевым), первый, второй и третий байты представляют собой интенсивности красного, зеленого и синего цветов соответственно. Если старший байт равен 1, два младших байта хранят индекс цвета в текущей палитре устройства, третий байт не используется и должен быть равен нулю. Если старший байт равен 2, остальные байты, как и в нулевом формате, показывают интенсивность цветовых составляющих.

Тип TColorRef позволяет менять глубину каждого цветового канала от 0 до 255, обеспечивая кодирование 16 777 216 различных оттенков (это соответствует режиму TrueColor ). Если цветовое разрешение устройства невелико, GDI подбирает ближайший возможный цвет из палитры. Если старший байт TColorRef равен нулю, цвет выбирается из текущей системной палитры (по умолчанию эта палитра содержит всего 20 цветов, поэтому результаты получаются далекими от совершенства). Если же старший байт равен 2, то GDI выбирает ближайший цвет из палитры устройства. В этом случае результаты получаются более приемлемыми. Если устройство имеет большую цветовую глубину и не задействует палитру, разницы между нулевым и вторым форматом COLORREF нет.

Хотя режимы HighColor (32 768 или 65 536 цветов) не обладают достаточной цветовой глубиной, чтобы передать все возможные значения TColorRef , палитра в этих режимах не используется и ближайший цвет выбирается не из палитры, а из всех цветов, которые способно отобразить устройство. Поэтому выбор нулевого формата в этих режимах дает хорошие результаты.

В Windows API определены макросы (а в модуле Windows , соответственно, одноименные функции) RGB , PaletteIndex и PaletteRGB . RGB принимает три параметра — интенсивности красного, зеленого и синего компонентов и строит из них значение типа TColorRef нулевого формата. PaletteIndex принимает в качестве параметра номер цвета в палитре и на его основе конструирует значение первого формата. Макрос PaletteRGB эквивалентен RGB , за исключением того, что устанавливает старший байт возвращаемого значения равным двум. Для извлечения интенсивностей отдельных цветовых компонентов из значения типа TColorRef можно воспользоваться функциями GetRValue , GetGValue и GetBValue .

В системе определены два специальных значения цвета: CLR_NONE ( $1FFFFFFF ) и CLR_DEFAULT ( $20000000 ). Они используются только в списках рисунков (image lists) для задания фонового и накладываемого цветов при выводе рисунка. CLR_NONE задаёт отсутствие фонового или накладываемого цвета (в этом случае соответствующий визуальный эффект не применяется). CLR_DEFAULT — установка цвета, заданного для всего списка.

В VCL для передачи цвета предусмотрен тип TColor , определенный в модуле Graphics . Это 4-байтное число, множество значений которого является множеством значений типа TColorRef . К системным форматам 0, 1 и 2 добавлен формат 255. Если старший байт значения типа TColor равен 255, то младший байт интерпретируется как индекс системного цвета (второй и третий байт при этом не учитываются). Системные цвета — это цвета, используемые системой для рисования различных элементов интерфейса пользователя. Конкретные RGB-значения этих цветов зависят от версии Windows и от текущей цветовой схемы. RGB-значение системного цвета можно получить с помощью функции GetSysColor . 255-й формат TColor освобождает от необходимости явного вызова данной функции.

Для типа TColor определен ряд констант, облегчающих его использование. Некоторые из них соответствуют определенному RGB-цвету ( clWhite , clBlack , clRed и т. п.), другие — определенному системному цвету ( clWindow , clHighlight , clBtnFace и т. п.). Значения RGB-цветов определены в нулевом формате. Это не приведет к потере точности цветопередачи в режимах с палитрой, т. к. константы определены только для 16-ти основных цветов, которые обязательно присутствуют в системной палитре. Значениям CLR_NONE и CLR_DEFAULT соответствуют константы clNone и clDefault . Они служат (помимо списков рисунков) для задания прозрачного цвета в растровом изображении. Если этот цвет равен clNone , изображение считается непрозрачным, если clDefault , в качестве прозрачного цвета берется цвет левого нижнего пиксела.

Везде, где требуется значение типа TColor, можно подставлять TColorRef , т. е. всем свойствам и параметрам методов класса TCanvas , имеющим тип TColor можно присваивать те значения TColorRef , которые сформированы функциями API. Обратное неверно: API-функции не умеют обращаться с 255-м форматом TColor . Преобразование из TColor в TColorRef осуществляется с помощью функции ColorToRGB . Значения нулевого, первого и второго формата, а также clNone и clDefault она оставляет без изменения, а значения 255-го формата приводит к нулевому с помощью функции GetSysColor . Эту функцию следует использовать при передаче значении типа TColor в функции GDI.

Применение кистей, перьев и шрифтов в GDI принципиально отличается от того, как это делается в VCL. Класс TCanvas имеет свойства Pen , Brush , и Font , изменение свойств которых приводит к выбору того или иного пера, кисти, шрифта. В GDI эти объекты самостоятельны, должны создаваться, получать свой дескриптор, «выбираться» в нужный контекст устройства с помощью функции SelectObject и уничтожаться после использования. Причем удалять можно только те объекты, которые не выбраны ни в одном контексте. Есть также несколько стандартных объектов, которые не нужно ни создавать, ни удалять. Их дескрипторы можно получить с помощью функции GetStockObject . Для примера рассмотрим фрагмент программы, рисующей на контексте с дескриптором DC две линии: синюю и красную (листинг 1.18). В этом фрагменте используется то, что функция SelectObject возвращает дескриптор объекта, родственного выбираемому, который был выбран ранее. Так, при выборе нового пера она вернет дескриптор того пера, которое было выбрано до этого.

Листинг 1.18. Рисование разными перьями с использованием GDI

SelectObject(DC, CreatePen(PS_SOLID, 1, RGB(255, 0, 0)));

MoveToEx(DC, 100, 100, nil);

LineTo(DC, 200, 200);

DeleteObject(SelectObject(DC, CreatePen(PS_SOLID, 1, RGB(0, 0, 255))));

MoveToEx(DC, 200, 100, nil);

LineTo(DC, 100, 200);

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

Для хранения растровых изображений в Windows существуют три формата: DDB, DIB и DIB-секция. DDB — это Device Dependent Format, формат, определяемый графическим устройством, на которое идет вывод. DIB — это Device Independent Bitmap, формат, единый для всех устройств. Формат DIB — это устаревший формат, который не позволяет использовать графические функции GDI для модификации картинки, модифицировать изображение можно, только одним способом: вручную изменяя цвета отдельных пикселов. В 32-разрядных версиях появился еще один формат — DIB-секция. По сути дела это тот же самый DIB, но дополненный возможностями рисовать на нем с помощью GDI-функций. Все различия между этими тремя форматами можно прочитать в замечательной книге [1]; мы же здесь ограничимся только кратким их обзором.

Формат DDB поддерживается самой видеокартой (или другим устройством вывода), поэтому при операциях с таким изображением задействуется аппаратный ускоритель графики. DDB-изображение хранится в выгружаемом системном пуле памяти (Windows NT/2000/XP) или в куче GDI (Windows 9x/ME). При этом размер DDB-растра не может превышать 16 Мбайт в Windows 9x/ME и 48 Мбайт в Windows NT/2000/XP. Формат DDB не переносим с одного устройства на другое, он должен использоваться только в рамках одного устройства. Прямой доступ к изображению и его модификация вручную невозможны, т. к. формат хранения изображения конкретным устройством непредсказуем. Модифицировать DDB можно только с помощью функций GDI. Цветовая глубина DDB-изображений определяется устройством.

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

Скорость работы с изображением в формате DIB-секции зависит только от производительности процессора, памяти и качества реализации графических алгоритмов системой (а они, надо сказать, реализованы в Windows очень неплохо). Скорость работы с изображением в формате DDB зависит еще и от драйвера и аппаратного ускорителя видеокарты. Во-первых, аппаратный ускоритель и драйвер могут поддерживать или не поддерживать рисование графических примитивов (в последнем случае эти примитивы рисует система: то, какие операции поддерживает драйвер, можно узнать с помощью функции GetDeviceCaps ). До недавнего времени была характерной ситуация, когда рисование картинки на DDB-растре и вывод такого растра на экран были заметно (иногда — в два-три раза) быстрее, чем аналогичные операции с DIB-секцией. Однако сейчас разница стала существенно меньше, производительность системы в целом выросла сильнее, чем производительность двумерных аппаратных ускорителей (видимо, разработчики видеокарт больше не считают двумерную графику узким местом и поэтому сосредоточили свои усилия на развитии аппаратных ускорителей 3D-графики). На некоторых мощных компьютерах можно даже столкнуться с ситуацией, когда DDB-изображение отстает по скорости от DIB.

Илон Маск рекомендует:  Выбираем онлайн-курсы английского языка

Класс TBitmap может хранить изображение как в виде DDB, так и в виде DIB- секции — это определяется значением свойства PixelFormat . Значение pfDevice означает использование DDB, остальные значения — DIB-секции с различной цветовой глубиной. По умолчанию TBitmap создает изображение с форматом pfDevice , но программист может изменить формат в любой момент. При этом создается новое изображение требуемого формата, старое копируется в него и уничтожается.

Со свойством PixelFormat тесно связано свойство HandleType , которое может принимать значения bmDIB и bmDDB . Изменение свойства PixelFormat приводит к изменению свойства HandleType , и наоборот.

Если вы собираетесь распечатывать изображение, содержащееся в TBitmap , то вы должны установкой свойств PixelFormat или HandleType обеспечить, чтобы изображение хранилось в формате DIB. Попытка вывести DDB-изображение на принтер приводит к непредсказуемым результатам (чаще всего просто ничего не выводится) из-за того, что драйвер принтера не понимает формат, совместимый с видеокартой.

При загрузке изображения из файла, ресурса или потока класс TBitmap обычно создает изображение в формате DIB-секции, соответствующее источнику по цветовой глубине. Исключение составляют сжатые файлы (формат BMP поддерживает сжатие только для 16- и 256-цветных изображений) — в этом случае создается DDB. В файле Graphics определена глобальная переменная DDBsOnly , которая по умолчанию равна False . Если изменить ее значение на True , загружаемое изображение всегда будет иметь формат DDB.

В справке сказано, что когда DDBsOnly = False , вновь создаваемые изображения по умолчанию хранятся в виде DIB-секций. На самом деле из-за ошибки в модуле Graphics (как минимум до 2007-й версии Delphi включительно) вновь созданное изображение всегда хранится как DDB независимо от значения DDBsOnly .

Класс TBitmap имеет свойство ScanLine , через которое можно получить прямой доступ к массиву пикселов, составляющих изображение. В справке написано, что это свойство можно использовать только с DIB-изображениями. Но на самом деле DDB-изображения тоже позволяют использовать это свойство, хотя и с существенными ограничениями. Если изображение хранится в DDB- формате, при обращении к ScanLine создастся его DIB-копия, и ScanLine возвращает указатель на массив этой копии. Поэтому, во-первых, ScanLine работает с DDB-изображениями очень медленно, а во-вторых, работает не с изображением, а с его копией, откуда вытекают следующие ограничения:

1. Копия создаётся на момент обращения к ScanLine , поэтому изменения, сделанные на изображении с помощью GDI-функций после этого, будут недоступными.

2. Каждое обращение к ScanLine создает новую копию изображения, а старая при этом уничтожается. Гарантии, что новая копия будет располагаться в той же области памяти, нет, поэтому указатель, полученный при предыдущем обращении к ScanLine , больше нельзя использовать.

3. Изменения, сделанные в массиве пикселов, затрагивают только копию изображения, но само изображение при этом не меняется. Поэтому в случае DDB свойство ScanLine дает возможность прочитать, но не изменить изображение.

Следует отметить, что TBitmap иногда создает DIB-секции, даже если свойства HandleType и PixelFormat явно указывают на использование DDB. Особенно часто это наблюдается для изображений большого размера. По всей видимости, это происходит, когда в системном пуле нет места для хранения DDB-изображения такого размера, и разработчики TBitmap решили, что в таком случае лучше создать DIB-изображение, чем не создавать никакого. Пример BitmapSpeed на прилагаемом компакт-диске позволяет сравнить скорость выполнения различных операций с DDB- и DIB-изображениями.

2D-графика средствами OpenGL на Delphi

Доброго всем времени!

Начал разбираться с API OpenGL и, как водится, столкнулся со многими препятствиями. После поиска средств по преодолению/уничтожению которых (в не безызвестной сети) решил запостить сию тему.

Да, знаю, — эта тема не нова и я должен с разгону разбиться о стену или выпить яду с бочки. Но поспешу пояснить: этот пост не приследует цель перемолоть перемолотое — он призван стать существенным довеском ко всему тому багажу знаний, который пользователи могут поиметь читая.

Итак, данный пост будет раскрывать тему программирования 2D-графики средствами OpenGL (под OpenGL будем понимать совокупность функций, поставляемых с Windows в библиотеке OpenGL32.dll) под Windows. В качестве языка выбран Delphi (с выходом Delphi 3.0 разработчики вежливо попросили называть язык Object Pascal языком Delphi), но это не значит, что разработанные приложения будут тянуть за собой всю эту монструозную тяжесть VCL — программировать будем используя лишь функции Win32 API.

Первым вопросом будет этот:

Какие функции имеет OpenGL для вывода массива пикселей на контекст отображения?
Знаю, что есть DrawPixels, но слышал, что такое решение будет достаточно медленным. Есть ли альтернативы?

  • D.R >#2
    13:50, 8 ноя. 2005

Самое быстрое — вывести QUAD, или 2 Triangle с текстурой.

D.Rider
А TRIANGLE_STRIP ещё быстрее :)

YgriK
Спасибо за линки — много интересного. Обязательно покопаюсь внимательней. Но, к сожалению, там много информации на непонятных мне языках: на C++ и на английском.

D.Rider, Sim
Если я правильно понял, то быстрее получается лишь за счёт того, что часть работы по формированию картинки OpenGL перекладывает с плеч CPU на плечи GPU, да?

Давайте сделаем так. Напишем примитивное приложение, которое при загрузке отобразит на поверхности своего единственного окна какую-нибуть bmp-картинку.

Наложим некоторые требования:
1. Размеры окна должны соответствовать размеру картинки;
2. Картинка выводится на экран без каких-либо геометрических искажений и преобразований, т. е. один пиксел картинки отобразиться как один пиксел на окне, все пикселы лежат рядом, как надо, и ни один пиксел не перекрывает другой.

Можно прибегнуть к Win32 API, можно и к VCL. Кому что ближе — в данном случае разница нам безразлична.
Вот пример модуля формы приложения, использующего GDI и VCL:

YgriK
>> там много информации на непонятных мне языках: на C++ и на английском.

bold
возможно.
но там скорее всего еще есть страшное слово glEnd без скобочек.

YgriK
Типа такого? В Дельфях все работает нормально. И со скобочками и без, там эти ошибки прощаются.

worker
>>Но, к сожалению, там много информации на непонятных мне языках: на C++ и на английском.
Упс %)

worker
Найди в Интернете, или лучше приобрети в книжном магазине, я думаю лучшую книгу для начинающих на русском языке «Графика OpenGL в проектах Delphi», автор — некий М.Краснов. Там есть все вопросы на твои ответы и более того.

Спасибо всем за предоставленные линки и рекомендации к прочтению . Потихоньку да помаленьку что-то начинаю понимать .
Спасибо, .Scotina (какое-то неловкое обращение получилось), за указание поименнованого бумажного скопления нужной мне информации; на выходных надеюсь приобрести (если обстоятельства дадут).

YgriK, bold
На счёт скобочек . В синтаксисе Pascal`я принято при вызове процедуры или функции без параметров указывать лишь её имя, но разрешается и скобочки писать. Так что, скорее, наличие скобочек не считается ошибкой, чем их отсутствие: и так, и так можно, но корректней, с точки зрения концепции Pascal`я, не ставить пустых скоб. Это та же история, что и про точку с запятой: в этом языке такой символ используется не как окончание команд, а как их разделитель, по этому правильно говорить не так: «перед end разрешается точку с запятой не ставить», а так: «перед end разрешается ставить точку с запятой».

Теперь непосредственно к теме.

Подразумеваеться, что моя программа будет грузить растр с файла, производить какие-нить операции с ним и отображать его на контексте окна. Всё!! (пока). От OpenGL мне нужен только вывод на контекст отображения, при том в натуральную величину — пиксел на пиксел. Я хочу отказаться от использования GDI, т. к. он достаточно медленный (так говорят, сам с OpenGL и DirectDraw не сравнивал). Итак, подскажите как мне подготовить (в каком виде) область памяти с растром, чтобы передать адресс на её начало какой(да, какой)-то функции OpenGL и . и всё, вроде . ?

worker
— На счёт скобочек .
я хочу посмотреть как ты этот код без скобочек будешь в сишном проекте запускать :))) смайлики сразу на обратные сменяться

ладно.. потрендели и хватит

YgriK
я хочу посмотреть как ты этот код без скобочек будешь в сишном проекте запускать :)))

Рекомендую захотеть посмотреть тему этого топа.
Спору нет, что когда дело дойдёт до серьйозного проекта про Delphi придёться забыть и вспомнить про C, но с этим не только пустые скобочки придёться дописывать.

. это всё патетический формализм.

Не поверите: дорвался до книги Френсиса Хилла «OpenGL для профессионалов: Программирование компьютерной графики»! Сижу и листаю в рабочее (ого! уже обеденное) время. Хочу найти типы растров, с которыми работает OpenGL .

Delphi/GDI +: Когда создается или уничтожается контекст устройства?

Обычно с помощью GDI + в Delphi вы можете использовать TPaintBox и рисовать во время события OnPaint:

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

Проблема, конечно, в том, когда я могу создать объект Графика? мне нужно знать, когда ручка становится доступной, а затем, когда она больше не действительна. Мне нужна эта информация, поэтому я могу создать и уничтожить объект Графика.

Решение Попытка Nº1

Я могу решить проблему создания, создав его, когда это действительно необходимо — в первый раз вызывается цикл рисования:

Но я должен знать, когда контекст устройства более недействителен, поэтому я могу уничтожить объект FGraphcis, чтобы он был снова создан в следующий раз. Если по какой-либо причине контекст устройства TPaintBox воссоздается, я буду ссылаться на недействительный контекст устройства при следующем вызове OnPaint.

Каков предназначенный механизм в Delphi для того, чтобы узнать, когда создаётся, уничтожается или воссоздается

Вы не можете со стандартным TPaintBox, потому что у TPaintBox есть Canvas типа TControlCanvas, для которого члены, имеющие отношение к этой проблеме, следующие:

Проблема заключается в том, что FreeHandle и SetControl не являются виртуальными.

Но: создается и назначается TControlCanvas:

Итак, что вы можете сделать, это создать нисходящий TMyControlCanvas, который имеет виртуальные методы, и TMyPaintBox, который назначает Canvas следующим образом:

Затем вы можете использовать методы в TMyControlCanvas для динамического создания и уничтожения TGPGraphics.

Это должно вас заставить.

Обнаружение создания очень просто. Просто переопределите CreateHandle в потоке TControlCanvas и помещайте вместо стандартного как показывает Jeroen. Обнаружение разрушения сложнее.

Один из способов избежать проблемы состоит в том, чтобы проверить, равен ли дескриптор TGpGraphics дескриптор макета, поэтому вместо того, чтобы обнаруживать момент освобождения контекста устройства, вы просто проверяете, прежде чем вам нужно знать.

Это, вероятно, не является надежным; значения дескриптора подлежат повторному использованию, поэтому, хотя значение HDC может быть одинаковым между двумя проверками, нет гарантии, что он по-прежнему относится к одному и тому же объекту OS-контекста OS.

Gdi: графика в delphi

Среду Windows не зря называют графической — весь текст, рисунки и элементы управления выводятся в графическом виде. Соответственно весь вывод приложений Windows, в отличие от рассмотренных ранее консольных программ, является графическим по своей природе.

Графика в Windows

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

Когда в форме производится построение рисунка, то выводимое изображение обрезается по ее краям, т.е. выводятся только те его части, которые помещаются на холст окна. Такой подход гарантирует, что вывод одного приложения не затронет клиентской области других приложений, на чем, собственно и основывается оконная система Windows.

За точку отсчета координат в окнах принимается левый верхний угол его клиентской области, ограниченной рамкой (т.е. как раз те габариты, что отражаются свойствами ClientHeight и ClientWidth). Для других элементов, имеющих холст для рисования, подобной рамки может и не быть — в таком случае рамками такого виртуального холста выступают границы объекта.

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

Хорошая новость состоит в том, что Delphi имеет целый ряд готовых к использованию компонент, заботиться о рисовании которых разработчику совершенно незачем, поскольку всю рутинную работу уже выполнили разработчики Delphi и VCL. Более того, в VCL есть множество «заготовок», на основании которых можно создавать собственные компоненты, опять-таки без непосредственного обращения к функциям графического вывода.


В то же время, рисование может быть полезным и при более простых ситуациях, например, когда требуется именно вывести какие-либо графические примитивы, или даже дать пользователю возможность что-нибудь начертить. Наконец, мы рассмотрим здесь вопрос рисования, чтобы еще раз проиллюстрировать работу классов и использование ООП, а заодно исследовать еще один фрагмент VCL.

Объект Canvas

Для многих элементов интерфейса в Windows-приложениях используется компоненты, основанные на классе TWinControl, являющегося прямым наследником уже рассмотренного нами класса TControl. Но множество других визуальных компонент происходят от другого его наследника — класса TGraphicControl, являющийся более простым и имеющим такое свойство, как Canvas, при помощи которого можно в буквальном смысле рисовать в Windows. Собственно говоря, свойство Canvas имеется не только у этого класса, но и у ряда других, в том числе и основанных на TWinControl. Впрочем, в любом случае это свойство будет представлено одним и тем же классом — TCanvas.

Объект Canvas инкапсулирует в себе взаимодействие с Windows GDI (Graphic Device Interface — интерфейс графического устройства). GDI обрабатывает весь графический вывод, предназначенный для экрана монитора, а так же для принтеров и иных печатающих устройств. Но поскольку взаимодействие с GDI, как, впрочем, и с другими подсистемами Windows API напрямую — дело хлопотное и чреватое ошибками, то в Delphi и был предусмотрен класс TCanvas, предоставляющий сравнительно простой и удобный доступ к GDI.

Сам по себе холст (Canvas) представляет собой поверхность, на которую можно выводить текст и иные графические изображения — линии, прямоугольники, эллипсы и т.д. Но при ближайшем рассмотрении можно увидеть, что этот холст представляет собой плоскость, состоящую из отдельных точек — пикселей. Пиксель — это базовый элемент графического ввода, представляющий собой отдельную точку. Фактически, при рисовании на холсте вы просто закрашиваете его отдельные точки тем или иным цветом. Но, разумеется, работая с холстом посредством методов, предоставляемых классом TCanvas, можно без лишних хлопот выводить не только точки, но и текст, линии, прямоугольники, многоугольники, окружности, и даже готовые изображения. Рассмотрим основные свойства и методы объекта Canvas, обратившись к таблице 9.1.

Таблица 9.1. Свойства и методы TCanvas

Свойство (метод) Тип значений или параметры Описание
Pixels Матрица TColor Предоставляет доступ к любому пикселю холста, чтобы узнать или изменить его цвет
Pen TPen Свойства пера для черчения линий
Brush TBrush Свойства кисти для заполнения внутренних областей фигур
Font TFont Свойства шрифта для вывода текста
MoveTo X, Y: Integer Устанавливает текущую позицию пера
LineTo X, Y: Integer Проводит линию от текущей позиции к указанной
TextOut X, Y: Integer; const Text: string Выводит заданный текст, начиная с указанных координат
Rectangle X1, Y1, X2, Y2: Integer или Rect: TRect Рисует прямоугольник указанных размеров. Цвет рамки определяется текущим значением свойства Pen, а цвет заливки – свойством Brush
Ellipse X1, Y1, X2, Y2: Integer или Rect: TRect Рисует эллипс, вписанных в прямоугольник указанных размеров. Цвет рамки определяется текущим значением свойства Pen, а цвет заливки – свойством Brush
Polygon Points: array of TPoint Рисует многоугольник по указанным вершинам
PolyLine Points: array of TPoint Рисует ломаную линию, соединяющую указанные точки
Draw X, Y: Integer; Graphic: TGraphic Выводит графическое изображение, начиная от указанных координат (левого верхнего угла)

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

Здесь мы обратились к 1-му элементу 1-го ряда массива, которой как раз и соответствует левому верхнему углу холста, и назначили ему значение clRed, т.е. установили красный цвет.

Поскольку с использованием различных методов объекта Canvas можно выводить не только точки, но и различные фигуры, а так же текст, то остальные свойства как раз позволяют настроить параметры вывода этих фигур и текста. Так, для текста используется свойство Font, являющееся, в свою очередь, классом TFont и имеющим такие свойства, как Color (цвет), Name (гарнитура шрифта), Size (размер) и Style (стиль). Последнее свойство имеет 4 флага, позволяющих сделать шрифт полужирным, наклонным, подчеркнутым или зачеркнутым:

Canvas.Font.Color:=clBlue; //шрифт будет синего цвета Canvas.Font.Name:=’Arial’; //выбрана гарнитура Arial Canvas.Font.Size:=12; //установлен размер шрифта в 12pt Canvas.Font.Style:=[fsBold,fsItalic]; //шрифт будет полужирным наклонным

Что касается таких свойств холста, как Pen и Brush, то для них предусмотрено изменение цвета и стиля линии (для Pen) или заполнения (для Brush). Кроме того, для пера можно определить ширину линии и режим наложения цвета. Соответственно, мы имеем следующие свойства:

  • Color — определяет цвет линии для пера или заполнения для кисти;
  • Style — определяет стиль линии или заливки. Для линии возможны следующие значения: psSolid, psDash, psDashDot, psDashDotDot, psClera и psInsideFrame. Для заливки: bsSolid, bsClear, bsHorizontal, bsVertical, bsFDiagonal, bsBDiagonal, bsCross, bsDiagCross;
  • Width — определяет толщину линии в пикселях.

Следует учитывать, что изменить стиль линии возможно только в том случае, если ее толщина установлена в 1 пиксель (что, впрочем, является значением по умолчанию). При любом другом значении толщины линия всегда будет сплошной — psSolid.

Черчение фигур

Чтобы лучше представить себе использование объекта Canvas, попробуем использовать его свойства и методы для рисования фигур. Для начала попробуем вывести простую линию на поверхность формы, для чего создадим новое приложение, щелкнем по автоматически созданной форме (Form1), после чего перейдем в окно инспектора объекта и на закладке Events дважды щелкнем по строке напротив надписи OnClick. В ответ на это Delphi создаст обработчик события FormClick в редакторе кода:

procedure TForm1.FormClick(Sender: TObject); begin end;

Теперь остается поместить в него код, рисующий линию. Поскольку у формы уже имеется свойство Canvas, то будет достаточно просто обратиться к его методам MoveTo и LineTo. Пусть линия начнется в точке со смещением в 10 пикселей от левого верхнего угла по вертикали и горизонтали, и продлится до точки в 200 пикселей по горизонтали. В результате код получится следующим:

procedure TForm1.FormClick(Sender: TObject); begin Form1.Canvas.MoveTo(10,10); Form1.Canvas.LineTo(200,10); end;

Теперь остается запустить приложение и щелкнуть по любому мету на форме. Результатом будет вывод тонкой горизонтальной линии. Добавив к коду процедуры вызов метода Ellipse, мы получим построение эллипса, а Rectangle — прямоугольника:

Здесь в обоих случаях мы построили правильные фигуры, т.е. окружность и квадрат. Но поскольку для класса TCanvas не определены методы, строящие именно эти фигуры, то мы использовали методы для построения эллипса и прямоугольника, рассматривая окружность и квадрат как частные случаи этих типов фигур. В то же время, при необходимости можно было бы создать собственные методы, добавив их к классу TCanvas. Сделать это, на самом деле, несложно: достаточно определить новый класс, являющийся наследником TCanvas, и определить для него 2 новых метода. Назовем такой класс TMyCanvas, а методы — Circle и Square:

TMyCanvas = class(TCanvas) procedure Circle(Rad, X, Y: integer); procedure Square(Size, X, Y: integer); end;

Определение этого класса следует поместить в части interface, непосредственно после определения класса TForm1, которое уже размещено в модуле самой Delphi. В части же implementation мы определим сами эти функции:

procedure TMyCanvas.Circle(Rad, X, Y: integer); begin end; procedure TMyCanvas.Square(Size, X, Y: integer); begin end;

Теперь остается написать код, выполняющий построение фигур. Для начала примем, тот факт, что для метода Circle параметр Rad означает радиус окружности, а параметры X и Y — ее центр. Таким образом, мы можем использовать унаследованный метод Ellipse, подставив нужные параметры в его вызов:

Ellipse(X-Rad, Y-Rad, X+Rad, Y+Rad);

Что касается метода Square, то его параметр Size будет означать размер стороны квадрата, а X и Y — координаты верхнего левого угла. Таким образом, можно использовать метод Rectangle, указав для него соответствующие параметры:

Rectangle(X, Y, X+Size, Y+Size);

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

procedure TForm1.FormClick(Sender: TObject); var MC: TMyCanvas; begin MC:=TMyCanvas.Create; // используем конструктор родительского класса MC.Handle:=Canvas.Handle; // назначаем холст окна областью вывода MC.Circle(50,200,100); // рисуем окружность диаметром 50 пикселей MC.Square(50,100,100); // рисуем квадрат со сторонами 50 пикселей MC.Free; end;

Разумеется, все методы, доставшиеся классу TMyCanvas в наследство от TCanvas, так же можно использовать, включая те же Create и Free, чем мы и воспользовались. Применительно к остальным свойствам и методам можно делать то же самое, например, установить толщину и цвет линии, вывести линию при помощи MoveTo и т.д.:

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

Рис. 9.1. Вывод графики на холст окна

Законченный код программы можно найти в каталоге Demo\Part2\Canvas. В нем же приведен еще один вариант использования метода Circle — для создания кольца из окружностей.

Вывод на печать

Вывод текста и графики возможен не только на дисплей, но и на принтер. Для этого так же используется свойство Canvas, но только не формы, а специального объекта Printer. Чтобы задействовать этот объект, необходимо подключить к программе модуль printers, указав его в списке используемых модулей, т.е. в uses. При этом не требуется ни создавать экземпляр класса TPrinter, ни заботиться о его удалении — достаточно просто использовать переменную Printer, которая создается для программы автоматически, если указан модуль Printers.

Помимо свойства Canvas, объект Printer имеет ряд иных свойств и методов, необходимых для осуществления печати. Среди методов этого объекта следует отметить, прежде всего, BeginDoc и EndDoc. Эти методы используются, соответственно, для начала и окончания процесса печати. Отметим так же метод NewPage, используемый для обозначения начала новой страницы, а так же метод Abort, прерывающий процесс печати. Что касается свойств, то они приведены в таблице 9.2.

Таблица 9.2. Свойства класса TPrinter

Свойство Тип Описание
Aborted Boolean Указывает, остановлена или нет печать пользователем
Canvas TCanvas Представляет холст листа
Copies Integer Определяет число копий для печати
Orientation TPrinterOrientation Определяет формат расположения бумаги – портретный (poPortrait), или ландшафтный (poLandscape)
PageHeight Integer Указывает на высоту страницы в пикселях
PageNumber Integer Указывает на текущую печатаемую страницу
PageWidth Integer Указывает на ширину страницы в пикселях
Printing Boolean Указывает, выполняется или нет печать в данный момент
Title String Определяет текст, идентифицирующий задание печати в менеджере принтеров Windows

Прежде, чем что-либо выводить не печать, необходимо проделать подготовительную работу. Это вызвано рядом причин, в частности, тем, что принтер не может печатать под обрез — следовательно, надо определить поля. Кроме того, у разных принтеров различное выходное разрешение, которое зависит как от модели, так и от настроек качества печати. В случае вывода текста так же следует определиться с количеством строк, помещающихся на странице. Тем не менее, в простейшем случае для вывода на печать бывает достаточно просто открыть документ для печати при помощи метода BeginDoc и назначить свойству Canvas принтера необходимое содержимое:

Printer.BeginDoc; Printer.Canvas.TextOut(150, 150, ‘Текст для печати’); Printer.EndDoc;

Однако если текст окажется слишком длинным, то он не перенесется на следующую строку, а просто не будет напечатан. Поэтому для вывода достаточно большого по объему текста, все-таки придется заниматься расчетами и готовить строки к выводу.

В то же время, если надо вывести только текст, причем его оформление не принципиально, то можно воспользоваться стандартной процедурой writeln, указав для нее вывод на принтер. Делается это при помощи процедуры AssignPrn:

var F: TextFile; . AssignPrn(F); Rewrite(F); writeln(F,’Текст для печати.’); CloseFile(F);

Разумеется, этот путь проще, в частности перенос длинных строк будет выполняться автоматически, но и недостатков у него хватает: мало того, что таким образом невозможно выводить графику, но и шрифт допускается использовать только один, без возможности изменять даже его размер или начертание в процессе вывода документа на печать.

Чтобы посмотреть, как работают оба этих метода, можно обратиться к примеру, находящемуся в каталоге Demo\Part2\Print.

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

Листинг 9.1. Печать текста и графики

procedure PrintAny; var LMargin, RMargin, TMargin, BMargin: integer; fH, HPos: integer; begin with Printer, Printer.Canvas do begin LMargin:=PageW ); Ellipse(LMargin*2,Hpos+LMargin,LMargin*4,Hpos+LMargin*3); // рисуем круг EndDoc; end; // конец width end; // конец процедуры PrintAny

Здесь сначала были определены поля, затем по этим полям был начерчен прямоугольник, образующий рамку страницы, после чего крупным полужирным и подчеркнутым шрифтом выведено слово «Заголовок», а под ним, уже обычным и более мелким текстом — фраза «Текст абзаца». После этого был выведен круг радиусом, равным ширине левого поля. Таким образом, на один документ были введены как собственно графические объекты, так и текстовые строки, которые, впрочем, в данном случае рассматриваются тоже как графические объекты.

Пример работы этой функции можно посмотреть в программе AnyPrint, которая расположена в каталоге Demo\Part2\Print2.

Илон Маск рекомендует:  Что такое код is_callable
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL