Интерфейс графического устройства GDI+. Графический вывод в бизнес-приложениях.

1. Интерфейс графического устройства GDI+. Пространства имен для работы с графикой

GDI (Graphics Device Interface – интерфейс графического устройства) – это подсистема Windows, которая обеспечивает поддержку аппаратно-независимой графики. Благодаря функциям GDI Windows-приложение может выполняться на самых различных компьютерах.

GDI+ – это новый набор программных интерфейсов, используемых в .NET. В .NET предусмотрено множество пространств имен, предназначенных для вывода двумерных графических изображений.

Основные типы для работы с графикой находятся в пространстве имен System.Drawing.

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

Для того чтобы определить, на какое устройство осуществляется вывод (весь экран, клиентская область окна, принтер и т. д.), используется понятие контекста устройства (device context). Это объект класса Graphics, содержащий методы для построения изображения и данные о графическом устройстве вывода. Для осуществления вывода создается контекст устройства и тем самым определяется конкретное устройство для вывода. А далее к созданному объекту можно применять все имеющиеся методы класса Graphics.

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

Контекст устройства можно получить из элемента управления или изображения, а затем ссылку на него присвоить объекту класса Graphics. Например:

public void Form1_Paint(object sender, PaintEventArgs e)

{

Graphics G = e.Graphics;

}

или

Graphics G = pictureBox1.CreateGraphics();

2. Системы координат GDI+

Система координат по умолчанию использует в качестве единицы измерения пиксели, а в качестве исходной точки – верхний левый угол. Ось X направлена вправо, а ось Y –вниз. Изменить единицу измерения можно с помощью свойства PageUnit объекта Graphics (например, на дюймы, миллиметры и т. д.).

В GDI+ предусмотрены следующие типы координатных систем:

  • мировые координаты (world coordinates) – указывают позицию точки, измеренную в пикселях от выбранной точки отсчета;
  • страничные координаты (page coordinates) – указывают позицию точки, измеренную в пикселях от левого верхнего угла клиентской области;
  • координаты устройства (device coordinates) – подобны страничным координатам за исключением того, что в качестве единиц измерения используются не пиксели, а другие единицы измерения.

Например, пусть пользователь пытается нарисовать линию:

myGraphics.DrawLine(myPen, 0, 0, 160, 80);

Координаты точек, которые здесь указываются: (0, 0) и (160, 180) – это мировые координаты. Прежде чем линия будет нарисована на экране, координаты преобразуются сначала в страничные, а потом в координаты устройства.

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

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

TranslateTransform() класса Graphics. Например, установить точку отсчета в положение 100, 50 можно следующим образом:

g.TranslateTransform(100, 50);

3. Служебные типы System.Drawing

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

Для передачи методам подобной информации в пространстве имен System.Drawing предусмотрены специальные служебные типы.

Тип Point определяет точку на рабочей поверхности, определяемой парой координат (X, Y). Для создания нового объекта Point нужно указать его координаты X и Y:

Point P1 = new Point(X, Y);

По другому, можно объявить объект Point и затем установить его свойства X и Y:

Point P1 = new Point(); P1.X = 34;

P1.Y = 60;

Координаты можно задавать и вещественными числами, но в этом случае вместо объекта Point нужно использовать объект PointF.

Тип Size определяет работу с размерами и задает ширину и высоту прямоугольника:

Size A = new Size(width, height);

Тип Rectangle задает прямоугольник. Его конструктор принимает в качестве аргументов координаты левого верхнего угла прямоугольника, его ширину и высоту.

Rectangle box = new Rectangle(X, Y, width, height);

Переменная box представляет прямоугольник, который пока не выводится на экран. Если его надо нарисовать, нужно будет передать его в качестве аргумента методу DrawRectangle() или FillRectangle() в зависимости от того, рисовать ли только контур или заполненный прямоугольник.

В еще одной форме конструктора класса Rectangle используется объект типа Size, определяющей размеры прямоугольника.

Rectangle box = new Rectangle(point, size);

Пример создания объекта Rectangle:

Point P(40, 50);

Size A(200, 150);

Rectangle box = new Rectangle(P, A);

Класс Region представляет собой внутреннюю область, занятую геометрической фигурой. Например:

Rectangle r = new Rectangle(0, 0, 100, 100); Region rgn = new Region(r);

После этого можно многочисленные методы этого класса: добавление фигур в регион, удаление их оттуда, объединение, пересечение, «xor» регионов, проверка на пустоту и бесконечность регионов и т. д.

