Tworzenie i stosowanie filtrów

Flash Player 9 i nowsze wersje, Adobe AIR 1.0 i nowsze wersje

Filtry umożliwiają stosowanie do bitmap i obiektów wyświetlanych szeregu efektów, od cieni aż po efekty ukośne i rozmycia. Każdy filtr zdefiniowano jako klasę, dlatego nałożenie filtru wymaga utworzenia instancji obiektów filtrujących, co nie różni się od konstruowania jakiegokolwiek z innych obiektów. Po utworzeniu instancji obiektu filtrującego można łatwo zastosować go obiektu wyświetlanego przez użycie właściwości filters obiektu, lub, w przypadku obiektu BitmapData, przez użycie metody applyFilter() .

Tworzenie filtru

W celu utworzenia obiektu filtrowania należy po prostu wywołać metodę konstruktora wybranej klasy filtra. Na przykład, w celu utworzenia obiektu DropShadowFilter należy użyć następującego kodu:

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

Mimo że nie pokazano tego tutaj, konstruktor DropShadowFilter() (podobnie jak konstruktory klas filtrów) przyjmują kilka opcjonalnych parametrów, których można użyć do dostosowania wyglądu efektu filtru.

Zastosowanie filtru

Po skonstruowaniu obiektu filtrującego można zastosować go do obiektu wyświetlanego lub obiektu typu BitmapData; sposób nałożenia filtra zależy od obiektu, wobec którego jest on stosowany.

Zastosowanie filtru do obiektu wyświetlanego

Zastosowanie efektu filtru do obiektu wyświetlanego wiąże się z użyciem jego właściwości filters . Właściwość filters obiektu wyświetlanego jest instancją Array, której elementy są obiektami filtrującymi stosowanymi do obiektu wyświetlanego. W celu zastosowania pojedynczego filtru do obiektu wyświetlanego należy utworzyć instancję filtru, dodać ją do instancji Array, a następnie przypisać ten obiekt Array do właściwości filters obiektu wyświetlanego:

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;

W celu przypisania wielu filtrów do obiektu należy po prostu dodać wszystkie te filtry do instancji Array przed przypisaniem ich do właściwości filters . Możliwe jest również dodanie wielu obiektów do obiektu Array przez przekazanie ich w charakterze parametrów do jego konstruktora. Na przykład w tym przykładzie zastosowano filtr efektu ukośnego oraz filtr blasku do uprzednio utworzonego obiektu wyświetlanego:

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;

Podczas tworzenia tablicy zawierającej filtry możliwe jest utworzenie jej za pomocą konstruktora new Array() (tak jak pokazano w poprzednich przykładach). Ponadto można również zastosować składnię literałową obiektu Array, ujmując filtry w nawiasy kwadratowe ( [] ). Na przykład ten wiersz kodu:

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

daje ten sam efekt, co wiersz następujący:

var filters:Array = [dropShadow, blur];

W przypadku nałożenia wielu filtrów na obiekty wyświetlane są one nakładane kolejno, a rezultaty ich działania kumulują się. Na przykład jeśli tablica filtrów ma dwa elementy, oraz filtr ukośny zostanie dodany jako pierwszy, a cień dodany jako drugi, filtr cienia zostanie dodany zarówno do filtru ukośnego, jak i do obiektu wyświetlanego. Dzieje się tak, ponieważ filtr cienia zajmuje drugą pozycję w tablicy filtrów. W celu zastosowania filtrów tak, aby się one nie kumulowały, należy nałożyć każdy filtr na nową kopię obiektu wyświetlanego.

W przypadku przypisywania do obiektu wyświetlanego tylko jednego lub kilku filtrów można utworzyć instancję filtru i przypisać ją do obiektu za pomocą prostej instrukcji. Na przykład poniższy wiersz kodu powoduje nałożenie filtru rozmycia na obiekt wyświetlany o nazwie myDisplayObject :

myDisplayObject.filters = [new BlurFilter()];

Poprzedni kod powoduje tworzenie instancji Array za pomocą składni literałowej (nawiasy kwadratowe), utworzenie instancji BlurFilter jako elementu tablicy oraz przypisanie tej tablicy do właściwości filters obiektu wyświetlanego o nazwie myDisplayObject .

Usuwanie filtrów z obiektu wyświetlanego

Usuwanie wszystkich filtrów z obiektu wyświetlanego jest proste i sprowadza się do przypisania właściwości filters wartości null:

myDisplayObject.filters = null;

Jeśli do obiektu przypisanych zostało wiele filtrów, z których tylko jeden chcemy usunąć, konieczne jest wykonanie kilku kroków w celu zmiany tablicy właściwości filters . Więcej informacji zawiera sekcja Potencjalne problemy podczas pracy z filtrami .

Zastosowanie filtru do obiektu BitmapData

Zastosowanie filtru do obiektu BitmapData wymaga użycia metody applyFilter() obiektu BitmapData:

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

Metoda applyFilter() powoduje nałożenie filtru na źródłowy obiekt BitmapData, co powoduje utworzenie nowego obrazu z efektem filtra. Metoda ta nie powoduje modyfikacji oryginalnego, źródłowego obrazu; zamiast tego, wynik nałożenia filtra na obraz źródłowy jest zapisywany w instancji BitmapData, dla której wywołano metodę applyFilter() .

Jak działa filtr

Nakładanie filtrów na obiekty wyświetlane odbywa się przez przekazanie do pamięci podręcznej oryginalnego obiektu w charakterze przezroczystej bitmapy.

Po nałożeniu filtru na obiekt wyświetlany środowisko wykonawcze zapisuje obiekt w pamięci podręcznej jako bitmapę na czas, przez jaki obiekt ma prawidłową listę filtrów. Ta źródłowa bitmapa jest następnie używana jako obraz oryginalny wobec wszystkich kolejnych elektów filtra.

Każdy obiekt wyświetlany ma z reguły dwie bitmapy: jedną z oryginalnym niefiltrowanym źródłowym obiektem wyświetlanym oraz drugą z końcowym obrazem po filtrowaniu. Przy renderowaniu wykorzystywany jest obraz końcowy. Jeżeli obiekt wyświetlany nie ulega zmianie nie ma potrzeby aktualizacji obrazu końcowego.

Potencjalne problemy podczas pracy z filtrami

Istnieje kilka potencjalnych źródeł problemów i pomyłek, o których należy pamiętać podczas pracy z filtrami.

Zapisywanie filtrów i bitmap w pamięci podręcznej

W celu nałożenia filtru na obiekt wyświetlany konieczne jest włączenie dla tego obiektu zapisywania bitmapy w pamięci podręcznej. Zastosowanie filtru do obiektu wyświetlanego, którego właściwość cacheAsBitmap jest ustawiona na false powoduje automatyczne ustawienie właściwości cacheAsBitmap obiektu na true . Gdy później wszystkie filtry obiektu wyświetlanego zostaną wyczyszczone, właściwość cacheAsBitmap uzyskuje wartość, którą ustawiono dla niej ostatnim razem.

Zmiana filtrów w czasie wykonywania

Jeśli wobec obiektu wyświetlanego zastosowano już jeden lub więcej filtrów, użytkownik nie może zmienić zestawu filtrów, dodając kolejne lub usuwając wybrane filtry z tablicy właściwości filters . Zamiast tego można jednak dodać lub zmienić zestaw nakładanych filtrów, dokonując zmian w osobnej tablicy, a następnie przypisując tę tablicę do właściwości filters, obiektu wyświetlanego w celu jej zastosowania do obiektu. Najprostszym sposobem wykonania tej operacji jest odczytanie tablicy właściwości filters do zmiennej Array i dokonanie modyfikacji w tej tymczasowej tablicy. Następnie należy ponownie przypisać tę tablicę do właściwości filters obiektu wyświetlanego. W bardziej złożonych przypadkach może zaistnieć potrzeba zachowania osobnej, głównej tablicy filtrów. Wszelkich zmian dokonuje się w tej właśnie głównej tablicy filtrów, a następnie ponownie przypisuje tablicę główną do właściwości filters obiektów wyświetlanych po każdej zmianie.

