ImageBuffer

Übersicht BlitzMax, BlitzMax NG Allgemein

Neue Antwort erstellen

Mathias-Kwiatkowski

Betreff: ImageBuffer

BeitragSo, Dez 01, 2013 8:24
Antworten mit Zitat
Benutzer-Profile anzeigen
ImageBuffer gibt es ja nicht mehr. was ich aus dem topic (link unten) entnommen habe. mein problem ist.

GrabImage = viel zu lahm
GrabPixmap = um die hälfte schneller als GrabImage damit immernoch viel zu langsamm.

was muss ich also genau machen um mehrere bilder als eines zu verknüpfen und dies dann direckt irgendwie in einem image zu bekommen?

z.b. habe ich in der vergangenheit ne gui geschrieben die extrem langsam war aufgrund 1 fenster (window) hatte 9 bilder, bei 10 fenstern habe ich 90 bilder die ich male, dann kommt noch drawtext was das ganze unschön herunterzieht.

gott segne weitere gadegts. all die probleme würden nicht sein wenn ich etwas in einem image direckt malen könnte ohne 500ms delay von grabimage abzuwarten (naja 500ms bei grossen fenstern, was aber möglich ist)

gibt es ein modul dazu? oder vergleichbare tutorials ect, andere hilfestellungen? wie machen es die guten profies hier von euch? Very Happy ich wüsste keine lösung und im internet habe ich bislang nichts gefunden was genau mein problem anspricht. am besten wäre wirklich der imagebuffer...

Eine kleine anmerkung habe ich noch
ich habe das gefunden, was genau mein problem beschreibt. war mir aber nicht sicher ob ich dieses alte topic verwenden darf und es somit aus der vergessenheit hole. aus diesem grund habe ich dieses eröffnet. falls es falsch gewesen ist bitte ich um die verschiebung in dem alten topic. (welches nicht von mir war)
leider ist dort aber auch keine lösung für mein problem enthalten.
https://www.blitzforum.de/foru...07608993fc


ich bedanke mich im vorraus, und hoffe das es eine gute möglichkeit gibt.
Skype: Anarchie1984
http://projektworks.de/maxbase/
Icq - Erneuert am 21.08.2017
Yahoo - Erneuert am 21.08.2017

BtbN

BeitragSo, Dez 01, 2013 8:45
Antworten mit Zitat
Benutzer-Profile anzeigen
Wenn du kein Problem damit hast, dass das nur mit OpenGL funktioniert, render einfach das Fenster in ein FBO, und nutz die resultierende textur wenn du das Fenster zeichnen willst.

Mathias-Kwiatkowski

BeitragSo, Dez 01, 2013 8:58
Antworten mit Zitat
Benutzer-Profile anzeigen
oha von open gl habe ich 0 ahnung und das was du geschrieben hast ehrlichgesagt habe ich es nicht verstanden , was also ist ein FBO und wie kann ich das ganze benutzen?
Skype: Anarchie1984
http://projektworks.de/maxbase/
Icq - Erneuert am 21.08.2017
Yahoo - Erneuert am 21.08.2017

BtbN

BeitragSo, Dez 01, 2013 8:59
Antworten mit Zitat
Benutzer-Profile anzeigen
Ein offscreen-rendertarget, was man mit einer Textur verknüpfen kann.
Du renderst dann da rein dein Fenster, und anschließend nurnoch die Textur, wenn du das fenster zeichnen willst.
Das ganze ist absolut echtzeitfähig, auch mit mehreren dutzend fenstern.

Midimaster

BeitragSo, Dez 01, 2013 9:03
Antworten mit Zitat
Benutzer-Profile anzeigen
ich kann mir gar nicht vorstellen, dass das Zeichnen der 9 Bilder pro Gadget die Performance herunterzieht. Windows macht das ja ähnlich und zerlegt seine Elemente in noch viel kleinere Teile. So wird z.B. immer nur das gezeichnet, was nicht von darüberliegenden Elementen sowoeso wieder verdeckt werden wird. Das Stichwort lautet hier RECTANGLE Struktur. Die setzen voll auf sowas....

Zudem sind ja 8 deiner 9 Elemente immer sehr klein. Hast Du schon mal geprüft, welcher Teil der Zeit für das mittlere Feld draugeht?


