Całkiem niedawno zachwalałem koncepcję kompilacji kodu JavaScript do bajtkodu w locie. Do silnika WebKit został dodany nowy silnik SquirrelFish wykonawczy bazujący na wirtualnej maszynie. Można oczekiwać że kolejne wersje uaktualnienia Adobe AIR będą bazować na nowej wersji WebKit. Ale jakoś nie mogłem uwierzyć statystykom Postanowiłem użyć swojego kodu Java Script na fib(30). Zainstalowałem Safari 3.1 Nie sprawiało mi to problemu. Okazało sie to wersja 3.1.1 (525.17) Pomyślałem że trzeba wsiąść najnowszego night builda silnika WebKit. Była to wersja WebKit r34367 z 5 czerwca 2008. Najpierw przetestowałem fib(30) na oficjalnym Safari 3.1.1 z wynikiem 1843 ms. Potem w folderze gdzie miałem zainstalowane Safari C:\Program Files\Safari plik WebKit.dll w wersji 3.525.18.0 zastąpiłem plikiem z night builda WebKit.dll w wersji3.526.0.0 (podmieniłem też folder WebKit.resources). I uruchomiłem test fib(30) z wynikiem 468 ms.
Tak cieszę najszybszą przeglądarką świata.
czwartek, 5 czerwca 2008
SquirrelFish
Autor: Michał Małaj o godzinie 20:48 0 komentarzy
czwartek, 29 maja 2008
Szybkie wektory
Wprowadzenie typoów parametrycznych albo jak kto woli generycznych (z C# 2 czy Java 1.5) miało na celu umożliwienie tworzenia tablic danego typu na poziomie języka programowania. Pewien Japończyk zrobił testy co to daje w kontekście wydajnościowym. Uprzedzam czytelników że ten typ danych ułatwia działania na API do rysowania ale nie przyśpiesza jego działania jeżeli chodzi o wydajność z tradycyjnym sposobem rysowania.
Zmodyfikuję kod do testów ale na początek zamieszczę kod do "okienka" w którym będziemy mogli podziwiać wyniki zamiast tradycyjnego trace(). Kod pochodzi z tej strony.
package com.senocular.utils {
import flash.display.Shape;
import flash.display.Sprite;
import flash.display.Stage;
import flash.display.GradientType;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.text.TextField;
import flash.text.TextFieldType;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
import flash.text.TextFieldAutoSize;
/**
* Creates a pseudo Output panel in a publish
* swf for displaying trace statements.
* For the output panel to capture trace
* statements, you must use Output.trace()
* and add an instance to the stage:
* stage.addChild(new Output());
*
* Note: You may want to place Output in an
* unnamed package to make it easier to
* trace within your classes without having
* to import com.senocular.utils.Output.
*/
public class Output extends Sprite {
private var output_txt:TextField;
private var titleBar:Sprite;
private static var instance:Output;
private static var autoExpand:Boolean = true;
private static var maxLength:int = 1000;
public function Output(outputHeight:uint = 150){
if (instance && instance.parent){
instance.parent.removeChild(this);
}
instance = this;
addChild(newOutputField(outputHeight));
addChild(newTitleBar());
addEventListener(Event.ADDED, added);
addEventListener(Event.REMOVED, removed);
}
// public methods
public static function trace(str:*):void {
if (!instance) return;
instance.output_txt.appendText(str+"\n");
if (instance.output_txt.length > maxLength) {
instance.output_txt.text = instance.output_txt.text.slice(-maxLength);
}
instance.output_txt.scrollV = instance.output_txt.maxScrollV;
if (autoExpand && !instance.output_txt.visible) instance.toggleCollapse();
}
public static function clear():void {
if (!instance) return;
instance.output_txt.text = "";
}
private function newOutputField(outputHeight:uint):TextField {
output_txt = new TextField();
output_txt.type = TextFieldType.INPUT;
output_txt.border = true;
output_txt.borderColor = 0;
output_txt.background = true;
output_txt.backgroundColor = 0xFFFFFF;
output_txt.height = outputHeight;
var format:TextFormat = output_txt.getTextFormat();
format.font = "_typewriter";
output_txt.setTextFormat(format);
output_txt.defaultTextFormat = format;
return output_txt;
}
private function newTitleBar():Sprite {
var barGraphics:Shape = new Shape();
barGraphics.name = "bar";
var colors:Array = new Array(0xE0E0F0, 0xB0C0D0, 0xE0E0F0);
var alphas:Array = new Array(1, 1, 1);
var ratios:Array = new Array(0, 50, 255);
var gradientMatrix:Matrix = new Matrix();
gradientMatrix.createGradientBox(18, 18, Math.PI/2, 0, 0);
barGraphics.graphics.lineStyle(0);
barGraphics.graphics.beginGradientFill(GradientType.LINEAR, colors, alphas, ratios, gradientMatrix);
barGraphics.graphics.drawRect(0, 0, 18, 18);
var barLabel:TextField = new TextField();
barLabel.autoSize = TextFieldAutoSize.LEFT;
barLabel.selectable = false;
barLabel.text = "Output";
var format:TextFormat = barLabel.getTextFormat();
format.font = “_sans”;
barLabel.setTextFormat(format);
titleBar = new Sprite();
titleBar.addChild(barGraphics);
titleBar.addChild(barLabel);
return titleBar;
}
// Event handlers
private function added(evt:Event):void {
stage.addEventListener(Event.RESIZE, fitToStage);
titleBar.addEventListener(MouseEvent.CLICK, toggleCollapse);
fitToStage();
toggleCollapse();
}
private function removed(evt:Event):void {
stage.removeEventListener(Event.RESIZE, fitToStage);
titleBar.removeEventListener(MouseEvent.CLICK, toggleCollapse);
}
private function toggleCollapse(evt:Event = null):void {
if (!instance) return;
output_txt.visible = !output_txt.visible;
fitToStage(evt);
}
private function fitToStage(evt:Event = null):void {
if (!stage) return;
output_txt.width = stage.stageWidth;
output_txt.y = stage.stageHeight - output_txt.height;
titleBar.y = (output_txt.visible) ? output_txt.y - titleBar.height : stage.stageHeight - titleBar.height;
titleBar.getChildByName("bar").width = stage.stageWidth;
}
}
}
Mając taki kod możemy przeprowadzić testy. Test Japończyka był prosty: chciał połączyć dane z tablicy za pomocą metody concat. Aby się upewnić jak szybko za działa łączenie na wartościach trzeba było przyjąć pewne założenia: operujemy na wartościach liczb 16, bo w ten sposób zapisane są kolory. Ponieważ Pixel Bender operuje na wartościach liczb przedstawiających kolory więc naturalną rzeczą będzie obsługa danych na kolorach. Oto kod operujący na typie Array.
package
{
import flash.display.Sprite;
import flash.utils.*;
import com.senocular.utils.Output;
[SWF(width=1024, height=1024, backgroundColor=0xFFFFFF, frameRate=30)]
public class CalcArray extends Sprite
{
private static const SIZE:uint = 1024;
private var startTime:Number;
private var endTime:Number;
public function CalcArray():void
{
stage.addChild(new Output());
var array:Array = [];
for (var h:int = 0; h < SIZE; h++)
{
for (var w:int = 0; w < SIZE; w++)
{
array.push(0xFF000000);
}
}
this.startTime=getTimer();
var i:int;
var l:int;
for (i = 0, l = array.length; i < l; i++)
{
array[i] += 0x00FF0000;
}
var da:Array = array.concat();
this.endTime=getTimer();
Output.trace(this.endTime-this.startTime +' ms');
l = SIZE * SIZE - 1;
Output.trace("Array => 0x" + da[l].toString(16).toUpperCase());
}
}
}
Może łączenie tablic nie jest takie interesujące ale wyraźnie pokazuje na korzyść stosowania danych parametrycznych. Oto kod z wektorami w pliku CalcVector.as :
package
{
import flash.display.Sprite;
import flash.utils.*;
import com.senocular.utils.Output;
[SWF(width=1024, height=1024, backgroundColor=0xFFFFFF, frameRate=30)]
public class CalcVector extends Sprite
{
private static const SIZE:uint = 1024;
private var startTime:Number;
private var endTime:Number;
public function CalcVector():void
{
stage.addChild(new Output());
var vector:Vector.< uint > = new Vector.();
for (var h:int = 0; h < SIZE; h++)
{
for (var w:int = 0; w < SIZE; w++)
{
vector.push(0xFF000000);
}
}
this.startTime=getTimer();
var i:int;
var l:int;
for (i = 0, l = vector.length; i < l; i++)
{
vector[i] += 0x00FF0000;
}
var dv:Vector.= Vector.< uint >(vector.concat());
this.endTime=getTimer();
Output.trace(this.endTime-this.startTime +' ms');
l = SIZE * SIZE - 1;
Output.trace("Vector => 0x" + dv[l].toString(16).toUpperCase());
}
}
}
Na dość wolnym komputerze wynik dla Array wyniósł mi 512 ms, a dla Vector 98 ms.
Skoro mam wolny komputer to wykonywanie Pixel Bender może spowolnić m.in dlatego że nie mam wielordzeniowego procesora. A jak ktoś ma nowszy procesor to nawet pójdzie szybciej. Ale zobaczmy kod Japończyka dla pliku Calc.pbk.
kernel ColorFilter
<
namespace : "muta";
vendor : "muta";
version : 1;
description : "This filter Fill color.";
>
{
parameter pixel4 color
<
defaultValue : pixel4(0, 0, 0, 1);
description : "color";
>;
input image4 src;
output pixel4 dst;
void evaluatePixel()
{
pixel4 tmp = sampleNearest(src, outCoord());
tmp.rgba += color;
dst = tmp;
}
}
A kod w pliku ShaderCalc.as jest taki
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Shader;
import flash.display.Sprite;
import flash.filters.ShaderFilter;
import flash.geom.Point;
import flash.utils.ByteArray;
import flash.utils.*;
import com.senocular.utils.Output;
[SWF(width=1024, height=1024, backgroundColor=0xFFFFFF, frameRate=30)]
public class CalcShader extends Sprite
{
private static const SIZE:uint = 1024;
private var startTime:Number;
private var endTime:Number;
[Embed(source="calc.pbj", mimeType="application/octet-stream")]
private var shaderData:Class;
public function CalcShader():void
{
stage.addChild(new Output());
var vector:Vector.= new Vector. ();
for (var h:int = 0; h < int =" 0;" bitmapdata =" new" bitmapdata =" new" point =" new" shader =" new" shaderfilter =" new" starttime="getTimer();" value =" [1,"> = dst.getVector(dst.rect);
this.endTime=getTimer();
Output.trace(this.endTime-this.startTime +' ms');
l = SIZE * SIZE - 1;
Output.trace("Shader + Vector => 0x" + ds[l].toString(16).toUpperCase());
addChild(new Bitmap(dst));
}
}
}
Dla mnie wynik wyniósł w działaniu powyższego kodu 160 ms. co i tak na słabym komuterze daje całkiem niezły wynik.
Autor: Michał Małaj o godzinie 13:19 0 komentarzy
poniedziałek, 26 maja 2008
As3 Wrapper
W poprzednim poście wspomniałem o pewnej bibliotece która miała pozwolić programistom JavaScriptu na bezproblemowe przejście do programowania w JavaScript 2.0 ( i zwiększyć wydajność obsługi skryptów). A dzisiaj odkryłem kolejną możliwość pisania skryptów JavaScript zamiast kodu w ActionScript 3 za pomocą biblioteki AS3Wrapper. Jest jeden duży plus tego rozwiązania: pozwala programistom generować po stronie serwera skrypty JavaScript, które miałyby wpływ na dynamiczny sposób wyświetlania zawartości przez Flash Playera.
Wadą tej biblioteki jest brak kodu źródłowego i ograniczenie na razie tego do modułów Papervision, Google Maps Flash API, jakiejś biblioteki do obsługi czcionek. Przykłady można pobrać stąd.
Oto kod wykorzystujący Google Maps Flash API
var player;
var map;
function pageLoad(){
player = new AS3Wrapper.Player("flashcontent",200,200,false);
player.onload = function(){
player.loadModule('GoogleMaps',onload2);
}
function onload2(e)
{
with(player)
with(com.google.maps){
map = new Map();
map.setKey('=== wpisz tutaj swój klucz Google API ====');
map.addEventListener(MapEvent.MAP_READY, onMapReady);
root.addChild(map);
}
}
}
function onMapReady(event)
{
with(player)
with(com.google.maps){
map.setCenter(new LatLng(40.736072,-73.992062), 14, MapType.NORMAL_MAP_TYPE);
}
}
Jak widać przypomina to czasy pisania w starym ActionScript 1.0
Autor: Michał Małaj o godzinie 15:19 0 komentarzy
niedziela, 25 maja 2008
JS 2.0 - prawie zrealizowana wizja....
W jednym poście pisałem o JavaScript 2.0 oraz o wydajności jakie to niesie ze sobą dla aplikacji Web 2.0. Moja wizja w której programiści Web 2.0 zaczynają pisać kod pod JavaScript 2.0 miała słaby punkt. Co zrobić ze starymi przeglądarkami? Otóż jest już rozwiązanie: ScreamingDonkey
Cała filozofia tego rozwiązania polega na tym, żeby napisać kod w JavaScript 2.0 wysłać go do FlashPlayera, FlashPlayer skompiluje kod JS 2.0 do postaci wykonywalnej przez AVM2 (Tamarin) dokona obliczeń i wyniki wyśle jako JSObject do przeglądarki. Nie byłem do tego przekonany z tego powodu, że to jest zbyt innowacyjne, aby programiści JavaScriptu zaczęli poważnie to potraktować.
Z innej strony raczej wierzę w to, że przeglądarka oparta na Adobe AIR "wygryzie" pozostałe przeglądarki internetowe. Ale trzeba uszanować tych którzy z jakiś tam powodów nie będą mogli korzystać z Adobe AIR. W tym momencie włączenie ScreamingDonkey jako silnika Java Script 2.0 do przeglądarki opartej na Adobe AIR rozwiązuje problem sytuację w której na stronie internetowej mamy kod JavaScript 2.0. Oznacza to dla użytkowników internetowych że ten kod zostanie wykonany w Firefoxie 2 i 3 (nie muszą czekać na wydanie Firefoxa 4), w Internet Exploratorach będzie mógł wybierać pomiędzy silnikiem ScreamingMonkey a ScreamingDonkey. Zagadką pozostaje Opera i jak Safari 3 - myślę, że w przypadku tych przeglądarek najszybciej wprowadzą natywna obsługę JavaScriptu 2.0
To co mnie naprawdę zdumiało to jest szybkość Flash Playera w testach wydajnościowych. W pewnym benchmarku otrzymałem wynik 13237.40 ms pod Firefoxem 2, a ten sam test w ScreamDonkey pod Firefoxem 2 z Flash Playerem 10 to 2160.00 ms. W praktyce otwiera to drogę przed obliczeniami graficznymi w przeglądarkach. Jednym słowem przeglądarka będzie mogła dość skutecznie uzupełniać Flash Playera. John Resig opublikował 8 maja bibliotekę JavaScript Processing.js do efektów graficznych na bazie silnika Processing. A Metal Hurlant 19 maja już opublikował wersję działającą z ScreamDonkey pod Firefoxem 2. A na horyzoncie mamy ActionScript 4, który będzie opracowywany na bazie EcmaScript4. Pracownik Adobe już napisał wprowadzenie do tego co będzie w kolejnej wersji EcmaScript 4 Hurlant już dodał obsługę bajtkodu es4 w bibliotece AS 3 Eval o.3 do FlashPlayera.
Autor: Michał Małaj o godzinie 11:39 0 komentarzy
sobota, 24 maja 2008
Blending Mode
Ponieważ japońscy programiści zajmują się ustalaniem jak można przerzucić obliczenia na shadery i wykorzystać ShaderJob do symulowania wielowątkowej pracy. To chciałbym pokazać kolejne możliwości Pixel Bender. Taka możliwością jest stosowanie trybów mieszania.
W Pixel Bender Toolkit jest kod shadera crossfade.pbk
kernel Crossfade
< namespace : "AIF";
vendor : "Adobe Systems";
version : 2;
description : "Crossfade between two images"; >
{
parameter float intensity;
input image4 frontImage;
input image4 backImage;
output pixel4 dst;
void evaluatePixel()
{
float4 frontPixel = sampleNearest(frontImage, outCoord());
float4 backPixel = sampleNearest(backImage, outCoord());
dst = mix(frontPixel, backPixel, intensity);
}
}
dst = (1.0 - intensity) * frontPixel + intensity * backPixel
A jaki napisać kod ActionScript 3 do obsługi trybów mieszania z bajtkodu crossfade.pbj?
Na przykład taki kod ActionScript 3 jak ten w pliku Crossfade.as
package{
import flash.display.MovieClip;
import flash.display.Shader;
import flash.events.Event;
import mx.core.BitmapAsset;
import flash.display.StageScaleMode;
[SWF(width="500", height="500")]
public class Crossfade extends MovieClip{
[Embed(source = 'front.jpg')]
[Bindable]
private var _frontImage:Class;
[Embed(source = 'back.jpg')]
[Bindable]
private var _backImage:Class;
[Embed(source="crossfade.pbj", mimeType="application/octet-stream")]
private var CrossShader:Class;
private var _shader:Shader;
private var _frontAsset:BitmapAsset;
private var _backAsset:BitmapAsset;
public function Crossfade()
{
super();
_frontAsset = new _frontImage() as BitmapAsset;
_backAsset = new _backImage() as BitmapAsset;
stage.scaleMode = StageScaleMode.NO_SCALE;
_shader = new Shader(new CrossShader());
_shader.data.frontImage.input = _frontAsset.bitmapData;
_shader.data.backImage.input = _backAsset.bitmapData;
_shader.data.intensity.value = [0.72];
addEventListener(Event.ENTER_FRAME, drawShader);
}
private function drawShader(e:Event):void
{
graphics.clear();
graphics.beginShaderFill(_shader);
graphics.drawRect(0, 0, 500, 500);
graphics.endFill();
}
}
}
Jak widać możliwości są całkiem spore.
Autor: Michał Małaj o godzinie 17:05 0 komentarzy
piątek, 23 maja 2008
Adobe Flex 4 i Pixel Bender
Aby programować pod Flash Player 10 trzeba ściągnąć najnowszą wersję Flex 4 SDK
Rozpakowujemy to archiwum do katalogu D:\Flex4\. Następnie dodajemy do zmiennej środowiskowej PATH ścieżkę D:\Flex4\bin\ gdzie mamy kompilator mxmlc.exe.
Trzeba jeszcze sprawdzić w pliku D:\flex4\frameworks\flex-config.xml czy mamy dobrze ustawione parametry do kompilacji pod Flash Player 10. Trzeba tylko do kompilacji używać takiego zapisu:
mxmlc.exe --target-player=10 PixelBender.as
Są 2 sposoby pisania kodu pod dla bajtkodu Pixel Bender. Pierwszy z nich polega na pobraniu czy wysłaniu pliku *.pbj do pliku SWF, a drugi polega na osadzeniu pliku *.pbj w pliku SWF
Zaczniemy od drugiego sposobu. Załadujemy grafikę i jak bajtkod shadera w plik SWF.
Oto kod shadera sepia3.pbk
kernel sepia
< namespace : "AIF";
vendor : "Adobe Systems";
version : 2;
description : "a variable sepia filter"; >
{
input image4 src;
output float4 dst;
void
evaluatePixel()
{
float4 rgbaColor;
float4 yiqaColor;
float4x4 YIQMatrix = float4x4(
0.299, 0.596, 0.212, 0.000,
0.587, -0.275, -0.523, 0.000,
0.114, -0.321, 0.311, 0.000,
0.000, 0.000, 0.000, 1.000
);
float4x4 inverseYIQ = float4x4(
1.0, 1.0, 1.0, 0.0,
0.956, -0.272, -1.10, 0.0,
0.621, -0.647, 1.70, 0.0,
0.0, 0.0, 0.0, 1.0
);
rgbaColor = sampleNearest(src, outCoord());
yiqaColor = YIQMatrix * rgbaColor;
yiqaColor.y = 0.2;
yiqaColor.z = 0.0;
dst = inverseYIQ * yiqaColor;
}
}
package {
import flash.display.Bitmap;
import flash.display.Shader;
import flash.display.Sprite;
import flash.events.Event;
import flash.filters.ShaderFilter;
[SWF(width="550",height="400",frameRate="24")]
public class Osadzony extends Sprite
{
[Embed(source="rose.jpg")]
private var Obraz:Class;
[Embed(source="sepia2.pbj", mimeType="application/octet-stream")]
private var Sepia:Class;
private var img:Bitmap;
private var shader:Shader;
private var filter:ShaderFilter;
public function Osadzony(){
img = new Obraz() as Bitmap;
img.x = stage.stageWidth/2 - img.width/2;
img.y = stage.stageHeight/2 - img.height/2;
addChild(img);
shader = new Shader(new Sepia());
filter = new ShaderFilter(shader);
img.filters = [filter];
}
}
}
Teraz napiszemy kod w pliku LoadedShader.as do sytuacji w której załadujemy lokalnie plik bajtkodu z Pixel Binder.
Jest jeszcze inny "sposób", który pokazuje jak plik shadera i grafikę załadować z zewnątrz (do testowania na serwerze). W pliku OuterPixelBender.as mamy taki kod ActionScript
package
{
import flash.display.*;
import flash.events.*;
import flash.net.*;
import flash.filters.*;
import flash.utils.*;
[SWF(width="550",height="400",frameRate="24")]
public class LoadedShader extends Sprite
{
[Embed(source="rose.jpg")]
private var Obraz:Class;
private var fr:FileReference;
private var shader:Shader;
private var shaderFilter:ShaderFilter;
private var loader:Loader;
private var img:Bitmap;
public function LoadedShader():void
{
fr = new FileReference();
fr.addEventListener(Event.SELECT, onSelect);
fr.addEventListener(Event.COMPLETE, onComplete);
var spr:Sprite = new Sprite();
addChild(spr);
img = new Obraz() as Bitmap;
img.x = stage.stageWidth/2 - img.width/2;
img.y = stage.stageHeight/2 - img.height/2;
spr.addChild(img);
spr.addEventListener(MouseEvent.CLICK, loaderClick);
}
private function loaderClick(e:Event):void
{
fr.browse();
}
private function onSelect(e:Event):void
{
fr.load();
}
private function onComplete(e:Event):void
{
shader = new Shader(fr.data);
shaderFilter = new ShaderFilter();
shaderFilter.shader = shader;
img.filters = [shaderFilter];
}
}
}
package
{
import flash.display.*;
import flash.events.*;
import flash.net.*;
import flash.filters.*;
import flash.utils.*;
public class OuterPixelBender extends Sprite
{
private var shader:Shader;
private var shaderFilter:ShaderFilter;
private var loader:Loader;
public function OuterPixelBender():void
{
var shaderLoader:URLLoader = new URLLoader();
shaderLoader.dataFormat = URLLoaderDataFormat.BINARY;
shaderLoader.addEventListener(Event.COMPLETE, shaderLoaded);
shaderLoader.load(new URLRequest("sepia2.pbj"));
loader = new Loader();
loader.load(new URLRequest("rose.jpg"));
addChild(loader);
}
private function shaderLoaded(e:Event):void
{
var shaderLoader:URLLoader = e.target as URLLoader;
try
{
shader = new Shader();
shader.byteCode = shaderLoader.data;
shaderFilter = new ShaderFilter();
shaderFilter.shader = shader;
loader.filters = [shaderFilter];
}catch (e:Error)
{
throw(e);
}
}
}
}
A tak na marginesie to zauważyłem, że mało kto pisze że, można już używać Adobe Flex 4 SDK i stąd taki przewrotny tytuł posta.
Autor: Michał Małaj o godzinie 10:33 0 komentarzy
czwartek, 22 maja 2008
Programowanie Shaderów w Pixel Bender
Jak wspomniałem za programowanie filtrów i shaderów (blendingu) w Flash Player 10 odpowiada technologia Bender Pixel. Najpierw instalujemy Pixel Bender Toolkit i instalacja przebiega po polsku. Po zainstalowaniu Pixel Bender Toolikt uruchamiamy to cudo - może sie pojawić komunikat po angielsku, że nie mamy karty GPU. Z menu File -> Load Images wybieramy jakiś obrazek. Następnie wybieramy skrypt z menu File -> Open Pixel Bender Kernel Filter i wskazujemy sciezkę C:\Program Files\Adobe\Adobe Utilities\Pixel Bender Toolkit\pixel bender files i wybieramy plik sepia.pbk. Uruchamiamy to z menu Build -> Run
Ustawiamy w pasku scrollera intensity na 0.2.Jeśli podoba się to nam to tworzymy plik bajtkod dla Flash Player 10. Z memu wybieramy File -> Export Pixel Bender Byte Code for Flash i zapisujemy jako sepia.pbj. Następnie wchodzimy na tą stronę. Klikamy w przycisk Add
Pojawi sie okno do wybrania pliku wybieramy (tam gdzie zapisaliśmy) plik sepia.pbj
Ale nie sobaczyliśmy czego chcieliśmy czyli efektu sepii. Wracamy do Pixel Bender Toolkit. W kodzie pliku sepia.pbk szukamy linijki 86 która zawiera kod:
yiqaColor.y = intensity;
zmieniamy to na
yiqaColor.y = 0.2;
Zapisujemy to wybierając z menu File -> Save Pixel Blender Kernel Filter As.. jako sepia2.pbk. A następnie wybieramy z memu File -> Export Pixel Bender Byte Code for Flash i zapisujemy jako sepia2.pbj. Następnie wchodzimy i odświeżamy tą stronę. Klikamy w przycisk Add. Pojawi sie okno do wybrania pliku i wybieramy (tam gdzie zapisaliśmy) plik sepia2.pbj. I zobaczyliśmy to co chcieliśmy.
Na początek programowania weźmiemy sobie kod który nic nie robi a jest podstawą do pracy nad kolejnymi skryptami
kernel NowyFiltr
< namespace : "info.malaj";
vendor : "Michal Malaj";
version : 1;
description : "Pierwszy kod";
>
{
input image4 src;
output pixel4 dst;
void
evaluatePixel()
{
dst = sampleNearest(src,outCoord());
}
}
Nazwa kernela nie może zawierać polskich znaków, i tak lepiej poza komentarzami i metatagami nie używać w plikach języka polskiego. Kopiujemy powyższy kod i zapisujemy go. Otóż powyższy kod działa w ten sposób, że pobiera dane z obrazka. Słowo kluczowe input oznacza że dane są wyjściowe typu image4 i są zdefiniowanej w zmiennej o nazwie src. Następne słowo kluczowe output oznacza że dane będą wynikiem końcowym jako typu pixel4 (oznacza to że informacje o kolorze jednego piksela są przechowywane w 4 wartościach RGBA) i są zdefiniowanej w zmiennej dst. Następnie mamy zwykła procedurę evaluatePixel(), która dokonuje obliczeń na tych danych. Tym razem mamy jedną linijkę kodu przypisujemy do zmiennej dst wynik działania funkcji sampleNearest(). Ta funkcja pobiera dane z obrazka i o jego współrzędnych i przekształca to na piksele, na których dalej będzie można dokonywać operacji. W tym przypadku można powiedzeć że zwraca informacje o pikselach w tym obrazku i nic więcej.
Oto kod przyciemnianie.pbk
kernel Przyciemnianie
< namespace : "malaj.info";
vendor : "Michał Małaj";
version : 1;
description : "efekt przyciemniania";
>
{
input image4 src;
output pixel4 dst;
void
evaluatePixel()
{
dst = 0.5 * sampleNearest(src,outCoord());
}
}
Kod przyciemnianiebezalfy.pbk
kernel Przyciemnianie
< namespace : "info.malaj";
vendor : "Michał Małaj";
version : 1;
description : "Przyciemnianie bez alfy";
>
{
input image4 src;
output pixel4 dst;
void
evaluatePixel()
{
float4 inputColor = sampleNearest(src,outCoord());
dst.rgb = 0.5 * inputColor.rgb;
dst.a = inputColor.a;
}
}
Podobnie można zrobić filtr ekspozycji. Potocznie mówi się że to jest rozjaśnianie.
kernel Rozjasnianie
< namespace : "info.malaj";
vendor : "Michal Malaj";
version : 1;
description : "Filtr ekspozycji";
>
{
input image4 src;
output pixel4 dst;
void
evaluatePixel()
{
float4 inputColor = sampleNearest(src, outCoord());
dst.rgb = pow(inputColor.rgb, float3(0.5));
dst.a = inputColor.a;
}
}
kernel Rozjasnianie
< namespace : "info.malaj";
vendor : "Michal Malaj";
version : 1;
description : "Filtr ekspozycji";
>
{
input image4 src;
output pixel4 dst;
parameter float exposure
<
minValue:float(-0.5);
maxValue:float(0.5);
defaultValue:float(0.0);
>;
void
evaluatePixel()
{
float4 inputColor = sampleNearest(src, outCoord());
dst.rgb = pow(inputColor.rgb, float3(1.0 - exposure));
dst.a = inputColor.a;
}
}
<languageVersion : 1.0;>
kernel BlackWhite
< namespace : "info.malaj";
vendor : "Michał Małaj";
version : 1;
description : "efekt czarno-białej fotografii";
>
{
input image4 src;
output pixel4 dst;
void
evaluatePixel()
{
dst = sampleNearest(src,outCoord()).rrra;
}
}
kernel ParamBlackWhite
< namespace : "info.malaj";
vendor : "Michał Małaj";
version : 1;
description : "efekty przejścia w czarno-białą fotografię";
>
{
input image4 src;
output pixel4 dst;
parameter float crossfade;
void
evaluatePixel()
{
dst = sampleNearest(src,outCoord());
float3 bw = dst.rrr;
dst.rgb = ((1.0 - crossfade) * dst.rgb) + (crossfade * bw);
}
}
<languageVersion : 1.0;>
kernel LunaBlackWhite
< namespace : "info.malaj";
vendor : "Michał Małaj";
version : 1;
description : "efekty lumy przejścia w czarno-białą fotografię";
>
{
input image4 src;
output pixel4 dst;
parameter float crossfade;
void
evaluatePixel()
{
dst = sampleNearest(src,outCoord());
float luminance = dst.r * 0.212 + dst.g * 0.715 + dst.b * 0.07;
dst.rgb = ((1.0 - crossfade) * dst.rgb) + (crossfade * float3(luminance));
}
}
kernel LunaDotBlackWhite
< namespace : "info.malaj";
vendor : "Michał Małaj";
version : 1;
description : "efekty lumy z iloczynem skalarnym przejścia w czarno-białą fotografię";
>
{
input image4 src;
output pixel4 dst;
parameter float crossfade;
const float3 lumMult = float3(0.212, 0.715, 0.07);
void
evaluatePixel()
{
dst = sampleNearest(src,outCoord());
float luminance = dot(dst.rgb, lumMult);
dst.rgb = mix(dst.rgb, float3(luminance), crossfade);
}
}
kernel Sepia
< namespace : "info.malaj";
vendor : "Michał Małaj";
version : 1;
description : "efekty przejścia w fotografię sepii";
>
{
input image4 src;
output pixel4 dst;
parameter float crossfade
<
minValue:float(0.0);
maxValue:float(1.5);
defaultValue:float(1.0);
>;
const float3 lumMult = float3(0.212, 0.791, 0.07);
const float3x3 sepiaMatrix = float3x3(0.400, 0.769, 0.189,0.349, 0.686, 0.168, 0.272, 0.534, 0.131);
void
evaluatePixel()
{
dst = sampleNearest(src,outCoord());
float3 sepia = dst.rgb * sepiaMatrix;
dst.rgb = mix(dst.rgb, sepia, crossfade);
}
}
Zawsze można wygenerować bajtkod z tych efektów dla Flash Playera 10. Jak widać kod efektów jest bardzo zwięzły. Warto pokazać jak za pomocą ActionScript 3 można wykorzystać ten bajtkod.
Autor: Michał Małaj o godzinie 13:23 4 komentarzy