Dodawanie dodatkowych filtrów

Poniższy kod ilustruje proces dodawania dodatkowego filtru do obiektu wyświetlanego, wobec którego zastosowano już jeden lub więcej filtrów. Najpierw filtr blasku jest stosowany wobec obiektu wyświetlanego o nazwie myDisplayObject ; następnie, po kliknięciu obiektu wyświetlanego, wywoływana jest funkcja addFilters() . Funkcja ta powoduje nałożenie na obiekt myDisplayObject dwu dodatkowych filtrów:

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);

Usuwanie jednego filtru z zestawu filtrów

Jeśli do obiektu wyświetlanego zastosowano wiele filtrów, a użytkownik chciałby usunąć jednej z nich, pozostawiając jednocześnie pozostałe filtry, powinien skopiować te pożądane filtry do tablicy tymczasowej. Następnie należy usunąć niepożądany filtr z tej tablicy i przypisać tablicę tymczasową do właściwości filters obiektu wyświetlanego. Kilka sposobów usunięcia jednego lub więcej elementów z tablicy opisano w rozdziale Pobieranie wartości i usuwanie elementów tablic .

Najprostszą sytuację stanowi usuwanie filtru nałożonego na obiekt jako ostatni (filtru będącego „na samym wierzchu”). W celu usunięcia filtru z tablicy należy posłużyć się metodą pop() klasy 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;

Podobnie, w celu usunięcia filtru nałożonego jako pierwszy (będącego na samym spodzie”) należy posłużyć się tym samym kodem, podstawiając metodę shift() klasy Array w miejsce metody pop() .

W celu usunięcia filtra znajdującego się w środkowej części tablicy filtrów (przy założeniu że tablica ta składa się z więcej niż dwóch filtrów) można posłużyć się metodą splice() . Konieczna jest jednak wówczas znajomość indeksu (pozycji w tablicy) filtru, który ma zostać usunięty. Na przykład, poniższy kod powoduje usunięcie drugiego filtru (filtru o indeksie 1) z obiektu wyświetlanego:

// 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;

Określanie indeksu filtru

Użytkownik musi wiedzieć, który filtr ma zostać usunięty, aby mógł poznać jego numer indeksu. Ponadto użytkownik musi wiedzieć (znając strukturę aplikacji) lub musu obliczyć numer indeksu tego filtru.

Najlepszym rozwiązaniem jest zaprojektowanie aplikacji w taki sposób, aby filtr, który ma zostać usunięty, znajdował się stale w tym samym położeniu w zestawie filtrów. Na przykład, jeśli dany jest pojedynczy obiekt wyświetlany, na który nałożono filtry konwolucji oraz cienia (dokładnie w takiej kolejności), to chcąc usunąć filtr cienia z zachowaniem filtru konwolucji (filtru znajdującego się na „na samym wierzchu”) użytkownik wie, że znajduje się on „na wierzchu”, a zatem wie, którą metodę Array należy zastosować (w tym przypadku Array.pop() w celu usunięcia filtru cienia).

Jeśli filtr, który ma zostać usunięty, jest zawsze danego typu, lecz niekoniecznie znajduje się w tej samej pozycji w zestawie filtrów, możliwe jest sprawdzenie typu danych każdego z filtrów w celu określenia, który z filtrów należy usunąć. Na przykład poniższy kod determinuje, który zestaw filtrów jest filtrem blasku, i usuwa ten filtr z zestawu.

// 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;

W bardziej złożonych przypadkach takich jak wówczas, kiedy filtr do usunięci jest wybierany w czasie wykonywania, najlepszym sposobem jest utworzenie osobnej, trwałej kopii tablicy filtrów, która pełni rolę głównej listy filtrów. W dowolnej chwili podczas dokonywania zmiany w zestawie filtrów należy również zmienić listę główną, a następnie przypisać tę tablicę filtrów jako właściwość filters obiektu wyświetlanego.