Dritter Tipp: Die 9 Elemente können mit DrawSubImageRectangle aus einer gemeinsamen Vorlage gezeichnet werden. dies bringt unter Monkey Zeitverbesserungen. Möglicherweise ja auch unter BlitzMax?
Gewinner des BCC #53 mit "Gitarrist vs Fussballer" http://www.midimaster.de/downl...ssball.exe

BtbN

BeitragSo, Dez 01, 2013 9:17
Antworten mit Zitat
Benutzer-Profile anzeigen
Windows erzeugt aber seine Fenster nicht durch das rendern von zig bildern, sondern wesentlich effizienter und mit für 2D Grafik optimierten funktionen.
Das Compositing von Windows Vista und höher wird außerdem vermutlich genau das selbe tun, und die Fenster in eine Textur rendern.

Mathias-Kwiatkowski

BeitragSo, Dez 01, 2013 9:23
Antworten mit Zitat
Benutzer-Profile anzeigen
naja 9 bilder zieht nichts runter es macht die masse...

9 bilder= 1 fenster
titel des fensters "Mathias" = weitere 7 bilder
nun nehmen wir an wir haben slider rechts und slider unten mit nem rahmen rahmen=1 bild slider 1 bild zusammen 4 bei 2 slidern
so jetzt haben wir nen button drin 1 bild button text "Hallo Welt" = 10 bilder

ich möchte nun gar nicht mit flexibelen textgadgets anfangen oder aren.

somit hätten wir 30 bilder für 1 fenster (1 FENSTER) bei mehrern fenstern bleibt da kaum noch platz für das darunterliegene spiel.
und wie gesagt textbasierte felder ausgelassen.

windows macht es im übrigen nicht so das fenster wird scaliert gerandert und ebenso als bild hinterlegt und wenn das fenster inaktiv ist wird es gar mit dem desktop hintergrund icons ect verschmelst, das bringt demnach mehr preformence als ob man ein _"teil gerüst" bastellt

so denke ich zumindest wird es miki soft machen. wenn man ein alten rechner mit wenig speicher zu hand hat ( mein netbook ) kann man dies auch fast deutlich sehen 1gb ram xD win 7 starter is darauf. und für das ding ist windows schon ein spiel. dort aber wie gesagt kann man es sehen das wenn ein fenster aktiv ist die cpu richtig gefressen wird.

was Btbn gesagt hat klingt gut nur gibt es dazu ein tutorial wie ich es auch umsetzen kann?
Skype: Anarchie1984
http://projektworks.de/maxbase/
Icq - Erneuert am 21.08.2017
Yahoo - Erneuert am 21.08.2017

BtbN

BeitragSo, Dez 01, 2013 9:25
Antworten mit Zitat
Benutzer-Profile anzeigen
Ist im OpenGL wiki mit einigen Beispielen dokumentiert.
http://www.opengl.org/wiki/Framebuffer_Object
 

PhillipK

BeitragSo, Dez 01, 2013 11:31
Antworten mit Zitat
Benutzer-Profile anzeigen
Und ich verweise gerne auf die Delphi-GL wiki seite, da die tutorials einfach super sind *find*

http://wiki.delphigl.com/index...fferobject

Ich für meinen teil habe sogut wie alles, was ich über OpenGL weiß, von dieser seite. Allerdings musst du die beispielcodes entsprechend interpretieren; die funktionsnamen sind aber 1zu1 auch in Blitzmax vorhanden.

DAK

BeitragSo, Dez 01, 2013 11:34
Antworten mit Zitat
Benutzer-Profile anzeigen
Bei einer GUI könnte es helfen, Flip nicht so oft aufzurufen / nicht jedes Frame was zu zeichnen, sondern nur, wenn es notwendig ist. Nimm mal z.B. Fraps und lege es über ein beliebiges GUI-Programm, z.B. Chrome und schau, wie es mit den FPS ausschaut. Bei mir hat Chrome üblicherweise 1 FPS außer wenn sich was am Bild ändert, dann hat es 30+ FPS. Wenn sich nichts ändert, braucht man auch nichts neu zeichnen.

Und wenn sich ein kleiner Teil des Bildschirms ändert aber nicht alles, wie es z.B. der Fall ist, wenn nur ein Fenster sich ändert, dann mal ein schwarzes Rect über das Fenster (statt einem CLS) und mal nur das Fenster neu.

