Главная » Silverlight » Раскрашивание пикселей

0

Одно из наиболее впечатляющих средств Silverlight 3 — раскрашивание пикселей (pixel shading) — используется для изменения внешнего вида элементов путем манипу­ляции пикселями перед выводом области содержимого на экран. Раскрашивание вы­полняется после операций преобразования и проецирования.

Раскрашивание в Silverlight — не менее мощное средство, чем графические над­стройки Adobe Photoshop. Оно позволяет накладывать на элементы такие эффекты, как размывание, сияние, водяная рябь, чеканка, обострение и т.д. Процедуру раскрашива­ния можно анимировать (см. главу 10) путем изменения параметров раскрашивания в реальном времени.

Каждый раскрашивающий объект (shader) представлен классом, производным от абстрактного класса Effect. Классы раскрашивания определены в пространстве имен System.Windows.Media.Effects. В надстройку Silverlight включены три базовых класса раскрашивания: BlurEffect, DropShadowEffeet и ShaderEffeet. В следующих разделах рассматривается каждый из них, а также способы интеграции в приложение дополни­тельных классов раскрашивания, предлагаемых независимыми поставщиками в раз­личных библиотеках. Интеграция выполняется с помощью класса ShaderEf feet.

Класс BlurEffect

Объект BlurEffect размывает визуальное содержимое элемента, как будто вы смотрите на него сквозь расфокусированную оптику. Интенсивность размывания опре­деляется свойством Radius. По умолчанию оно равно 5.

Для эффекта размывания нужно создать объект раскрашивания и присвоить его свойству Effect обрабатываемого элемента.

<Button Content>"Размывание, Radius=2" Padding="5"> <Button.Effect>

<BlurEffeet Radius="2"></BlurEffect> </Button.Effect>

</Button>

На рис. 9.16 показан эффект размывания, примененный к ряду стандартных кнопок при трех разных значениях Radius.

Рис. 9.16. Размытые кнопки

Класс DropShadowEffeet

Объект DropShadowEf feet создает эффект отбрасывания тени элементом. Для управ­ления эффектом предназначены свойства, приведенные в табл. 9.4.

Таблица 9.4. Свойства класса DropShadowEf feet

Имя

Описание

Color (Цвет)

Цвет тени; по умолчанию это свойство имеет значение Black (Черная)

ShadowDepth

Расстояние между тенью и содержимым в пикселях (по умолчанию равно 5)

(Глубина тени)

Окончание табл. 9.4

Имя

Описание

BlurRadius (Радиус размывания)

Степень размытости тени (как в объекте BlurEffect); по умолчанию равно 5

Opacity (Непрозрачность)

Дробное число от 0 (тень полностью непрозрачная) до 1 (тень полностью прозрачная)

Direction (Направление)

Положение тени относительно содержимого, выраженное углом от 0 до 360°; при значении 0 тень размещена справа; угол отсчитывается против часовой стрелки; по умолчанию свойство равно 315, при этом тень размещена справа снизу от элемента

На рис. 9.17 показано несколько эффектов отбрасывания тени, применен­ных к текстовым блокам. Ниже приведена разметка текстовых блоков с объектами

DropShadowEffeet.

Рис. 9.17. Эффекты отбрасывания тени

CTextBlock FontSize="20" Margin="3"> <TextBlock.Effect>

<DropShadowEf fect></DropShadowEf fect> </TextBlock.Effect>

<TextBlock.Text>Базовая тень</TextBlock.Text> </TextBlock>

<TextBlock FontSize="20" Margin="3"> <TextBlock.Effect>

<DropShadowEffeet Color="SlateBlue"></DropShadowEffect> </TextBlock.Effect»

<TextBlock.Техt>Светло-голубая TeHb</TextBlock.Text> </TextBlock>

<TextBlock FontSize="20" Foreground="White" Margin="3"> <TextBlock.Effect»

<DropShadowEffeet BlurRadius="15"></DropShadowEffect> </TextBlock.Effect»

<TextBlock. Text>Размытая тень от белого текста </TextBlock.Text> </TextBlock>

<TextBlock FontSize="20" Foreground="Magenta" Margin="3"> <TextBlock.Effect»

<DropShadowEffeet ShadowDepth="0"></DropShadowEf fect> </TextBlock.Effect>

<TextBlock.Text>Близкaя TeHb</TextBlock.Text>

</TextBlock>

<TextBlock FontSize="20" Foreground="LimeGreen" Margin="3"> <TextBlock.Effect>

<DropShadowEffect ShadowDepth="25"></DropShadowEffect> </TextBlock.Effect>

<TextBlock.Text>Oтдaлeннaя TeHb</TextBlock. Text>

</TextBlock>

К сожалению, класса, группирующего эффекты, не существует. Поэтому за один раз к элементу можно применить только один эффект. Однако имитировать многие эффек­ты можно, применяя их к контейнеру более высокого уровня. Например, можно сначала применить тень к элементу TextBlock, а затем вложить его в панель StackPanel и при­менить к ней эффект размывания. В большинстве случаев использовать этот трюк не рекомендуется, потому что он существенно ухудшает производительность. Вместо этого поищите в библиотеках классов эффект, который сделает все, что вам нужно.

Класс ShaderEffeet

Класс ShaderEffect не предоставляет готовых к использованию средств создания визуальных эффектов. Это абстрактный класс, предназначенный для наследования пользовательскими классами раскрашивания. С его помощью можно применить клас­сы независимых поставщиков, позволяющие создавать эффекты, намного более мощ­ные, чем размывание и отбрасывание тени.

Для многих покажется странным тот факт, что классы раскрашивания написаны не на С#, а на специализированном языке HLSL (High Level Shader Language — вы­сокоуровневый язык раскрашивания), разработанном как часть технологии DirectX. Преимущества такого подхода очевидны: DirectX и HLSL используются уже много лет и разработчики графических приложений успели создать огромное количество проце­дур раскрашивания, которые можно использовать в Silverlight.

Для создания класса раскрашивания нужно разработать код HLSL. В первую оче­редь, установите пакет DirectX SDK (www.msdn.microsoft. com/en-us/directx/aa937788. aspx). Его достаточно для создания и компиляции кода HLSL в файл .ps (с помощью инструмента командной строки fxc.exe). Файл .ps необходим для пользовательского класса, наследующего ShaderEffect. Можете также применить бесплатный инструмент Shazzam (www.shazzam-tool.com), предоставляющий удобный редактор файлов HLSL. Кроме того, программа Shazzam содержит несколько образцов классов раскрашивания, которые можно использовать в качестве шаблонов.

Разработка файлов HLSL в данной книге не рассматривается. Рассмотрим лишь использование существующего файла HLSL. Когда код HLSL скомпилирован в файл  его можно применить в проекте Silverlight. Добавьте файл . ps в существующий проект, выделите его в окне Solution Explorer (Проводник решений) и присвойте свойству Build Action (Операция построения) значение Resource (Ресурс). После этого нужно создать пользовательский класс, производный от ShaderEffect и ссылающийся на ресурс.

Предположим, процедура раскрашивания скомпилирована в файл Effect.ps. Ее можно использовать в следующем коде.

public class CustomEffect : ShaderEffect