Na przykład w poniższym kodzie na obiekt wyświetlany nakładanych jest wiele filtrów konwolucji, co pozwala utworzyć różne efekty wizualizacyjne. Następnie, w toku działania aplikacji jeden z tych filtrów jest usuwany, natomiast inne pozostają. W takim przypadku kod umożliwia zachowane głównej kopii tablicy filtrów, a także odwołania do filtra, który ma zostać usunięty. Odnajdywanie i usuwanie konkretnych filtrów działa podobnie, jak w poprzednim przypadku, z jednym wyjątkiem: zamiast na tymczasowej kopii tablicy filtrów, manipulacje są prowadzone na kopii głównej filtrów, która jest następnie przypisywana do obiektu wyświetlanego.

// 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; 
}

Takie założenia (w przypadku porównywania zapisanego odwołania filtru do pozycji w tablicy filtrów w celu określenia filtra do usunięcia) wymagają zachowania osobnej kopii tablicy filtrów — kod nie będzie bowiem działał w przypadku porównania zapisanego odwołania filtra do elementów w tablicy tymczasowej, skopiowanej z właściwości filters obiektu wyświetlanego. Dzieje się tak, ponieważ wewnętrznie, po przypisaniu tablicy do właściwości filters środowisko wykonawcze tworzy kopię każdego z obiektów filtru w tablicy. Kopie te (nie zaś obiekty oryginalne) są nakładane na obiekt wyświetlany, a po odczytaniu właściwości filters do tablicy tymczasowej zawiera ona odwołania do skopiowanych obiektów filtrujących, nie zaś odwołania do oryginalnych obiektów filtrujących. Podobnie, podjęcie próby określenia indeksu filterToRemove w poprzednim przykładzie przez porównanie go do filtrów w tymczasowej tablicy filtrów skutkuje brakiem zgodnych wyników.

Transformacje filtrów i obiektów

Żaden filtrowany obszar — np. cień — poza prostokątem stanowiącym obwiednię obiektu wyświetlanego nie jest uważany za część powierzchni przez mechanizm wykrywania kolizji (określający, czy dana instancja nakłada się lub przecina z inna instancją). Ponieważ metody wykrywania kolizji klasy DisplayObject opierają się na grafice wektorowej, nie można przeprowadzić wykrywania kolizji na wyniku stanowiącym bitmapę. Na przykład, nałożenie filtra ukośnego na instancję przycisku powoduje, że dla fragmentu instancji objętej działaniem filtra ukośnego nie jest dostępne wykrywanie kolizji.

Skalowanie, obracanie i pochylanie nie są obsługiwane przez filtry; jeśli filtrowany obiekt wyświetlany jest skalowany (a ani scaleX , ani scaleY nie wynoszą 100%), efekt filtra nie podlega skalowaniu razem z instancją. Oznacza to, że oryginalny kształt instancji podlega obrotowi, skalowaniu i pochylaniu; filtr zaś nie podlega ani obrotowi, ani skalowaniu, ani pochylaniu razem z tą instancją.

Możliwe jest animowanie instancji wraz z filtrem w celu utworzenia realistycznych efektów, lub zagnieżdżanie instancji oraz korzystanie z klasy BitmapData do animowania filtrów w celu uzyskania takiego efektu.

Obiekty Filters i Bitmap

Po nałożeniu dowolnego filtra na obiekt BitmapData właściwość cacheAsBitmap jest automatycznie ustawiana na wartość true . W ten sposób filtr jest faktycznie nakładany na kopię obiektu, nie zaś na oryginał.

Ta kopia jest następnie umieszczana na ekranie (na obiekcie oryginalnym) możliwie jak najbliżej najbliższego piksela. W przypadku zmiany obwiedni bitmapy oryginalnej kopia bitmapy z filtrem jest tworzona ponownie z obiektu oryginalnego, co pozwala uniknąć rozciągnięcia lub zniekształcenia.

W przypadku skasowania wszystkich filtrów dla obiektu wyświetlania właściwość cacheAsBitmap jest resetowana do wartości, jaką miała ona przed nałożeniem filtru.