Класс WriteableBitmap

179

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

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

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

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

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

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

y*wb.PixelWidth + x

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

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

Цвет каждого пикселя задается через палитру ARGB. Ниже приведен код, проходящий по всем пикселям и заполняющий их случайными числами:

using System.Windows.Media.Imaging;
   
...   
   
private void Button_Click_1(object sender, RoutedEventArgs e)
{
            WriteableBitmap wb = new WriteableBitmap(
                (int)this.ActualHeight,
                (int)this.ActualWidth);

            Random rand = new Random();
            for (int y = 0; y < wb.PixelHeight; y++)
            {
                for (int x = 0; x < wb.PixelWidth; x++)
                {
                    int red = 0, green = 0, blue = 0, alpha = 255;

                    // Создание вертикальных линий через каждые 5 пикселей
                    // и горизонтальных через каждые 7
                    if ((x % 5 == 0) || (y % 7 == 0))
                    {
                        // Цвет создается случайным образом, но немного зависит
                        // от координат X и Y, что создает эффект градиента
                        red = (int)((double)x / wb.PixelWidth * 255);
                        green = rand.Next(100, 255);
                        blue = (int)((double)y / wb.PixelHeight * 255);
                    }
                    else
                    {
                        red = (int)((double)x / wb.PixelWidth * 255);
                        green = rand.Next(100, 255);
                        blue = (int)((double)y / wb.PixelHeight * 255);
                    }

                    // Установка значений пикселей
                    int pixelColorValue = (alpha << 24) | (red << 16) |
                     (green << 8) | (blue << 0);

                    wb.Pixels[y * wb.PixelWidth + x] = pixelColorValue;
                }
            }

            // Выводим изображение на экран
            img.Source = wb; 
}
Динамически сгенерированное растровое изображение

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

В некоторых ситуациях необходимо передать содержимое объекта WriteableBitmap в веб-службу или сохранить его в файле. Это можно сделать с помощью массива, предоставляемого свойством WriteableBitmap.Pixels, однако такой подход непрактичный, потому что данные битовой карты типичного изображения потребляют огромный объем памяти. Поэтому было бы желательно преобразовать изображение в более экономный стандартный формат, например JPEG. Однако, к сожалению, в Silverlight не встроена поддержка программного манипулирования данными JPEG. Впрочем, можно воспользоваться какой-либо библиотекой сторонних поставщиков, например fjcore или пакетом .NET Image Tools.

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

В предыдущем примере изображение генерировалось попиксельно с помощью кода. Класс 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, как описано выше.

Пройди тесты
Лучший чат для C# программистов