Главная » Разработка для Windows Phone 7 » Работа с пикселами Windows Phone 7 как с 32-разрядными целыми без знака

0

Ранее в данной главе я продемонстрировал, как создавать пустой объект Texture2D, используя один из его конструкторов:

Texture2D texture = new Texture2D(this.GraphicsDevice, width, height);

Как и для заднего буфера, и для RenderTarget2D, формат описания пикселов определяется членом перечисления SurfaceFormat. Свойство Format (Формат) объекта Texture2D, созданного с помощью этого простого конструктора, будет иметь значение SurfaceFormat.Color. Это означает, что каждый пиксел описывается 4 байтами (или 32 битами) данных, по одному байту для значений красного, зеленого и синего и еще один байт для альфа-канала, который определяет непрозрачность этого пиксела.

Также возможно (и очень удобно) рассматривать каждый пиксел как 32-разрядное целое без знака, что в С#обозначают как uint. Цвета представляются 8-разрязными шестнадцатеричными значениями типа uint:

AABBGGRR

Каждая буква представляет четыре бита. Если имеется Texture2D, загружаемый как содержимое или создаваемый, как показано выше, и его свойство Format имеет значение SurfaceFormat, все значения битов растрового изображения можно получить, предварительно создав массив типа uint, достаточно большой для включения всех пикселов:

uint[] pixels = new uint[texture.width * texture.height];

После этого все пикселы Texture2D передаются в массив следующим образом:

texture.GetData<uint>(pixels);

GetData – обобщенный метод, в котором необходимо просто указать тип данных массива. Перегрузки GetData позволяют получать выборки пикселов, соответствующие прямоугольным подмножествам растрового изображения, или передавать пикселы растрового изображения в массив pixels с определенным смещением.

RenderTarget2D наследуется от Texture2D, поэтому эта методика может применяться и к объектам RenderTarget2D.

Также можно передать данные из массива pixels назад в растровое изображение:

texture.SetData<uint>(pixels);

Пикселы в массиве pixels организованы по строкам, начиная с самой верхней строки. Пикселы в каждой строке размещаются слева направо. Для конкретной строки y и столбца x растрового изображения массив pixels индексируется по следующей простой формуле:

pixels[y * texture.width + x]

Структура Color имеет одно исключительно удобное свойство – PackedValue (Упакованное значение). Оно обеспечивает преобразование объекта Color в uint конкретного формата, который необходим для этого массива, например:

pixels[y * texture.width + x] = Color.Fuchsia.PackedValue;

Кстати, Color и uint так тесно взаимосвязаны, что как альтернативный вариант можно создать массив pixels типа Color:

Color[] pixels = new Color[texture.Width * texture.Height];

После этого данный массив может использоваться в GetData

texture.GetData<Color>(pixels);

и SetData

texture.SetData<Color>(pixels);

и для задания отельным пикселам значений Color:

pixels[y * texture.width + x] = Color.AliceBlue;

Единственным обязательным условием является последовательность.

Можно создавать объекты Texture2D в других цветовых форматах, но массив пикселов должен включать члены соответствующего размера, например, ushort для формата SurfaceFormat.Bgr565. Следовательно, формат SurfaceFormat.Color использовать легче всего, поэтому именно его я придерживаюсь в данной главе.

Рассмотрим простой пример. Предположим, для игры требуется создать градиентный фон, меняющийся слева направо от синего к красному. Это реализовано в проекте GradientBackground (Градиентный фон). Рассмотрим поля:

Проект XNA: GradientBackground Файл: Game1.cs (фрагмент, демонстрирующий поля)

public class Game1 : Microsoft.Xna.Framework.Game {

GraphicsDeviceManager graphics; SpriteBatch spriteBatch;

Rectangle viewportBounds; Texture2D background;

}

Вся основная работа выполняется перегруженным методом LoadContent: создается растровая матрица на базе размера Viewport (здесь используется свойство Bounds, которое имеет удобные целочисленные размеры) и заполняется данными. Интерполяция для создания градиента выполняется методом Color.Lerp по значению x:

Проект XNA: GradientBackground Файл: Game1.cs (фрагмент)

protected override void LoadContent() {

spriteBatch = new SpriteBatch(GraphicsDevice); viewportBounds = this.GraphicsDevice.Viewport.Bounds;

background = new Texture2D(this.GraphicsDevice, viewportBounds.Width,

viewportBounds.Height);

Color[] pixels = new Color[background.Width * background.Height];

for (int x = 0; x < background.Width; x++) {

Color clr = Color.Lerp(Color.Blue, Color.Red,

(float)x / background.Width);

for (int y = 0; y < background.Height; y++) pixels[y * background.Width + x] = clr;

background.SetData<Color>(pixels);

Не забывайте вызывать SetData после заполнения данными массива pixels! Приятно предполагать наличие некоторой неявной привязки между Texture2D и массивом, но на самом деле ее нет.

Метод Draw просто отрисовывает Texture2D, как обычно: Проект XNA: GradientBackground Файл: Game1.cs (фрагмент)

protected override void Draw(GameTime gameTime) {

spriteBatch.Begin();

spriteBatch.Draw(background, viewportBounds, Color.White); spriteBatch.End();

base.Draw(gameTime);

}

Получаем такой градиент:

Хотя кажется, что код подразумевает сотни переходных оттенков между чисто-синим и чисто-красным, 16-битовое цветовое разрешение экрана устройства Windows Phone 7 позволяет четко отображать только 32 полосы[25].

Для данного конкретного примера, в котором Texture2D остается неизменным в направлении сверху вниз, нет необходимости в таком большом количестве строк. Кстати, объект background может быть создан всего в одну строку:

background = new Texture2D(this.GraphicsDevice, viewportBounds.Width, 1);

Весь остальной код LoadContent основывается на свойствах background.Width и background.Height, поэтому больше ничего менять не надо (хотя циклы, конечно, можно было бы упростить). В методе Draw растровые изображения растягиваются для заполнения Rectangle:

spriteBatch.Draw(background, viewportBounds, Color.White);

Ранее в данной главе я создавал белый RenderTarget2D размером 1×1, используя такой код:

tinyTexture = new RenderTarget2D(this.GraphicsDevice, 1, 1); this.GraphicsDevice.SetRenderTarget(tinyTexture);

this.GraphicsDevice.Clear(Color.White); this.GraphicsDevice.SetRenderTarget(null);

С Texture2D то же самое можно сделать всего двумя строками кода, используя инициализацию массива прямо в строке:

tinyTexture = new Texture2D(this.GraphicsDevice, 1, 1); tinyTexture.SetData<Color>(new Color[] { Color.White });

Источник: Чарльз Петзольд, Программируем Windows Phone 7, Microsoft Press, © 2011.

По теме:

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