Создание и применение фильтров

Flash Player 9 и более поздних версий, Adobe AIR 1.0 и более поздних версий

Фильтры позволяют применять ряд эффектов к растровым объектам и экранным объектам (от тени до скосов и размытия). Каждый фильтр определен как класс. Поэтому применение фильтров предполагает создание экземпляров объектов фильтров, что неотличимо от создания любого другого объекта. После создания экземпляра объекта фильтра его можно легко применить к экранному объекту, воспользовавшись свойством filters объекта. В случае объекта BitmapData это делается с помощью метода applyFilter().

Создание фильтра

Чтобы создать объект фильтра, просто вызовите метод конструктора для выбранного класса фильтра. Например, чтобы создать объект DropShadowFilter, воспользуйтесь следующим кодом:

import flash.filters.DropShadowFilter; 
var myFilter:DropShadowFilter = new DropShadowFilter();

Хотя здесь это не показано, конструктор DropShadowFilter() (как и конструкторы всех классов фильтра) принимает ряд необязательных параметров, которые можно использовать для настройки внешнего вида эффекта фильтра.

Применение фильтра

После создания объекта фильтра его можно применить к экранному объекту или объекту BitmapData. Метод применения фильтра зависит от объекта, к которому он применяется.

Применение фильтра к экранному объекту

Когда эффекты фильтров применяются к экранному объекту, это происходит с помощью свойства filters. Свойство filters экранного объекта является экземпляром Array, элементы которого являются объектами фильтров, примененными к экранному объекту. Чтобы применить к экранному объекту один фильтр, создайте экземпляр фильтра, добавьте его к экземпляру Array и присвойте этот объект Array свойству filters экранного объекта:

import flash.display.Bitmap; 
import flash.display.BitmapData; 
import flash.filters.DropShadowFilter; 
 
// Create a bitmapData object and render it to screen 
var myBitmapData:BitmapData = new BitmapData(100,100,false,0xFFFF3300); 
var myDisplayObject:Bitmap = new Bitmap(myBitmapData); 
addChild(myDisplayObject); 
 
// Create a DropShadowFilter instance. 
var dropShadow:DropShadowFilter = new DropShadowFilter(); 
 
// Create the filters array, adding the filter to the array by passing it as  
// a parameter to the Array() constructor. 
var filtersArray:Array = new Array(dropShadow); 
 
// Assign the filters array to the display object to apply the filter. 
myDisplayObject.filters = filtersArray;

Если нужно присвоить объекту несколько фильтров, просто добавьте все фильтры к экземпляру Array перед присвоением его свойству filters. К объекту Array можно добавить несколько объектов, передав их конструктору в качестве параметров. Например, этот код применяет фильтр фаски и фильтр свечения к ранее созданному экранному объекту:

import flash.filters.BevelFilter; 
import flash.filters.GlowFilter; 
 
// Create the filters and add them to an array. 
var bevel:BevelFilter = new BevelFilter(); 
var glow:GlowFilter = new GlowFilter(); 
var filtersArray:Array = new Array(bevel, glow); 
 
// Assign the filters array to the display object to apply the filter. 
myDisplayObject.filters = filtersArray;

Для создания массива, содержащего фильтры, можно использовать конструктор new Array() (как показано в предыдущих примерах), либо воспользоваться синтаксисом литерала Array, поместив фильтры в квадратные скобки ([]). Например, следующая строка кода:

var filters:Array = new Array(dropShadow, blur); 

выполняет то же действие, что и строка кода

var filters:Array = [dropShadow, blur];

Если к экранным объектам применяется несколько фильтров, они вступают в силу последовательно и имеют кумулятивный эффект. Например, если массив фильтров располагает двумя элементами (фильтром фаски, который добавляется первым, и фильтром тени, который добавляется вторым), фильтр тени применяется как к фильтру фаски, так и к экранному объекту. Это происходит из-за того, что фильтр тени находится в массиве фильтров на второй позиции. Если требуется применять фильтры без кумулятивного эффекта, необходимо применить каждый фильтр к новой копии экранного объекта.

Если один или несколько фильтров только присваиваются экранному объекту, можно создать экземпляр фильтра и присвоить его объекту в одной инструкции. Например, следующая строка кода применяет фильтр размытия к экранному объекту с именем myDisplayObject:

myDisplayObject.filters = [new BlurFilter()];

Предыдущий код создает экземпляр Array с помощью синтаксиса литерала Array (квадратные скобки), создает экземпляр BlurFilter в качестве элемента объекта Array и присваивает этот объект Array свойству filters экранного объекта с именем myDisplayObject.

Удаление фильтров из экранного объекта

Удаление всех фильтров из экранного объекта настолько же просто, как и присвоение нулевого значения свойству filters:

myDisplayObject.filters = null;

Если к объекту применены несколько фильтров и требуется удалить только один из фильтров, следует выполнить ряд действий, чтобы изменить массив свойства filters. Дополнительные сведения см. в разделе Возможные проблемы при работе с фильтрами.

Применение фильтра к объекту BitmapData

Применение фильтра к объекту BitmapData требует использования метода applyFilter() для объекта BitmapData:

var rect:Rectangle = new Rectangle(); 
var origin:Point = new Point(); 
myBitmapData.applyFilter(sourceBitmapData, rect, origin, new BlurFilter());

Метод applyFilter() применяет фильтр к исходному объекту BitmapData, в результате чего появляется новое фильтрованное изображение. Этот метод не изменяет исходный оригинал; вместо этого результат применения фильтра к исходному изображению сохраняется в экземпляре BitmapData, по отношению к которому применяется метод applyFilter().

Как работают фильтры

Фильтрация экранных объектов выполняется путем кэширования копии исходного объекта в качестве прозрачного растрового изображения.

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

Каждый экранный объект, как правило, имеет два растровых изображения: одно с исходным нефильтрованным экранным объектом, а другое для конечного изображения, полученного после фильтрации. Конечное изображение используется при визуализации. Если экранный объект не изменяется, конечное изображение не обновляется.

Возможные проблемы при работе с фильтрами

При работе с фильтрами необходимо учитывать ряд потенциальных источников путаницы или проблем.

Кэширование фильтров и растровых изображений

Чтобы применить фильтр к экранному объекту, необходимо включить кэширование растровых изображений для этого объекта. В случае применения фильтра к экранному объекту, свойство cacheAsBitmap которого имеет значение false, свойству объекта cacheAsBitmap автоматически устанавливается значение true. Если впоследствии вы удалите все фильтры из экранного объекта, свойству cacheAsBitmap будет назначено последнее из присвоенных значений.

Изменение фильтров во время выполнения

Если к экранному объекту уже применен один или несколько фильтров, набор фильтров невозможно изменить путем добавления дополнительных фильтров или удаления фильтров из массива свойства filters. Вместо этого, чтобы добавить или изменить применяемый набор фильтров, необходимо внести изменения в отдельный массив, а затем присвоить данный массив свойству filters экранного объекта для фильтров, применяемых к объекту. Это проще всего сделать путем считывания массива свойства filters в переменную Array и внесения модификаций в этот временный массив. После этого массив повторно назначается свойству filters экранного объекта. В более сложных случаях может быть необходимо сохранить отдельный главный массив фильтров. В него вносятся изменения, а затем главный массив повторно назначается свойству filters экранного объекта после каждого изменения.

Добавление дополнительного фильтра

Процесс добавления дополнительного фильтра к экранному объекту, к которому уже применен один или несколько фильтров, показан в следующем коде. Сначала к экранному объекту с именем myDisplayObject применяется фильтр свечения; затем при нажатии на экранный объект вызывается функция addFilters(). В этой функции к myDisplayObject применяются два дополнительных фильтра:

import flash.events.MouseEvent; 
import flash.filters.*; 
 
myDisplayObject.filters = [new GlowFilter()]; 
 
function addFilters(event:MouseEvent):void 
{ 
    // Make a copy of the filters array. 
    var filtersCopy:Array = myDisplayObject.filters; 
 
    // Make desired changes to the filters (in this case, adding filters). 
    filtersCopy.push(new BlurFilter()); 
    filtersCopy.push(new DropShadowFilter()); 
 
    // Apply the changes by reassigning the array to the filters property. 
    myDisplayObject.filters = filtersCopy; 
} 
 
myDisplayObject.addEventListener(MouseEvent.CLICK, addFilters);

Удаление фильтра из набора фильтров

Если к экранному объекту применено несколько фильтров и требуется удалить один из этих фильтров, продолжая применять остальные фильтры к объекту, фильтры копируются во временный массив, ненужный фильтр удаляется из массива, а затем выполняется повторное назначение временного массива свойству filters экранного объекта. В разделе «Получение значений и удаление элементов массива» указан ряд способов удаления одного или нескольких элементов из любого массива.

Наиболее прямой способ состоит в удалении самого верхнего фильтра объекта (фильтра, примененного к объекту последним). Метод pop() класса Array используется для удаления фильтра из массива:

// Example of removing the top-most filter from a display object  
// named "filteredObject". 
 
var tempFilters:Array = filteredObject.filters; 
 
// Remove the last element from the Array (the top-most filter). 
tempFilters.pop(); 
 
// Apply the new set of filters to the display object. 
filteredObject.filters = tempFilters;

Точно так же, чтобы удалить самый нижний фильтр (первый фильтр, примененный к объекту), используется тот же код, подставляющий метод shift() класса Array вместо метода pop().

Чтобы удалить фильтр из середины массива фильтров (предполагая, что массив имеет более двух фильтров), необходимо воспользоваться методом splice(). Вы должны знать указатель (позицию в массиве) удаляемого фильтра. Например, следующий код удаляет второй фильтр (фильтр с указателем 1) из экранного объекта:

// Example of removing a filter from the middle of a stack of filters 
// applied to a display object named "filteredObject". 
 
var tempFilters:Array = filteredObject.filters; 
 
// Remove the second filter from the array. It's the item at index 1  
// because Array indexes start from 0. 
// The first "1" indicates the index of the filter to remove; the  
// second "1" indicates how many elements to remove. 
tempFilters.splice(1, 1); 
 
