User's collector

Внимание!   Данная опция будет доступна только после того, как вы авторизуетесь.
   запомнить меня 

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

У нас есть два интерфейса IHuman и IMonster, в которых объявлен метод kill, только в первом случае метод в качестве аргумента принимает IMonster-жертву, а во втором — в роли жертвы должен выступать IHuman. При попытке создать класс Werewolf, который должен являться как и IHuman, так и IMonster мы окажемся в затруднительной ситуации, поскольку как я уже сказал ранее ActionScript 3 не поддерживает перегрузки методов.

В сложившейся ситуации у нас есть два варианта наших действий. Первый вариант предполагает изменение условий задачи, проще говоря мы можем переименовать методы, например killMonster для IHuman и killHuman для IMonster. Если у вас есть возможность пойти по этому пути, то можете не колебаться и смело выбирать его. Второй вариант, исходит из того, что вы не можете изменить имена методов. В этом случае, можно создать два отдельных класса, каждый из которых реализует необходимый интерфейс и использовать их вместе в составе третьего класса Werewolf. Здесь есть маленькая тонкость. Как между двумя этими классами расшарить внутреннюю логику Werewolf. Например, в методе kill этих классов вызвать приватный метод prepareToKill класса Werewolf. В этой ситуации я для себя нашел небольшой workaround — создание класса оборотня.

Для начала нам потребуются выше обзначенные интерфейсы.

Actionscript:
  1. package
  2. {
  3.     public interface IHuman
  4.     {
  5.         function kill (victim:IMonster):void;
  6.     }
  7. }

Actionscript:
  1. package
  2. {
  3.     public interface IMonster
  4.     {
  5.         function kill (victim:IHuman):void;
  6.     }
  7. }

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

Actionscript:
  1. package
  2. {
  3.     public class Werewolf
  4.     {
  5.         private var _monster:IMonster = null;
  6.         private var _human:IHuman = null;
  7.        
  8.         public function Werewolf()
  9.         {
  10.             _monster = new Monster();
  11.             _human = new Human();
  12.         }
  13.        
  14.         public function asHuman ():IHuman
  15.         {
  16.             return _human;
  17.         }
  18.        
  19.         public function asMonster ():IMonster
  20.         {
  21.             return _monster;
  22.         }
  23.     }
  24. }
  25.  
  26.  
  27. internal class Monster implements IMonster
  28. {   
  29.     public function Monster ()
  30.     {
  31.     }
  32.    
  33.     public function kill (victim:IHuman):void
  34.     {
  35.         trace("human killed");
  36.     }
  37. }
  38.  
  39. internal class Human implements IHuman
  40. {
  41.     public function Human ()
  42.     {
  43.     }
  44.    
  45.     public function kill (victim:IMonster):void
  46.     {
  47.         trace("monster killed");
  48.     }
  49. }

И так мы имеем класс Werewolf, который может быть использован как IMonster, так и IHuman, для этого необходимо вызвать соответсвующий метод. Теперь вернемся к условиям, а именно, кроме реализации классов Monster и Human, нам требуется расшарить для них внутреннюю логику Werewolf. Так как эти классы могут существовать только в составе Werewolf мы могли бы смело дать им возможность вызывать приватные методы Werewolf. Однако, как мы знаем приватный метод, он на то и приватный, чтобы быть доступным только в рамках своего класса. Но нам никто не мешает создать собственную область видимости. А помогут нам в этом пространтсва имен. Чтобы создать неймспейс, видимый только в пределах наших классов, объявим его как и классы Monster и Human вне пакета. Таким образом методы, объявленные в этом неймспейсе, могут быть вызваны, любым из наших трех классов.

Actionscript:
  1. package
  2. {
  3.     public class Werewolf implements IWerewolf
  4.     {
  5.         private var _monster:IMonster = null;
  6.         private var _human:IHuman = null;
  7.        
  8.         public function Werewolf()
  9.         {
  10.             super();
  11.             _monster = new Monster(this);
  12.             _human = new Human(this);
  13.         }
  14.        
  15.         werewolf_private function prepareToKill ():void
  16.         {
  17.             trace("prepared for kill");
  18.         }
  19.        
  20.         public function asHuman ():IHuman
  21.         {
  22.             return _human;
  23.         }
  24.        
  25.         public function asMonster ():IMonster
  26.         {
  27.             return _monster;
  28.         }
  29.     }
  30. }
  31.  
  32. internal namespace werewolf_private;
  33.  
  34. internal class Monster implements IMonster
  35. {
  36.     private var _owner:Werewolf = null;
  37.    
  38.     public function Monster (owner:Werewolf)
  39.     {
  40.         _owner = owner;
  41.     }
  42.    
  43.     public function kill (victim:IHuman):void
  44.     {
  45.         _owner.werewolf_private::prepareToKill();
  46.         trace("human killed");
  47.     }
  48. }
  49.  
  50. internal class Human implements IHuman
  51. {
  52.     private var _owner:Werewolf = null;
  53.    
  54.     public function Human (owner:Werewolf)
  55.     {
  56.         _owner = owner;
  57.     }
  58.    
  59.     public function kill (victim:IMonster):void
  60.     {
  61.         _owner.werewolf_private::prepareToKill();
  62.         trace("monster killed");
  63.     }
  64. }

