Март 2011
Руководство по actionscript. часть 5, стр. 035
// Получаем значение цвета пиксела, уровень канала Alpha которого // установлен в 255, из прозрачного изображения var imgData-.BitmapData = new BitmapData(20, 20. true. OxFFFFFFFF); trace(imgData. getPixel32(0. 0)); // Выводит: 4294967295
// (исходные данные были сохранены)
Класс ColorPicker: пример использования метода getPixel32()
Теперь, когда мы понимаем, как получить значение цвета пиксела, применим наши знания в реальной ситуации. Предположим, что мы разрабатываем интернет-приложение для создания приглашений на вечеринки. Пользователи приложения сначала выбирают фотографию, которая будет помещена на приглашение, а затем задают подходящий цвет для текста приглашения. Чтобы пользователь мог поэкспериментировать с различными цветами, приложение предоставляет специальную форму для выбора цвета. Когда пользователь перемещает указатель мыши над выбранным изображением, цвет текста приглашения автоматически изменяется в соответствии с цветом пиксела, над которым в данный момент находится указатель мыши. В листинге 26.4 продемонстрирован код для палитры выбора цвета с тестовым изображением sunset. jpg. Изучите комментарии, чтобыпонять, как происходит получение значения цвета под указателем мыши.
Листинг 26.4. Палитра выбора цвета на основе изображения
package { import flash. display.*; import flash. events.*; import flash. text.*; import flash. net.*;
// Устанавливает цвет объекта TextField в соответствии с цветом
// выбранного в изображении пиксела.
public class ColorPicker extends Sprite { private var img:Bitmap; // Объект Bitmap
private var imgContainer:Sprite; // Контейнер для объекта Bitmap private var t:TextField; // Раскрашиваемый объект TextField
// Метод-конструктор
public function ColorPicker( ) {
// Создаем объект TextField и добавляем его в иерархию отображения
// объекта ColorPicker
t = new TextField( );
t. text = «Please come to my party…»;
t. autoSize = TextFieldAutoSize. LEFT;
addChild(t);
// Загружаем изображение
var loader-.Loader = new Loader ( );
1oader. contentLoaderInfо. addEventLi stener(Event. INIT,
initListener);
1oader.1oad(new URLRequest(«sunset. jpg»));
}
// Вызывается, когда инициализация изображения завершена private function initListener (e:Event):void {
// Получаем ссылку на загруженный объект Bitmap
img = е. target. content;
// Помещаем загруженное растровое изображение в объект Sprite, чтобы // мы могли реагировать на взаимодействия с мышью imgContainer = new Sprite( ); i mgConta i ner. addChi1d(i mg);
// Добавляем объект Sprite в иерархию отображения объекта ColorPicker addChild(imgContainer); imgContainer. у = 30;
// Регистрируем приемник для получения уведомлений о перемещении мыши imgContainer. addEventLi stener(MouseEvent. M0USE_M0VE, mouseMoveListener);
}
// Вызывается, когда происходит перемещение мыши над объектом Sprite. // содержащим изображение
private function mouseMoveListener (e:MouseEvent):void { // Устанавливаем цвет текста в соответствии с цветом пиксела, // находящегося в данный момент под указателем мыши
t. textColor = img. bitmapData. getPixel32(e. localX, e. localY);
}
}
}
Получение цвета области пикселов
Методыэкземпляраде1Р1хе132 ( ) иgetPixel ( ) класса BitmapData применяются для получения значения цвета отдельного пиксела. В отличие от этого, метод экземпляра getPixels ( ) класса BitmapData используется для получения значений цвета целой прямоугольной области пикселов. Метод getPixels ( ) может быть использован в любом из следующих сценариев:
? при передаче области растрового изображения между модулями программы;
? при использовании собственного алгоритма для обработки фрагмента растрового изображения;
? при отправке фрагмента или всего растрового изображения на сервер в необработанном бинарном формате.
Руководство по actionscript. часть 5, стр. 036
Метод getPixels ( ) принимает следующий обобщенный вид:
объектаtmapData. getPi xels(область)
Здесь объектВтtmapData — объект BitmapData, из которого будут возвращаться значения цвета пикселов, а область — объект flash. geom. Rectangle, описывающий область возвращаемых пикселов. Метод getPixels ( ) возвращает объект ByteArray, содержащий 32-битные целочисленные значения цвета. Объект ByteArray — это список значений цвета для пикселов в указанной прямоугольной области, обход которых происходит слева направо и сверху вниз. Например, рассмотрим следующую диаграмму растрового изображения размером 4×4, пикселы которого для простоты обозначены буквами от А до Р:
А В С D Е F G Н I J К L М N О Р
Не забывая, что пиксел левого верхнего угла растрового изображения находится в точке с координатой (0; 0), если мы воспользуемся методом getPixels ( ) для получения значений цвета прямоугольной области пикселов от точки (2; 1) до точки (3; 3), возвращаемый объект ByteArray будет содержать следующие пикселы в таком порядке:
G, Н. К, L, 0. Р
Стоит отметить, что объект ByteArray представляет собой одномерный список и не содержит никакой информации о размерах и позиции прямоугольной области, из которой были получены данные пикселы. Таким образом, чтобы восстановить растровое изображение из пикселов, хранящихся в объекте ByteArray, в том порядке, в котором они находились ранее, мы должны иметь свободный доступ к ширине, высоте и позиции исходного прямоугольника. Информация об исходном прямоугольнике может быть присвоена переменной или даже добавлена в сам объект ByteArray.
Чтобы попрактиковаться в использовании метода get Pixel s ( ), скопируем прямоугольную область из одного растрового изображения в другое изображение. Сначала мы создадим два объекта BitmapData. Первый объект представляет квадрат синего цвета размером 20 х 20 пикселов, а другой — квадрат зеленого цвета размером 30 х 30 пикселов:
var blueSquare:BitmapData = new BitmapData(20. 20. false. OxFFOOOOFF); var greenSquare:BitmapData = new BitmapDataOO. 30. false. OxFFOOFFOO);
Затем мы определяем прямоугольную область пикселов, которую хотим получить из квадрата зеленого цвета. Левый верхний угол прямоугольника находится в точке с координатой (5; 5), а его ширина и высота равна 10 пикселам.
var rectRegion:Rectangle = new Rectangle(5. 5. 10. 10);
Теперь мы получаем пикселы зеленого цвета:
var greenPixels:ByteArray = greenSquare. getPixels(rectRegion);
Чтобы перенести пикселы зеленого цвета на квадрат синего цвета, мы используем метод экземпляра set Pixels ( ) класса BitmapData. Однако перед вызовом метода set Pixel s ( ) мы должны установить указатель файла объекта ByteArray в значение 0, чтобы метод setPixels( ) начал чтение значений цвета пикселов с начала списка:
greenPixels. position = 0;
Теперь мы можем прочитать пикселы из объекта ByteArray greenPixels и сохранить их в объекте BitmapData blueSquare:
blueSquare. setPi xels(rectRegi on. greenPi xels);
Чтобы убедиться, что все работает так, как ожидалось, мы отображаем два растровых изображения на экране:
var blueBmp:Bitmap = new Bitmap(blueSquare); var greenBmp:Bitmap = new Bitmap(greenSquare); addChild(blueBmp); addChild(greenBmp); greenBmp. x = 40;
На рис. 26.5 показаны результаты выполнения предыдущего кода.
Руководство по actionscript. часть 5, стр. 037
Рис. 26.5. Пикселы, скопированные из объекта ByteArray
Если при копировании пикселов между двумя растровыми изображениями размеры копируемого прямоугольника и целевого прямоугольника совпадают (как в предыдущем примере), мы можем использовать удобный метод экземпляра copyР ixe 1 s ( ) клaccaBitmapDataвмecтoкoмбинaциимeтoдoвgetPixels ( ) HsetPixels( ). К другим внутренним методам экземпляра класса BitmapData, предоставляющим удобный доступ к типичным операциям копирования, относятся: copyChannel ( ), clone ( ), merge ( ) и draw ( ). Дополнительную информацию можно найти далее, в разд. «Копирование графики в объект BitmapData» этой главы.
Другие инструменты анализа
В этом разделе мы узнали, как анализировать пикселы объекта BitmapData, используя методы getPixel32 ( ), getPixel ( ) и getPixels ( ). Кроме того класс BitmapData предоставляет несколько других, более специализированных инструментов для анализа пикселов:
? compare ( ) — проверяет, есть ли отличие между пикселами двух растровых изображений;
? getColorBoundsRect ( ) — определяет, какая область растрового изображения содержит указанный цвет;
? hitTest ( ) — определяет, перекрывают ли пикселы растрового изображения некоторую точку, прямоугольник или другое растровое изображение.
Подробную информацию о перечисленных методах можно найти в описании класса BitmapData в справочнике по языку ActionScript корпорации Adobe.
Внесение изменений в растровое изображение
Основные инструменты для присваивания новых цветов пикселам существующего растрового изображения являются точным отражением инструментов, предназначенных для анализа растрового изображения. К ним относятся методы setPixel32 ( ), setPixel ( ) и setPixels ( ). Метод setPixel32 ( ) присваивает новое четырехканальное значение цвета пикселу в виде 32-битного целого числа. Он принимает следующий вид:
объектШtmapData. setPixe!32(x, у, цвет)
Здесь обьектВ! tmapData — экземпляр класса BitmapData, содержащий пиксел, значение цвета которого будет изменяться; х и у — горизонтальная и вертикальная позиции данного пиксела; цвет — новое значение цвета, присваиваемое пикселу. Например, следующий код создает растровое изображение, представляющее квадрат синего цвета, и затем присваивает его левому верхнему пикселу значение белого цвета:
var imgData:BitmapData = new BitmapData(20, 20. false. OxFFOOOOFF); imgData. setPixel32(0. 0. OxFFFFFFFF);
В отличие от этого, метод setPixel ( ), который принимает такой же общий вид, как и метод setPixel32( ), устанавливает только значения каналов Red, Green и Blue цвета пиксела, не изменяя исходное значение канала Alpha. Например, следующий код создает полупрозрачное растровое изображение, представляющее квадрат синего цвета, и затем присваивает его левому верхнему пикселу значение белого цвета. Поскольку вместо метода setPixel32( ) используется setPixel ( ), левый верхний пиксел сохраняет свое исходное значение канала Alpha (Охбб):
var imgData:BitmapData = new BitmapData(20. 20, true. 0x660000FF); imgData. setPixeKO. 0. OxFFFFFF):
После завершения операции set Pixel ( ) значением цвета левого верхнего пиксела будет являться число Охб 6FFFFFF.
Руководство по actionscript. часть 5, стр. 038
Любое значение канала Alpha, указываемое в числе, которое передается в метод setPixel ( ), будет проигнорировано. Например, в следующем коде мы присваиваем значение цвета пиксела, используя число, в котором для канала Alpha указано значение сс. Несмотря на это, после завершения операции значением цвета левого верхнего пиксела по-прежнему будет являться число 0x66FFFFFF:
imgData. setPixeKO. 0. OxCCFFFFFF);
Повышение производительности с помощью метода BitmapData. lockC)
По умолчанию, всякий раз, когда над некоторым объектом BitmapData вызывается метод setPixel32 ( ) или setPixel ( ), экземпляры класса Bitmap, ссылающиеся на этот объект, получают уведомление об изменении данных. Когда методы setPixel32( ) или setPixel ( ) вызываются друг за другом внутри одного цикла кадра (например, когда каждому пикселу в растровом изображении присваивается значение цвета), подобные уведомления могут привести к снижению производительности. Для повышения производительности мы можем использовать метод экземпляра lock ( ) класса BitmapData.
Вызов метода lock ( ) над объектом BitmapData запрещает среде выполнения Flash уведомлять зависимые объекты В i tmap при вызове методов setPixel32( ) или setPixel ( ). Таким образом, если вы собираетесь использовать методы setPixel32 ( ) или setPixel ( ) друг за другом, всегда вызывайте метод lock ( ). После его вызова присвойте все желаемые значения цвета пикселов; затем вызовите метод экземпляра unlock ( ) класса BitmapData. Метод unlock ( ) позволяет среде Flash при необходимости уведомить все зависимые объекты Bitmap.
Данный подход продемонстрирован в листинге 26.5. В этом коде используется цикл для присваивания случайного цвета каждому пикселу в объекте BitmapData размером 500 х 500 пикселов. Обратите внимание на вызов метода lock ( ) перед циклом и вызов метода unlock ( ) после цикла, выделенные полужирным шрифтом.
Листинг 26.5. Использование метода BitmapData. lock() для повышения производительности
// Создаем растровое изображение
var imgData:BitmapData = new BitmapData(500. 500. true. 0×00000000); var bmp:Bitmap = new Bitmap(imgData);
// Вызываем метод lock( ) imgData. lock( );
// Устаналиваем значения цвета пикселов var col or:uint;
for (var i:int = 0; i < imgdata.height ; i++) { for (var j:int = 0; j < imgdata.width; j++) { color = math.f1oor(math.random( )*0xffffffff); imgdata.setpixel32(j, i, color);
}
}
// Вызываем метод unlock( ) imgData. unlock( );
При тестировании кода из листинга 26.5 в рабочей версии приложения Flash Player на компьютере с процессором Pentium 4 2,6 ГГц одна итерация цикла занимает приблизительно 100 мс. Без использования метода lock ( ) одна итерация занимает примерно 125 мс. Иными словами, при использовании метода lock ( ) код выполняется приблизительно на 20 % быстрее.
Руководство по actionscript. часть 5, стр. 039
При измерении производительности среды Flash всегда выполняйте тесты в рабочей, а не в отладочной версии. Производительность в рабочей версии приложения зачастую оказывается в два раза выше, чем в отладочной.
Класс ScribbleASS: пример использования метода setPixel32()
Присваивание цвета пикселу в растровом изображении имеет множество практических применений: от создания собственных эффектов до коррекции фотографий или генерации динамического интерфейса. Рассмотрим всего одно практическое применение метода set Pixel 32 ( ) — простую программу для рисования. В листинге 26.6 представлена адаптация на языке ActionScript 3.0 программы Scribble. Выполнение этого кода приводит к созданию пустого растрового изображения, на котором пользователь рисует линии с помощью мыши. Когда пользователь перемещает мышь, удерживая нажатой левую кнопку, на пустом растровом изображении рисуется пиксел черного цвета.
Листинг 26.6. Очень простая программа рисования ScribbleAS3
package {
import flash. display.*; import flash. events.*; import flash. ui.*; import flash. geom.*;
// Простое приложение для рисования. Рисует одну точку на объекте
// BitmapData всякий раз, когда возникает событие MouseEvent. M0USE_M0VE
// при нажатой левой кнопке мыши.
public class ScribbleAS3 extends Sprite {
// Растровое изображение, отображаемое на экране
private var canvas:Bitmap;
// Содержит растровое изображение, обеспечивая интерактивность private var canvasContaiпег:Sprite;
// Линия вокруг растрового изображения private var border:Shape;
// Сообщает о том, нажата ли кнопка мыши в настоящий момент private var isDrawing:Boolean = false;
// Конструктор
public function SeribbleAS3 ( ) { createCanvas( ); registerForInputEvents( );
// Предотвращаем изменение размеров окна приложения stage. scaleMode = StageScaleMode. N0_SCALE:
}
// Создает пустое растровое изображение, на котором будем рисовать private function createCanvas (width:int = 200, height:int = 200):void {
// Определяем объект BitmapData, который будет хранить пиксельные
// данные для рисунка пользователя
var canvasData:BitmapData = new BitmapData(width, height,
false. OxFFFFFFFF);
// Создаем новый отображаемый объект Bitmap, используемый // для отображения объекта canvasData canvas = new Bitmap(canvasData);
// Создаем объект Sprite, который будет содержать объект Bitmap. Класс // Bitmap не поддерживает события ввода; следовательно, помещаем его // в объект Sprite, чтобы пользователь мог взаимодействовать с этим // объектом.
Руководство по actionscript. часть 5, стр. 040
CanvasContainer = new SpriteC );
// Добавляем растровое изображение bitmap в экземпляр canvasContainer // класса Sprite
canvasContai ner. addChi1d(canvas);
// Добавляем экземпляр canvasContainer класса Sprite (и содержащийся // в нем объект Bitmap) в иерархию отображения данного объекта addChild(canvasContai пег);
// Создаем границу вокруг области рисования, border = new ShapeC ); border. graphics. lineStyled, OxFFOOOOOO); border. grapnics. drawRect(0, 0, width, height); addChild(border);
}
// Регистрирует приемники для необходимых событий мыши и клавиатуры
private function registerForlnputEvents ( ):void { // Регистрируем приемники для событий нажатия кнопки мыши // и перемещения мыши от объекта canvasContainer canvasContai ner. addEventLi stener(MouseEvent. M0USE_D0WN,
mouseDownListener);
canvasContai ner. addEventLi stener(MouseEvent. M0USE_M0VE,
mouseMoveListener);
// Регистрируем приемники для событий отпускания кнопки мыши и нажатия
// клавиши от объекта Stage (то есть для глобальных событий).
Руководство по actionscript. часть 5, стр. 041
// Используем объект Stage, поскольку событие отпускания кнопки мыши
// должно всегда завершать рисование, даже если указатель мыши
// не находится над областью рисования. Подобным образом нажатие
// пробела должно всегда приводить к стиранию рисунка, даже когда
// объект canvasContainer не имеет фокуса.
stage. addEventLi stener(MouseEvent. MOUSEJJP, mouseUpLi stener);
stage. addEventLi stener(KeyboardEvent. KEY_D0WN, keyDownLi stener);
}
// Устанавливает цвет указанного пиксела
public function drawPoint (x:int, y:int, colonuint = OxFFOOOOOO):void { canvas. bitmapData. setPixel32(x, y, color);
}
// Отвечает на события MouseEvent. MOUSE JDOWN
private function mouseDownListener (e:MouseEvent):void {
// Устанавливаем флажок, указывающий на то, что основная кнопка мыши
// в настоящий момент нажата
isDrawing = true;
// Рисуем точку в позиции, где произошел щелчок кнопкой мыши. drawPoint(е.1ocalX, е. localY);
}
// Отвечает на события MouseEvent. M0USE_M0VE
private function mouseMoveListener (e:MouseEvent):void {
// Рисуем точку, когда мышь перемещается над областью рисования
// при нажатой левой кнопке мыши
if (isDrawing) {
// Используем переменные 1ocalX и localY, чтобы получить позицию // указателя относительно объекта canvasContainer. drawPoi nt(е.1ocalX, е. localY);
// Обновляем экран сразу после завершения выполнения // данной функции-приемника события е. updateAfterEvent( );
}
// Отвечает на события MouseEvent. MOUSEJJP
private function mouseUpListener (e:MouseEvent):void {
// Устанавливаем флажок, указывающий на то, что в настоящий момент
// основная кнопка мыши отпущена
isDrawing = false;
}
// Отвечает на события KeyboardEvent. KEYJDOWN
private function keyDownListener (e:KeyboardEvent):void {
// Стираем рисунок, когда пользователь нажимает клавишу Пробел. Чтобы // очистить рисунок, мы присваиваем всем пикселам значение белого // цвета.
Руководство по actionscript. часть 5, стр. 042
If (е. charCode == Keyboard. SPACE) { canvas. bitmapData. fillRect(new Rectangle(0, 0,
canvas. width, canvas. height), OxFFFFFFFF);
}
}
}
}
Присваивание цвета области пикселов
Методы setPixel32( )nsetPixel( ) используются для присваивания значения цвета отдельному пикселу. В отличие от этого, метод экземпляра setPixels( ) класса BitmapData применяется для присваивания значений цвета целой прямоугольной области пикселов.
Метод setPixels ( ) имеет следующий обобщенный вид:
o6beKTBitmapData. setPixels(область, пикселыВуЬеАггау)
Здесь обьектВПшарОаЬа — объект BitmapData, пикселам которого присваиваются значения цвета, область — объект flash. geom. Rectangle, описывающий область пикселов, которым будет присвоен цвет, а пикселыВуЬеАггау — объект ByteArray, содержащий беззнаковые 32-битные целые числа, определяющие присваиваемые значения цвета.
Руководство по actionscript. часть 5, стр. 043
Метод setPixels( ) заполняет указанную прямоугольную область в направлении слева направо и сверху вниз, начиная со значения цвета объекта пикселыВуЬеАггау, находящегося в текущей позиции указателя файла (то есть в позиции пикселыВуЬеАггау. position).
Например, рассмотрим следующую диаграмму растрового изображения размером 4×4, пикселы которого для простоты обозначены буквами от А до Р:
А В С D Е F G Н I J К L М N О Р
Теперь рассмотрим следующую диаграмму массива байт, содержащего шесть 32-битных беззнаковых целочисленных значений цвета, обозначенных символами от С1 до С6:
CI С2 СЗ С4 С5 С6
Не забывайте, что пиксел левого верхнего угла растрового изображения находится в точке с координатой (0; 0). Если мы воспользуемся методом setPixels ( ) для заполнения прямоугольной области пикселов от точки (1; 0) до точки (3; 1) с помощью предыдущего массива байт, растровое изображение будет выглядеть следующим образом:
A CI С2 СЗ Е С4 С5 Сб I J К L М N 0 Р
Попробуем проделать то же самое в коде. Сначала создадим квадрат красного цвета размером 4×4 пиксела:
var imgData:BitmapData = new BitmapData(4, 4, false, OxFFFFOOOO);
Теперь мы создадим массив байт, который содержит шесть значений цвета — все они обозначают зеленый цвет. Для демонстрационных целей мы создадим массив байт вручную, однако обычно он формируется программным путем, возможно, с помощью вызова метода getPixels ( ) или в результате выполнения пользовательского алгоритма, возвращающего значения цвета. Массив будет выглядеть следующим образом:
var byteArray:ByteArray = new ByteArray( ); byteArray. writeUnsignedlnt(OxFFOOFFOO); byteArray. writeUnsignedInt(OxFFOOFFOO); byteArray. writeUnsignedlnt(OxFFOOFFOO); byteArray. writeUnsignedlnt(OxFFOOFFOO); byteArray. writeUnsignedlnt(OxFFOOFFOO); byteArray. writeUnsignedlnt(OxFFOOFFOO);
Далее мы устанавливаем позицию, с которой метод set Pixels ( ) должен начать чтение значений цвета из массива байт. Мы хотим, чтобы метод setPixels ( ) начаЛ чтение с самого начала массива байт, поэтому присваиваем переменной экземпляра position класса ByteArray значение 0: byteArray. position = 0;
Наконец, заполняем прямоугольную область в растровом изображении цветами та тшлъъ (й&тл
imgData. setPixels(new Rectangled.0,3,2), byteArray);
I Обратите внимание, что позиция и размеры объекта Rectangle, передаваемого в метод м$ 4 * setPixels(), определяются с помощью координаты левого верхнего угла и ширины/высоты _ Щх прямоугольника, а не координат левого верхнего угла и правого нижнего угла.
Руководство по actionscript. часть 5, стр. 044
Стоит отметить, что, если данные в объекте пикселыВуЬеАггау закончатся до того, как будет заполнена указанная прямоугольная область, среда выполнения Flash сгенерирует исключение EOFError. Например, если мы увеличим размер предыдущей прямоугольной области с 3 х 2 пиксела (6 пикселов) до 3 х 3 пиксела (9 пикселов) следующим образом:
imgData. setPixels(new Rectangled,0,3,3), byteArray);
произойдет следующая ошибка:
Error: Error #2030: End of file was encountered.
На русском языке она будет звучать так: Ошибка #2030: достигнут конец файла.
Кроме того, подобная ошибка может возникнуть, если после создания объекта ByteArray мы забудем установить его позицию в 0 (что является гораздо более
распространенной ошибкой в программировании, чем указание неправильных размеров прямоугольника или представление недостаточного количества значений цвета).
I Перед вызовом метода setPixels() не забывайте устанавливать позицию указываемого м$ а • входного массива байт.
Метод setPixels ( ) обычно применяется для создания растрового изображения на основе сериализованных бинарных данных, полученных из некоторого внешнего источника, например сервера или совместно используемого локального объекта.
Другие инструменты изменения изображений
В этом разделе мы узнали, как можно изменять пикселы в объекте BitmapData с помощью методов setPixel32 ( ), setPixel ( ) и setPixels ( ). Класс BitmapData также предоставляет несколько других, более специализированных инструментов для работы с пикселами:
? fillRect ( ) — присваивает заданный цвет пикселам из прямоугольной области;
? floodFill ( ) — присваивает заданный цвет всем пикселам, окружающим некоторый пиксел р, цвет которых соответствует цвету данного пиксела (подобно инструменту заливки, который присутствует во многих программах для работы с графикой);
? scroll ( ) — изменяет позицию всех пикселов растрового изображения на заданную величину по горизонтали и вертикали.