// Apply the new set of filters to the display object. 
filteredObject.filters = tempFilters;

Определение указателя фильтра

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

Наиболее оптимальный подход состоит в разработке собственного приложения, чтобы удаляемый фильтр всегда находился на одной и той же позиции в наборе фильтров. Например, если у вас имеется единичный экранный объект, к которому применен фильтр свертки и фильтр тени (именно в таком порядке), и требуется удалить фильтр тени, сохранив фильтр свертки, этот фильтр находится в известной позиции (самый верхний фильтр). Благодаря этому вы всегда можете заранее узнать, какой метод Array использовать (в данном случае Array.pop() для удаления фильтра тени).

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

// Example of removing a glow filter from a set of filters, where the 
//filter you want to remove is the only GlowFilter instance applied  
// to the filtered object. 
 
var tempFilters:Array = filteredObject.filters; 
 
// Loop through the filters to find the index of the GlowFilter instance. 
var glowIndex:int; 
var numFilters:int = tempFilters.length; 
for (var i:int = 0; i < numFilters; i++) 
{ 
    if (tempFilters[i] is GlowFilter) 
    { 
        glowIndex = i; 
        break; 
    } 
} 
 
// Remove the glow filter from the array. 
tempFilters.splice(glowIndex, 1); 
 
// Apply the new set of filters to the display object. 
filteredObject.filters = tempFilters;

В более сложном случае, например, тогда, когда удаляемый фильтр выбирается во время выполнения, наилучший подход состоит в сохранении отдельной постоянной копии массива фильтра, который служит главным списком фильтров. Каждый раз при внесении изменений в набор фильтров измените главный список, а затем используйте этот массив фильтра как свойство filters экранного объекта.

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

// Example of removing a filter from a set of  
// filters, where there may be more than one  
// of that type of filter applied to the filtered  
// object, and you only want to remove one. 
 
// A master list of filters is stored in a separate, 
// persistent Array variable. 
var masterFilterList:Array; 
 
// At some point, you store a reference to the filter you 
// want to remove. 
var filterToRemove:ConvolutionFilter; 
 
// ... assume the filters have been added to masterFilterList, 
// which is then assigned as the filteredObject.filters: 
filteredObject.filters = masterFilterList; 
 
// ... later, when it's time to remove the filter, this code gets called: 
 
// Loop through the filters to find the index of masterFilterList. 
var removeIndex:int = -1; 
var numFilters:int = masterFilterList.length; 
for (var i:int = 0; i < numFilters; i++) 
{ 
    if (masterFilterList[i] == filterToRemove) 
    { 
        removeIndex = i; 
        break; 
    } 
} 
 
if (removeIndex >= 0) 
{ 
    // Remove the filter from the array. 
    masterFilterList.splice(removeIndex, 1); 
 
    // Apply the new set of filters to the display object. 
    filteredObject.filters = masterFilterList; 
}

В данном подходе (когда вы сравниваете сохраненную ссылку на фильтр с элементами в массиве фильтров, чтобы определить, какой фильтр следует удалить) необходимо сохранить отдельную копию массива фильтров. Код не действует, если вы сравниваете сохраненную ссылку на фильтр с элементами во временном массиве, скопированными из свойства filters экранного объекта. Это происходит потому, что когда вы присваиваете массив свойству filters, среда выполнения создает копию каждого объекта фильтра в массиве внутренним образом. Эти копии (в отличие от исходных объектов) применяются к экранному объекту. И когда вы считываете свойство filters во временный массив, временный массив содержит ссылки на скопированные объекты фильтра, а не на ссылки на исходные объекты фильтра. Вследствие этого, если в предыдущем примере вы пытались определить указатель filterToRemove, сравнивая его с фильтрами во временном массиве фильтров, совпадения найдено не было.

Фильтры и трансформации объекта

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

Масштабирование, вращение и сдвиг не поддерживаются фильтрами; если сам фильтрованный объект масштабируется (если scaleX и scaleY не составляют 100 %), эффект фильтра не масштабируется вместе с экземпляром. Это означает, что исходная фигура экземпляра вращается, масштабируется или сдвигается, но фильтр не вращается, не масштабируется и не сдвигается вместе с экземпляром.

Экземпляр можно анимировать с помощью фильтра, чтобы создать реалистичные эффекты или вложенные экземпляры и воспользоваться классом BitmapData, чтобы анимировать фильтры для получения этого эффекта.

Фильтры и объекты Bitmap

Если к объекту BitmapData применяется любой фильтр, свойству cacheAsBitmap автоматически присваивается значение true. Таким образом, фильтр фактически применяется к копии объекта, а не к оригиналу.

Эта копия затем помещается на основной экран (над исходным объектом) как можно ближе к ближайшему пикселу. Если границы исходного растрового объекта изменяются, растровое изображение фильтрованной копии воссоздается на основе оригинала, а не растягивается и не искажается.

Если вы удалите все фильтры для экранного объекта, свойство cacheAsBitmap примет значение, которое было ему присвоено перед применением фильтра.