Таким образом, мы получили метод prepareToKill видимый только в пределах наших классов. Аналогичным образом можно расшарить и прочую логику. На этом можно было бы и остановиться, но есть один момент, каждая из составляющих как Human, так и Monster не являются Werewolf-ом. Решается это достаточно просто. Введем интерфейс IWerewolf, где и обозначим соответствующий API.

Actionscript:
  1. package
  2. {
  3.     public interface IWerewolf
  4.     {
  5.         function asHuman ():IHuman;
  6.        
  7.         function asMonster ():IMonster;
  8.     }
  9. }

Теперь остается только реализовать этот интерфейс всеми тремя классами.

Actionscript:
  1. package
  2. {
  3.     public class Werewolf implements IWerewolf
  4.     {
  5.         private var _monster:IMonster = null;
  6.         private var _human:IHuman = null;
  7.        
  8.         public function Werewolf()
  9.         {
  10.             super();
  11.             _monster = new Monster(this);
  12.             _human = new Human(this);
  13.         }
  14.        
  15.         werewolf_private function prepareToKill ():void
  16.         {
  17.             trace("prepared for kill");
  18.         }
  19.        
  20.         public function asHuman ():IHuman
  21.         {
  22.             return _human;
  23.         }
  24.        
  25.         public function asMonster ():IMonster
  26.         {
  27.             return _monster;
  28.         }
  29.     }
  30. }
  31.  
  32. internal namespace werewolf_private;
  33.  
  34. internal class Monster implements IMonster, IWerewolf
  35. {
  36.     private var _owner:Werewolf = null;
  37.    
  38.     public function Monster (owner:Werewolf)
  39.     {
  40.         _owner = owner;
  41.     }
  42.    
  43.     public function kill (victim:IHuman):void
  44.     {
  45.         _owner.werewolf_private::prepareToKill();
  46.         trace("human killed");
  47.     }
  48.    
  49.     public function asHuman ():IHuman
  50.     {
  51.         return _owner.asHuman();
  52.     }
  53.    
  54.     public function asMonster ():IMonster
  55.     {
  56.         return this;
  57.     }
  58. }
  59.  
  60. internal class Human implements IHuman, IWerewolf
  61. {
  62.     private var _owner:Werewolf = null;
  63.    
  64.     public function Human (owner:Werewolf)
  65.     {
  66.         _owner = owner;
  67.     }
  68.    
  69.     public function kill (victim:IMonster):void
  70.     {
  71.         _owner.werewolf_private::prepareToKill();
  72.         trace("monster killed");
  73.     }
  74.    
  75.     public function asHuman ():IHuman
  76.     {
  77.         return this;
  78.     }
  79.    
  80.     public function asMonster ():IMonster
  81.     {
  82.         return _owner.asMonster();
  83.     }
  84. }

Проверим что у нас получилось.

Actionscript:
  1. package
  2. {
  3.     import flash.display.Sprite;
  4.  
  5.     public class World extends Sprite
  6.     {
  7.         public function World()
  8.         {
  9.             var werewolf:Werewolf = new Werewolf();
  10.            
  11.             // "Кастим" werewolf как IMonster и как IHuman
  12.             trace(werewolf.asMonster() is IMonster);
  13.             trace(werewolf.asHuman() is IHuman);
  14.            
  15.             // Проверяем является ли каждая составляющая IWerewolf
  16.             trace(werewolf.asMonster() is IWerewolf);
  17.             trace(werewolf.asHuman() is IWerewolf);
  18.            
  19.             // Ну а здесь уже мистика :)
  20.             trace((werewolf.asMonster() as IWerewolf).asHuman() is IHuman);
  21.             trace((werewolf.asHuman() as IWerewolf).asMonster() is IMonster);
  22.            
  23.             // Попробуем убить нашим оборотнем как человека так и монстра
  24.             var knight:Knight = new Knight();
  25.             var zombie:Zombie = new Zombie();
  26.            
  27.             werewolf.asMonster().kill(knight);
  28.             werewolf.asHuman().kill(zombie);
  29.         }
  30.     }
  31. }

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

15 комментариев

Теги:

Создание swc-библиотек — задача, с которой рано или поздно сталкивается каждый actionscript-разработчик, и способы ее решения зависят во многом от тех инструментов, которые есть в распоряжении программиста. В своей работе я стараюсь минимально использовать возможности среды, насколько это возможно, разумно и позволяют условия, поэтому описанные в статье действия минимально привязаны к возможностям конкретной среды разработки и могут быть с легкость выполнены в рамках любой IDE, использующей Flex 3 SDK.
Однако, дабы материал был более предметным, в качестве среды разработки я использовал FDT. В любом случае, весь процесс сборки будет возложен на плечи Ant, так что, как я уже сказал, повторить приведенные действия в другой среде разработки будет не сложно. Приступим.

В качестве примера создадим swc, содержащий в себе два простых, но, на мой взгляд, весьма полезных класса — CookieManager и FirebugConsole. Начнем с создания нового проекта — File → New → New Flash Project. В появившемся окне диалога задаем имя проекта, его расположение, версию ActionScript, а также указываем, что хотим использовать Pure AS(Flex 3).

Приступим к созданию структуры проекта. Для этого создадим основные директории:

  • bin — в эту папку будет скомпилен наш итоговый swc;
  • src — директория с файлами исходных кодов (после создания, необходимо добавить эту папку в Classpath);
  • build — здесь будут располагаться файлы, отвечающие за процесс сборки swc-библиотеки.

Как только все директории созданы, можно приступать к их наполнению. Для начала в папке src разместим исходные коды, думаю здесь сложностей возникнуть не должно. Следующий этап — создание файлов в директории build. Нам потребуются следующие файлы:

  • local.properties — файл, в котором будут прописаны пути до компилятора в локальной системе;
  • build.properties — файл, с параметрами сборки;
  • build.xml — собственно сам build-файл;
  • compc-config.xml – конфигурационный файл для компилятора compc.

Самое время приступить к наполнению вышеперечисленных файлов. Начнем с local.properties:

flex.sdk.dir=E:/tools/flex3sdk
compc.path=${flex.sdk.dir}/bin/compc.exe

В первой строке мы прописали путь до Flex SDK, во второй — относительно него указали путь до компилятора compc, так что правим только первую строку согласно своим условиям и переходим к build.properties:

source.dir=${basedir}/src
output.dir=${basedir}/bin

output.file=riafactory.swc

Здесь тоже все весьма просто, указали пути до src и bin, а так же прописали имя файла нашей swc-библиотеки.

Если все готово, переходим к написанию build-файла. В нашем случае он будет небольшим, и состоять из двух задач: build и compile.swc.

<?xml version="1.0" encoding="utf-8" ?>
<project name="RIAFactory classes" basedir=".." default="build">

	<property file="build/local.properties" />
	<property file="build/build.properties" />

	<property name="compc.args" value="-load-config '${output.dir}/compc-config.xml'" />

	<target name="build">
		<antcall target="compile.swc" />
	</target>

	<target name="compile.swc">
		<filter token="flex.sdk.dir" value="${flex.sdk.dir}" />
		<filter token="source.dir" value="${source.dir}" />
		<filter token="output.dir" value="${output.dir}" />
		<filter token="output.file" value="${output.file}" />
		<copy file="${basedir}/build/compc-config.xml" tofile="${output.dir}/compc-config.xml" filtering="true" />
		<exec executable="${compc.path}">
			<arg line="${compc.args}" />
		</exec>
		<delete file="${output.dir}/compc-config.xml" />
	</target>

</project>

Как вы наверно обратили внимание, компилятору передается единственный параметр –load-config указывающий путь до конфигурационного файла. По сути, использование конфигурационного файла является альтернативой передачи множества параметров компилятору, а поскольку build.xml в идеале должен отвечать только за последовательность этапов процесса сборки, то, на мой взгляд, этот подход является предпочтительным.

Переходим к compc-config.xml:

<?xml version="1.0" encoding="utf-8"?>
<flex-config>
	<compiler>
		<external-library-path>
			<path-element>@flex.sdk.dir@/frameworks/libs/player/playerglobal.swc</path-element>
		</external-library-path>
		<source-path>
			<path-element>@source.dir@</path-element>
		</source-path>
	</compiler>
	<output>@output.dir@/@output.file@</output>
	<include-classes>
		<class>ru.riafactory.external.FirebugConsole</class>
		<class>ru.riafactory.net.CookieManager</class>
	</include-classes>
</flex-config>

В конфигурационном файле мы прописали путь до playerglobal.swc, относительно корневой директории Flex SDK, указали пути до src и до итоговой swc-библиотеки, а также перечислили те классы, который хотим включить в наш swc-файл.

Примечание: узел <include-classes> можно опустить, в этом случае компилятор вкомпилит все найденные классы в директориях указанных в source-path, однако наличие этого узла позоляет вам точно знать, какие классы будут вкомпилены.

