Тема: «Работа с графикой»
План
1. Компонент для работы с графикой
2. Компонент ImageList
3. Работа с таймером
4. Элементы анимации
1. Компонент для работы с графикой
Нередко возникает потребность украсить свое приложение изображениями. Для работы с графическими файлами используется компонент Image (Additional). Основное его свойство, которое может содержать изображения - Picture. Нажмите на кнопку с тремя точками у этого свойства или просто сделайте двойной щелчок на Image, и перед вами откроется окно Picture Editor, которое позволяет загрузить в свойство Picture какой-нибудь графический файл (кнопка Load), а также сохранить открытый файл под новым именем на диске.
Когда вы в процессе проектирования загрузили изображение из файла в компонент Image, он не просто отображает его, но и сохраняет в приложении. Это дает вам возможность поставлять ваше приложение без отдельного графического файла. Впрочем, в Image можно загружать и внешние графические файлы в процессе выполнения приложения.
У компонента Image есть еще ряд важных свойств.
Если установить свойство Autosize в true, то размер компонента Image автоматически подгонится под размер помещенной в него картинки. Если же свойство Autosize установлено в false, то изображение может не поместиться в компонент или, наоборот, площадь компонента может оказаться много больше площади изображения.
Другое свойство - Stretch позволяет подогнать не сам компонент под размер рисунка, а рисунок под размер компонента. При этом возможно, что пропорции сторон изображения будут искажены. Устанавливать Stretch в true может иметь смысл только для каких-то узоров или фоновых изображений. Свойство Stretch не действует на изображения пиктограмм, которые не могут изменять своих размеров.
Свойство Center, установленное в true, центрирует изображение на площади Image. Если размер рисунка окажется больше площади компонента, то края изорбражения будут усечены.
Свойство Proportional позволяет подогнать рисунок под размер компонента с сохранением пропорций. Но при этом возможно, что будет заполнена не вся плоскость компонента Image. В єтом случае рекомендуется использовать данное свойство со свойством Center.
Рассмотрим еще одно свойство - Transparent (прозрачность). Если Transparent ровно true, то изображение в Image становится прозрачным. Это можно использовать для наложения изображений друг на друга. Учтите, что свойство Transparent действует только на файлы в формате BMP. Прозрачным будет сделан цвет, который имеет пиксель изображения, расположенный в левом нижнем углу исходной картинки.
Создание форм с фоновым рисунком
Как известно в формы есть свойство Color, с помощью которой можно задать для окна любой цвет фона. Но ваша программа может иметь еще более привлекательный вид, если в качестве фона формы выбрать графическое изображение. Для этого необходимо выполнить действия
Рисунок, помещенный в компонент Image, встраивается в приложение, одже получаемый файл программы увеличивается на размер графического файла.
Диалоги работы с графическими файлами
В Delphi есть компоненты для открытия и сохранения графических файлов. Они находятся на вкладке Dialogs и называются Openpicturedialog (Dialogs) и Savepicturedialog (Dialogs). Эти диалоги имеют те же свойства и методы, что и стандартные диалоги по работе с файлами. Отличие в том, что в самом диалоговом окне есть специальная область для предварительного просмотра содержимого графических файлов.
Свойство Picture компонента Image в свою очередь имеет методы для открытия и сохранения файлов:
Image1.Picture.LoadFromFile('имя_файла'); // открыть файл
Image1.Picture.SaveToFile('имя_файла'); // сохранить файл
Команда открытия файла может иметь вид:
if OpenPictureDialog1.Execute then
Image1.Picture.LoadFromFile(OpenPictureDialog1.FileName);
Команда сохранения файла может иметь вид:
if SavePictureDialog1.Execute then
Image1.Picture.SaveToFile(SavePictureDialog1.FileName);
Сохранение графики в различных форматах
По умолчанию Delphi сохраняет все изображения в формате BMP независимо от того, какое расширение файла вы укажете. Для хранения в другие форматы надо использовать специальные модули итипы данных для конвертирования информации.
1. Сохранение в формате BMP.
Формат BMP поддерживается автоматически.
Пример. На форме есть компонент Image с изображением. Необходимо сохранить изображение в формат BMP.
// переменная для работы с графикой в формате BMP
var bmp: TBitMap;
begin
// если пользователь не указал имя для сохранения файла
// программа завершает работу
if not SavePictureDialog1.Execute then Exit;
// создаем экземпляр переменной
bmp:=TBitMap.Create;
// в переменную Загружаем рисунок с компонента Image (Конвертируем автоматически)
bmp.Assign(Image1.Picture.Graphic);
// задаем расширение для файла
SavePictureDialog1.DefaulExt := 'bmp';
// сохраняем файл на диске
bmp.SaveToFile (savepicturedialog1.filename);
// удаляем переменную
bmp.Free;
end;
2. Сохранение в формате JPG.
Для работы с этим форматом существует стандартный модуль JPEG, который нужно подключить в приложении.
Пример. Предыдущий пример перепишем для сохранения изображения в формате JPG
// переменная для работы с графикой в формате JPG
var jpg: TJpegImage;
begin
// если пользователь не указал имя для сохранения файла
// программа завершает работу
if not SavePictureDialog1.Execute then Exit;
// создаем экземпляр переменной
jpg:=TJpegImage.Create;
// в переменную Загружаем рисунок с компонента Image (Конвертируем автоматически)
jpg.Assign (Image1.Picture.Graphic);
// задаем расширение для файла
SavePictureDialog1.DefaulExt := 'jpg';
// сохраняем файл на Диске
jpg.SaveToFile (SavepictureDialog1.FileName);
// удаляем переменную
jpg.Free;
end;
3 . Сохранение в формате GIF.
Для работы с этим форматом т Реба использовать внешний модуль GifImage, которого нет в Delphi. Для использования этого модуля скопируйте файл GifImage.pas в папку с вашим проектом и подключите модуль GifImage в коде программы.
Пример. Предыдущий пример перепишем для сохранения изображения в формате GIF
// переменная для работы с графикой в формате GIF
var gif: TGifImage;
begin
// если пользователь не указал имя для сохранения файла
// программа завершает работу
if not SavePictureDialog1.Execute then Exit;
// создаем экземпляр переменной
gif:=TGifImage.Create;
// в переменную Загружаем рисунок с компонента Image (Конвертируем автоматически)
gif.Assign(image1.Picture.Graphic);
// задаем расширение для файла
SavePictureDialog1.DefaulExt := 'gif';
// сохраняем файл на диске
gif.SaveToFile(SavePictureDialog1.FileName);
// удаляем переменную
gif.Free;
end;
4 . Сохранение в формате PNG.
Для работы с этим форматом PNG использовать внешний модуль PNGImage, которого нет в Delphi. Для использования этого модуля скопируйте содержимое папки PNGImage в папку с вашим проектом и подключите модуль PNGImage в коде программы.
Особенностью работы этого формата является то, что изображение сначала конвертируется в BMP, а затем - в PNG.
Пример. Предыдущий пример перепишем для сохранения изображения в формате PNG
// переменная для работы с графикой в формате BMP и PNG
var bmp: TBitMap;
png: TPNGObject;
begin
// если пользователь не указал имя для сохранения файла
// программа завершает работу
if not SavePictureDialog1.Execute then Exit;
// создаем экземпляр переменной для BMP формата
bmp:=TBitMap.Create;
// в переменную Загружаем рисунок с компонента Image (конвертируем автоматически)
bmp.Assign(Image1.Picture.Graphic);
// создаем экземпляр переменной для PNG формата
png:=TPNGObject.Create;
// в переменную Загружаем BMP изображения
png.Assign(bmp);
// задаем расширение для файла
SavePictureDialog1.DefaulExt := 'png;
// сохраняем файл на диске
png.SaveToFile (SavePictureDialog1.FileName);
// удаляем переменную
png.Free;
end;
Изменение размера изображения
Для изменения размера использую следующий подход:
- создать экземпляр объекта TBitMap и загрузить в него исходное изображение;
- создать второй экземпляр объекта TBitMap и задать для него новые ширину и высоту;
- с помощью WinAPI функции скопировать исходный TBitMap в новый TBitMap;
- полученное изображение готово для дальнейших операций.
Пример: пусть исходное изображение отображается в компоненте Image1. На форме находятся два текстовых поля. В первое поле вводится новая ширина, а во второе - новая высота. При щелчком на кнопке исходное изображение меняется в указанных размеров.
procedure TForm3.Button1Click (Sender: TObject);
// переменные для работы с изображением
// bmp - исходное изображение
// tempbmp - полученное изображение после изменения размера
var
bmp, tempbmp: TBitMap;
begin
// если один из размеров изображения не указан
if (Edit1.Text = '') or (Edit2.Text = '') then
begin
// выдаем сообщение об ошибке
Application.MessageBox('Выполнение операции невозможно. Укажите правильно размер изображения',
'Ошибка', MB_OK + MB_IconError);
// завершаем работу процедуры
Exit;
end;
// создаем экземпляр класса для исходного изображения
bmp:=TBitMap.Create;
// помещаем в класс изображение из компонента Image на главной форме
bmp.Assign(Image1.Picture.Graphic);
// создаем экземпляр класса для нового изображения
tempbmp := TBitMap.Create;
// задаем новом изображению ширину и высоту из полей на форме
tempbmp.Width:=StrToInt(Edit1.Text);
tempbmp.Height:=StrToInt(Edit2.Text);
// задаем параметры нового изображения
tempbmp.PixelFormat:=pf24bit;
// задаем для нового изображения гладкое масштабирование
SetStretchBltMode(tempbmp.Canvas.Handle, 4);
// копируем исходное изображение в новое (происходит увеличение)
StretchBlt(tempbmp.Canvas.Handle, 0,0, tempbmp.Width, tempbmp.Height
bmp.Canvas.Handle, 0,0, bmp.Width, bmp.Height, SRCCOPY);
// в компонент Image помещаем полученное изображение
Image1.Picture.Assign(tempbmp);
// удаляем из памяти Экземпляры класса
tempbmp.Free;
bmp.Free;
// выдаем сообщение об успешном завершении операции
Application.MessageBox('Обработка завершена. Вы можете сохранить изображение в файл' ,
'Операция завершена', MB_OK + MB_IconInformation);
end;
Рассмотренный пример требует указать ширину и высоту. Если указывать эти две величины, то может нарушиться коэффициент соотношения сторон рисунка. Поэтому целесообразнее поступать так:
- рассчитать коэффициент пропорции по формуле ширина / высота;
- указывайте одно из значений: если указана ширина, то высота рассчитывается как ширина / коэффициент; если указана высота, то ширина рассчитывается как высота * коэффициент.
Пример. Пусть в рассмотренных выше примере на форме есть одно текстовое поле для указания новой ширины рисунка. Тогда в код нужно внести изменения:
procedure TForm3.Button1Click (Sender: TObject);
// переменные для работы с изображением
// bmp - исходное изображение
// tempbmp - полученное изображение после изменения размера
// p - коэффициент пропорции
var
bmp, tempbmp: TBitMap;
p: real;
begin
// если один из размеров изображения не указан
if (Edit1.Text = '') or (Edit2.Text = '') then
begin
// выдаем сообщение об ошибке
Application.MessageBox('Выполнение операции невозможно. Укажите правильно размер изображения' ,
'Ошибка', MB_OK + MB_IconError);
// завершаем работу процедуры
Exit;
end;
// создаем экземпляр класса для исходного изображения
bmp:=TBitMap.Create;
// помещаем в класс изображение из компонента Image на главной форме
bmp.Assign(Image1.Picture.Graphic);
// Рассчитываем коэффициент пропорции исходного изображения
p:=bmp.Width / bmp.Height;
// создаем экземпляр класса для нового изображения
tempbmp:=TBitMap.Create;
// задаем новому изображению ширину из поля
tempbmp.Width:=StrToInt (Edit1.Text);
// задаем новому изображению высоту по формуле через коэффициент пропорции
tempbmp.Height:=Int(temp bmp.Width / P) ;
// задаем параметры нового изображения
tempbmp.PixelFormat:=pf24bit;
// задаем для нового изображения гладкое масштабирование
SetStretchBltMod(tempbmp.Canvas.Handle, 4);
// копируем исходное изображение в новое (происходит увеличение)
StretchBlt(tempbmp.Canvas.Handle, 0,0, tempbmp.Width, tempbmp.Height,
bmp.Canvas.Handle, 0,0, bmp.Width, bmp.Height, SRCCOPY);
// в компонент Image помещаем полученное изображение
Image1.Picture.Assign(tempbmp);
// удаляем из памяти Экземпляры класса
tempbmp.Free;
bmp.Free;
// выдаем сообщение об успешном завершении операции
Application.MessageBox('Обработка завершена. Вы можете сохранить изображение в файл',
'Операция завершена', MB_OK + MB_IconInformation);
end;
Пример. Пусть в рассмотренных выше примере на форме есть одно текстовое поле для указания новой высоты рисунка. Тогда в код нужно внести изменения:
procedure TForm3.Button1Click (Sender: TObject);
// переменные для работы с изображением
// bmp - исходное изображение
// tempbmp - полученное изображение после изменения размера
// p - коэффициент пропорции
var
bmp, tempbmp: TBitMap;
p: real;
begin
// если один из размеров изображения не указан
if (Edit1.Text = '') or (Edit2.Text = '') then
begin
// выдаем сообщение об ошибке
Application.MessageBox('Выполнение операции невозможно. Укажите правильно размер изображения',
'Ошибка', MB_OK + MB_IconError);
// завершаем работу процедуры
Exit;
end;
// создаем экземпляр класса для исходного изображения
bmp:=TBitMap.Create;
// помещаем в класс изображение из компонента Image на главной форме
bmp.Assign(Image1.Picture.Graphic);
// Рассчитываем коэффициент пропорции исходного изображения
p:=bmp.Width / bmp.Height;
// создаем экземпляр класса для нового изображения
tempbmp:=TBitMap.Create;
// задаем новом изображению высоту из поля
tempbmp.Height := StrToInt(Edit1.Text);
// задаем новом изображению ширину по формуле через коэффициент пропорции
tempbmp.Width := Int(tempbmp.Height * P) ;
// задаем параметры нового изображения
tempbmp.PixelFormat:=pf24bit;
// задаем для нового изображения гладкое масштабирование
SetStretchBltMode(tempbmp.Canvas.Handle, 4);
// копируйте исходное изображение в новое (происходит увеличение)
StretchBlt(tempbmp.Canvas.Handle, 0,0, tempbmp.Width, tempbmp.Height,
bmp.Canvas.Handle, 0,0, bmp.Width, bmp.Height, SRCCOPY);
// в компонент Image помещаем полученное изображение
Image1.Picture.Assign(tempbmp);
// Удаляем из памяти Экземпляры класса
tempbmp.Free;
bmp.Free;
// выдаем сообщение об успешном завершении операции
Application.MessageBox('Обработка завершена. Вы можете сохранить изображение в файл',
'Операция завершена', MB_OK + MB_IconInformation);
end;
Работа с буфером обмена
Существует возможность обмениваться графической информацией через буфер обмена. Для этого можно использовать специальный объект Clipboard, что имеет методы копирования и вставки. Для работы с этим объектом нужно в коде формы подключить модуль Clipbrd.
Для копирования содержимого компонента Image в буфер обмена можно использовать код:
Clipboard.Assign(Image1.Picture);
Для вырезания содержимого компонента Image в буфер обмена можно использовать код:
Clipboard.Assign(Image.Picture);
// очищаем компонент image1
Image1.Picture:=nil;
Вставка содержимого буфера обмена в компонент Image связана с проблемой формата данных. Если в буфере находится текст, то попытка вставки приведет к ошибке. Поэтому нужно проверять формат информации в буфере перед вставкой. Это можно сделать с помощью кода:
// если в буфере обмена графическая информация
if Clipboard.HasFormat(cf_picture) then
// вставляем содержание буфера обмена
Image1.Picture.Assign(Clipboard);
Объект Clipboard имеет специальный метод HasFormat, что позволяет определить формат данных в буфере обмена. Данный метод может принимать значения:
Компонент ImageList (Win32) уже рассматривался ранее как хранилище изображений для панелей инструментов, меню и других компонентов. Однако данный компонент может не только отображать свое содержимое на кнопках, вкладках и в меню. В ImageList есть специальный метод, позволяющий прочитать любой рисунок из коллекции и отобразить его на форме в компоненте Image.
Метод имеет вид:
ImageList.GetBitmap (index, Bitmap);
Считывание изображения с помощью данного метода можно выполнить с помощью кода:
// описываем переменную типа tbitmap
var a: TBitmap;
begin
// создаем экземпляр этой переменной
a:=TBitmap.Create;
// считываем изображения с index = 0 переменную
ImageList1.GetBitmap (0, a)
// отражаем переменную в компоненте
Image1.Picture.Assign (a)
end;
Timer (System) - это специальный компонент, позволяющий выполнить определенное действие через заданный промежуток времени: скрыть окно формы-заставки, организовать анимационнОЕ движение фигур и т.п. Компонент является невидимым и может располагаться в любом месте формы.
Компонент имеет следующие свойства:
Пусть есть компонент ImageList с 5 картинками. Написать код, отображающий эти картинки в компоненте Image поочередно через определенный интервал времени.
В событии таймера OnTimer напишите код:
// считываем k-е изображение в переменную а
ImageList1.GetBitmap (k, a);
// отражаем изображение в компоненте image
Image1.Picture.Assign(a);
// если текущее изображение не последнее
if k<4 then
<4 then >// переходим к следующему
k:=k + 1
// если текущее изображение последнее
else
// переходим на первое
k:=0;
Чтобы приведеныйн код работал, необходимо:
k:=0;
a:=TBitmap.Create;
4. Элементы анимации
Анимация предусматривает эффект движения компонентов по форме. Для создание эффекта анимации у каждого компонента используют свойства:
Left - задает отступ компонента от левого края формы в пикселях. При увеличении свойства компонент движется вправо, при уменьшении - влево;
Top - задает отступ компонента от верхнего края формы в пикселях. При увеличении свойства компонент движется вниз, при увеличении - вверх.
Движение можно выполнять автоматически с помощью таймера или при нажатии на клавиши (например, стрелок управления курсором).
При движении компонентов по форме иногда нужно определить факт достижения компонентом края формы. В этом случае у формы используются свойства, которые сохраняют реальный размер формы без ее границ и строки заголовка. Эти свойства имеют имена ClientWidth (ширина) и ClientHeight (высота).
Пусть имеется форма вида:

На форме есть компонент Shape (Additional). С помощью таймера организовать движение компонента по форме с эффектом скролинга в зависимости от выбранного направления с помощью переключателей RadioGroup.
Для таймера напишем код:
procedure TForm1.Timer1Timer (Sender: Tobject);
begin
// анализируем выбран переключатель
case RadioGroup1.ItemIndex of
// если выбран первый переключатель
0:
if Shape1.Left <= ClientWidth-Shape1.Width then
Shape1.left := Shape1.Left + 1
else
Shape1.Left := - Shape1.Width;
// если выбран второй переключатель
1:
if Shape1.Left> = - Shape1.Width then
Shape1.Left := Shape1.Left-1
else
Shape1.Left := ClientWidth;
// если выбран третий переключатель
2:
if Shape1.Top <= ClientHeight-Shape1.Height then
Shape1.Top:=Shape1.Top+1
else
Shape1.Top = - Shape1.Height;
// если выбран четвертый переключатель
3:
if Shape1.Top> = - Shape1.Height then
Shape1.Top:=Shape1.Top-1
else
Shape1.Top:=ClientHeight;
end;
end;
Перепишем пример, создав анимацию с эффектом отскока от краев формы. Пусть имеется форма вида:

Для организации отскока на форме нужно разместить два компонента Timer. Timer1 будет перемещать фигуру по горизонтали, а Timer2 - по вертикали.
На форме по умолчанию выбран первый переключатель, поэтому для компонента Timer2 установим свойство Enabled = False.
Для таймера Timer1 напишем код:
// если фигура коснулась левого края формы
if Shape1.Left <= 0 then
// меняем направление движения
h := 1
// если фигура коснулась правого края формы
else if Shape1.Left> = ClientWidth-Shape1.Width then
// меняем направление движения
h := - 1;
// смещаем фигуру по форме
Shape1.Left = Shape1.Left + h;
Для компонента Timer2 напишем код:
// если фигура коснулась верхнего края формы
if Shape1.Top <= 0 then
// меняем направление движения
v := 1
// если фигура коснулась нижнего края формы
else if Shape1.Top> = ClientHeight-Shape1.Height then
// меняем направление движения
v := - 1;
// смещаем фигуру по форме
Shape1.Top:=Shape1.Top+v;
При выборе направления движения мы будет активировать нужный таймер для движения фигуры. Для компонента RadioGroup в события OnClick напишем код:
case RadioGroup1.ItemIndex of
// если выбран первый переключатель
0:
begin
// активируем только первый таймер
Timer1.Enabled := true;
Timer2.Enabled := false;
end;
// если выбран второй переключатель
1:
begin
// активируем только второй таймер
Timer1.Enabled := false;
Timer2.Enabled := true;
end;
// если выбран третий переключатель
2:
begin
// активируем оба таймера
Timer1.Enabled := true;
Timer2.Enabled := true;
end;
end;
Для работы приведенных кодов необходимо выполнить ряд дополнительных операций:
v = - 1;
h = - 1;
Вопросы для самоконтроля
1. Опишите компонент Delphi, предназначенный для отображения графической информации на форме.
2. Какие диалоги для работы с графическими файлами вы знаете? Приведите примеры их использования.
3. Опишите принципы работы с графикой через буфер обмена. Примеры.
4. Опишите компонент ImageList. Как выполнить чтение изображения из этого компонента? Приведите пример.
5. Опишите назначение таймера и принцип работы с ним.