4. Работа с цветом. Цветовые модели: модель RGB. Задание цветов в GDI+

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

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

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

Цветовые модели могут быть аппаратно-зависимыми (их пока большинство, RGB в их числе) и аппаратно-независимыми. В большинстве современных визуализационных пакетов (например, в Photoshop) можно преобразовывать изображение из одной цветовой модели в другую.

Одной из основных цветовых моделей, используемых в том числе и в GDI+, является модель RGB. Эта модель образована на трех базовых цветах: красном (red), зеленом (green) и синем (blue).

Обычно ее называют моделью аддитивных основных цветов. Все цвета образуются смешиванием этих трех основных в разных пропорциях (т. е. с разными яркостями). На рисунке 1 представлена цветовая модель RGB.

Цветовая модель RGB

Рисунок 1 Цветовая модель RGB

Модель является аппаратно-зависимой, так как значения базовых цветов (а также точка белого) определяются качеством монитора.

В таблице 1 приведены значения некоторых цветов в числовой модели RGB

Таблица 1 – Значения числовой модели RGB

Цвет R G B
Красный (red) 255 0 0
Зеленый (green) 0 255 0
Синий (blue) 0 0 255
Фиолетовый (magenta) 255 0 255
Голубой (cyan) 0 255 255
Желтый (yellow) 255 255 0
Белый (white) 255 255 255
Черный (black) 0 0 0

В GDI+ данная модель модифицировалась в ARGB, где A (alpha) задает значение прозрачности.

Для задания цвета в .NET используется структура Color. Задать значение цвета можно с помощью метода FromArgb(). Например:

Color myColor = Color.FromARGB(255, 0, 255)

Однако чаще всего для выбора цвета используются статические свойства этой структуры, которые задают конкретные цвета:

Color c = Color.Black;

Для обеспечения возможности выбора цвета в процессе работы программы можно использовать класс ColorDialog, который обеспечивает стандартное диалоговое окно выбора цвета.

Для работы с ним нужно создать объект этого класса (или поместить соответствующий компонент на форму) и вызвать для него метод ShowDialog().

Чтобы получить значение выбранного цвета, можно использовать свойство Color данного класса.

5. Работа с кистями: виды кистей. Способы создания перьев

Рассмотрим два вспомогательных класса, необходимых для рисования фигур. Класс Pen (перо) используется для указания способа рисования линий.

Связанный с ним класс Brush (кисть) позволяет указать способ закрашивания экранных областей. Особенность этих классов состоит в том, что вряд ли когда-нибудь придется вызывать их методы напрямую. Нужно просто создать объект Pen или Brush с нужным цветом и другими свойствами, а потом передать методам рисования, которые их требуют.

GDI+ включает несколько разных видов кистей. Каждый тип кисти представлен классом, унаследованным от абстрактного класса Brush. На рисунке 2 изображены кисти, входящие в абстрактный класс Brush.

Типы кистей GDI+

Рисунок 2 – Типы кистей GDI+

Штриховая и градиентные кисти относятся к пространству имен System.Drawing.Drawing2D.

Простейшая кисть (SolidBrush), означает, что область должна быть заполнена сплошным цветом:

SolidBrush b = new SolidBrush(Color.Red);

Кисть также можно создать, используя класс Brushes, статические свойства которого возвращают кисть заданного цвета.

SolidBrush br = (SolidBrush)Brushes.Blue; Brush br = Brushes.Blue;

Изменение цвета кисти:

b.Color = Color.Green;

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

Стиль штрихования выбирается из перечисления HatchStyle. В таблице 2 приведены примеры некоторых штриховых кистей.

Таблица 2 – Стили штриховых кистей

Стиль кисти Внешний вид Стиль кисти Внешний вид
Horizontal SmallConfetti
Vertical ZigZag
Cross HorizontalBrick
DiagonalCross Shingle
DashedHorizontal SolidDiamond

Примеры конструирования штриховых кистей:

//фоновый цвет – черный

HatchBrush brush1 = new HatchBrush(HatchStyle.SolidDiamond, Color.Green);

HatchBrush brush2 = new HatchBrush(HatchStyle.Cross, Color.Red, Color.White);

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

Pen pen1 = new Pen(Color.Blue); //толщина = 1 Pen pen2 = new Pen(Color.Black, 4);

Pen pen3 = new Pen(brush2, 10);

pen1.Color = Color.Red; //изменение цвета

6. Графический вывод текста. Работа со шрифтами. Стандартное диалоговое окно выбора шрифта