Теперь все готово, чтобы собрать swc. Открываем панель Ant (Window → Show View → Ant), в ней нажимаем на кнопку Add Buildfile, выбираем build.xml, после чего выделяем его в панели и нажимаем Run the Default Target of the Selected Buildfile. В консоли должны появиться сообщения о ходе сборки, среди которых самым приятным для нас должно стать BUILD SUCCESSFUL. Если все так, смотрим в папку bin (не забывая сделать Refresh во Flash Explorer) и видим там свежесобранный swc.

На этом можно было и закончить, если бы не один момент. Получившийся swc будет отлично подключаться к проектам, разрабатываемых в средах, использующих Flex SDK, однако все попытки подключить его к проекту во Flash IDE путем копирования в папку Flash-компонентов будут тщетны.

Причина этому на самом деле проста и в определенной степени логична. В панели Components появляются только те swc, который содержат в себе компоненты. Хотя точнее будет сказать, Flash просматривает swc на предмет наличия в нем компонентов и, если таковые имеются, отображает их в панели. Таким образом, чтобы Flash увидел наш swc и позволил использовать классы, находящиеся в нем, нам придется перекомпилировать swc, указав, что в нем имеются компоненты.

Для этого нам потребуется manifest.xml. Справедливости ради, стоит отметить, что манифест служит ни сколько целям компиляции Flash CS3 совместимых swc, сколько огранизационным целям, по крайней мере, так гласит документация. И так, приступим к написанию manifest.xml, предварительно создав его в директории src.

<?xml version="1.0" encoding ="utf-8"?>
<componentPackage>
	<component id="FirebugConsole" class="ru.riafactory.external.FirebugConsole" />
	<component id="CookieManager" class="ru.riafactory.net.CookieManager" />
</componentPackage>

В манифесте мы прописываем необходимые компоненты, указав их идентификатор и класс, понятное дело, что id должен быть уникальным.

Теперь, когда у нас есть manifest.xml, нам потребуется немного изменить параметры компиляции. Начнем с build.properties, добавим в него пару строк:

namespace.uri=http://www.riafactory.ru
namespace.manifest.file=manifest.xml

В первой строке, мы указываем uri пространства имен наших компонентов, во второй — имя файла манифеста.

Далее внесем изменения в build.xml. Теперь задача compile.swc выглядит следующим образом:

<target name="compile.swc">
	<filter token="flex.sdk.dir" value="${flex.sdk.dir}" />
	<filter token="source.dir" value="${source.dir}" />
	<filter token="output.dir" value="${output.dir}" />
	<filter token="output.file" value="${output.file}" />
	<filter token="namespace.uri" value="${namespace.uri}" />
	<filter token="namespace.manifest.file" value="${namespace.manifest.file}" />
	<copy file="${basedir}/build/compc-config.xml" tofile="${output.dir}/compc-config.xml" filtering="true" />
	<exec executable="${compc.path}">
		<arg line="${compc.args}" />
	</exec>
	<delete file="${output.dir}/compc-config.xml" />
</target>

Ну и последнее, что нам потребуется сделать — это внести соответсвующие поправки в compc-config.xml, после которых он должен выглядеть так:

<?xml version="1.0" encoding="utf-8"?>
<flex-config>
	<compiler>
		<external-library-path>
			<path-element>@flex.sdk.dir@/frameworks/libs/player/playerglobal.swc</path-element>
		</external-library-path>
		<source-path>
			<path-element>@source.dir@</path-element>
		</source-path>
		<namespaces>
			<namespace>
				<uri>@namespace.uri@</uri>
				<manifest>@source.dir@/@namespace.manifest.file@</manifest>
			</namespace>
		</namespaces>
	</compiler>
	<include-namespaces>
		<uri>@namespace.uri@</uri>
	</include-namespaces>
	<output>@output.dir@/@output.file@</output>
	<compute-digest>false</compute-digest>
	<include-classes>
		<class>ru.riafactory.external.FirebugConsole</class>
		<class>ru.riafactory.net.CookieManager</class>
	</include-classes>
</flex-config>

Теперь снова откомпилируем наш swc и попробуем его подключить к Flash CS3. Для этого скопируем его в директорию с Flash компонентами (можете создать там отдельную папку для сторонних swc):

  • для Windows C:\Program Files\Adobe\Adobe Flash CS3\en\Configuration\Components
  • для MacOS HD:Applications:Adobe Flash CS3:Configuration:Components

Теперь запускаем Flash, открываем панель Components (Window → Components или Ctrl+F7) и видим в ней компоненты из нашего swc (если Flash уже запущен необходимо сделать Reload из контекстного меню панели).

Теперь перетаскиваем компоненты в Library, после чего смело можем пользоваться классами из swc во Flash CS3. Ну а в качестве проверки можем вставить в первый кадр следующий код:

Actionscript:
  1. import ru.riafactory.net.CookieManager;
  2. import ru.riafactory.external.FirebugConsole;
  3.  
  4. trace(CookieManager);
  5. trace(FirebugConsole);

После чего нажимаем Ctrl+Enter и видим в Output:

[class CookieManager]
[class FirebugConsole]

Что означает, что Flash прекрасно видит классы из swc. Архив с FDT проектом.

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

Еще один момент, который стоит отметить. В панели Components Flash IDE, отображаются все компоненты, входящие в состав swc, а точнее перечисленные в файле catalog.xml, который находится внутри swc-библиотеки. Здесь есть важный момент, перетаскивая в Library один из компонентов, мы фактически импортируем все классы, расположенных в том же swc. Это легко проверить, удалите из Library один из добавленных компонентов и попробуйте снова скомпилировать приложение, результаты вызовов trace останутся неизменными. В этом случае, если в состав вашего swc входит библиотека классов, не являющихся компонентами как таковыми, то более красивым решением будет создание пустого класса, своим названием характеризующего обшие задачи библиотеки. Скажем, если в swc расположена библиотека классов логгинга, то общий класс может называться LoggingClasses или что-нибудь в этом роде, и именно его следует указать в качестве единственного компонента входящего в swc.

Что касается создания визуальных компонентов для Flash CS3 силами Flex SDK, здесь, к сожалению, не могу дать каких либо полезных замечаний. Единственный известный мне способ создания таких компонентов — использование Flash IDE и класса fl.livepreview.LivePreviewParent. Если, кто-то знает решение обозначенной задачи, милости прошу в комментарии.

Ну и наконец, тем из вас, кто дожил до этих строк и кому представленного материала показалось мало или остались вопросы, несколько ссылок по теме:

12 комментариев

Теги:

С выходом бета версии Flash Player 10 многие обратили внимание, что наряду с новыми возможностями компания Adobe проделала работу по локализации плеера. Однако, локализация коснулась не только плеера, но и компиляторов поставляемых в составе Flex 3 SDK (речь безусловно идет о сборках датированных позднее 15 мая 2008 и поддерживающих компиляцию под новую версию плеера).
Те из вас, кто не устоял перед соблазном опробовать новые возможности плеера во Flex Builder, наверно обратили внимание, что ошибки выдаваемые компилятором теперь на русском языке, при условии что соответствующая локаль установлена в системе. Для тех, кто не видел этой красоты приведу несколько примеров:

1084: Синтаксическая ошибка: rightparen перед rightbrace
1086: Синтаксическа ошибка: ожидается точка с запятой перед leftbrace
1017: Определение базового класса Sbrite не найдено.

Не знаю кого как, но меня подобное новшество мягко говоря смущает. Благо отучить компилятор изъясняться русским языком оказалось не так сложно. Первые попытки задать локаль в файле [FLEX_SDK_DIR]/bin/jvm.config оказались тщетны, доблестный Flex Builder попросту игнорирует это файл, да и по умолчанию при запуске компилятора из командной строки используется родная en_US локаль. Поэтому дальнейшие усилия были направлены на Eclipse. Здесь все оказалось просто. Добавляем пару строчек в файл eclipse.ini в корневой директории Eclipse c установленным Flex Builder:

CODE:
  1. -Duser.language=en
  2. -Duser.country=US

Теперь перезапускаем Eclipse и пересобираем проект, после чего видим привычные глазу сообщения об ошибках.

Напоследок отмечу, если вы не используете Flex Builder и хотите установить нужную вам локаль, то сделать это можно в файле [FLEX_SDK_DIR]/bin/jvm.config. Просто допишите соответствующие параметры в строку java.args. Это может выглядеть так:

CODE:
  1. java.args=-Xmx384m -Dsun.io.useCanonCaches=false -Duser.language=ru -Duser.region=RU

Надеюсь кому-нибудь эта заметка будет полезна.

4 комментария

Теги:

О выходе Flash Player 10 beta aka «Astro» не писал только ленивый и «Garbage Collector». Однако последний, из этих двух, все же решил исправиться.

Одной из «фишек» нового плеера стала возможность создавать свои собственные графические фильтры. Вот, собственно, об этом сегодня и пойдет речь.

Создание собственных фильтров стало возможно благодаря новому продукту от Adobe под названием Pixel Bender Toolkit (раннее известного как Adobe Image Foundation Toolkit). Pixel Bender Toolkit использует собственный C-подобный язык описания алгоритмов преобразования изображений. Что ж, давайте попробуем создать собственный фильтр и применить его в нашем ActionScript 3 приложении.

Шаг 1: Подготовка

Прежде чем приступить к нашему знакомству с обозначенной темой, нам придется немного подготовиться. Во-первых, нам потребуется сам Pixel Bender Toolkit для написания фильтра и его компиляции. А во-вторых, нам потребуется настроить свою среду разработки. Приступим.

Инструкцию по скачиванию и установке Pixel Bender Toolkit можно найти на Adobe Labs. О том, как настроить свою среду разработки, можно почитать следующие статьи:

Шаг 2: Написание и компиляция собственного эффекта

Для первого знакомства, я думаю, стоит взять простой эффект — сепия. Так как пока мои навыки написания собственных фильтров на Pixel Bender не велики, я решил воспользоваться готовым решением (правда код подвергся небольшой корректировке в свете некоторых изменений в языке).

<languageversion : 1.0;>

kernel Sepia
<   namespace:      "popforge::ImageProcessing";
    vendor:         "Joa Ebert";
    version:        1;
    description:    "A good looking sepia filter using Y transform";
>
{
    input image3 source;
    output pixel3 result;

    void evaluatePixel()
    {
        pixel3 color = sampleLinear(source, outCoord());

        float y = 0.299 * color.r + 0.587 * color.g + 0.114 * color.b;

        float r = min(1.0, y + 0.19);
        float g = max(0.0, y - 0.055);
        float b = max(0.0, y - 0.22);

        result = pixel3(r, g, b);
    }
}

И так у нас есть исходный код фильтра. Скомпилируем его.

Запускаем Pixel Bender Toolkit и создаем новый фильтр (File → New Pixel Bender Kernel Filter). Теперь в окно редактора вставляем исходный код и компилируем его (File → Export Pixel Bender Byte Code for Flash). Полученный файл с расширение .pbj мы и будем использовать в нашем приложении.

Шаг 3: Написание приложения

Для демонстрации применения фильтра мы напишем простое приложение. В его задачи будет входить загрузка изображения, его отображение, загрузка фильтра и показ этого же изображения, но уже с применением фильтра. Дальше подробно расписывать не буду, ибо вся суть в коде. Остановлюсь лишь на некоторых моментах. Для применения фильтра нам потребуется новоиспеченный класс flash.display.Shader, который должен получить фильтр в бинарном виде. Результат применения фильтра мы отрисовываем в экземпляре Shape с помощью метода graphics.beginShaderFill.

Actionscript:
  1. package
  2. {
  3.     import flash.display.Bitmap;
  4.     import flash.display.Loader;
  5.     import flash.display.Shader;
  6.     import flash.display.Shape;
  7.     import flash.display.Sprite;
  8.     import flash.display.StageScaleMode;
  9.     import flash.events.Event;
  10.     import flash.net.URLLoader;
  11.     import flash.net.URLLoaderDataFormat;
  12.     import flash.net.URLRequest;
  13.    
  14.     [SWF(width='500', height='510', frameRate='31', backgroundColor='0x000000')]
  15.     public class RuntimePixelBenderExample extends Sprite
  16.     {
  17.         private var _image:Bitmap = null;
  18.         private var _shape:Shape = null;
  19.         private var _imageLoader:Loader = null;
  20.         private var _filterLoader:URLLoader = null;
  21.         private var _sepiaShader:Shader = null;
  22.        
  23.         public function RuntimePixelBenderExample()
  24.         {
  25.             stage.scaleMode = StageScaleMode.NO_SCALE;
  26.             loadImage();
  27.         }
  28.        
  29.         private function loadImage ():void
  30.         {
  31.             _imageLoader = new Loader();
  32.             _imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaded);
  33.             _imageLoader.load(new URLRequest("assets/image/bender.jpg"));
  34.         }
  35.        
  36.         private function loadFilter ():void
  37.         {
  38.             _filterLoader = new URLLoader();
  39.             _filterLoader.dataFormat = URLLoaderDataFormat.BINARY;
  40.             _filterLoader.addEventListener(Event.COMPLETE, onFilterLoaded);
  41.             _filterLoader.load(new URLRequest("assets/filter/Sepia.pbj"));
  42.         }
  43.        
  44.         private function applyFilter ():void
  45.         {
  46.             _sepiaShader.data.source.input = _image.bitmapData;
  47.            
  48.             _shape = new Shape();
  49.             _shape.y = 255;
  50.            
  51.             _shape.graphics.beginShaderFill(_sepiaShader);
  52.             _shape.graphics.drawRect(0, 0, 500, 255);
  53.             _shape.graphics.endFill();
  54.            
  55.             addChild(_shape);
  56.         }
  57.        
  58.         private function onImageLoaded (event:Event):void
  59.         {
  60.             _image = _imageLoader.content as Bitmap;
  61.             addChild(_image);
  62.             loadFilter();
  63.         }
  64.        
  65.         private function onFilterLoaded (event:Event):void
  66.         {
  67.             _sepiaShader = new Shader(_filterLoader.data);
  68.             applyFilter();
  69.         }
  70.     }
  71. }

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

Actionscript:
  1. package
  2. {
  3.     import flash.display.Bitmap;
  4.     import flash.display.Loader;
  5.     import flash.display.Shader;
  6.     import flash.display.Shape;
  7.     import flash.display.Sprite;
  8.     import flash.display.StageScaleMode;
  9.     import flash.events.Event;
  10.     import flash.net.URLRequest;
  11.     import flash.utils.ByteArray;
  12.    
  13.     [SWF(width='500', height='510', frameRate='31', backgroundColor='0x000000')]
  14.     public class CompiledPixelBenderExample extends Sprite
  15.     {
  16.         private var _image:Bitmap = null;
  17.         private var _shape:Shape = null;
  18.         private var _imageLoader:Loader = null;
  19.        
  20.         [Embed(source="assets/filter/Sepia.pbj", mimeType="application/octet-stream")]
  21.         private var SepiaFilter:Class;
  22.        
  23.         private var _sepiaShader:Shader = null;
  24.        
  25.         public function CompiledPixelBenderExample ()
  26.         {
  27.             stage.scaleMode = StageScaleMode.NO_SCALE;
  28.             loadImage();
  29.         }
  30.        
  31.         private function loadImage ():void
  32.         {
  33.             _imageLoader = new Loader();
  34.             _imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoaded);
  35.             _imageLoader.load(new URLRequest("assets/image/bender.jpg"));
  36.         }
  37.        
  38.         private function applyFilter ():void
  39.         {
  40.             _sepiaShader = new Shader(new SepiaFilter());
  41.             _sepiaShader.data.source.input = _image.bitmapData;
  42.            
  43.             _shape = new Shape();
  44.             _shape.y = 255;
  45.            
  46.             _shape.graphics.beginShaderFill(_sepiaShader);
  47.             _shape.graphics.drawRect(0, 0, 500, 255);
  48.             _shape.graphics.endFill();
  49.            
  50.             addChild(_shape);
  51.         }
  52.        
  53.         private function onImageLoaded (event:Event):void
  54.         {
  55.              _image = _imageLoader.content as Bitmap;
  56.              addChild(_image);
  57.              applyFilter();
  58.         }
  59.     }
  60. }

Исходный код проекта можно скачать здесь.

Послесловие

Новые возможности Flash Player 10 вскоре сильно изменят внешний вид будущих flash-приложений. И с одной из таких возможностей мы уже познакомились. Замечу, мы рассмотрели пример применения простого фильтра, не требующего параметризации. В остальных случаях читайте документацию (она идет в комплекте с Pixel Bender Toolkit), пишите фильтры и делайте мир лучше (жду русскоязычный блог про Pixel Bender). Что ж, на этом на сегодня все. Надеюсь вам было интересно.

Для тех, кто все еще здесь

Напоследок, несколько полезных ссылок:

7 комментариев

Теги:

В число самых ожидаемых мною программных продуктов, безусловно, входит Saffron UML Tool. Тем, кто не слышал или слабо представляет себе, о чем идет речь, следует почитать пост Роста об этом инструменте и посмотреть на эту красоту.




К чему это все я, последний пост в блоге разработчика Samuel Agesilas'a датирован февралем этого года, в котором содержится не двусмысленный намек на дату релиза этого инструмента. Но «воз и ныне там». Периодически заглядываю почитать комментарии, и вот недавно наткнулся на интересный комментарий, оставленный неким Jeff D. Привожу его целиком без перевода, как есть:

I can confirm that having talked to Sam last night, Saffron has been bought by Microsoft and will no longer be released as an AS3/Air app. Sam is currently working with Microsoft engineers to move the code base over to WPF/Silverlight with a view to a release in Q1 next year as an online app available as part of the Visual Studio package.

Не берусь судить насколько это правда, но никаких опровержений или подтверждений от Самуэля до сих пор нет. Неужели мы потеряли Saffron?

12 комментариев

Теги:

Одно из заявленных нововведений 10 версии flash player’а — третье измерение. Нет, нам не обещают полноценной 3D-графики, по большому счету она и не требуется, но те 3D-эффекты, которые были показаны на Adobe MAX 2007, смотрятся впечатляющее. Так что, кто-то с нетерпением ждет выхода новой версии Flash Player, а кто-то не стал ждать и предлагает поиграть с третьим измерением уже сегодня.
Five3D — небольшая открытая библиотека, которая позволяет с легкостью добавить трехмерности вашим приложениям. Сразу оговорюсь Five3D не является очередным 3D-движком, как Papervision3D или Away3D. Five3D предоставляет аналоги привычных DisplayObject’ов, за той лишь разницей, что они обладают возможностью позиционироваться в трехмерном пространстве.

Five3D доступна как для ActionScript 2 (версия 1.0), так и для ActionScript 3 (версия 2.0). Первая версия библиотеки для ActionScipt 3 предоставляла возможности для отрисовки в 3D только векторной графики. Но буквально вчера Mathieu Badimon (автор Five3D) выложил обещанную версию 2.1 с поддержкой трехмерных преобразований для растровых объектов, в том числе и видео. Так что, если вы хотите попробовать некоторые возможности «Astro» уже сегодня, Five3D вам будет весьма интересен.

7 комментариев

Теги:

Будущий релиз Flex 3 породил массу статей, посвященных новшествам и изменениям как в самом фреймворке, так и в новой версии Flex Builder. На этом фоне без внимания остались изменения, произошедшие в компиляторах, поставляемых в комплекте с Flex 3 SDK. Сегодня я предлагаю исправить эту несправедливость. :-)
Как вы уже поняли из заголовка, речь пойдет о новой возможности mxmlc — условной компиляции. Условная компиляция позволяет разработчику включать или исключать блоки кода для конкретной сборки вашего приложения. Компилятор mxmlc дает возможность определить значения констант на этапе компиляции. Это стало возможно благодаря появлению параметра –define. Давайте посмотрим на общий синтаксис этого параметра:

CODE:
  1. -define=namespace::variable_name,value

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

CODE:
  1. mxmlc -define+=CONFIG::debug,true [прочие параметры]

Примечание: Обратите внимание на то, что вместо = используется +=. При использовании =, предыдущие переменные переданные с помощью параметра –define теряются.

Теперь в исходном коде нашего приложения мы можем выделить необходимый для отладки и совершенно нежелательный для финальной версии приложения блок кода следующим образом:

Actionscript:
  1. CONFIG::debug
  2. {
  3.     // код, который должен выполняться на этапе разработки
  4. }

При сборке финальной версии приложения, нам достаточно будет изменить в параметрах компилятора значение CONFIG::debug на false, и весь отладочный блок кода будет исключен.
В качестве значений переменных, передаваемых компилятору могут выступать не только булевы величины, но и числа, строки и вычисляемые выражения:

CODE:
  1. // числовое значение
  2. -define+=OPTIONS::someNumber,3
  3.  
  4. // строковое значение
  5. -define+=NAMES::author,"vooparker"
  6.  
  7. // вычисляемое выражение
  8. -define+=OPTIONS::someNumber,"3-1"

Если вы используете для сборки своего проекта собственный config.xml, то определить значение переменных вы можете в своем конфигурационном файле в блоке <compiler>…</compiler>. Например:

XML:
  1. <compiler>
  2.     ...
  3.     <define>
  4.         <name>NAMES::author</name>
  5.         <value>"vooparker"</value>
  6.     </define>
  7.     <define>
  8.         <name>OPTIONS::someNumber</name>
  9.         <value>3-1</value>
  10.     </define>
  11. </compiler>

Тех из вас, кому представленный материал показался интересным, отсылаю к первоисточнику. Удачи! :)

8 комментариев

Теги:

Все мы помним, как это началось: в апреле 2006 года с очередным обновлением Inernet Explorer компания Microsoft изменила механизм активации ActiveX содержимого на веб-страницах. Серая рамка и надпись «click to activate» стали большим сюрпризом для многих разработчиков...
На днях стало известно, что Microsoft собирается отказаться от существующего метода активации. Иными словами, пользователю больше не нужно будет кликать на активное содержимое для его активации. По информации в MSDN окончательно «Click to activate» станет историей в апреле 2008.

В этой ситуации появляется резонный вопрос, не станет ли это событие «началом конца» для SWFObject, ведь свою популярность SWFObject получил именно после знаменитого обновления IE? Мое личное мнение по этому вопросу — нет, не станет. Задача SWFObject была в предоставлении удобного внедрения flash-контента в веб-страницу, и в этом плане SWFObject доказал свое право на существование.

3 комментария

Теги:

Если вам интересны физические движки, то определенно вы должны посмотреть на Box2DFlashAS3. Box2DFlashAS3 — это портированный с C++ на ActionScript 3 физический движок Box2D. Примечательно, что Box2DFlashAS3 как и его прародитель распространяется с открытыми исходными кодами.
На странице проекта вы можете ознакомиться с несколькими демо (не забывайте использовать клавиши «Влево», «Вправо» и «R»), иллюстрирующими возможности движка, а также скачать исходные коды как самого движка так и вышеупомянутых демо сцен.

Для тех кому интересна данная тема, хочу напомнить, что Box2DFlashAS3 не единственный в своем роде физический движок на языке ActionScript 3. В распоряжении as3-разработчика также есть:

Есть чем дополнить список?

13 комментариев

Теги:

Silverlight или Flash? Этот вопрос телекомпания «Первый канал» для себя уже решила. И решение это было не в пользу Flash.
9 ноября компания запустила веб-трансляцию телешоу «Король ринга», использовав для вещания технологию Microsoft Silverlight. Каковы причины выбора именно Silverlight мы можем только догадываться ;) , но факт остается фактом.

27 комментариев

Теги: