czwartek, 15 maja 2008

Flash Player 10 Beta

Adobe wypuściło wersję Flash Playera 10 Beta. Ale mnie zainteresowało jak mogę już brać się za kodowanie do Flash Playera 10 Beta. Wystarczyłoby ściągnąć nighty builda Flex 3 SDK. Obejrzałem dema, ale miałem problem ze ściągnięciem kodów źródłowych, ale zamieszczę linki do tych źródeł: IK Sample, Pixel_Bender Sample, NewTextEngine, Native3D.

Z powodu braku dokumentacji (przynajmniej ja jeszcze ją nie znalazłem - będzie wraz z dokumentacja do Flex 4), to warto się skoncentrować na filmach Justina Everett-Church, który pokazuje o obsłudze 3D, o Pixel Bender, nowym silniku do wyświetlania tekstu wykorzystującym technologię Saffron 3.1, o nowych możliwościach obsługi GPU i co za tym idzie znaczny wzrost wydajności. Jedynym materiałem w dzisiejszym dniu o nowym API FlashPlayera jest ten kurs

Najbardziej intrygującą rzeczą jest Flie reference runtime access co pozwala użytkownikowi załądować np plik graficzny, prosto z komputera i obsługiwać go z poziomu FlashPlayera a następnie zapisać efekt na dysk twardy jako strumień danych (ByteArray oczywiście).

Efektem intensywnego lobbyingu developerów w akcji Adobe, Make Some Noise było dodanie możliwości generowania dźwięku o czym napisał inżynier Tinic Uro tworzący FlashPlayera.

Dodano nową nowość do ActionScriptu 3 - typ danych Vector który jest parametrycznym typem danych. Może będzie trzeba już uczyć się ActionScriptu 4.

Utworzono nowy komponent pod kodową nazwą "vellum_flash" do wielokolumnowego tekstu, z obsługą tabel, "pływających ramek", obsługi innych systemów pisma. Ten komponent będzie wymagał nauki nowego języka znacznikowego XFL (który ma zastąpić format FLA).

Kolejna rzeczą zadziwiającą jest wykorzystanie P2P dzięki dodaniu obsługi Media Flow Protocol (był on kiedyś dostępny). Następnie dzięki dodaniu nowego kodeka Speex Audio Codex odpowiedzialnego za obsługę kodowanie dźwięku z mikrofonu może ktoś zrobi konkurenta dla Skypa i telefonii internetowej.

Mam też z pliku playerglobal.swc z Flex 3 SDK informacje o nowym API. Adobe już wypuściło dokumentację API w tym pliku Są nowe przestrzenie nazw. To najlepiej pokazuje nowe możliwości. Flash Playera. Do tej pory Japończycy opublikowali API do fp10 Przestrzeń nazw odpowiadającą za obróbkę dźwięku to flash.sampler.*

Nowe klasy z flash.display.* to
flash.display.IGraphicsPath
flash.display.IGraphicsData
flash.display.GraphicsPath
flash.display.ShaderPrecision
flash.display.TriangleCulling
flash.display.Shader
flash.display.ShaderData
flash.display.ShaderParameterType
flash.display.ShaderInput
flash.display.GraphicsShaderFill

Klasy związane z nowym silnikiem do obsługi tekstu są w pakiecie flash.text.engine.*
flash.text.engine.ContentElement
flash.text.engine.TextJustifier
flash.text.engine.SpaceJustifier
flash.text.engine.TypographicCase
flash.text.engine.TabAlignment
flash.text.engine.ElementFormat
flash.text.engine.FontMetrics
flash.text.engine.GraphicElement
flash.text.engine.TextLine
flash.text.engine.TextRotation
flash.text.engine.TextBlock
flash.text.engine.LigatureLevel
flash.text.engine.TextBaseline
flash.text.engine.FontPosture
flash.text.engine.TextLineValidity
flash.text.engine.TextLine
flash.text.engine.GroupElement

Klasy z pakietu flash.ui.* - odpowiadają za interakcję
flash.ui.ContextMenuItem
flash.ui.ContextMenuBuiltInItems
flash.ui.KeyLocation
flash.ui.MouseCursor
flash.ui.Keyboard

Klasy z pakietu flash.geom.* zostały wzbogacone o obsługe przekształceń
flash.geom.PerspectiveProjection
flash.geom.Vector3D
flash.geom.Utils3D

Dostały też dodane całkiem nowe klasy powiązane z innymi nowymi możliwościami Flash Playera 10

flash.events.ShaderEvent
flash.events.ContextMenuEvent
flash.desktop.ClipboardFormats
flash.filters.ShaderFilter
flash.media.SoundCodec
flash.system.JPEGLoaderContext
flash.net.DynamicPropertyOutput
flash.net.IDynamicPropertyWriter

środa, 14 maja 2008

Modelowanie danych w Google App Engine

Zauważyliśmy że trzeba większa uwagę poświęcić na modelowaniu danych. Ale najlepszym mechanizmem do nauki jest interaktywna konsola
http://localhost:8080/_ah/admin/interactive

