Руководство по 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 ( ) — изменяет позицию всех пикселов растрового изображения на заданную величину по горизонтали и вертикали.
Руководство по actionscript. часть 5, стр. 045
Подробное описание перечисленных методов можно найти в описании класса BitmapData в справочнике по языку ActionScript корпорации Adobe.
Класс BitmapData также поддерживает различные фильтры, эффекты и операции копирования, которые могут быть использованы для управления пикселами растрового изображения. Дополнительную информацию можно получить далее, в разд. «Копирование графики в объект BitmapData» и «Применение фильтров и эффектов» этой главы.
Изменение размеров растрового изображения
Когда изменяются размеры объекта Bitmap, ссылающегося на объект BitmapData, с помощью переменных scaleX и scaleY или width и height, размеры изображения на экране меняются, однако нижележащий объект BitmapData остается неизменным. Чтобы изменить размер нижележащего объекта BitmapData на самом деле, мы должны произвести его повторную выборку с помощью метода экземпляра draw ( ) класса BitmapData {повторная выборка означает изменение числа пикселов в изображении). Общая методика выглядит следующим образом.
1. Получить ссылку на исходный объект BitmapData.
2. Нарисовать масштабированную версию исходного объекта BitmapData в новом объекте BitmapData.
3. Наконец, связать исходный объект Bitmap с новым, масштабированным объектом BitmapData.
Перечисленные шаги продемонстрированы в листинге 26.7.
Листинг 26.7. Повторная выборка растрового изображения
// Получаем временную ссылку на исходный объект BitmapData var originalBitmapData:BitmapData = originalBitmap. bitmapData;
// Устанавливаем величину, которая будет определять коэффициент // масштабирования растрового изображения var scaleFactonNumber = .5:
// Вычисляем новые размеры масштабированного растрового изображения var newWidth:int = originalBitmapData. width * scaleFactor, var newHeight;int = originalBitmapData. height * scaleFactor;
// Создаем новый объект BitmapData, размеры которого позволят уместить // масштабированное растровое изображение
var scaledBitmapData:BitmapData = new BitmapData(newWidth, newHeight,
ori gi nalBitmapData. transparent);
// Создаем матрицу преобразований, с помощью которой будет происходить // масштабирование растрового изображения var scaleMatrixiMatrix = new Matrix( ); matrix. scale(scaleFactor, scaleFactor);
// Переносим масштабированное растровое изображение
// в новый объект BitmapData
sealedBi tmapData. draw(ori gi nalBi tmapData, matri x);
// Заменяем исходный объект BitmapData. новым масштабированным объектом BitmapData originalBitmap. bitmapData = scaledBitmapData;
В следующем разделе мы узнаем более подробно о методе draw ( ).
Руководство по actionscript. часть 5, стр. 046
Копирование графики в объект BitmapData
Значения цвета пикселов могут быть скопированы в объект BitmapData из двух источников: другого объекта BitmapData или любого экземпляра DisplayOb j ect.
Чтобы скопировать любой экземпляр класса Di splayOb j ect в объект BitmapData, мы применяем метод draw ( ), который копирует значения цвета из объекта-источника в объект-получатель BitmapData. В процессе копирования пикселы, сохраняемые в объекте BitmapData, могут быть преобразованы, смешаны или сглажены.
Чтобы скопировать значения цвета из другого объекта BitmapData, можно использовать либо метод draw ( ), либо любой из следующих методов класса BitmapData.
? copyPixels ( ) —копирует значения цвета из прямоугольной области пикселов объекта-источника BitmapData в объект-получатель BitmapData. Источ-
ник и получатель могут являться одним объектом, позволяя копировать пикселы из одной области изображения в другую область того же изображения.
? copyChannel ( ) — копирует отдельный цветовой канал из прямоугольной области пикселов объекта-источника BitmapData в объект-получатель BitmapData. Источник и получатель могут являться одним объектом, позволяя копировать пикселы из одной области изображения в другую область того же изображения.
? clone ( ) —создает новый объект BitmapData, дублируя существующий объект BitmapData.
? merge ( ) — смешивает вместе каналы двух объектов BitmapData, создавая новое изображение, в котором одно изображение оказывается наложенным на другое. Объект-источник и объект-получатель BitmapData могут быть одним объектом, позволяя смешивать два канала одного и того же изображения.
В этом разделе мы сосредоточимся на методах draw ( ) ncopyPixels ( ). Дополнительную информацию о других методах копирования можно найти в описании класса BitmapData в справочнике по языку ActionScript корпорации Adobe.
Метод экземпляра draw() класса BitmapData
Метод draw ( ) имеет следующий обобщенный вид:
целевой0бъектВ1tmapData. йгам(источник, матрицаПреобразования, цветовыеПреобразования, режимСмешения, областьОбрезки, сглаживание)
Здесь целевойОбъектВ! tmapData — объект BitmapData, в который будут перенесены пикселы. Параметры метода draw ( ) описаны ниже.
Руководство по actionscript. часть 5, стр. 047
? источник — экземпляр класса DisplayObject или BitmapData, графические данные которого будут перенесены в объект целевой0бъектВ1 tmapData. Это единственный обязательный параметр метода draw ( ). Стоит отметить, что, когда значением параметра источник является объект DisplayObject, при переносе в объект целевой0бъектВ1 tmapData его преобразования не включаются. Тем не менее преобразования объекта источник могут быть включены вручную путем передачи значения переменной источник. transform. matrix в качестве параметра матрицаПреобразований метода draw ( ), а значение переменной ^crovwM. transform. colorTransform — в качестве параметра цветовоеПреобразование метода draw ( ). В качестве альтернативы объект целевойОбъектВ! tmapData может быть связан с объектом BitmapData, переменная экземпляра transform которого ссылается на переменную источник. transform.
? матрицаПреобразования — необязательный объект Matrix, описывающий любое перемещение (то есть изменение позиции), масштабирование, вращение и искажение, которое должно быть применено к пикселам, переносимым в объект целевойОбъектВ! tmapData. Информацию об использовании объекта Matrix для выполнения графических преобразований можно найти в описании класса Matrix в справочнике по языку ActionScript корпорации Adobe и в разделе Programming ActionScript 3.0 > Flash Player APIs > Working With Geometry > Using Matrix objects документации корпорации Adobe. Общий пример матричных преобразований можно найти по адресу http://windowssdk. msdn. microsoft. com/en-us/library/ms536397.aspx и http://www. senocular. com/flash/tutorials/transformmatrix.
Стоит отметить, что гарантировать достаточный размер объекта целевойОбъ-ектЕН tmapData для хранения преобразованного объекта источник должен программист. В интерфейсе API приложения Flash Player 9 не предусмотрено никакой возможности для предварительного получения размера преобразованного объекта источник. Такая возможность может быть включена в будущие версии сред Flash, например, в виде метода generateTransf ormRect ( ) (разработанного после существующего метода generateFilterRect ( ) ). Чтобы отдать свой голос в поддержку подобного метода, посетите страницу http://www. adobe. com/ cfusion/mmform/index. cfm? name=wishform.
? цветовыеПреобразования — необязательный объект ColorTransf orm, описывающий любые цветовые изменения, которые должны быть применены к пикселам, переносимым в объект целевойОбьектВ1tmapData. Цветовые преобразования задаются независимо для каждого цветового канала либо с помощью множителя (числа, на которое умножается существующее значение цветового канала), либо с помощью смещения (числа, которое прибавляется к существующему значению цветового канала), либо с помощью обоих способов. Информацию по использованию объекта ColorTransf orm для выполнения графических преобразований можно найти в описании класса ColorTransformB справочнике по языку ActionScript корпорации Adobe.
? режимСмешения — необязательная константа класса В1 endMode, обозначающая тип смешения, который должен быть применен к пикселам, переносимым в объект целевойОбьектВ1tmapData. Смешение означает использование формул для объединения значений цвета объекта источник с отображаемыми объектами, которые визуально располагаются позади него, обычно с целью создания эффекта наложения. Поддерживаемыми режимами смешения являются BlendMode. MULTIPLY, BlendMode. SCREEN, BlendMode. HARDLIGHT и многие другие, которые знакомы пользователям программы Adobe Photoshop. Реализация режимов смешения в языке ActionScript основывается на стандарте SVG консорциума W3C (описание этого стандарта доступно по адресу http://www. w3.orgДR/2003/WD-SVG12-20030715/#compositing) и исследовании Дженса Грашела (Jens Gruschel), опубликованного по адресу http://www. pegtop. net/delphi/articles/blendmodes. Описание каждого доступного режима смешения и изображения, иллюстрирующие результаты применения этих режимов, можно найти в описании переменной экземпляра blendMode класса DisplayObject в справочнике по языку ActionScript корпорации Adobe.
? областьОбрезки — необязательный объект Rectangle, обозначающий прямоугольную область объекта целевойОбъектВ1 tmapData, в которую будут перенесены графические данные объекта источник.
? сглаживание — необязательный параметр типа Boolean, который обозначает, должно ли выполняться сглаживание растрового изображения во время рисования. Этот параметр оказывает влияние на результат только в том случае, когда объект источник является объектом BitmapData и указанный объект матрицаПреобразования задает параметры масштабирования или вращения. При этом, когда параметру сглаживание присвоено значение true, объект источник отображается в объекте целевойОбъектВ1 tmapData с использованием алгоритма сглаживания растровых изображений языка ActionScript. Когда параметру
сглаживание присвоено значение false, объект источник отображается в объекте целевойОбъектЕН tmapData без сглаживания. Изображение, выводимое с использованием сглаживания, выглядит менее «зазубренным» или «пикселизирован-ным», чем изображение, отображаемое без сглаживания. Это проиллюстрировано на рис. 26.6, где показано небольшое исходное изображение (вверху), которое увеличивается в три раза с помощью объекта Matrix с применением сглаживания (слева) и без применения сглаживания (справа).
Вывод на экран растрового изображения с применением сглаживания занимает больше времени, чем вывод без сглаживания. Чтобы достичь максимально возможной производительности, передавайте в качестве параметра сглаживание значение false; чтобы достичь максимально возможного качества изображения, передавайте в качестве параметра сглаживание значение true. Параметр сглаживание оказывает влияние только на текущую операцию draw ( ); он никак не влияет на применение сглаживания к объекту целевойОбьектВ! tmapData в дальнейшем.
Рис. 26.6. Сглаживание растрового изображения
Метод draw ( ) обычно применяется для:
? объединения нескольких отображаемых объектов в одно растровое изображение;
? растеризации векторного содержимого (то есть преобразования векторов в растровое изображение) с целью применения некоторого эффекта.