Interpolation per Palette (Grafikspielerei)
Verfasst: 10.07.2007 22:32
Basierend auf diesen Thread habe ich r7mk4 einen Interpolationsalghorythmus entworfen, der auf "Palettenbasis" arbeitet. Der Funktion kann also ein Array mit indexierten Farben übergeben werden und es interpretiert den Index als interpolierbaren Wert (beginnend ab 0).
Das ganze läuft mit über 60 Frames auf 640x480-Bildschirm auf beim 3.0 Ghz Pentium 4 flüsig. Ab 800x600 gehts in Richung 32 Frames, dennoch bietet sich auch die "Autostretch-Variante" an, mit der ein WindowedScreen verdoppelt wird und dadurch der fehlende Detailgrad ausgeglichen wird.
Schauts euch einfach mal an.
Hab oben einen Frame-Begrenzer eingebaut, damit der Effekt auch sichtbar wird
...
Läuft nur auf 8bit, 24bit u. 32bit (war zu faul 16 bit auch noch zu zerhacken...)
8bit verwendet direkt die Screen-Palette.
Weitere Optimierungen wären z.B. nicht direkt auf den Screen, sondern auf ein Sprite im Hauptspeicher zu schreiben...
Btw: Spielt mal mit den Werten u. Einstellungen rum. Da ergeben sich teilweise wunderhübsche Animationen
...
Das ganze läuft mit über 60 Frames auf 640x480-Bildschirm auf beim 3.0 Ghz Pentium 4 flüsig. Ab 800x600 gehts in Richung 32 Frames, dennoch bietet sich auch die "Autostretch-Variante" an, mit der ein WindowedScreen verdoppelt wird und dadurch der fehlende Detailgrad ausgeglichen wird.
Schauts euch einfach mal an.
Hab oben einen Frame-Begrenzer eingebaut, damit der Effekt auch sichtbar wird

Läuft nur auf 8bit, 24bit u. 32bit (war zu faul 16 bit auch noch zu zerhacken...)
8bit verwendet direkt die Screen-Palette.
Code: Alles auswählen
; Interpolation auf Palettenbasis
; (c) Max 'Kekskiller' Beutling
#IMAGE_SIZE_DIVIDER = 1 ;weder teiler für die bildhöhe -> je höher desto schneller, aber auch desto geblurrter...
#MAX_FRAMES = 17 ;anzahl der maximalen frames
;------------------------------------------------------------------------------------------------------------------------------
; fenster öffnen...
InitSprite()
OpenWindow(0, 0,0, 640,480, "interpolation test", #PB_Window_ScreenCentered|#PB_Window_TitleBar|#PB_Window_SystemMenu)
OpenWindowedScreen(WindowID(0), 0,0, WindowWidth(0)/#IMAGE_SIZE_DIVIDER,WindowHeight(0)/#IMAGE_SIZE_DIVIDER, 1,0,0)
AddKeyboardShortcut(0, #PB_Shortcut_F1, 0)
AddKeyboardShortcut(0, #PB_Shortcut_F2, 1)
; enstellungen...
Global field_w.w, field_h.w, block_width.w, block_height.w
field_w = 64
field_h = 48
block_width = WindowWidth(0) / #IMAGE_SIZE_DIVIDER / field_w
block_height = WindowHeight(0) / #IMAGE_SIZE_DIVIDER / field_h
; vorbereiten...
Global Dim field.l(field_w-1,field_h-1)
; einzelne farben -> unabhängig von der anzahl der farben hat die funktion weiterhin konstante geschwindigkeit. so kann man theoretisch ganze
;farbkombinationen vorberechnen, um sie später schneller einzuzeichnen!
Global gradient_color_count.c, gradient_presets.c
gradient_presets = 4
Global Dim gradient_color_count(gradient_presets)
Global Dim gradient_colors.l(gradient_presets-1, 20) ;temporär mal zwanzig, is ja nur nen test...
; nette farben!
gradient_color_count(0) = 9
gradient_colors(0, 0) = $00ff00
gradient_colors(0, 1) = $00ffab
gradient_colors(0, 2) = $00ffff
gradient_colors(0, 3) = $00abff
gradient_colors(0, 4) = $0000ff
gradient_colors(0, 5) = $ab00ff
gradient_colors(0, 6) = $ff00ff
gradient_colors(0, 7) = $ff00ab
gradient_colors(0, 8) = $ff0000
; wetterkarte (oder so in diese richtung!)
gradient_color_count(1) = 5
gradient_colors(1, 0) = $00ff00
gradient_colors(1, 1) = $00ffab
gradient_colors(1, 2) = $00ffff
gradient_colors(1, 3) = $00abff
gradient_colors(1, 4) = $0000ff
; plasmastream
gradient_color_count(2) = 7
gradient_colors(2, 0) = $ff0000
gradient_colors(2, 1) = $ff5500
gradient_colors(2, 2) = $ffaa00
gradient_colors(2, 3) = $ffee00
gradient_colors(2, 4) = $ffff00
gradient_colors(2, 5) = $ffabab
gradient_colors(2, 6) = $ff00ff
; schwarz, grau, weiß
gradient_color_count(3) = 3
gradient_colors(3, 0) = $000000
gradient_colors(3, 1) = $898989
gradient_colors(3, 2) = $ffffff
Global current_preset.c
current_preset = 0
;farbveränderungsmodi
Global current_mode.c, modes.c
current_mode = 1
modes = 3
;------------------------------------------------------------------------------------------------------------------------------
; strukturen
Structure CHAR
c.c
EndStructure
Structure RGB
r.c
g.c
b.c
EndStructure
Structure RGBA
r.c
g.c
b.c
a.c
EndStructure
Structure interpol_pixel
StructureUnion
c.CHAR
rgb.RGB
rgba.RGBA
EndStructureUnion
EndStructure
; adressen/größen u. procs fürs rendern
Global *_interpol_buffer.l
Global _interpol_pitch.l
Global _interpol_bytes.c
Global _interpol_format.c
Procedure GetInterpolationBuffer() ; <- sollte vor jeder renderschleife aufgerufen werden, am besten direkt nach jedem startdrawing..
*_interpol_buffer = DrawingBuffer()
_interpol_pitch = DrawingBufferPitch()
_interpol_format = DrawingBufferPixelFormat()
Select _interpol_format
Case #PB_PixelFormat_8Bits : _interpol_bytes = 1
Case #PB_PixelFormat_15Bits : _interpol_bytes = 2
Case #PB_PixelFormat_16Bits : _interpol_bytes = 2
Case #PB_PixelFormat_24Bits_RGB : _interpol_bytes = 3
Case #PB_PixelFormat_24Bits_BGR : _interpol_bytes = 3
Case #PB_PixelFormat_32Bits_RGB : _interpol_bytes = 4
Case #PB_PixelFormat_32Bits_BGR : _interpol_bytes = 4
EndSelect
EndProcedure
; ~~ Die Prozedur ~~
; Parameter:
; x.w = x-position
; y.w = y-position
; w.w = breite
; h.w = höhe
; *colors.RGBA = pointer auf array mit farbwerten, kann gan popeliges array mit long-werten sein
; hauptsache die index-nummern der farben gehen nicht über die anzahl der element hinaus!
; col1.c = index der oberen, linken farbe
; col2.c = index der oberen, rechten farbe
; col3.c = index der unteren, rechten farbe
; col4.c = index der unteren, linken farbe
Procedure DrawInterpolatedBox(x.w,y.w, w.w,h.w, *colors.RGBA, col1.c, col2.c, col3.c, col4.c)
Protected zx.l, zy.l
; brauchen hier nur die index-übergangswerte berechnen -> ist halt nur 1 wert fürn index
Protected g1to2.f, g3to4.f, g4to1.f
g1to2 = (col2 - col1) / w
g3to4 = (col3 - col4) / w
g4to1 = (col4 - col1) / h
; für schreiben der werte
Protected *drawpos.interpol_pixel, linepush.l ; der "zeilenvorschub" für eine pixelzeile, sprich die anzahl pixel zum nächsten zeilenanfang
*drawpos = *_interpol_buffer + y * _interpol_pitch + x * _interpol_bytes ; pointer auf 1. pixel setzen
linepush = _interpol_pitch - _interpol_bytes * w ; zeilenvorschub errechnen (pixelzeilenlänge - breite einer pixelzeile des interpolationsrechteckes)
Protected *destcol.RGBA
Protected destcol_index_float.f
Protected a1.f, a2.f
Protected index_step.f
Protected destcol_index.w ;ACHTUNG: beschissenerweise MUSS das ein Wordwert sein. Aufgrund der Instabilität von Float kommen teilweise extrem kleine
;negative Zahlen raus, die durch diese Rechnung als 255-Zahl interpretiert werden, WENN man ein char As Zahl nimmt.
;Ein Word muss daher her!
For zy = 0 To h-1
; startfarbe setzen
destcol_index_float = col1 + g4to1 * zy
; alpha-anteile von oberem u. unterem farbübergang errechnen
a1 = zy / h
a2 = 1.0 - a1
;übergangswert vorberechnen (wird fortlaufend addiert)
index_step = g1to2 * a2 + g3to4 * a1
For zx = 0 To w-1
; farbe erhöhen
destcol_index_float + index_step
; index aufrunden
; (Die Floatumwandlung frisst ziemlich viel Geschwindigkeit! Leider ist sie nötig, damit die Darstellung auch korrekt ist...)
destcol_index = Int(destcol_index_float)
If destcol_index_float - destcol_index > 0.5
destcol_index + 1
EndIf
; je nach pixeltiefe unterschiedlich formatiert einzeichnen (bisher nur 8, 24 und 32!)
If _interpol_format = #PB_PixelFormat_8Bits
*drawpos\c\c = destcol_index
ElseIf _interpol_format = #PB_PixelFormat_24Bits_RGB
*destcol = *colors + destcol_index * SizeOf(RGBA)
*drawpos\rgb\r = *destcol\r
*drawpos\rgb\g = *destcol\g
*drawpos\rgb\b = *destcol\b
ElseIf _interpol_format = #PB_PixelFormat_24Bits_BGR
*destcol = *colors + destcol_index * SizeOf(RGBA)
*drawpos\rgb\b = *destcol\r
*drawpos\rgb\g = *destcol\g
*drawpos\rgb\r = *destcol\b
ElseIf _interpol_format = #PB_PixelFormat_32Bits_RGB
*destcol = *colors + destcol_index * SizeOf(RGBA)
*drawpos\rgba\r = *destcol\r
*drawpos\rgba\g = *destcol\g
*drawpos\rgba\b = *destcol\b
ElseIf _interpol_format = #PB_PixelFormat_32Bits_BGR
*destcol = *colors + destcol_index * SizeOf(RGBA)
*drawpos\rgba\b = *destcol\r
*drawpos\rgba\g = *destcol\g
*drawpos\rgba\r = *destcol\b
EndIf
; auf nächsten pixel zeigen
*drawpos + _interpol_bytes
Next
; zeilenvorschub auf bildschirm
*drawpos + linepush
Next
EndProcedure
;------------------------------------------------------------------------------------------------------------------------------
; paar funktionen...
Procedure Randomize()
Protected zx.l, zy.l
For zy = 0 To field_h-1
For zx = 0 To field_w-1
Select current_mode
Case 0
field(zx,zy) = Random(gradient_color_count(current_preset)-1)
Case 1
field(zx,zy) + 1
If field(zx,zy) > gradient_color_count(current_preset)-1
field(zx,zy) = Random(gradient_color_count(current_preset)-1)
EndIf
Case 2
field(zx,zy) - 1
If field(zx,zy) < 0
field(zx,zy) = Random(gradient_color_count(current_preset)-1)
EndIf
EndSelect
Next
Next
EndProcedure
Procedure DrawArray()
Protected zx.l, zy.l
For zy = 0 To field_h-2
For zx = 0 To field_w-2
DrawInterpolatedBox(zx*block_width,zy*block_height, block_width,block_height, @gradient_colors(current_preset, 0), field(zx,zy), field(zx+1,zy), field(zx+1,zy+1), field(zx,zy+1))
Next
Next
EndProcedure
;------------------------------------------------------------------------------------------------------------------------------
; hauptschleife...
Global time.l, fps.l, delay.l, framedelay.l
framedelay.l = 1000 / #MAX_FRAMES
MessageRequester("Info", "F1 = toggel color presets" + Chr(10) + "F2 = toggle color change mode", #MB_OK)
Repeat
Select WindowEvent()
Case #PB_Event_CloseWindow
End
Case #PB_Event_Menu
Select EventMenu()
Case 0
current_preset + 1
If current_preset >= gradient_presets
current_preset = 0
EndIf
Case 1
current_mode + 1
If current_mode >= modes
current_mode = 0
EndIf
EndSelect
EndSelect
Randomize()
ClearScreen($111111)
StartDrawing(ScreenOutput())
time = ElapsedMilliseconds()
GetInterpolationBuffer()
DrawArray()
delay = ElapsedMilliseconds() - time
If delay = 0
SetWindowTitle(0, "FPS: 60+")
Else
fps = 1000 / delay
SetWindowTitle(0, "FPS: " + Str(fps))
EndIf
StopDrawing()
FlipBuffers()
;nötige verzögerung berechnen
If delay < framedelay
Delay(framedelay - delay)
EndIf
ForEver
Btw: Spielt mal mit den Werten u. Einstellungen rum. Da ergeben sich teilweise wunderhübsche Animationen