Основной класс для работы со шрифтами в GDI+ – это класс Font. Объекты этого класса представляют конкретные шрифты, установленные на компьютере. В этом классе предусмотрено множество перегруженных конструкторов, но наиболее часто используются следующие варианты:

//создаем объект Font, указывая имя шрифта и его размер

Font f1 = new Font(“Times New Roman”, 14);

//создаем объект Font, указывая имя, размер и стиль Font f2 = new Font(“Arial”, 50, FontStyle.Bold | FontStyle.Underline);

При создании f2 использовались стили из перечисления FontStyle. При этом можно задавать несколько стилей одновременно. Значения из перечисления FontStyle представлены в таблице 3.

Таблица 3 – Значения FontStyle

Элемент перечисления FontStyle Стиль
Bold Полужирный
Italic Курсив
Regular Обычный текст
Strikeout Зачеркнутый
Underline Подчеркнутый

После настройки необходимых параметров объекта Font нужно передать их методу DrawString() класса Graphics. Этот метод многократно перегружен, но обычно приходится указывать стандартный набор информации: текстовую строку, которая будет выводиться, используемые шрифт и кисть (цвет текста), а также к оординаты вывода. Например:

G.DrawString(“Hello, world!”,f,new SoliBrush(Color.Red),100,50);

Для того чтобы предоставить пользователю возможность выбрать нужный шрифт для вывода, используется стандартное диалоговое окно выбора шрифта (класс FontDialog). Чтобы вызвать на экран такое окно, надо создать объект класса FontDialog или поместить соответствующий компонент на форму и применить к нему метод ShowDialog(). Далее с помощью свойства Font можно получить выбранный пользователем шрифт, а с помощью свойства Color – цвет.

7. Методы рисования линий и фигур

Класс Graphics включает большое количество методов, которые позволяют рисовать разнообразные линии, контурные и сплошные фигуры. Основные методы перечислены в таблице 4.

Таблица 4 – Методы класса Graphics

Метод Типичные параметры Тип рисуемой фигуры
DrawLine() Перо, начальная и конечная точки Одиночная прямая линия
DrawRectangle() Перо, положение и размер Контур прямоугольника
DrawEllipse() Перо, положение и размер Контур эллипса
FillRectangle() Кисть, положение и размер Сплошной прямоугольник
FillEllipse() Кисть, положение и размер Сплошной эллипс
DrawLines() Перо, массив точек Серия линий, соединяющих каждую точку со следующей
DrawBezier() Перо, 4 точки Гладкая кривая через две конечные точки. Остальные точки управляют ее формой
DrawCurve() Перо, массив точек Гладкая кривая через все точки
DrawArc() Перо, прямоугольник, два угла Сегмент круга между заданными углами
DrawClosed-

Curve()

Перо, массив точек Гладкая кривая с прямой линией между и последней точками
DrawPie() Перо, прямоугольник, два угла Клиноподобный контур внутри прямоугольника
FillPie() Кисть, прямоугольник, два угла Сплошная клиноподобная область внутри прямоугольника
DrawPolygon() Перо, массив точек Подобно DrawLines(), но с соединением первой и последней точек для замыкания фигуры

8. Практическая работа. Разработка графических бизнес-приложений с использованием GDI+

Задание. Построить график функции y = e-4x·|cos(15x)|. Самостоятельно выбрать удобные параметры настройки. Пользователь задает количество точек графика, коэффициент упругости, а также шрифт для подписей осей координат.

8.1. Визуальное проектирование диалогового окна

Внешний вид окна приложения с описанием используемых компонентов приведен на рисунке 1.

Внешний вид приложения

Рисунок 1 – Внешний вид приложения

Для графической панели pictureBox1 устанавливаются следующие дополнительные свойства (таблица 1).

Таблица 1 – Свойства компонента pictureBox1

Свойство Значение Описание
BorderStyle Fixed3D Стиль границы
BackColor White Цвет фона

Коэффициент упругости графика может принимать значение в диапазоне от 0 до 1. Поэтому для счетчика TensitionNumericUpDown устанавливаются следующие свойства (таблица 2).

Таблица 2 – Свойства счетчика TensitionNumericUpDown

Свойство Значение Описание
Minimum 0 Минимальное значение
Maximum 1 Максимальное значение
Increment 0,1 Шаг инкремента
DecimalPlaces 1 Количество знаков после запятой

