Урок:5 Пытаемся работать с изображениями (Мысли о анимации)

Собственно назвать это уроком можно с большой натяжкой, так – больше общие замечания по работе с изображениями в Silverlight.

Итак, изображения, на самом деле это одна из самых слабых сторон Silverlight ‘a. Возможностей тут не так уж много. Давайте их посмотрим.

Так как Silverlight оперирует с объектами и их свойствами, битмап на страницу можно добавить только двумя способами:

· 1) Добавив объект Image

· 2) Использовав ImageBrush уже на существующем объекте

Отсюда все возможности и ограничения по работе с битмапами.

Посмотрим что можно сделать добавив объект Image на страницу. Если Вам уже известен рисунок на момент написания кода. То добавить его, будет проще всего используя xaml:

   1:          <Image Source="Media/Ship.png"
   2:                 Height="100" Width="100"
   3:                 Canvas.Left="20" Canvas.Top="20" />

Если же Вы хотите добавить битмап динамически, то предлагаю следующее:

   1:              Image image = new Image(); // создаем новый экземпляр объета Image
   2:              Uri uri = new Uri("Media/Ship.png", UriKind.Relative); // создаем сылку на сам битмап
   3:              ImageSource img = new System.Windows.Media.Imaging.BitmapImage(uri); // создаем источник
   4:              image.SetValue(Image.SourceProperty, img); // присваеваем нашему объету Image источник
   5:              LayoutRoot.Children.Add(image); // собственно добавляем на канвас или куда необходимо
   6:              // Выставляем аналогично предидущему листингу свойства
   7:              image.Height = 100;
   8:              image.Width = 100;
   9:              image.SetValue(Canvas.TopProperty, 20.0);
  10:              image.SetValue(Canvas.LeftProperty, 20.0);

Обратите внимание, первое – что такой подход стал необходим только при бета 2. Второе – на данный момент Silverlight работает с растровыми файлами только PNG или JPG форматов. В интернете уже можно найти классы позволяющие читать BMP, но думаю что это дело вкуса каждого что именно ему использовать.

С добавлением объекта Image разобрались, давайте посмотрим, что с ним можно сделать. Из специфических возможностей работы с изображениями доступно совсем немногое :

Stretch – растягивает рисунок по объекту в соответствии с параметром. Возможны следующие варианты. Fill(не сохраняя пропорции полностью заполняет полотно объекта) None(не растягивает-размеры из файла) Uniform(сохраняя пропорции отображает весь битмап на полотно) UniformToFill(сохраняя пропорции полностью заполняет полотно объекта).

Clip – вырезает кусок изображения источника указанной геометрии.

   1:          <Image Source="Media/Ship.png"
   2:                 Height="100" Width="100"
   3:                 Canvas.Left="20" Canvas.Top="20"
   4:                 Stretch="UniformToFill">
   5:              <Image.Clip>
   6:                  <EllipseGeometry
   7:                      RadiusX="100"
   8:                      RadiusY="75"
   9:                      Center="100,75"/>
  10:              </Image.Clip>
  11:          </Image>

Тут надо сделать несколько замечаний: Первое – геометрию, которую нужно вырезать из изображения просто поместите в тег <Image.Clip>. Геометрия может быть любой. Второе – Если вы вырезаете кусок изображения это не значит, что невидимая часть изображения исчезает – ее просто не видно. Как следствие координаты вырезанного участка будут соответствовать полному изображению. Я к чему веду: если вы захотите сделать битмап анимацию используя Clip и будете вырезать тайлы из сета, то тайл будет выводится не с относительными координатами 0,0 на объекте а как часть огромного но невидимого тайлсета. У меня было желание и я пробовал так реализовать битмап анимацию, но меня не устроил такой подход ввиду его огромной требовательности к ресурсам. Могу сказать что при таком подходе, если кто-то захочет поэкспериментировать, надо будет не только указывать какие части тайлсета вырезать, но перемещать весь сет чтобы тайл выводился в необходимом месте.

OpacityMask – Аналогично предыдущему только работает с альфа каналом. Используйте кисти с градиентом, чтобы получить плавное проявление рисунка или сплошную кисть со сложным рисунком.

Вы наверное согласитесь – не сильно большие возможности. Но существует еще один способ работы с изображением в Silverlight’e – это использовать ImageBrush. Просто приведу листинг с СДК:

   1:  <!-- TextBlock with an image brush applied to the text. -->
   2:  <TextBlock
   3:    Canvas.Top="120"
   4:    FontFamily="Verdana"
   5:    FontSize="72"
   6:    FontStyle="Italic"
   7:    FontWeight="Bold">
   8:    SHRUBBERY
   9:    <TextBlock.Foreground>
  10:      <ImageBrush ImageSource="forest.jpg"/>
  11:    </TextBlock.Foreground>
  12:  </TextBlock>

