Урок: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: }
Если кто сообразит для этого безобразия удобоваримый класс с отдельным потоком и кешированием, буду премного благодарен. Если нет – что ни будь придумаю сам.
Все.
Комментариев пока нет
Ответить