Robimy analizę przykładu 1:1. Wystarczy poniższy kod skopiować w interaktywną konsole i uruchomić.


from django.db import models
from google.appengine.ext import db

class Car(db.Model):
brand = db.StringProperty(required=True)
wheels = db.ListProperty(db.Key)

class Human(db.Model):
name = db.StringProperty(required=True)
drives = db.ReferenceProperty(reference_class=Car)
spouse = db.SelfReferenceProperty()
owns = db.ListProperty(db.Key)

class Wheel(db.Model):
isBroken = db.BooleanProperty(default=False)
position = db.StringProperty(choices=set(["left_front",
"left_back",
"right_front",
"right_back"]))

# tworzymy obiekt Jacka
jack = Human(name="Jack")
# tworzymy obiekt mercedesa
mercedes = Car(brand="Mercedes")
# przypisujemy obiekt mercedesa jako pojazd Jacka
jack.drives = mercedes.put()
#wstawiamy dane do obiektu Jacka
jack.put()
# wyświetlamy dane
print >> sys.stdout, "Jack ma samochod marki "+jack.drives.brand
# Jacek ma samochód marki Mercedes

Trzeba być ostrożnym bo modelowanie klasyczne jeden do jednego wcale nie jest takie automatyczne obsługiwane przez ORM

from django.db import models
from google.appengine.ext import db

class Car(db.Model):
brand = db.StringProperty(required=True)
wheels = db.ListProperty(db.Key)

class Human(db.Model):
name = db.StringProperty(required=True)
drives = db.ReferenceProperty(reference_class=Car)
spouse = db.SelfReferenceProperty()
owns = db.ListProperty(db.Key)

class Wheel(db.Model):
isBroken = db.BooleanProperty(default=False)
position = db.StringProperty(choices=set(["left_front",
"left_back",
"right_front",
"right_back"]))

jack = Human(name="Jack")
mike = Human(name="Mike")
mercedes = Car(brand="Mercedes")
mercedesid = mercedes.put()

jack.drives = mercedesid
jack.put()

mike.drives = mercedesid
mike.put()

print >> sys.stdout, "Mike ma samochod marki "+mike.drives.brand
#Mike ma samochod marki Mercedes

Ważne jest rozróżnienie odesłania do własnej klasy w kodzie: czyli model odwołuje sie do własnego modelu np: człowiek odwołuje sie do innego człowieka albo albo wynika z oczywistości, że mąż ma żonę co w praktyce oznacza że model model ma odniesienie do siebie samego.

from django.db import models
from google.appengine.ext import db


class Car(db.Model):
brand = db.StringProperty(required=True)
wheels = db.ListProperty(db.Key)

class Human(db.Model):
name = db.StringProperty(required=True)
drives = db.ReferenceProperty(reference_class=Car)
spouse = db.SelfReferenceProperty()
owns = db.ListProperty(db.Key)

class Wheel(db.Model):
isBroken = db.BooleanProperty(default=False)
position = db.StringProperty(choices=set(["left_front",
"left_back",
"right_front",
"right_back"]))
# one-to-one self
bob = Human(name="Bob")
jane = Human(name="Jane")

bob.spouse = jane.put()
bob.put()
b_spouse = Human.get(bob.spouse.key())
print >> sys.stdout, "Bob's spouse is "+b_spouse.name
# Bob's spouse is Jane


Relacja wzajemności. Oto przykład

from django.db import models
from google.appengine.ext import db


class Car(db.Model):
brand = db.StringProperty(required=True)
wheels = db.ListProperty(db.Key)

class Human(db.Model):
name = db.StringProperty(required=True)
drives = db.ReferenceProperty(reference_class=Car)
spouse = db.SelfReferenceProperty()
owns = db.ListProperty(db.Key)

class Wheel(db.Model):
isBroken = db.BooleanProperty(default=False)
position = db.StringProperty(choices=set(["left_front",
"left_back",
"right_front",
"right_back"]))

bob = Human(name="Bob")
jane = Human(name="Jane")

bob.spouse = jane.put()
jane.spouse = bob.put()
jane.put()

j_spouse = Human.get(jane.spouse.key())
print >> sys.stdout, "Jane's spouse is "+j_spouse.name
b_spouse = Human.get(bob.spouse.key())
print >> sys.stdout, "Bob's spouse is "+b_spouse.name

# Jane's spouse is Bob
# Bob's spouse is Jane


Problem w tym, że nikt nie sprawdza poprawności tych relacji.

Relacja jeden do wielu. W programowaniu oznacza, że dany model ma przodka bądź rodzica. Popatrzmy na poniższy kod:

from django.db import models
from google.appengine.ext import db

class Car(db.Model):
brand = db.StringProperty(required=True)
wheels = db.ListProperty(db.Key)

class Human(db.Model):
name = db.StringProperty(required=True)
drives = db.ReferenceProperty(reference_class=Car)
spouse = db.SelfReferenceProperty()
owns = db.ListProperty(db.Key)

class Wheel(db.Model):
isBroken = db.BooleanProperty(default=False)
position = db.StringProperty(choices=set(["left_front",
"left_back",
"right_front",
"right_back"]))

# one-to-many using parent
bmw = Car(brand="BMW")
bmw.put()

lf = Wheel(parent=bmw,position="left_front")
lf.put()

lb = Wheel(parent=bmw,position="left_back")
lb.put()

rf = Wheel(parent=bmw,position="right_front")
rf.put()

# uh, snap, the 4th wheel is broken!
rb = Wheel(parent=bmw,position="right_back",isBroken=True)
rb.put()

# from car to wheels
bmwWheels = Wheel.all().ancestor(bmw)
print >> sys.stdout, "The BMW has the wheels: "
for wheel in bmwWheels:
print >> sys.stdout, "- "+wheel.position

# The BMW has the wheels:
# - left_front
# - right_back
# - right_front
# - left_back

# from wheel to car
brokenWheels = Wheel.gql("WHERE isBroken = :broken",
broken=True)
print >> sys.stdout, "The following cars are broken: "
for wheel in brokenWheels:
print >> sys.stdout, "- "+wheel.parent().brand

# The following cars are broken:


Co jest ciekawego w tym kodzie? Użycie GQL jako zapytania w modelu
w razie czego proponuję poczytać o tym
http://code.google.com/appengine/docs/datastore/entitiesandmodels.html
Teraz weźmiemy kolejny przykład

from django.db import models
from google.appengine.ext import db

class Car(db.Model):
brand = db.StringProperty(required=True)
wheels = db.ListProperty(db.Key)

class Human(db.Model):
name = db.StringProperty(required=True)
drives = db.ReferenceProperty(reference_class=Car)
spouse = db.SelfReferenceProperty()
owns = db.ListProperty(db.Key)

class Wheel(db.Model):
isBroken = db.BooleanProperty(default=False)
position = db.StringProperty(choices=set(["left_front",
"left_back",
"right_front",
"right_back"]))

class OwnedCar(db.Model):
brand = db.StringProperty(required=True)
owner = db.ReferenceProperty(Human, required=True)


paul = Human(name = "Paul")
paul.put()

pauls_bmw = OwnedCar(brand = "BMW", owner = paul)
pauls_bmw.put()

pauls_mercedes = OwnedCar(brand="Mercedes", owner=paul)
pauls_mercedes.put()

pauls_cars = paul.ownedcar_set

print >> sys.stdout, "Paul's cars: "
for car in pauls_cars:
print >> sys.stdout, "- "+car.brand


Relacje jeden do wielu można zrealizować przy pomocy listy

from django.db import models
from google.appengine.ext import db

class Car(db.Model):
brand = db.StringProperty(required=True)
wheels = db.ListProperty(db.Key)

class Human(db.Model):
name = db.StringProperty(required=True)
drives = db.ReferenceProperty(reference_class=Car)
spouse = db.SelfReferenceProperty()
owns = db.ListProperty(db.Key)

class Wheel(db.Model):
isBroken = db.BooleanProperty(default=False)
position = db.StringProperty(choices=set(["left_front",
"left_back",
"right_front",
"right_back"]))

class OwnedCar(db.Model):
brand = db.StringProperty(required=True)
owner = db.ReferenceProperty(Human, required=True)


# one-to-many using list
dodge = Car(brand="Dodge")
w1 = Wheel(position="left_front")
w2 = Wheel(position="left_back")
w3 = Wheel(position="right_front")
w4 = Wheel(position="right_back")
dodge.wheels = [w1.put(),w2.put(),w3.put(),w4.put()]
dodge.put()

dodgeWheels = Wheel.get(dodge.wheels)
print >> sys.stdout, "The Dodge has the wheels: "
for wheel in dodgeWheels:
print >> sys.stdout, "-"+wheel.position


Możemy dalej analizować przykłady relacji wielu do wielu

from django.db import models
from google.appengine.ext import db

class Car(db.Model):
brand = db.StringProperty(required=True)
wheels = db.ListProperty(db.Key)

class Human(db.Model):
name = db.StringProperty(required=True)
drives = db.ReferenceProperty(reference_class=Car)
spouse = db.SelfReferenceProperty()
owns = db.ListProperty(db.Key)

class Wheel(db.Model):
isBroken = db.BooleanProperty(default=False)
position = db.StringProperty(choices=set(["left_front",
"left_back",
"right_front",
"right_back"]))

class OwnedCar(db.Model):
brand = db.StringProperty(required=True)
owner = db.ReferenceProperty(Human, required=True)

# many-to-many using list+db.Key
jack = Human(name="Jack")
bob = Human(name="Bob")

vw = Car(brand="VW")
chevy = Car(brand="Chevy")

carpool = [vw.put(),chevy.put()]

jack.owns = carpool
jack.put()

bob.owns = carpool
bob.put()

chrysler = Car(brand="Chrysler")
jack.owns.append(chrysler.put())
jack.put()

jackOwns = Car.get(jack.owns)
print >> sys.stdout, "Jack owns: "
for car in jackOwns:
print >> sys.stdout, "- "+car.brand

# Jack owns:
# - VW
# - Chevy
# - Chrysler

whoOwnsTheChevy = Human.gql("WHERE owns = :car",car=chevy)
print >> sys.stdout, "These humans own the Chevy: "
for who in whoOwnsTheChevy:
print >> sys.stdout, "- "+who.name

# These humans own the Chevy:
# - Jack


Powyższe przykłady nie zapewniały unikalności odesłań co może sprawić że dany obiekt może należeć do róznych obiektów tego samego modelu. Zanim dodamy nowy model spawdzimy czy juz istnieje.


from django.db import models
from google.appengine.ext import db

class Car(db.Model):
brand = db.StringProperty(required=True)
wheels = db.ListProperty(db.Key)

class Human(db.Model):
name = db.StringProperty(required=True)
drives = db.ReferenceProperty(reference_class=Car)
spouse = db.SelfReferenceProperty()
owns = db.ListProperty(db.Key)

class Wheel(db.Model):
isBroken = db.BooleanProperty(default=False)
position = db.StringProperty(choices=set(["left_front",
"left_back",
"right_front",
"right_back"]))

class OwnedCar(db.Model):
brand = db.StringProperty(required=True)
owner = db.ReferenceProperty(Human, required=True)

# many-to-many using list+db.Key
jack = Human(name="Jack")
bob = Human(name="Bob")

vw = Car(brand="VW")
chevy = Car(brand="Chevy")

carpool = [vw.put(),chevy.put()]

jack.owns = carpool
jack.put()

bob.owns = carpool
bob.put()

chrysler = Car(brand="Chrysler")
jack.owns.append(chrysler.put())
jack.put()

def unique(lst):
d = {}
for item in lst:
d[item] = 1
return d.keys()

jeep = Car(brand="Jeep")
jack.owns = unique(jack.owns + [jeep.put()])
jack.put()

jackOwns = Car.get(jack.owns)
print >> sys.stdout, "Jack owns: "
for car in jackOwns:
print >> sys.stdout, "- "+car.brand

# Jack owns:
# - VW
# - Chevy
# - Chrysler

whoOwnsTheChevy = Human.gql("WHERE owns = :car",car=chevy)
print >> sys.stdout, "These humans own the Chevy: "
for who in whoOwnsTheChevy:
print >> sys.stdout, "- "+who.name

# These humans own the Chevy:
# - Jack


Możliwości modelowania w GAE są o wiele większe niż to co jest opisane na tych przykładach.

wtorek, 13 maja 2008

Witaj świecie w Google App Engine

Instalujemy Google App Engine SDK Od 28 maja 2008 roku można publikować własne serwisy oparte na Google App Engine.

Po zainstalowaniu można sobie wsiąść się za tworzenie prostej aplikacji w Django. Bo GAE opiera się na silniku frameworka Django. Jeżeli komuś zależy na testowaniu aplikacji w klasycznym Django w Polsce to może wypróbować hosting na megiteam.pl przez 15 dni.

Po zainstalowaniu GAE SDK zrobiłem folder C:\appEngine, następnie utworzymy podkatalog witaj i tak do katalogu C:\appengine\witaj dodałem plik konfiguracyjny app.yaml oraz plik skryptu witaj.py

Ponieważ Google App Engine opiera się na WCGI więc trzeba stworzyć plik konfiguracyjny app.yaml Kod pliku app.yaml:


# nazwa aplikacji
application: appengine
#wersja aplikacji
version: 1
#silnik wykonawczy
runtime: python
#wersja silnika wykonawczego
api_version: 1

#lista adresów URL które maja być obsługiwane
handlers:
- url: /.*
script: witaj.py
# powyżej pokazuje że wszystkie adresy maja
# być obsługiwane przez skrypt witaj.py


Pliki powinny być zakodowane w UTF-8. Oto kod w pliku witaj.py

print 'Content-Type: text/plain'
print ''
print 'Witaj świecie'


W folderze C:\appengine\ zrobimy plik wsadowy start.bat z takim kodem

dev_appserver.py -d witaj


Teraz uruchomiamy plik C:\appengine\start.bat, a potem wpisujemy w przeglądarce internetowej http://localhost:8080/.

Tak wygląda pierwszy krok. Drugi krok to uruchomienie panela administracyjnego
http://localhost:8080/_ah/admin/datastore z przeglądarką danych i z interaktywna konsolą.

Następną aplikację zrobimy na bazie Djangoguestbook. Jest ona w folderze
C:\Program Files\Google\google_appengine\demos\guestbook

Robimy folder C:\appengine\djangoguestbook. W tym folderze dodajemy plik konfiguracyjny app.yaml z takim kodem

application: djangoguestbook
version: 1
runtime: python
api_version: 1

handlers:
- url: .*
script: main.py


Tworzymy plik main.py z takim kodem

import os
import sys
# Google App Engine imports.
from google.appengine.ext.webapp import util

# tworzymy własny plik z ustawieniami
from django.conf import settings
settings._target = None
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'

# Import various parts of Django.
import django.core.handlers.wsgi
import django.core.signals
import django.dispatch.dispatcher
import django.db


def main():
# Wtworzymy aplikacje Django WSGI.
application = django.core.handlers.wsgi.WSGIHandler()

# Uruchamiamy WSGI CGI z ta naszą aplikacja.
util.run_wsgi_app(application)

# konstruktor
if __name__ == '__main__':
main()


Skoro już wiemy co mamy uruchamiać, to następnie trzeba zrobić plik z ustawieniami aplikacji setting.py Oto kod w pliku setting.py:

import os

DEBUG = True
TEMPLATE_DEBUG = DEBUG
APPEND_SLASH = False
LANGUAGE_CODE = 'en-us'
SITE_ID = 1

# If you set this to False, Django will make some
# optimizations so as not
# to load the internationalization machinery.
USE_I18N = True

# URL prefix for admin media -- CSS, JavaScript and images.
# Make sure to use a
# trailing slash.
# Examples: "http://foo.com/media/", "/media/".
ADMIN_MEDIA_PREFIX = '/media/'

# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.load_template_source',
'django.template.loaders.app_directories.load_template_source',
)

MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.doc.XViewMiddleware',
)
# odnośnik do pliku konfiguracyjnego urls.py
ROOT_URLCONF = 'urls'
# odnośnik do folderu w którym jest aplikacja
ROOT_PATH = os.path.dirname(__file__)
#wskazuje na miejsce gdzie są szablony Django
TEMPLATE_DIRS = (
os.path.join(ROOT_PATH, 'templates')
)


Można zauważyć, że odwołujemy się do adresów URL w pliku urls.py, a oto jego kod:

from django.conf.urls.defaults import *

urlpatterns = patterns('',
(r'^$', 'views.index'),
(r'^sign$', 'views.sign'),
)

Widać w nim 2 adresy internetowe: jeden do wyświetlania formularza do wpisywania danych, a drugi do rejestracji, jeśli ktoś nie jest jeszcze zalogowany. Tak więc ustawiliśmy aplikację Django, teraz trzeba zająć się danymi aplikacji czyli jej modelem oraz widokami w szablonach.

Na początek zajmiemy się danymi, które mają być przechowywane w bazie danych na serwerach Google obsługujących GAE. W praktyce lokalny serwer WWW umieszcza swoje dane w plikach. Tworzymy plik model.py

from google.appengine.ext import db

class Post(db.Model):
author = db.UserProperty()
message = db.StringProperty(multiline=True, required=True)
date = db.DateTimeProperty(auto_now_add=True)

Utworzyliśmy tabelę Post z 3 kolumnami (author, message, date) W tej tabeli będą zapisywane dane z formularza i jak odczytywane poprzednie wpisy. Cala logika aplikacji oparta o obsługę widoków jest trzymana w pliku views.py

import datetime

from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response

from google.appengine.api import users

from models import Post
from forms import GuestbookForm

def index(request):
query = Post.gql('ORDER BY date DESC')
form = GuestbookForm()
return render_to_response('index.html',
{'posts': query.fetch(20),
'form': form
}
)

def sign(request):
form = GuestbookForm(request.POST)
if form.is_valid():
post = Post(message=form.clean_data['message'])
if users.GetCurrentUser():
post.author = users.GetCurrentUser()

post.put()

return HttpResponseRedirect('/')


Trzeba zwrócić uwagę na to, że do obsługi zapytań używamy dialektu GQL, który jest podobny do SQL. Nasz aplikacja ma też odrębna klasę wiążącą formularz z danymi czyli klasę GuestbookForm, której kod jest w pliku forms.py

from django import newforms as forms

class GuestbookForm(forms.Form):
message = forms.CharField(label='Sign the Guestbook', widget=forms.Textarea())


Widzimy jak za pomocą modułu Django newforms tworzy się nowe pole formularza, które zostanie użyte w szablonach.

Teraz utworzymy nowy katalog templates. A w katalogu C:\appengine\djangoguestbook\templates dodajemy plik index.html, z takim kodem:



W ten sposób pozostało nam zrobienie skryptu djangoguestbook.bat w katalogu C:\appengine z takim skryptem:

dev_appserver.py -d djangoguestbook


Wystarczy uruchomić ten plik C:\appengine\djangoguestbook.bat
I zobaczyć jak to działa.

niedziela, 11 maja 2008

Pixel Bender

Szukając informacji o tym jakie technologie będą w Flash Player 10. Pomyślałem że jest coś takiego jak Adobe Image Foundation. W kwietniu jakoś po cichu wprowadzono AIF Toolkit Technology Preview 2. W maju Adobe postanowiło zmienić nazwę technologii na Pixel Bender (bo chyba termin Hydra uknuli z mitologii podobnie jak z Apollo). Wcześniejszej wersji nie mogłem przetestować bo była ograniczona do pewnych kart GPU. Obecna wersja pozwala już przetwarzać na procesorach CPU, co daje już możliwości pokazania jak może wyglądać obsługa efektów mieszania w Flash Player 10. Oczywiście że Flash Player ma już wsparcie ze strony GPU jak oglądamy w trybie pełnoekranowym, ale nie mamy wpływu na to jak to działa. Z technologią Pixel Bender będziemy mieli wpływ na obsługę efektów. Od strony technologicznej wygląda na to że zostanie wykorzystane LLVM obsługujące tą technologię.

Czym jest LLVM? Niskopoziomową biblioteką pozwalającą programistom C i C++ wykorzystywać dobrodziejstwa z takich rzeczy jak JIT czy obsługa kodu źródłowego w "locie" wraz z jego weryfikacją. W praktyce można by powiedzieć że wystarczy napisać kod w takim języku niskopoziomowym (coś w stylu asemblera) to przy pomocy odpowiednich narzędzi wygeneruje kod dla danego języka programowania bądź już skompilowaną bibliotekę którą można użyć w innych projektach. Bardziej przystępnej informacji można się dowiedzieć na tej stronie Dość kusząca staje się wizja w którym kod w C/C++ może zostać skonwertowany do postaci języka niskopoziomowego/bajtkodu a taki kod wystarczy potem załadować do Flash Playera. Na razie Adobe wybrało tą technologię do wykorzystywania CPU w Pixel Bender.
Jak to może wyglądać w Flash Player 10? Kod napisany w Pixel Bender jest przekształcany do postaci bajtkodu ABC dzięki LLVM. Następnie dzięki wsparciu ze strony karty graficznej zostaje bajtkod wykorzystywany do generowania efektów. Może Flash Player zechce bezpośrednio przekształcać kod binarny z LLVM to byłaby rewolucja, ze względu na to że skompilowanie kodu źródłowego w C/C++ Flash Playera czy Adobe AIR sprawiłoby, że otrzymalibyśmy takiego samego Flash Playera czy Adobe AIR prawie na wszystkie platformy. To już spekulacja z mojej strony.

Programowanie w Pixel Bender nie należy do łatwych jeżeli ktoś wcześniej nie programował shaderów. Trzeba wejść na stronę i pobrać AIF Toolkit. Zainstalować. Należy przeczytać wprowadzenie (pokazuje przykład jak robić filtr naświetlania - ekspozycji) i plik specyfikacji języka Hydra 1.0 (w AIF Toolkit wystarczy kliknąć CTRL+H)
Jak komuś się to spodoba to może wejść na stronę Galerii gdzie są prezentowane różne przykłady (większość z nich wymaga przepisania na nowo ze względu na zmiany w kodowaniu)
Bardzo ciekawe są przykłady użycia klasycznego mieszania obrazów (teorię można poczytać w tym miejscu) . Zawsze warto mieć w zakładkach blogi ludzi związanych z Pixel Bender - Kevina Goldsmitha i jak Briana Ronana.
Dla niecierpliwych proponuję zapoznanie się z biblioteką ImageProcessing z tym przykładem w Flex 2.

poniedziałek, 5 maja 2008

Adobe Open Screen Project

Duży nacisk ze strony telefonii mobilnej i jak producentów elektroniki użytkowej sprawił, że Adobe musiało wyjść naprzeciw ich oczekiwaniom. Problem polegał na tym że Adobe wie, że coraz więcej rozwiązań technologicznych powstaje w oparciu o licencje Open Source. Trzymając się restrykcyjnie licencji sprawiło by problemy z potencjalnymi klientami którzy potrzebują "własnych" wersji Flash Playerów czy bibliotek o otwartym kodzie. Doświadczenia z producentami telefonów komórkowych pozwoliło Adobe zmienić model biznesowy. Jak sie dąży do opanowania rynku telefonów komórkowych i wejścia w segment elektroniki użytkowej, a dochody z licencjonowania Flash Playera nie są wysokie, to uwolnienie restrykcji licencyjnych może przynieść większy dochód w postaci większego zapotrzebowania na wsparcie i konsultacje w wdrażaniu Flash Playerów przez producentów elektroniki użytkowej.
Adobe w pewien sposób kontroluje sytuację dostarczając użytkownikom Flash Playera i jak narzędzia dla designerów w postaci Flash CS3. Producenci telefonów komórkowych i elektroniki użytkowej doszli do wniosku, że jeżeli chcą dodawać nowe funkcjonalności do FlashLite czy Flash Playera, bo tego chcą konsumenci to muszą wywrzeć wpływ na Adobe, aby pozwoliło razem tworzyć nowe wersje rozwiązań. Na przykład w Symbianie OS czy w UIQ pojawia sie coraz więcej technologii, które wymagają zintegrowania z FlashLite (na przykład posiadają GPS, akcelatory graficzne, ekran dotykowy, bluetooth).

Myślę, że Adobe doszło do wniosku, że pora rozpocząć głęboką fragmentaryzację technologi Flash, pośrednio dążąc do zastąpienia Java Mobile. Technologia J2ME powstała dość dawno i w pewnym sensie midlety były dobrym rozwiązaniem na słabe możliwości ówczesnych urządzeń. A teraz? Programiści i konsumenci chcą rozwiązań na miarę większych możliwości. Pośrednio w tym interes mają zaawansowani użytkownicy, bo nie muszą uczyć się się zawiłości C++, Java Mobile, im wystarczy ze znają Flasha, podstawy Action Scriptu, coś więcej o HTML, CSS i JavaScript. Dzięki temu mogą pokusić się o tworzenie własnych programików na urządzenia mobilne. Widać to na przykładzie popularności iPhone, wystarczyło żeby programiści i zaawansowani użytkownicy pisali i modyfikowali gierki internetowe do iPhone. Ten sukces chciałoby powtórzyć wiele innych firm i stąd naturalną rzeczą wybór padł na FlashLite i Adobe AIR Mobile.

Gdy patrzę bliżej na to co się działo przez ostatnie lata od połączenia się Adobe z Macromedia dochodzę do wniosku, że Adobe może zmierza do standaryzacji swoich technologii. Obecnie mamy 3 instytucje standaryzujace ISO, ECMA, W3C. Ale problem w tym jak umiejscowić standard formatu SWF. Spójrzmy na to co zrobiło Adobe.
Najpierw widzimy Adobe jako prekusora formatu SVG w W3C i wprowadziło własną implementację standardu SVG w postaci wtyczki Adobe SVG Viewer. Ale ten format nie zyskał popularności w wyniku rosnącej popularności technologii Flash.
Adobe brało udział w powstawaniu formatu CSS2, a takze zaproponowało XMP (Extensible Metadata Platform)jako standardu metadanych dla danych graficznych (a ostatnio dla danych typu video) na bazie standardu RDF.
Natomiast w kolejnej instytucji standaryzujacej ECMA Adobe ma wpływ na
ECMA-262 ECMAScript (standardised JavaScript)
ECMA-357 ECMAScript for XML (E4X)
ECMA-363 Universal 3D file format (jest jako dodatek do Adobe Readera)
Myślę że nie wiele by brakowało do ustalenia formatu SWF jako standardu ECMA ( jako standardu dla multimediów)

A ostatnia instytucja standaryzująca to ISO. W niej Adobe czuje się silne dzięki opublikowaniu standardu PDF. Podsumowując Adobe woli koegzystencję z Microsoftem, dzięki czemu może liczyć na wsparcie ze strony Microsoftu w pracach nad standardami. Jak można zauważyć mamy kilka standardów odnośnie danej technologii (dla wydruku to XPS w ECMA, a PDF w ISO czy dla modeli trójwymiarowych U3D w ECMA i X3D w ISO czy nieformalnie Collada, w formatach dokumentu elektronicznego OOXML w ECMA i ISO oraz OpenDocument w ISO, a także w zakresie multimediów dla internetu (SVG, CSS, SMIL, PNG wg W3C a JPEG, JPEG2000, MPEG-4 w tym GFX, AFX, OpenType, BIFS, AVC/H.264 wg ISO)

Pozostaje tylko czekać jak format SWF stanie się formalnym standardem ( a dokładniej jako format ABC to binarny zapis dla JavaScript 2 i EX4).

Podsumowując można spodziewać sie fragmentaryzacji platformy Flash w tym kierunku konsorcjum takiej jak Java Specification Request. Warto poczytać o Open Screen Project a także Adobe Open Source

czwartek, 17 kwietnia 2008

Benchmarki

Wszedłem sobie na portugalskojęzyczną stronę z wynikami testów jak ktoś próbował pokazać jak wydajny jest Tamarin. Trudno mi było uwierzyć, że ActionScript 3 jest szybszy od Pythona. Sam postanowiłem sprawdzić jak to z tym jest? Faktycznie, jak używamy silnego typowania i buforowania łańcuchów danych to się okazuje że nawet wydajnościowo dorównuje C/C++

Najdziwniejszy okazał się silnik skryptowy w Windows, bo nie mogłem zrozumieć dlaczego IE 7 wywala się. Jak użyłem WHScript to się przeraziłem. Poniższy skrypt został wykonany po 10290 ms.


function test() {
var s = "";
var e = 0.0;
var f = 1.0;
for(var i = 1; i <= 10000; i++) {
s += "teste";
e += 1.0/f;
f *= i;
}
return e
}
start = new Date();
test();
end = new Date();
addTime = end-start;
WScript.Echo(addTime + " ms.");


W IE7 taki test powodował pad przeglądarki. Nie zrażony tym chciałem zobaczyć jak JavaScript 2 w IE 7 wykona taki skrypt:


IE7 z JavaScript2 wykonał to w ciągu 375 ms. Opera wykonała to w 203 ms. a Firefox 2 w ciągu 855 ms.

Zastanawiam się czy może być szybciej? Owszem. Wyobrażcie sobie taką przeglądarkę w której mógłbym napisać taki kod



W tym przypadku dochodzi czas na wewnwtrzna kompilację do bajtkodu w pamięci i jego wykonanie. W ten sposób mielibyśmy jedną najszybszych przeglądarek. Coś jeszcze?
Zamiast kazać przeglądarce dokonywać kompilacji wystarczy samo załadowanie bajtkodu.

Wystarczyłoby dodać taka linijkę kodu w dokumencie HTML


Bajtkod jest wyjątkowo wydajny. Zajmuje bardzo mało miejsca. Kod źródłowy w AS3 zajmował mi 578 bajtów to po kompilacji do bajtkodu tylko 291 bajtów. A to jeszcze można poddać kompresji do formatu SWF.

Coś mi się wydaje że trzeba napisać taką przeglądarkę na bazie silnika Flash Playera i wspieraną przez Adobe AIR niż czekać aż upowszechni się JavaScript2.

W praktyce mamy już takie biblioteki jak AS3 Eval czy swfassist
Z tej pierwszej można by zrobić konwersję kodu do bajtkodu, a z tej drugiej ładowanie bajtkodu z plików SWF do aplikacji. A cała koncepcja tkwi w napisaniu odpowiedniego plugina do htmlwrappera

środa, 16 kwietnia 2008

Japońskie triki

Zawsze podziwiałem Japończyków za ich podejście do technologii. W przyswajaniu nowinek technologicznych wyprzedzają resztę świata. Mają bardzo praktyczne podejście. Co przejawia się w tym że ich blogi są pełne wskazówek tłumaczących działanie bibliotek czy rozwiązywaniem pewnych problemów. Adobe pewnego czasu wypuściło kod źródłowy silnika Tamarin z propozycją aby fundacja Mozilla go rozwijała. Ponieważ postanowiono Tamarina dopiero włączyć do Firefoxa 4 (czyli inaczej musi upłynąć sporo czasu). To tylko Japończyków zainteresował Tamarin na poważnie, że zrobili z tego aplikację do uruchamiania skryptów.
Po prostu poczytali sobie o MMgc, o AVM2
, o kompilacji - zajrzeli do repozytorium kodów źródłowych i skompilowali sobie intepreter powłoki (avmshell) aby móc z linii poleceń wykonywać skrypty w ActionScript 3.
Otóż na razie niewielu interesuje się Tamarinem. Otóż warto sprawdzić sobie czym sobie zasłuzył: szybkością. Otóż jest już wersja alfa dla JavaScript2 do ściągnięcia. Zainstalowałem i napisałem sobie skrypcik


Efekt był taki że miałem dla wykonania tej samej funkcji fib(30) i IE7 z tym powyższym silnikiem osiągnęło 3938 ms co biorąć pod uwagę że ta wersja jeszcze jest niedopracowana to i tak już osiąga całkiem dobry wynik.

Natomiast dla porównania napisałem analogiczny kod w JavaScript

IE7 - 10922 ms
Firefox 2 1875 ms
Opera 3000 ms

Idąc dalej pomyślałem że całkiem będzie inaczej jak wykorzystam avmplus.exe jako silnik wykonawczy dla skryptów pisanych w ActionScript. Potrzebowałem pliku wykonawczego więc znalazłem to u Japończyków. I pomyślałem że warto sprawdzić jak to zadziała

Napisałem kod w ActionScript


function fib(n) {
if(n <= 1)
return 1;
else
return fib(n-1) + fib(n-2);
}
start = new Date();
print("fib 30 = " + fib(30));
end = new Date();
addTime = end-start;
print(addTime + " ms.");

Najpierw skompilowałem to do pliku fib.abc za pomocą asc.jar z Flex SDK

java -jar asc.jar fib.as

a potem uruchomiłem to za pomocą następującego polecenia

avmplus.exe fib.abc

Wynik ? 1109 ms

Poczytałem o tym co można zrobić z tym małym plikiem avmplus.exe Okazało się, że można wiele od Japończyków nauczyć się. Pasjonaci ActionScriptu w Japonii zrobili sobie serwis Spark project w którym dzielą się swoimi kodami. Całkiem niedawno (15 marca) zrobili sobie konferencję poświęconą formatowi SWF o nazwie Shibuya.abc W sieci zamieścili nawet nagrania z tej konferencji.
Warto poczytać o tym co można zrobić z Tamarinem. Postawili na ActionScript 3 jako technologię serwer-side.

Następnie pokazali że warto wymyślać nowe języki programowania które w wyniku kompilacji otrzyma się SWF. Streszczenie można poczytać tu



O optymalizacji Flash Lite można posłuchać i poczytać


O dekompilacji i czytaniu kodu binarnego ActionScriptu z SWF można pooglądać


O tym jak rozwija się Gnash - opensourcowy Flash Player można poczytać i pooglądać.


I chyba najciekawsza prezentacja o tym jak można programować w systemie Windows różne aplikacje w ActionScript 3 za pomocą biblioteki winQuery i awmplus.exe


Szkoda, że tak mało się pisze o tym co wymyślają Japończycy.