§ 17. Анимация

17.4. Фрагменты изображений

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

Для организации анимации движения можно использовать битовые образы, которые были рассмотрены в §14. Для вывода битового образа на Image можно использовать разные методы (пример 17.7). Использование метода Draw было рассмотрено в §14. Для применения других методов необходимо выделять на рисунке прямоугольную область.

Переменные Pr1 и Pr2 из таблицы примера 17.7 описаны типом TRect. Тип TRect — это структура с 4 полями (Left, Top, Right, Bottom), которая используется для хранения верхней левой и нижней правой вершин прямоугольника. Задавать значения можно непосредственным присваиванием значений или с помощью функций Rect или Bounds (пример 17.8).

Пример 17.9. Изменить проект из примера 14.5 так, чтобы животных можно было рисовать в выбранном масштабе.

Этапы выполнения задания

  1. Поместить на форму два компонента Image, компоненты LabeledEdit, Button и диалог OpenPictureDialog.
  2. Изменить свойства Caption у компонента Button1 на «Выбрать».
  3. Установить у компонента Image2 значение true для свойства AutoSize (рисунок будет загружаться в реальном размере).
  4. Изменить у компонента LabeledEdit1 свойство Caption на «Коэффициент масштабирования», свойство Text — на 1,3.
  5. Описать как глобальные переменные два битовых образа: Ris1 — для хранения исходного изображения, Ris2 — для хранения изображения с учетом масштаба;
  6. Написать обработчик события OnCreate для формы, в котором загрузить из файла изображение леса в компонент Image1.
  7. Написать обработчик события OnClick для компонента Button1.
    7.1. Загрузить в битовый образ Ris1 изображение из выбранного файла.
    7.2. Вывести его в Image2 методом Assign.
  8. Написать обработчик события OnMouseDown для компонента Image1, в котором
    8.1. Задать прямоугольник, длины сторон которого получаются как длины сторон исходного изображения, умноженные на коэффициент масштабирования.

    8.2. Задать размеры Ris2 в соответствии с масштабом.
    8.3. Скопировать изображение с Ris1 на Ris2, используя метод StretchDraw.
    8.4. Методом Draw вывести изображение на Image1.

При использовании метода CopyRect можно копировать прямоугольную область, находящуюся на одной канве, в прямоугольную область, находящуюся на другой канве (пример 17.10).

Если размеры прямоугольников совпадают, то получаем точную копию. Если размер источника меньше или больше, то копия масштабируется так, чтобы вписаться в прямоугольник (пример 17.11). Прозрачность фона при этом не поддерживается.

При использовании метода CopyRect изображения можно выводить, отразив их слева-направо или сверху-вниз. Для этого нужно заменить значения полей в структуре, описывающей прямоугольник:

  //сверху вниз
  Pr2 = Pr1;
  Pr2.bottom = Pr1.top;[1]
  Pr2.top = Pr1.bottom;
  //слева направо
  Pr3 = Pr1;
  Pr3.left = Pr1.right;
  Pr3.right = Pr1.left;

При необходимости можно изменить все четыре значения — в этом случае получим зеркальное отображение рисунка.

В примере 17.12. изображения копировались с битового образа Pict1 в битовый образ Pict2 для того, чтобы можно было затем вывести изображение с прозрачным фоном.



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

Пример 17.7. Методы вывода битового образа (Ris1):

Требуемый результат

Метод

Копирование графики

Draw(x, y, Ris1)

Копирование с масштабирова-нием

StretchDraw(Pr1, Ris1)

Копирование прямоугольного участка канвы

CopyRect(Pr1, 
Ris -> Canvas, Pr2)

Полное копирование всех свойств (в том числе и размеров)

Picture->Assign(Ris1)

Пример 17.8. Определение прямоугольной области:

TRect Pr1, Pr2, Pr3;

//Определение прямоугольной 
//области указанием значений //полей

  Pr1.left = x1;

  Pr1.top = y1;

  Pr1.right = x2;

  Pr1.bottom = y2;

//Определение прямоугольной 
//области по координатам

  Pr2 = Rect(x1, y1, x2, y2);

//Определение прямоугольной 
//области путем задания 

//верхнего левого угла  и

//длины, ширины прямоугольника

  Pr3 = Bounds(x1, y1, w, h);

Пример 17.9. Форма на этапе проектирования:

Обработчик события OnCreate для формы.

void __fastcall TForm1::
FormCreate(TObject *Sender)

{

  Image1 ->Picture ->
     LoadFromFile
("лес.bmp");

  OpenPictureDialog1 -> InitialDir =

      ExtractFilePath(ParamStr(0));

}

 Обработчик события OnClick для компонента Button1.

void __fastcall TForm1::
Button1Click(TObject *Sender)

{

  if (OpenPictureDialog1 ->Execute()){

  Ris1 -> LoadFromFile
  
(OpenPictureDialog1 ->FileName);

  Image2 -> Picture -> Assign(Ris1);

  w = Ris1 -> Width;

  h = Ris1 -> Height;

  }

}

Обработчик события OnMouseDown для компонента Image1.

void __fastcall TForm1::
Image1MouseDown(TObject *Sender, 
TMouseButton Button, 
TShiftState Shift, 
int X, int Y)
{
  TRect Pr1;
  double k = StrToFloat
    (LabeledEdit1 -> Text);
  Pr1 = Bounds(00,
    int(k * w), int(k * h));
  Ris2 -> Width = int(k * w);
  Ris2 -> Height = int(k * h);
  Ris2 -> Canvas ->StretchDraw(Pr1, Ris1);
  Ris2 -> Transparent = true;
  Image1 -> Canvas -> Draw(X, Y, Ris2);
}

Работающее приложение:

Пример 17.10. Формат команды CopyRect:

Пример 17.11. Использование метода CopyRect:

Пример 17.12. Копирование с отражением:

Обработчик события для кнопки:

void __fastcall TForm1::
Button1Click(TObject *Sender)

{

  TRect Pr1, Pr2, Pr3, Pr4;

  Pr1 = Bounds(00, w, h);

  Image1 -> Canvas -> Draw(250200, Pict1);

  //сверху вниз

  Pr2 = Pr1;

  Pr2.bottom = Pr1.top - 1;

  Pr2.top = Pr1.bottom;

  Pict2 -> Canvas -> CopyRect(Pr2,

      Pict1 -> Canvas, Pr1);

  Image1 -> Canvas -> Draw(50300, Pict2);

  //слева направо

  Pr3 = Pr1;

  Pr3.left = Pr1.right;

  Pr3.right = Pr1.left;

  Pict2 -> Canvas -> CopyRect(Pr3, 

      Pict1 -> Canvas, Pr1);

  Image1 -> Canvas -> Draw(100200, Pict2);

  //сверху-внизслева-направо

  Pr4 = Pr1;

  Pr4.left = Pr1.right;

  Pr4.right = Pr1.left;

  Pr4.top = Pr1.bottom;

  Pr4.bottom = Pr1.top;

  Pict2 -> Canvas -> CopyRect(Pr4,

     Pict1 -> Canvas, Pr1);

  Image1 -> Canvas -> Draw(350300, Pict2);

}