Также на форму нужно поместить компонент FontDialog (диалоговое окно выбора шрифта), для которого следует установить в true свойство ShowColor. Благодаря этому свойству в окне можно будет выбирать не только характеристики шрифта, но и его цвет. При желании можно также изменить шрифт по умолчанию (свойство Font).

8.2. Проектирование программного кода

Для обеспечения графического вывода данному приложению понадобятся кисть и шрифт. Поэтому в класс формы необходимо добавить объекты классов Font (шрифт) и SolidBrush (сплошная кисть):

Font font; SolidBrush brush;

Создание этих объектов может производиться в обработчике события Load или в конструкторе формы:

//шрифт берем установленный по умолчанию font = fontDialog1.Font;

//создаем сплошную кисть черного цвета brush = new SolidBrush(Color.Black);

При нажатии на кнопку «Выбрать шрифт» на экране должно появляться соответствующее диалоговое окно. Если после выбора настроек шрифта пользователь нажимает кнопку OK, то выбранные установки должны передаться объектам font и brush:

private void FontButton_Click(object sender, EventArgs e)

{

//если выбор шрифта завершен нажатием кнопки OK if (fontDialog1.ShowDialog() == DialogResult.OK)

{

//получить параметры шрифта из диалогового окна font = fontDialog1.Font;

//получить цвет шрифта из того же окна brush.Color = fontDialog1.Color;

}

}

Функцию, для которой будет рисоваться график, можно задать отдельно:

private double f(double x)

{

return Math.Exp(-4 * x) * Math.Abs(Math.Cos(15 * x));

}

Обработчик нажатия кнопки «Нарисовать график» состоит из нескольких частей:

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

Более подробное описание кода содержится в комментариях к листингу:

private void GraphicButton_Click(object sender, EventArgs e)

{

//Начало координат графика int x0 = 15;

int y0 = (int)(pictureBox1.Height * 0.85);

//Масштаб по оси Х

int Mx = pictureBox1.Width - 2 * x0;

//Масштаб по оси Y int My = -y0 + 10;

//Число точек графика

int M = (int)PointsNumericUpDown.Value;

//Создание графического объекта

Graphics G = pictureBox1.CreateGraphics();

//Очистка PictureBox1 G.Clear(Color.White);

//Описание и создание массива точек Point[] p = new Point[M];

//Цикл по числу точек графика for (int n = 0; n < M; n++)

{

//Физические координаты double x = (double)n / M;

double y = f(x);

//Экранные координаты int xi = (int)(x0 + Mx * x); int yi = (int)(y0 + My * y);

//заносим в массив вычисленные значения координат p[n] = new Point(xi, yi);

}

//коэффициент упругости графика

float tensition = (float)TensitionNumericUpDown.Value;

//рисование графика G.DrawCurve(Pens.Blue, p, tensition);

//Рисование оси Х

G.DrawLine(Pens.Black, x0, y0, x0 + Mx, y0);

//Рисование оси Y

G.DrawLine(Pens.Black, x0, y0, x0, y0 + My);

//Разметка оси Х

for (int n = 0; n <= 10; n++)

{

4);

}

}

//физическая координата штриха double x = n / 10.0;

//экранная координата штриха

int xi = (int)(x0 + Mx * x);

//Наносим штрих

G.DrawLine(Pens.Black, xi, y0, xi, y0 + 4);

//Наносим число

G.DrawString(x.ToString(), font, brush, xi - 9, y0 +

После создания данного обработчика при нажатии на кнопку «Нарисовать график» в компоненте PictureBox отображается график функции. Однако, если окно программы перекрыть другим окном или перетащить его (или только часть окна с графиком) за пределы экрана, а затем вернуть назад, то рисунок (или его часть) исчезнет.

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

Если форма или какой-то компонент поврежден, генерируется событие Paint, которое и нужно обрабатывать для перерисовки. Сгенерировать событие Paint вручную можно с помощью метода Invalidate().

Таким образом, для данного приложения весь код рисования нужно перенести из обработчика щелчка кнопки «Нарисовать график» в обработчик события Paint компонента pictureBox1.

При этом строка

Graphics G = pictureBox1.CreateGraphics();

заменяется на

Graphics G = e.Graphics;

т. е. графический объект для рисования мы получаем из параметра обработчика.

Обработчик кнопки «Нарисовать график» теперь будет содержать всего одну строчку – принудительный вызов перерисовки графической панели.

private void GraphicButton_Click(object sender, EventArgs e)

{

pictureBox1.Invalidate();

}

Теперь график будет выведен в панели постоянно: при запуске программы, при перекрытии окна, при нажатии кнопки (с новыми параметрами).