Лабораторная работа №3 по КГ 2009_2010

advertisement
Лабораторная работа №3
Тема: «Методы создания и редактирования изображений»
Кроме выполняемого кода и констант в загрузочном модуле приложения Microsoft
Windows находятся дополнительные данные — ресурсы.
Что такое ресурсы?
Приложение Microsoft Windows может хранить в виде ресурсов текстовые строки,
значки, курсоры, графические изображения, меню, диалоговые окна, произвольные
массивы данных и т. д. Физически ресурсы находятся внутри exe-файла приложения. Они
могут загружаться в оперативную память автоматически при запуске приложения или по
запросу приложения (явному или неявному). Такой механизм обеспечивает экономное
использование оперативной памяти, так как все редко используемые данные можно
хранить на диске и загружать в память только при необходимости.
Ресурсы можно редактировать без повторной трансляции программного проекта.
Это означает, что Вы сможете легко перевести все сообщения и тексты меню приложения
на другой национальный язык, отредактировать графические изображения или любые
другие ресурсы, даже не имея в своем распоряжении исходные тексты.
В приложениях Microsoft .NET Framework тоже используется концепция ресурсов.
В частности, ресурсы таких приложений могут содержать значки и графические
изображения.
Если приложение должно рисовать в своих окнах значки или графические
изображения, целесообразно включить их в ресурсы приложения. В этом случае данные
из файлов, содержащих значки или изображения, будут переписаны в файл сборки
приложения. Таким образом, Вы сможете поставлять приложение как единый
загрузочный файл, не «комплектуя» его дополнительно файлами графических
изображений.
Значки
Создайте приложение DrawIconApp обычным образом, пользуясь мастером проектов.
Затем перепишите в каталог проекта любой файл значка, взяв его, например, из каталога
значков системы Microsoft Visual Studio .NET.
Далее включите значок в проект приложения, щелкнув название проекта правой
клавишей мыши в окне Solution Explorer и выбрав затем строку контекстного меню Add
Existing Item. В результате всех этих действий файл значка появится в списке файлов
Вашего проекта.
Щелкните этот файл левой клавишей мыши, а затем установите значение свойства Build
Action для файла значка равным Embedded Resource.
В результате выполнения этих действий файл значка будет добавлен в ресурсы
приложения.
Например, вывод значка по щелчку мыши в окне формы.
Создайте обработчик события Form1_MouseDown, получающий управление, когда
пользователь щелкает левой клавишей мыши окно приложения:
private void Form1_MouseDown(object sender,
System.Windows.Forms.MouseEventArgs e)
{
Graphics g = Graphics.FromHwnd(this.Handle);
Icon myIcon = new Icon(GetType(),"имя файла со значком");
g.DrawIcon(myIcon, e.X, e.Y);
}
1
Этот обработчик получает контекст отображения с помощью метода Graphics.FromHwnd,
а затем создает объект класса Icon, представляющий значок. Для создания этого объекта
мы использовали конструктор с двумя параметрами.
Через первый параметр конструктору класса Icon мы передаем ссылку на объектсборку, в котором хранится наш ресурс — значок из файла *.ICO. Мы можем получить
такую ссылку при помощи метода GetType.
Что же касается второго параметра, то через него мы передаем имя ресурса,
которое в нашем случае будет совпадать с именем файла значка.
Функция DrawIcon рисует значок myIcon в точке, где был курсор мыши в момент
щелчка.
Всего существует два перегруженных метода DrawIcon:
public void DrawIcon(Icon, Rectangle);
public void DrawIcon(Icon, int, int);
В первом варианте метода параметр типа Rectangle задает прямоугольную область,
внутри которой будет нарисован значок. При этом значок будет масштабирован таким
образом, чтобы занять собой всю заданную область.
Независимо от значка myIcon, фрагмент кода, приведенный ниже, рисует в левом
верхнем углу значок с размерами 100x100 пикселов:
g.DrawIcon(myIcon, new Rectangle(0, 0, 100, 100));
Если исходный значок меньше указанных размеров, при рисовании он будет
растянут, а если больше — сжат.
Для рисования значков можно также использовать метод DrawIconUnstretched:
public void DrawIconUnstretched(Icon, Rectangle);
Этот метод рисует значок внутри области, заданной с помощью второго параметра. В
отличие от метода DrawIcon, метод DrawIconUnstretched не растягивает и не сжимает
значок, а рисует его в точном соответствии с оригинальными размерами значка.
Не обязательно включать значок в проект приложения и добавлять его к ресурсам. Можно
воспользоваться вариантом конструктора класса Icon, допускающим указание в своем
единственном параметре имени или пути к файлу значка. Если указать только имя значка,
то при запуске приложения файл значка должен будет находиться в том же каталоге, что и
exe-файл самого приложения.
Использование кисти
Кисти предназначены для «закрашивания» пространства между линиями. Для кисти
можно определить: цвет, текстуру или даже изображение. Класс Brush является
абстрактным, поэтому можно использовать производные классы, от Brush, такие как
SolidBrush, HatchBrush, LinearGradientBrush и т. п.
Создавать объекты кистей (выбрав из заранее готового набора) можно при помощи типовколлекций Brushes и System. Brushes, также определенных в пространстве имен System.
Drawing. Создание объектов из этих типов коллекции производится при помощи их
статических свойств. Далее можно передать созданный объект кисти в качестве параметра
соответствующему методу объекта Graphics. В ранее мы передавали объект класса
SolidBrush методу Graphics. DrawString() для вывода текстовых строк. Кроме того,
2
выбранную кисть также можно использовать для создания объекта Реn (перо). Это перо
сможет рисовать линии, используя все возможности кисти (например, линия будет
покрыта текстурами или изображением).
Таблица 2. Методы FillXXXX() класса Graphics
Метод
FillClosedCurve()
FillEllipse()
FillPie()
FillPolygon()
FillRectangle(),
FillRectangles()
FillRegion()
Значение
Закрашивает область внутри замкнутой кривой
Закрашивает область внутри эллипса
Закрашивает область внутри сегмента эллипса
Закрашивает область внутри многоугольника
Закрашивает область внутри прямоугольника (нескольких прямоугольников)
Закрашивает внутреннюю область объекта Region (Region — это внутренняя
область геометрической фигуры)
Подключите
using System.Drawing.Drawing2D;
и объявите переменную LinearGradientBrush:
****
public partial class Form1 : Form
{
private Brush LinearGradientBrush;
****
Пример кода с использованием кистей:
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Rectangle r = new Rectangle(0, 10, 100, 100);
LinearGradientBrush theBrush = null;
int y = 10;
// Получаем все значения перечисления LinearGradientMode
Array obj = Enum.GetValues(typeof(LinearGradientMode));
// Выводим прямоугольник, используя значения LinearGradientMode
for (int x = 0; x < obj.Length; x++)
{
// Настраиваем кисть
LinearGradientMode temp = (LinearGradientMode)obj.GetValue(x);
theBrush = new LinearGradientBrush(r, Color.Red, Color.Blue, temp);
// Выводим имя значения перечисления
g.DrawString(temp.ToString(), new Font("Times New Roman", 10),
new SolidBrush(Color.Black), 0, y);
// Заполняем прямоугольник при помощи градиентной кисти
g.FillRectangle(theBrush, 150, y, 200, 50);
y += 80;
}
Использование штриховых кистей
Более сложное «закрашивание» можно произвести при помощи производного от Brush
класса HatchBrush, определенного в пространстве имен System. Drawing. Drawing2D. Этот
тип позволяет закрасить внутреннюю область объекта при помощи большого количества
шаблонов штриховки, определенных в перечислении HatchStyle. Этих шаблонов много,
поэтому в табл. 3 приведены только некоторые из них.
Таблица 3. Значения перечисления HatchStyle (стили штриховки)
Значение
BackwardDiagonal
Crass
DiagonalCross
Forward Diagonal
Hollow
Horizontal
Описание
Диагональная штриховка с наклоном вправо
«Крестообразная» штриховка, состоящая из пересекающихся вертикальных и
горизонтальных линий
Еще одна разновидность «крестообразной» штриховки, состоящая из
пересекающихся диагональных линий
Диагональная штриховка с наклоном влево
«Пустая» кисть, которая ничего не рисует
Горизонтальная штриховка
3
Pattern
Solid
Vertical
Штриховка, которая создается на основе указанного пользователем растрового
изображения
кисть без всякой штриховки (аналогично обычному типу SolidBrush)
Вертикальная штриховка
При создании объекта HatchBrush обязательно нужно указать два цвета: цвет «переднего
плана» и цвет фона. В качестве примера приведем код, который будет выводить эллипсы
со штриховкой:
private void Form_Paint(object sender, PaintEventArgs e)
{Graphics g = e.Graphics;
int y = 10;
// Помещаем в массив все члены перечисления HatchStyle
Array obj = Enum.GetValues(typeof(HatchStyle));
// Выводим эллипс со штриховкой, соответствующей членам перечисления
HatchStyle
// с 1 по 10
for(int х =0; х < 10; х++)
{
// Настраиваем кисть
HatchStyle temp = (HatchStyle)obj.GetValue(x);
HatchBrush theBrush = new HatchBrushUemp.Color.White, Color.Black);
// Выводим имя каждого из значений перечисления
g.DrawString(temp.ToString(), new Font ("Times New Roman", 10),
new SolidBrush (Color.Black), 0, y)
// Закрашиваем эллипс штриховой кистью
g.FillEllipse(theBrush, 150, y, 200, 25);
y += 40;
}}
Использование текстурных кистей
Помимо штриховых кистей, в распоряжении разработчика GDI+ имеются также
текстурные кисти, представленные типом TextureBrush. Эти кисти «закрашивают»
отведенную для этого область текстурой — то есть указанным растровым изображением.
Проиллюстрируем применение класса TextureBrush на примере. Этот класс будет
использован для того, чтобы:
• залить всю клиентскую площадь формы текстурой из файла;
• залить выводимый на форме текст текстурой на основе файла .
В первую очередь (в конструкторе для формы) создаем два объекта TextureBrush.
Обратите внимание, что конструктор TextureBrush требует ссылки на объект Image:
public partial class Form1 : Form
{// переменные типа Brush для загрузки изображений
private Brush texturedTextBrush;
private Brush texturedBGroundBrush;
public Form1()
{
InitializeComponent();
this.Paint += new PaintEventHandler(Form1_Paint);
// Загружаем изображение для текстуры формы
Image bGroundBrushImage = new Bitmap("D:/3.png");
texturedBGroundBrush = new TextureBrush(bGroundBrushImage);
// Загружаем изображение для текстуры теста
Image textBrushImage = new Bitmap("D:/2.png");
texturedTextBrush = new TextureBrush(textBrushImage);
}
Привяжем их к форме и тексту на форме соответственно:
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Rectangle r = ClientRectangle;
// Выводим облака на форме
4
g.FillRectangle(texturedBGroundBrush, r);
// Текст должен быть больший - чтобы можно было разглядеть текстуру
g.DrawString("Компьютерная графика", new Font("Arial", 60, FontStyle.Bold |
FontStyle.Italic), texturedTextBrush, r);
}
Использование градиентных кистей
Градиентная кисть, представлена типом LinearGradientBrush. Основное назначение этого
типа — обеспечить плавное смешение двух цветов для получения градиентного перехода.
Особенность использования этого типа заключается в том, что необходимо указать
направление цветового перехода при помощи значений из перечисления
LinearGradientMode (табл.4).
Таблица 4. Значения перечисления LinearGradientMode
Значение
BackwandDiagonal
ForwardDiagonal
Horizontal
Vertical
Описание (направление перехода)
Из верхнего правого угла в нижний левый
Из верхнего левого угла в нижний правый
Слева направо
Сверху вниз
Рассмотрим каждое из значений LinearGradientMode в действии, нарисуем на форме
серию прямоугольников при помощи LinearGradientBrush. В качестве цветов, между
которыми будет происходить переход, выберем Color. Red и Color. Blue:
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
Rectangle r = new Rectangle(0,10,100,100);
LinearGradientBrush theBrush = null;
int y=10;
// Получаем все значения перечисления LinearGradientMode
Array obj = Enum.GetValues(typeof(LinearGradientMode));
// Выводим прямоугольник, используя значения LinearGradientMode
for (int x = 0; x < obj.Length; x++)
{
// Настраиваем кисть
LinearGradientMode temp = (LinearGradientMode)obj.GetValue(x);
theBrush = new LinearGradientBrush(r, Color.Red, Color.Blue, temp);
// Выводим имя значения перечисления
g.DrawString(temp.ToString(), new Font("Times New Roman", 10),
new SolidBrush(Color.Black), 0, y);
// Заполняем прямоугольник при помощи градиентной кисти
g.FillRectangle(theBrush, 150, y, 200, 50);
y += 80;}}
Растровые и векторные изображения
В ОС Microsoft Windows используются два формата изображений — аппаратнозависимый DDB (Device-Dependent Bitmap) и аппаратно-независимый DIB (DeviceIndependent Bitmap).
Согласно определению, данному в документации, изображение DDB есть набор
битов в оперативной памяти, который может быть отображен на устройстве вывода
(например, выведен на экран видеомонитора или распечатан на принтере). Внутренняя
структура изображения DDB жестко привязана к аппаратным особенностям устройства
вывода. Поэтому представление изображения DDB в оперативной памяти полностью
зависит от устройства вывода. Иногда такие изображения называют растровыми,
подчеркивая тот факт, что их можно рассматривать как совокупность строк растра
(горизонтальных линий развертки).
5
Если бы в Microsoft Windows можно было работать только с изображениями DDB,
было бы необходимо иметь отдельные наборы изображений для каждого типа
видеоадаптера и каждого видеорежима, что, очевидно, крайне неудобно.
Аппаратно-независимое изображение DIB содержит описание цвета пикселов
изображения, которое не зависит от особенностей устройства отображения. ОС Microsoft
Windows после соответствующего преобразования может нарисовать такое изображение
на любом устройстве вывода. Несмотря на некоторое замедление процесса вывода по
сравнению с выводом изображений DDB, универсальность изображений DIB делает их
весьма привлекательными для хранения изображений.
При создании обычных приложений Microsoft Windows, работающих файлами
изображений, программисту следовало учитывать, что существует множество форматов
таких файлов. Файлы изображений могут содержать таблицу цветов или цветовую
палитру, могут быть сжаты с использованием тех или иных алгоритмов. Приложение
должно также анализировать структуру графических файлов с целью обнаружения
возможных ошибок, так как отображение содержимого неправильного файла может
создать полный хаос на экране.
Кроме растровых изображений используются так называемые векторные
изображения. Если растровые изображения содержат описание цвета каждого пиксела,
векторные изображения состоят из описаний отдельных графических объектов, из
которых состоит изображение. Это могут быть линии, окружности и т. п. Некоторые
графические редакторы, например, Corel Draw, Microsoft Draw, Microsoft Visio, для
внешнего представления изображения используют векторный формат.
Сравнивая эти форматы, отметим, что каждый из них имеет свои преимущества и
свои недостатки.
Растровые изображения, как правило, выводятся на экран быстрее, так как их
внутренняя структура аналогична (до некоторой степени) структуре видеопамяти. К
недостаткам растровых изображений можно отнести большой объем памяти,
требующийся для их хранения (которых доходит до десятков и сотен Мбайт),
невозможность масштабирования без потери качества изображения, а также сложность
выделения и изменения отдельных объектов изображения.
Векторные изображения состоят из описаний отдельных элементов, поэтому они
легко масштабируются. С помощью такого графического редактора, как, например, Corel
Draw, Вы без особого труда сможете выделить отдельный элемент изображения и
изменить его внешний вид. Следует отметить, что некоторые устройства вывода, такие
как плоттер (графопостроитель), способен работать только с векторными изображениями,
так как с помощью пера можно рисовать только линии. В операционной системе Microsoft
Windows и многих офисных приложениях широко используется такой векторный формат
изображений, как метафайл WMF (Windows Metafile).
Вывод изображений
Тип System.Drawing. Image используется для вывода изображений. Класс Image
определяет множество свойств и методов, которые можно использовать для настройки
параметров выводимого изображения. К примеру, при помощи свойств Width, Height и
Size можно получить или установить размеры изображения. Кроме того, в пространстве
имен System. Drawing. Imaging определено множество типов для проведения сложных
преобразований изображений.
Наиболее важные члены класса Image представлены в табл.5. Многие из этих членов
являются статическими, а некоторые — абстрактными.
6
Таблица 5. Члены класса Image
Член
FromFile()
FromHbitmap()
FromStream()
Height
Width
Size
Physical Dimensions
Horizontal Dimensions
Vertical Resolution
GetBounds()
Save()
Назначение
Этот статический метод предназначен для создания объекта Image из
файла
Создает объект Bitmap на основе идентификатора окна (Window handle)
Позволяет создать объект Image, используя в качестве источника поток
данных
Все эти свойства предназначены для работы с размерами (измерениями)
изображения
Возвращает прямоугольник, представляющий текущую область, занятую
изображением
Позволяет сохранить изображение в файл
Загрузка изображения
Для загрузки изображения в память используется метод Image.FromFile, передавая ему
самый первый путь к файлу, извлеченный из нулевой ячейки массива files:
Image img;
…
img = Image.FromFile(“путь к файлу”);
После того как изображение будет загружено, приложение сообщает о необходимости
перерисовки его окна, вызывая для этого метод Invalidate:
Invalidate();
В результате будет создано событие Paint, обработчик которого займется рисованием
загруженного изображения в окне программы.
Так как при рисовании изображения используем масштабирование, это же событие будем
создавать и при изменении размеров окна приложения:
private void Form1_Resize(object sender, System.EventArgs e)
{
Invalidate();
}
Изменение размеров окна приводит к генерации события Resize.
Рисование загруженного изображения
Код обработчика события Form1_Paint, на который возложена работа по рисованию
загруженного изображения:
private void Form1_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
if(img != null)
{
7
RectangleF winRect = new RectangleF(0, 0, e.Graphics.VisibleClipBounds.Width,
e.Graphics.VisibleClipBounds.Height);
SizeF size = new SizeF(img.Width / img.HorizontalResolution, img.Height /
img.VerticalResolution);
float scale = Math.Min(winRect.Width / size.Width, winRect.Height / size.Height);
size.Width *= scale;
size.Height *= scale;
e.Graphics.DrawImage(img, winRect.X + (winRect.Width - size.Width) / 2,
winRect.Y + (winRect.Height - size.Height) / 2, size.Width, size.Height);
}
}
Как видите, большая часть программного кода выполняет масштабирование, а для
рисования вызываем один из перегруженных методов Graphics.DrawImage.
Класс Image является абстрактным, и создавать объекты этого класса нельзя.
Обычно объявленные переменные Image присваиваются объектам класса Bitmap. Кроме
того, можно создавать объекты класса Bitmap напрямую. Например, предположим, что
необходимо вывести на форму три изображения. Можем объявить три переменные Image,
а затем использовать для каждой из них объекты Вitmap;
public partial class Form1 : Form
{
// Объявляем переменные типа Image
private Image bMapImageA;
Вывод полученных изображений производится с помощью специального метода, который
называется — DrawImage(). Этот метод многократно перегружен, поэтому есть
множество вариантов того, как поместить изображение в нужное место на форме. Кроме
того, для настройки параметров выводимого изображения можно использовать с этим
методом значения перечислений ImageAttributes и GraphicsUnit. Координаты вывода
изображения можно указать при помощи объектов Point (точка), Rectangle
(прямоугольник), целочисленными значениями или значениями с плавающей запятой.
Например:
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
// Используем объекты класса Bitmap
bMapImageA = new Bitmap("D:\\1.png");
// Выводим изображения при помощи Graphics.DrawImage()
g.DrawImage(bMapImageA, 10, 10);
}
Класс Bitmap позволяет выводить изображения, которые хранятся в файлах самого
разного формата. Например:
// Тип Bitmap поддерживает все распространенные форматы!
Bitmap myBMP = new Bitmap(“CoffeeCup.bmp"};
Bitmap myGIF = new Bitmap(“Candy.gif");
Bitmap ntyJPEG = new Bitmap(“Clock.jpg");
Bitmap myPNG = new Bitmap("Speakers.png");
// Выводим изображения при помощи Graphics.DrawImage()
8
g.DrawImage(myBmp, 10, 10);
g.DrawImage(myGIF, 220, 10);
g.DrawImage(myJPEG, 280, 10);
g.DrawImage(myPNG, 150, 200);
Вывод изображений и двойная буферизация.
Часто встречающейся проблемой при создании динамично меняющейся графики (и, в
частности, анимации) является мерцание. Для его устранения традиционно использовался
закадровый буфер, и эта возможность также имеется в GDI+. Объект Graphics можно
создать "теневым", используя в качестве основы готовый экземпляр Bitmap.
Способ уменьшения мерцания – использовать двойную буферизацию вывода – когда
изображение сначала готовится "за кадром", а затем переносится на экран, устраняя
мерцание при анимации:
Рассмотрим пример вывода с двойной буферизацией в GDI+ :
using System.Threading;//добавляем для использования задержки
using System.Collections;//для использования методов вывода изображений
Добавьте метод Paint
private void Form1_Paint(object sender, PaintEventArgs e)
{//Graphics g = e.Graphics;
Graphics displayGraphics = e.Graphics;
int k=0;
while(k <300)
{
Pen p = new Pen(Color.Red);
SolidBrush b= new SolidBrush(Color.Yellow);
SolidBrush b1= new SolidBrush(Color.Green);
//создаем объект - изображение, которое будет включать все, что нарисовано на //форме
Image i = new Bitmap(ClientRectangle.Width, ClientRectangle.Height);
Graphics g = Graphics.FromImage(i);
g.Clear(Color.Aqua);
Thread.Sleep(300);//задержка на 300 миллисекунд
g.FillEllipse(b,k,150,30,30);
g.FillEllipse(b1,k+5,160,5,5);
g.FillEllipse(b1,k+20,160,5,5);
g.DrawArc(p,k+5,165,20,10,0,180);
g.FillRectangle(b1,0,180,300,100);
k=k+10;
b.Dispose();//уничтожаем кисть
b1.Dispose();
//выводим на форму, созданное изображение
displayGraphics.DrawImage(i, ClientRectangle);
i.Dispose();//уничтожаем изображение
}
}
}
Для сравнения закомментируйте строки выделенные красным цветом и снимите
комментарий со строки : Graphics g = e.Graphics;
9
ИНДИВИДУАЛЬНЫЕ ЗАДАНИЯ:
1. Добавить значок в заголовок окна формы.
2. Раскрасить рисунок, созданный в первой лабораторной работе, с использованием
разных типов кистей.
3. 1 Вариант: В форму добавить картинку, которая меняет место положение по
нажатию на клавиши вверх, вниз, влево, вправо.
2 Вариант: В форму добавить картинку, которая меняет размеры по нажатию на
клавиши мыши: на левую увеличивается, на правую уменьшается.
3 Вариант : Создать объект, который за счет использования градиентной кисти,
раскрасить таким образом, чтобы появился эффект объема.
4 Вариант: Создать объект, который при нажатии клавиши мыши, меняет место
положения по горизонтали, и изменяется вид штриховой заливки.
5 Вариант: Создать объект, который при нажатии клавиши Т, меняет вид
текстурной заливки, при нажатии клавиши Ш и изменяется вид штриховой
заливки, при нажатии клавиши Ц меняет цвет на зеленый с градиентной заливкой.
6 Вариант: Вставить картинку в форму вокруг нарисовать декоративную рамку, по
нажатии на любую клавишу клавиатуры изменяется вид заливки рамки: с
штриховой на градиентную, а затем на текстурную.
3. Анимировать изображение, созданное во второй лабораторной работе.
КОНТРОЛЬНЫЕ ВОПРОСЫ:
1. Как создать кисть?
2. Методы, используемые для закрашивания областей?
3. Типы кистей, их основные различия?
4. Как создать штриховое заполнение области?
5. Как создать текстурное заполнение области?
6. Как создать градиентное заполнение области?
7. Как вывести изображение в форму?
8. Методы для настройки параметров изображения?
9. Для чего используют двойную буферизацию?
10. Методы, которые используются при двойной буферизации, как вывести
изображение, как создать изображение?
10
Download