Выводит текст используя в качестве чернил фотографию леса. Очень интересно с точки зрения спецэффектов, но не сильно полезно для нас для написания игр.

С замечаниями от чайника-программиста закончили. Перейдем теперь к замечаниям чайника-дизайнера.

1) Если можете, используйте анимацию объектами это сильная сторона Silverlight’а. Как пример можете посмотреть проект лосгарден. Очень красиво. И достаточно просто реализуется анимация при объектном подходе. Например, можно нарисовать суставы и двигать их, меняя угол или местоположение – сложно, но можно. А вот, например анимация отбойного молотка при объектном подходе становится намного проще.

2) Никто не заставляет вас пользоваться «чистым вектором». Активно комбинируйте растровые изображения и анимацию свойств объектов. Это даст вам возможность создавать реалистическую анимацию, правда немного не стандартным способом. Например, вам необходимо анимировать разбитый космический спутник: никто не мешает вам нарисовать его, в PNG вырезать в нем пару дырок, загрузить его в контрол Silverlight, а под битмап контейнера разместить прямоугольник голубого цвета периодически меняющий цвет на черный. В итоге вы получите Silverlight контрол с изображением спутника, который можете двигать и вращать, причем картинка спутника будет не статичной – периодически сквозь разломы будут просвечивать голубые электрические разряды.

Вот собственно и все вроде. Честно могу сказать я лапатил интеренет около месяца, но так и не смог найти реализацию битмап анимации для Silverlight’a. Представляете как мне стало грустно от того что нельзя сделать стандартными методами и тут нашел небольшую статейку с идеей. Честно еще не сильно разбирался, но хотелось бы услышать ваши мысли по данному поводу.

А вот и мой примерчик простой процедурной битмап анимации на Silverlight’е. Для нее вам понадобится только набор спрайтов названых особым образом :

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Net;
   5:  using System.Windows;
   6:  using System.Windows.Controls;
   7:  using System.Windows.Documents;
   8:  using System.Windows.Input;
   9:  using System.Windows.Media;
  10:  using System.Windows.Media.Animation;
  11:  using System.Windows.Shapes;
  12:  using System.Text;
  13:  
  14:  namespace SilverlightApplication9
  15:  {
  16:      public partial class Page : UserControl
  17:      {
  18:          Uri uri;
  19:          ImageSource img;
  20:          StringBuilder file_patch;
  21:          Storyboard animation_timer;
  22:          int frame_counter = 1;
  23:          const int c_frame_count = 7; // количество кадров в анимации
  24:          const int c_fps_count = 15; // количество кадров в секунду
  25:  
  26:          public Page()
  27:          {
  28:              InitializeComponent();
  29:  
  30:  
  31:              //uri = new Uri("Media/frame1.png", UriKind.Relative); // создаем ссылку на 1 битмап
  32:              img = new System.Windows.Media.Imaging.BitmapImage(uri); // создаем источник
  33:              animation_bitmap.SetValue(Image.SourceProperty, img);
  34:  
  35:              animation_timer = new Storyboard(); // создаем наш таймер
  36:              this.Resources.Add("animation_timer", animation_timer); // добавляем его в ресурсы страницы
  37:              animation_timer.Completed += new EventHandler(animation_timer_Completed);
  38:              // выставляем время между кадрами
  39:              animation_timer.Duration = new Duration(TimeSpan.FromMilliseconds((int)(1000 / c_fps_count)));
  40:              animation_timer.Begin(); // собственно запускаем его
  41:          }
  42:  
  43:  
  44:          void animation_timer_Completed(object sender, EventArgs e)
  45:          {
  46:              // сюда добавляем то что надо сделать в цикле bitmap анимации
  47:              file_patch = new StringBuilder();
  48:              file_patch.Append("Media/frame" + frame_counter + ".png");
  49:  
  50:              frame_counter++;
  51:              if (frame_counter > c_frame_count) frame_counter = 1;
  52:  
  53:              uri = new Uri(file_patch.ToString(), UriKind.Relative); // меня немного смущает создание новой ссылки
  54:                                                                      // как нового объекта
  55:              img.SetValue(System.Windows.Media.Imaging.BitmapImage.UriSourceProperty, uri);// источник
  56:              //animation_bitmap.SetValue(Image.SourceProperty, img);  - это уже НЕНУЖНО!!!
  57:  
  58:              animation_timer.Begin();
  59:          }
  60:  
  61:      }
  62:  }

Если кто сообразит для этого безобразия удобоваримый класс с отдельным потоком и кешированием, буду премного благодарен. Если нет – что ни будь придумаю сам.

Все.

Комментариев пока нет

Ответить