Image Bank Funktionen

Übersicht BlitzBasic Codearchiv

Neue Antwort erstellen

 

Krischan

Betreff: Image Bank Funktionen

BeitragDi, Feb 24, 2009 17:13
Antworten mit Zitat
Benutzer-Profile anzeigen
Da ich mich gerade mit Heightmaps beschäftige möchte ich Euch gerne diese schnellen Bankfunktionen vorstellen, die sich mit (Graustufen-)Heightmaps basierend auf Banks beschäftigen. Es ist viel schneller und effektiver, die Heightmaps als Banks zu laden oder als Bank vorgespeicherte Bilder einzulesen wenn man diese später manipulieren möchte, z.B. bilineares resampling für Vergrösserung / Verkleinerung oder Blending mit anderen Heightmaps. Für Realtime-Bildmanipulationen sind diese Funktionen sehr geeignet und ständigem Readpixel/Writepixel vorzuziehen.

Schaut Euch die Demo an, dann seht ihr, was gemeint ist und spielt ein wenig mit dem Scale-Wert herum. Bei Scale=2.0 steht es noch 1:1 zwischen Bank und Image. Aber geht mal höher... Es gibt hier folgende Funktionen (nicht alle werden in der Demo verwendet):

LoadImageToBank(filename$)
Lädt ein "normales" Bild in eine Bank, es wird aber nur der Rot-Kanal verwendet.

LoadBank(filename$,width%,height%)
Lädt ein Bankimage aus einer Datei, es müssen aber Breite/Höhe des Bildes angegeben werden.

SaveBank(bank%,filename$)
Speichert ein Bankimage als Datei.

ScaleBank(bank%,width%,height%,ratio#)
Skaliert ein Bankimage mit der aktuellen Breite/Höhe um den Ratio-Faktor (auch Float möglich) mit einem bilinearem Filter, also nahtlose Ränder möglich.

CreateImageFromBank(bank%,width%,height%)
Erstellt aus der Bank wieder ein zeichenfähiges Bild, z.B. für die Ausgabe. Breite/Höhe müssen hier mit der tatsächlichen Grösse übereinstimmen.

GetBankPixel(bank%,px%,py%,width%)
Holt einen Wert an der Stelle px/py aus der Bank heraus, also quasi ein ReadPixelUltraFast.

SetBankPixel(value%,bank%,px%,py%,width%)
Schreibt einen Wert an die Stelle px/py in der Bank, also quasi ein WritePixelUltraFast.

CopyBankRect(bank%,sx%,sy%,width%,height%,bankwidth%)
Ähnlich dem Copyrect-Befehl. SX/SY bestimmen die Startposition, width/height die Breite/Höhe des Ausschnitts und bankwidth die Breite des Gesamtbildes. Achtung: keine Abfrage, ob der Bereich ausserhalb der Bank ist!

GetR(c%) / GetG(c%) / GetB(c%) / CombineRGB(r%,g%,b%)
Hilfsfunktionen für die Umrechnung von RGB-Farbanteilen für Readpixel(fast)/Writepixel(fast)

Die beiden weiteren Funktionen LinearInterpolate und Bilinear werden von der ScaleBank Funktion verwendet und erlauben lineare/bilineare Interpolation und sind somit universell verwendbar.

Hier noch die Grafik, die für die Demo verwendet wird:
user posted image

Code: [AUSKLAPPEN]
ms=MilliSecs()

hmap$="planet.png"      ; Heightmap
scale#=2.0            ; Skalierungsfaktor
width=128            ; Breite der Heightmap
height=64            ; Höhe der Heightmap
swidth=width*scale      ; Skalierte Breite
sheight=height*scale   ; Skalierte Höhe

Graphics swidth,sheight+height,32,2

; Bild in Bank einlesen
image=LoadImageToBank(hmap$)

; Bild aus Bank erstellen
output1=CreateImageFromBank(image,width,height)

; Skalieren und Bild erstellen
image=ScaleBank(image,width,height,scale)
output2=CreateImageFromBank(image,swidth,sheight)

; Bilder ausgeben
DrawImage output1,0,0
DrawImage output2,0,height

; Zeitmessung
AppTitle "Bank: "+(MilliSecs()-ms)+"ms"

; Tastendruck abwarten
WaitKey

ms=MilliSecs()

; zum Vergleich: Bild normal laden (mit Tformfilter)
image=LoadImage(hmap$)
TFormFilter 1

; Originalbild zeichnen
DrawImage image,0,0

; skaliertes Bild zeichnen
ResizeImage image,swidth,sheight
DrawImage image,0,height

; Zeitmessung
AppTitle "Image: "+(MilliSecs()-ms)+"ms"

; Tastendruck abwarten
WaitKey

End



Function Bilinear#(bank%,width%,height%,x#,y#)
   
   Local x0#=Floor(x)
   Local y0#=Floor(y)
   Local x1#=Floor(x+1)
   Local y1#=Floor(y+1)
   Local dx#=x-x0
   Local dy#=y-y0
   
   Local x02x%=1
   Local x03x%=1
   Local y02x%=1
   Local y03x=1
   
   If x0<0 Then x02x=0
   If x1>width-1 Then x03x=0
   If y0<0 Then y02x=0
   If y1>height-1 Then y03x=0
   
   If x02x=0 Then x0=0
   If y02x=0 Then y0=0
   If x03x=0 Then x1=0
   If y03x=0 Then y1=0
   
   Local r0=GetBankPixel(bank,x0,y0,width)*x02x*y02x
   Local r1=GetBankPixel(bank,x1,y0,width)*x03x*y02x
   Local r2=GetBankPixel(bank,x1,y1,width)*x03x*y03x
   Local r3=GetBankPixel(bank,x0,y1,width)*x02x*y03x
   
   Return LinearInterpolate(LinearInterpolate(r0,r1,dx),LinearInterpolate(r3,r2,dx),dy)
   
End Function

Function LinearInterpolate#(x1#,x2#,mu#=0.5)
   
   Return (x1*(1.0-mu)+x2*mu)
   
End Function

Function GetBankPixel(bank%,px%,py%,width%)
   
   Return PeekByte(bank,(py*width)+px)
   
End Function

Function SetBankPixel(value%,bank%,px%,py%,width%)
   
   PokeByte bank,(py*width)+px,value
   
End Function

Function CopyBankRect(bank%,sx%,sy%,width%,height%,bankwidth%)
   
   Local x%,y%,h%
   Local output%=CreateBank(width*height)
   
   For y=sy To sy+height-1
      
      For x=sx To sx+width-1
         
         h=GetBankPixel(bank,x,y,bankwidth)
         SetBankPixel(h,output,x-sx,y-sy,width)
         
      Next
      
   Next
   
   Return output
   
End Function

Function ScaleBank(bank%,width%,height%,ratio#)
   
   Local x%,y%,c%
   Local ratio2w#=(ratio#*width-1)/(width-1.0)
   Local ratio2h#=(ratio#*height-1)/(height-1.0)
   
   Local newbank%=CreateBank((width*ratio)*(height*ratio))
   
   For y=0 To (height*ratio)-1
      
      For x=0 To (width*ratio)-1
         
         Local u# = x*1.0/ratio2w#
         Local v# = y*1.0/ratio2h#
         
         c%=Bilinear(bank,width,height,u,v)
         
         SetBankPixel(c,newbank,x,y,width*ratio)
         
      Next
      
   Next
   
   Return newbank
   
End Function

Function LoadBank(filename$,width%,height%)
   
   Local f%=OpenFile(filename$)
   
   Local bank%=CreateBank(width*height)
   
   ReadBytes(bank,f,0,BankSize(bank))
   
   CloseFile f
   
   Return bank
   
End Function

Function SaveBank(bank%,filename$)
   
   Local f%=WriteFile(filename$)
   
   WriteBytes (bank,f,0,BankSize(bank))
   
   CloseFile f
   
End Function

Function LoadImageToBank(filename$)
   
   Local x%,y%,rgb%,h%
   Local image%=LoadImage(filename$)
   Local width%=ImageWidth(image)
   Local height%=ImageHeight(image)
   Local bank%=CreateBank(width*height)
   Local buffer%=ImageBuffer(image)
   
   LockBuffer buffer
   
   For x=0 To width-1
      
      For y=0 To height-1
         
         h=GetR(ReadPixelFast(x,y,buffer))
         
         SetBankPixel(h,bank,x,y,width)
         
      Next
      
   Next
   
   UnlockBuffer buffer
   
   FreeImage image
   
   Return bank
   
End Function

Function GetR(c%)
   
   Local r=(c And $ff0000)/$10000: Return r
   
End Function

Function GetG(c%)
   
   Local g=(c And $ff00)/$100: Return g
   
End Function

Function GetB(c%)
   
   Local b=c And $ff: Return b
   
End Function

Function CombineRGB(r%,g%,b%)
   
   Return r*$10000+g*$100+b
   
End Function

Function CreateImageFromBank(bank%,width%,height%)
   
   Local x%,y%,h%,r%,g%,b%
   
   Local image%=CreateImage(width,height)
   
   Local buffer%=ImageBuffer(image)
   
   LockBuffer buffer
   
   For x=0 To width-1
      
      For y=0 To height-1
         
         h=GetBankPixel(bank,x,y,width)
         
         WritePixelFast x,y,CombineRGB(h,h,h),buffer
         
      Next
      
   Next
   
   UnlockBuffer buffer
   
   Return image
   
End Function


Und für die, die sich fragen, wozu man das praktisch verwenden kann: im Moment versuche ich, einen Planeten mit 1024x512 Pixeln mittels dieser Funktionen, einer Fraktalmap und einer Shadowmap in eine hochauflösende 8192x4096 Colormap zu verwandeln, so dass aber die Grundstruktur (Kontinente, Berge, Wasser) erhalten bleibt. Dazu schneide ich 64x64 grosse Blöcke aus der Heightmap heraus, skaliere diese auf 512x512, blende sie mit einer fraktalen Heightmap und erzeuge daraus eine Colormap, die mit einer vorberechneten Shadowmap gemischt wird. Und der ganze Vorgang dauert nur etwa 200ms. Die Blöcke blenden übrigens nahtlos ineinander über, danke dem bilinearem resampling.

Hier das Ergebnis: oben links das Ausgangsbild aus zwei nebeneinanderliegenden 64x64er Blöcken und der Rest wurde komplett nur daraus berechnet:
user posted image

Chrise

BeitragDi, Feb 24, 2009 19:47
Antworten mit Zitat
Benutzer-Profile anzeigen
flottes Teil und das Ergebnis kann sich sehen lassen Smile

Schade nur, dass das Bild skaliert wird, wenn es zu groß ist.
 

Krischan

BeitragDi, Feb 24, 2009 22:43
Antworten mit Zitat
Benutzer-Profile anzeigen
Chrise hat Folgendes geschrieben:
Schade nur, dass das Bild skaliert wird, wenn es zu groß ist.


?!? Verstehe ich nicht, was meinst Du damit?

Neue Antwort erstellen


Übersicht BlitzBasic Codearchiv

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group