Die Android-GUI z.B. hat eine View-Hirachie. Du hast da als oberstes Element das ganze Fenster in dem alle sichtbaren Elemente als Kinder (oder Kinder von Kindern) angehängt sind. Wenn ein Element ein Bildupdate braucht (z.B. wenn sich der Text eines TextViews oder so ändert), dann wird für den invalidate() aufgerufen, was dafür sorgt, dass dieses Element neu gezeichnet wird. Dieses invalidate() wird dann an alle Kindelemente weitergegeben, und auch die werden dann neugezeichnet. Auf diese Weise wird immer alles was notwendig ist neugezeichnet und alles was kein Update gehabt hat, bleibt einfach unverändert am Bildschirm.

Dafür brauchst du gar kein render to texture sondern musst nur auf den Framebuffer rendern. Musst hald schauen, dass du immer beide Buffer (Front- und Backbuffer) gemeinsam updatest.

Gleichzeitig ist es auch wesentlich schneller als per render to texture. Bei render to texture renderst du jedes Teilbild (=Fenster) bei Veränderungen in eine Grafik. Ab dann musst du jedes Frame jedes Bild neu zeichnen. Ist zwar schneller als wenn du jedes Frame alles von neu zeichnest, aber du musst trotzdem jedes Frame was machen.
Diese Methode hat also hohe Kosten bei Änderungen (um auf die Textur zu rendern) und mittlere Kosten zwischen den Änderungen.

Verwendest du den Inhalt des Framebuffers, dann brauchst du nur bei Änderungen einmal was zeichnen und ohne Änderungen brauchst du gar nichts zeichnen (eventuell wäre es trotzdem nicht schlecht, ein mal pro Sekunde oder so alles neu zu zeichnen um eventuelle Bildfehler auszubügeln).
Du hast also hohe Kosten bei Änderungen (ähnlich wie bei render to texture, nur dass du nicht auf die Textur sondern den Framebuffer renderst) und quasi keine Kosten zwischen den Änderungen.

Musst nur aufpassen: Wenn sich zwei Fenster überlappen und eines verschoben wird, musst du beide neuzeichnen.
Gewinner der 6. und der 68. BlitzCodeCompo
  • Zuletzt bearbeitet von DAK am So, Dez 01, 2013 11:40, insgesamt 2-mal bearbeitet

BtbN

BeitragSo, Dez 01, 2013 11:35
Antworten mit Zitat
Benutzer-Profile anzeigen
Da es keinen bezeichnebaren frontbuffer gibt, ist das nicht möglich und wäre auch enorm instabil und aufwändig im vergleich zu einer simplen Render-To-Texture lösung.

DAK

BeitragSo, Dez 01, 2013 11:44
Antworten mit Zitat
Benutzer-Profile anzeigen
Natürlich kannst du nicht direkt auf den FB zeichnen, aber folgendes geht:

FB (altes Bild)
BB (egal was)

Render -> BB
flip()
Render -> BB (der ja der FB war)

Das war, was ich gemeint habe, mit beide Buffer berendern.

Der zweite Render auf den BB ist notwendig, damit man das Ganze beim nächsten Mal wiederholen kann.

Das ist genau das System, was die Android GUI verwendet. Kann man sich bei Android 4+ sogar anzeigen lassen, welche Bildteile gerade aktualisiert werden (Einstellungen -> Entwickleroptionen -> Bildschirmaktualisierungen anzeigen).

Ist mies für Spiele (wegen den höher als normalen Kosten für Änderungen) aber für GUIs ist das wie man es machen sollte. Die Render-to-Texture hat dabei aber die gleichen Nachteile für Spiele.
Gewinner der 6. und der 68. BlitzCodeCompo

BtbN

BeitragSo, Dez 01, 2013 13:10
Antworten mit Zitat
Benutzer-Profile anzeigen
Es ist in keiner weise garantiert dass man nach einem Flip den alten Frontbuffer wieder vor sich hat.
Und den kram schonmal vorrendern und dann pausieren ist auch absolut keine option, weil dann das ganze Programm nicht mehr flüssig auf eingaben reagieren kann, und das ganze außerdem für Ingame-GUIs mehr als suboptimal wäre.