{

public CustomEffect () {

//На ресурс ссыпается ORI, // синтаксис которого описан в главе 6 // AssemblyName;component/ResourceFileName Uri pixelShaderUri = new

Uri(«CustomEffectTest;component/Effect.ps", UriKind.Relative);

// Загрузка файла .ps PixelShader = new PixelShader(); PixelShader.UriSource = pixelShaderUri;

}

)

После этого класс раскрашивания можно применить на любой странице. В первую очередь, сделайте пространство имен доступным, добавив его в код.

CUserControl

xmlns:local="clr-namespace:CustomEffectTest" ..

Создайте экземпляр пользовательского класса раскрашивания и присвойте его свой­ству Effect обрабатываемого элемента.

<Image> <Image.Effect>

<local: CustomEf fectx/local: CustomEf f ect> </Image.Effect> </Image>

Немного усложнив код, можно передать пользовательскому классу раскрашивания входные аргументы. Для этого нужно создать соответствующее зависимое свойство, вы­звав статический метод RegisterPicelShaderSamplerProperty О .

Совет. Если вы не являетесь профессиональным графическим программистом, то вам лучше не писать код HLSL самому, а взять готовые образцы кода и немного модифицировать их, а еще лучше — приобрести компоненты независимых поставщиков, содержащие классы раскрашивания. Например, в Silverlight 3 прекрасно работает бесплатная библиотека WPF Pixel Shader Effects Library (www. codeplex. com/wpf fx). Она содержит огромное количество визуальных эффектов, таких как водоворот, инверсия цветов, шум, кристаллизация, ржавчина, потеки, ореол, зернистость. Кроме того, вы можете анимировать раскрашивание (см. главу 10), в результате чего оно будет происходить в реальном времени на глазах у пользователя.

Класс WriteableBitmap

В главе 5 рассматривается вывод растровых изображений на экран с помощью эле­мента Image. Элемент Image всего лишь выводит готовое изображение, он не содержит средств создания или редактирования изображений.

Для этого предназначен класс WriteableBitmap, производный от класса BitmapSource. Объект BitmapSource используется для установки свойства Image. Source (непосред­ственно в коде С# или неявно в разметке XAML). Объект BitmapSource содержит доступ­ную только для чтения рефлексию растровых данных, а объект WriteableBitmap — мас­сив пикселей, доступных для изменения, что открывает ряд интересных возможностей.

Генерация растрового изображения

Наиболее прямолинейный способ использования класса WriteableBitmap состоит в создании всего растрового изображения вручную. На первый взгляд, этот процесс мо­жет показаться слишком трудоемким и скучным, однако его можно автоматизировать с помощью кода С#. Данный способ полезен при создании фрагментов изображений и визуализации музыкальных файлов или научных данных. Необходимо создать про­цедуру, которая динамически рисует данные на основе коллекции двухмерных фигур (см. главу 8) или манипулирует непосредственно пикселями.

Для генерации растрового изображения нужно сначала создать в памяти битовую карту (bitmap) с помощью класса WriteableBitmap (иногда ее называют растровой кар­той). Ее ширина и высота на данном этапе задаются в пикселях. Ниже приведен код, создающий изображение с размерами текущей страницы.

WriteableBitmap wb = new WriteableBitmap( (int)this.ActualWidth, (int)this.ActualHeight);

Затем нужно заполнить пиксели. Для этого используется свойство Pixels, содер­жащее одномерный массив пикселей. Пиксели размещаются слева направо построчно. Строки заполняют изображение сверху вниз. Для получения конкретного пикселя ис­пользуйте следующую формулу, в которой координата Y умножается на длину строки и к произведению добавляется координата X требуемого пикселя.

y*wb.PixelWidth+x

Например, для установки в коде пикселя с координатами (40, 100) можно применить следующий оператор.

wb. Pixels [100 * wb. PixelWidth + 40] = …;

Цвет каждого пикселя задается с помощью одного целого числа без знака. Чтобы создать это число, нужно упаковать ряд величин (alpha, red, green и blue), каждая Из которых может принимать значения от 0 до 255. Значение alpha определяет прозрач­ность пикселя, red — интенсивность красной компоненты, green — интенсивность зе­леной компоненты и blue — интенсивность синей компоненты. Упаковать значения в одно число можно путем сдвига кодов чисел.

int alpha = 255;

int red = 100;

int green = 200;

int blue = 75;

int pixelColorValue =

(alpha « 24) | (red « 16) | (green « 8) | (blue « 0) ;

wb.Pixels[pixelPosition] = pixelColorValue;

Ниже приведен код, проходящий по всем пикселям и заполняющий их случайными числами с примесью регулярной решетки (рис. 9.18).

Random rand = new Random();

for (int у = 0; у < wb.PixelHeight; y++)

{

int red = 0; int green = 0; int blue = 0;

// Изменение цвета для создания вертикальных линий II через каждые 5 пикселей и горизонтальных II линий через каждые 7 пикселей

if ((х % 5 == 0) || (у % 7 == 0)) {

// Цвет выбирается случайным образом, но немного // зависит от координат X и Y, что создает // впечатление градиента

red = (int)((double)у / wb.PixelHeight * 255);

green = rand.Next (100, 255);

blue = (int) ((double) x / wb.PixelWidth * 255);

}

else {

/ / Вычисление цвета для пикселей, не принадлежащих // линиям решетки

red = (int) ( (double) х / wb.PixelWidth * 255) ; green = rand.Next (100, 255);

blue = (int) ( (double) у / wb. PixelHeight * 255);

// Установка значений пикселей

int pixelColorValue = (alpha « 24) | (red « 16) I

(green « 8) I (blue « 0); wb.Pixels[y * wb.PixelWidth + x] = pixelColorValue;

Рис. 9.18. Динамически сгенерированное растровое изображение

}

}

По завершении процесса генерации нужно вывести изображение на экран. Легче всего это сделать с помощью элемента Image.

/

img.Source = wb;

После вывода сгенерированного изображения можно читать массив Pixels и моди­фицировать значения пикселей. Это дает возможность создать процедуры редактиро­вания и тестирования растровой карты. Пример тестирования растровой карты в игре можно найти на сайте www. tinyurl.com/mroklb.

Копирование визуального содержимого

В предыдущем примере изображение генерировалось по пикселям с помощью кода. Класс WriteableBitmap предоставляет еще одну возможность создания изобра­жения: скопировать визуальное содержимое существующего элемента. Другое назва­ние этой операции — захват (capturing). Сначала нужно создать экземпляр объекта WriteableBitmap обычным способом и определить его размеры, а затем скопировать содержимое элемента в объект с помощью метода Render (). Метод Render () принима­ет два параметра: элемент, содержимое которого нужно захватить, и объект преобра­зования (или группа преобразований), с помощью которого при необходимости можно будет изменить изображение. Если преобразовывать захваченное содержимое не нуж­но, передайте значение null.

Ниже приведен код, который захватывает всю страницу Silverlight, имитируя копи­рование экрана. Скопировать весь экран нельзя, потому что доступ к содержимому за пределами области Silverlight создал бы проблемы безопасности.

// Поиск элемента самого высокого уровня UserControl mainPage =

(UserControl)Application.Current.RootVisual;

// Создание растровой карты WriteableBitmap wb = new WriteableBitmap!(int)mainPage.ActualWidth, (int)mainPage.ActualHeight) ;

// Копирование содержимого в растровую карту wb.Render(mainPage, null); wb.Invalidate();

// Вывод растрового изображения img.Source = wb;

После вызова метода Render () нужно запустить метод Invalidate (), который при­кажет растровой карте сгенерировать свое содержимое, задержав управление на время, необходимое для выполнения операции.

Заполнив растровую карту, ее можно вывести на экран с помощью объекта Image, как описано выше.

Совет. Метод Writeablebitmap. Render () особенно полезен с элементом MediaElement, потому что позволяет захватить кадр воспроизводимого видеофайла (см. главу 11).

Резюме

В этой главе рассмотрены расширенные средства двухмерной модели рисования Silverlight. При создании двухмерной графики важно понимать принципы использо­вания графических инструментов, чтобы извлечь из них все, на что они способны. Например, изощренные эффекты можно создавать, изменяя кисти, используемые для заливки фигур, применяя объекты преобразований к графическим компонентам, из­меняя прозрачность фигур и т.д. Вы можете добавлять, изменять и удалять любые индивидуальные компоненты графики. Рассмотренные способы рисования легко со­вмещаются со средствами анимации, обсуждаемыми в следующей главе. Например, средствами анимации можно плавно поворачивать фигуры с помощью свойства Angle объекта RotateTransform, постепенно прорисовывать элементы с помощью свойства DrawingGroup. Opacity, создавать анимированные градиенты с помощью кистей и клас­сов раскрашивания. В следующей главе вы еще раз встретитесь с примерами использо­вания рассмотренных технологий преобразования и раскрашивания.

Источник: Мак-Дональд, Мэтью. Silverlight 3 с примерами на С# для профессионалов. : Пер. с англ. —- М. : ООО «И.Д. Вильяме», 2010. — 656 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии