Лекция № 10

Тема: «Работа с графикой»

 

План

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, что позволяет определить формат данных в буфере обмена. Данный метод может принимать значения:

2. Компонент ImageList

Компонент 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;

 

3. Работа с таймером

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. Элементы анимации

  Анимация предусматривает эффект движения компонентов по форме. Для создание эффекта анимации у каждого компонента используют свойства:

 

Движение можно выполнять автоматически с помощью таймера или при нажатии на клавиши (например, стрелок управления курсором).

При движении компонентов по форме иногда нужно определить факт достижения компонентом края формы. В этом случае у формы используются  свойства, которые сохраняют реальный размер формы без ее границ и строки заголовка. Эти свойства имеют имена 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. Опишите назначение таймера и принцип работы с ним.