Generell wäre das ganze unterfangen völlig sinnlos kompliziert.
Einfach das Fenster in eine Textur rendern und gut ist.

DAK

BeitragSo, Dez 01, 2013 21:12
Antworten mit Zitat
Benutzer-Profile anzeigen
Aus der Dokumentation:
Zitat:
Flip swap the front and back buffers of the current graphics objects.

Also doch, man kriegt den Frontbuffer zurück. Wie das ganze üblicherweise gemacht wird, ist dass nicht tatsächlich der Inhalt des Buffers rübergeschrieben wird, sondern nur die Referenz auf den Buffer geändert wird. Man hat dann hald einen Speicherbereich A und einen B. Vor dem Flip ist FB = A, BB = B, nach dem Flip ist FB = B, BB = A. Der Inhalt wird dabei nicht verändert.

Für ein Spiel, dessen veränderliche Objekte in der GUI liegen (z.B. Animationen usw. die in der GUI in einem Fenster ablaufen) ist dieses System perfekt. Liegt die GUI über veränderbaren Objekten, dann geht es nicht. Muss man hald richtig planen.

Wo hast du die Idee mit dem Pausieren her? Davon habe ich nichts geschrieben. Was ich gemeint habe ist, dass du einfach nicht neuzeichnest, nicht dass du irgendwas pausierst. Deine Hauptschleife rennt weiter, nur wenn es keinen Input gibt und sonst auch keine Änderungen, dann zeichnest du einfach nicht.

@Instabil und kompliziert:
Ich hab grad in rund einer halben Stunde eine kleine Demo geschrieben. Die ist noch nicht wirklich optimiert (Block-invalidation funktioniert noch nicht richtig), rennt aber auch schon so sauschnell. Bei 500 Fenstern mit je 5 klickbaren Buttons braucht es im Leerlauf 1% CPU bei mir. Das klicken der Knöpfe kostet gar nichts an Leistung. Einzig das Verschieben eines Fensters schiebt die CPU auf 7% hoch, da da noch alle Fenster neu gezeichnet werden.
Wenn ich die Blockinvalidation noch richtig hinkrieg, dann würde es auch beim Verschieben von Fenstern nur noch die Fenster, die überschoben werden, neuzeichnen.

Hat man eine sehr hierarchische GUI (was ja hier nicht der Fall ist, da mein GUI-Baum nur drei Ebenen tief ist (Desktop-Fenster-Buttons), die eher in die Tiefe geht und weniger in die Breite, dann ist auch der momentane Ansatz schon extrem schnell.

BlitzMax: [AUSKLAPPEN]
SuperStrict

Framework BRL.GLMax2D
Import BRL.LinkedList
Import BRL.StandardIO
Import BRL.Timer

Graphics(800, 600)

Type TDesktop Extends TGUIElem
Function Create:TDesktop(width:Int, height:Int)
Local out:TDesktop = New TDesktop
out.width = width
out.height = height
Return out
End Function

Method draw(x:Int,y:Int,force:Int)
If (force Or (isValid>0))
SetColor(0,32,32)
DrawRect(x,y,width,height)
EndIf
Super.draw(x,y,force)
End Method
End Type

Type TWindow Extends TGUIElem
Field locked:Int=False
Field lmx:Int,lmy:Int
Function Create:TWindow(px:Int,py:Int,width:Int, height:Int)
Local out:TWindow = New TWindow
out.x = px
out.y = py
out.width = width
out.height = height
Return out
End Function

Method draw(px:Int,py:Int,force:Int)
If (force Or (isValid>0))
SetColor(64,64,64)
DrawRect(px,py,width,height)
EndIf
Super.draw(px,py,force)
End Method

Method update(px:Int,py:Int)
Super.update(px,py)
If (MD1=True)
If (locked=True)
MD1 = False
MH1 = False
If (lmx<>MouseX() And lmy<>MouseY())
Local oldx:Int = x
Local oldy:Int = y
x = x + MouseX()-lmx
y = y + MouseY()-lmy
lmx = MouseX()
lmy = MouseY()
Local minx:Int = Min(oldx,x)
Local miny:Int = Min(oldy,y)
Local maxx:Int = Max(oldx,x)+width
Local maxy:Int = Max(oldy,y)+height
parent.postInvalidateBlock(minx,miny,maxx-minx,maxy-miny)
EndIf
ElseIf (MouseX()>px And MouseX()<px+width And MouseY()>py And MouseY()<py+height)
MD1 = False
MH1 = False
locked = True
lmx = MouseX()
lmy = MouseY()
EndIf
Else
locked = False
EndIf
End Method
End Type

Type TButton Extends TGUIElem
Field text:String, num:Int
Function Create:TButton(px:Int,py:Int,width:Int, height:Int, text:String)
Local out:TButton = New TButton
out.x = px
out.y = py
out.width = width
out.height = height
out.text = text
Return out
End Function

Method draw(x:Int,y:Int,force:Int)
If (force Or (isValid>0))
SetColor(128,128,128)
DrawRect(x,y,width,height)
SetColor(0,0,0)
DrawText(text,x+2,y+2)
EndIf
Super.draw(x,y,force)
End Method

Method update(x:Int,y:Int)
Super.update(x,y)
If (MH1=True)
If (MouseX()>x And MouseX()<x+width And MouseY()>y And MouseY()<y+height)
MH1 = False
MD1 = False
num = num+1
text = "Test"+num
parent.postInvalidate()
EndIf
EndIf
End Method
End Type

Type TGUIElem
Global invalidList:TList = CreateList()
Global MH1:Int
Global MH2:Int
Global MH3:Int
Global MD1:Int
Global MD2:Int
Global MD3:Int
Field parent:TGUIElem
Field x:Int,y:Int,width:Int,height:Int
Field children:TList = CreateList()
Field isValid:Int = 2

Field id:Int

Function updateMouse()
MH1 = MouseHit(1)
MH2 = MouseHit(1)
MH3 = MouseHit(1)
MD1 = MouseDown(1)
MD2 = MouseDown(2)
MD3 = MouseDown(3)
End Function

Method postInvalidate()
invalidList.addLast(Self)
invalidate()
End Method

Method invalidate()
isValid = 2;
For Local child:TGUIElem = EachIn children
child.invalidate()
Next
End Method

Method postInvalidateBlock(bx:Int,by:Int,bw:Int,bh:Int)
invalidList.addLast(Self)
Print(bx+" "+by+" "+bw+" "+bh)
invalidateBlock(bx,by,bw,bh)
End Method

Method invalidateBlock(bx:Int,by:Int,bw:Int,bh:Int)
isValid = 2;
For Local child:TGUIElem = EachIn children
If (child.x<=bx+bw Or child.x+child.width>=bx)
If (child.y<=by+bh Or child.y+child.height>=by)
child.invalidate()
Print("invalidated "+id)
EndIf
EndIf
Next
End Method

Method draw(x:Int,y:Int,force:Int)
For Local child:TGUIElem = EachIn children
child.draw(x+child.x,y+child.y,force)
Next
isValid = isValid-1
End Method

Method update(x:Int,y:Int)
For Local child:TGUIElem = EachIn children
child.update(x+child.x,y+child.y)
Next
End Method

Method addChild(child:TGUIElem)
children.addLast(child)
child.parent = Self
End Method
End Type

Local desk:TDesktop = TDesktop.Create(800,600)
desk.id = -1
For Local i:Int = 0 To 500
Local window:TWindow = TWindow.Create((10+i*5) Mod 700, (10+i*5) Mod 540,60,100)
desk.addChild(window)
window.id = i
For Local j:Int = 0 To 4
Local button:TButton = TButton.Create(5,2+j*20,50,16,"Test")
window.addChild(button)
Next
Next

Local timer:TTimer = CreateTimer(60)

While (Not KeyHit(KEY_ESCAPE))
TGUIElem.updateMouse()
desk.draw(0,0,False)
Flip
desk.draw(0,0,False)
desk.update(0,0)
WaitTimer(timer)
Wend
Gewinner der 6. und der 68. BlitzCodeCompo

BtbN

BeitragMo, Dez 02, 2013 2:37
Antworten mit Zitat
Benutzer-Profile anzeigen
Nein, was im Backbuffer nach dem SwapBuffers ist, ist völlig undefiniert.
Es hängt vom verwendeten OpenGL system(GLX, WGL, ...) und Treiber ab, und ist absolut nichts, wodrauf man sich in irgend einer Weise verlassen kann.

Und was willst du eigentlich beweisen? Dass eine enorm komplexe und anfällige Lösung, wenn richtig implementiert, eventuell funktionieren kann?
Ist ja schön und gut, das ganze in eine Textur zu rendern wären ein paar Zeilen, und würde keinerlei Probleme verursachen, und ist vorallem wunderbar für Ingame-GUIs geeignet, wo man nicht mal eben aufhören kann zu rendern.

count-doku

BeitragMo, Dez 02, 2013 9:45
Antworten mit Zitat
Benutzer-Profile anzeigen
Moin,

Um mal von der (unnötigen) Diskussion zu Mathias Problem zurückzukommen, ich löse das ganze mit klepto's Render2Texture. Das verwendet die Framebuffer Objekts von OpenGL, welche oben schonmal angesprochen wurden.
https://www.blitzforum.de/foru...hlight=fbo
Unten in seinem Code ist auch ein Beispiel drinne,
im Grunde musst du einfach nur:
BlitzMax: [AUSKLAPPEN]


Global Img:TImage = CreateImage(512, 512) ' Bild und entsprechendes FBO erstellen
MidHandleImage Img
Local IB:TImageBuffer = TImageBuffer.SetBuffer(Img)

IB.BindBuffer() ' Lenkt die Ausgabe auf das vorher geladene FBO umleiten
IB.Cls(1.0,,,0.4) ' Alpha, Rot, Grün, Blau ; Normale Zeichenbefehle folgen
SetColor 255,255,255
DrawLine 0,0,512,512
DrawLine 0,512,512,0
IB.UnBindBuffer() ' Ausgabe zurück auf den Backbuffer.
SetViewport 0,0,800,600


Das Bild kann dann später mit dem normalen DrawImage gezeichnet werden.
Wichtig ist nur, dass du OpenGL Graphicsdriver lädst und glewInit() aufrufst. Steht aber alles im Beispielcode drinne.

Ist dann wie der ImageBuffer in BB Very Happy

lg,
Count-Doku

Mathias-Kwiatkowski

BeitragDo, Dez 12, 2013 18:02
Antworten mit Zitat
Benutzer-Profile anzeigen
das ist eine sehr gute variante, ich versuch seit dieser zeit als du es gepostet hast anzuwenden, aber eines hindert mich.

mach dir ein punkt im image (links oben also plot 0,0,) und dieser punkt ist dann nicht mehr oben links sondern im image unten rechts, warum is das so und wie geht es anders? so das es nicht mehr spiegelverkehrt ist?
Skype: Anarchie1984
http://projektworks.de/maxbase/
Icq - Erneuert am 21.08.2017
Yahoo - Erneuert am 21.08.2017

count-doku

BeitragDo, Dez 12, 2013 22:58
Antworten mit Zitat
Benutzer-Profile anzeigen
Oh ja sry, habe ich vergessen zu erwähnen. Es ist spiegelverkehrt xD

Einfach entweder schon beim Render2Texture oder später beim Zeichnen ein SetScale 1,-1 oder so verwenden. Je nachdem um welche Achse es gespiegelt ist.

Ein Codebeispiel, hier wird beim Anzeigen gespiegelt:
BlitzMax: [AUSKLAPPEN]
TImageBuffer.Init(800, 600) 'Same as Graphics but set to GLDriver + glewinit 

Global Img:TImage = CreateImage(512, 512)

Local IB:TImageBuffer = TImageBuffer.SetBuffer(Img)

IB.BindBuffer()

IB.Cls(0, 0, 0, 1)

SetColor 255, 255, 255
DrawRect(1, 1, 10, 10)

IB.UnBindBuffer()
SetViewport 0, 0, 800, 600

SetClsColor 127, 127, 127

While Not (KeyHit(KEY_ESCAPE) Or AppTerminate())
Cls

SetScale 1, -1
DrawImageRect Img, 0, 512, 512, 512
SetScale 1, 1

Flip
Wend


Du könntest auch das gespiegelte Bild wieder in ein neues Rendern, das wird dann aber glaube ich, zu rechenaufwendig.
Aber einfach immer das SetScale sollte nicht den Unterschied machen, zumal du es ja sowieso bei so ziemlich allen R2T Objekten brauchen wirst Smile



lg,
Count-Doku

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Allgemein

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group