Screen Fading
BlitzPlus Forums/BlitzPlus Programming/Screen Fading
| ||
| What is the best/fastest way to cross fade two 640x480 images? I've written my own set of functions that use banks and the lockedpixels functionality of B+. It's a brute force approach and runs fine on most systems. However, users with lower spec'd machines/graphics cards are reporting sloooow fades, and I'd like to address this. So, is there a sure-fire way of cross fading that'll work well on all machines? |
| ||
| Try using the gamma. It's not really meant for this but it's very fast. |
| ||
| There's no easy way to do it. Brute force is the only way. You might find you get better performance if you put the RGB values seperate into banks before you start your fade loop. This might mean you get a bit of a stutter before it starts. However, this would mean no readpixelfast at run time. Then all you're going to be doing is 6 readbytes from a bank (r1, g1, b1 and r2, g2, b2) and one writepixelfast per pixel. This isn't tested, just a though. |
| ||
| Thought as much - Thanks guys. >You might find you get better performance if you put the RGB values seperate into banks before you start your fade loop. That's exactly what I do now, and it works just great on the majority of PCs. As you say, there's a momentary stutter and a temporary increase in memory usage, but it works well. I'll post my code here in case I've missed some obvious optimisations (and others may find it useful?). Cheers |
| ||
; Title: 640x480 full-screen fade
; Version: 4.01 (09/Sep/2004)
; Author: Leigh Bowers
;Graphics 640, 480, 16, 2
;img1% = LoadImage("Themes\Evolution\640x480\Images\Loading.bmp")
;img2% = LoadImage("Archon001.bmp") ; CreateImage(640, 480)
;WaitKey
;lngStart% = MilliSecs()
;ScreenFade img1
;WaitKey
;ScreenFade img2, 0.05
;ScreenFade 0, 0.1, $ff, $ff, $ff
;ScreenFade
;WaitKey
Function ScreenFade(pimgDestinationImage% = 0, pdblFadeSpeed# = 0.1, ColourR% = 0, ColourG% = 0, ColourB% = 0, pimgSourceImage% = 0)
If Prefs\Debug Then Return
lngBank% = CreateBank(2 * (307200 * 3))
; Take a copy of the front buffer and the destination screen
If pimgSourceImage = 0 Then
SetBuffer FrontBuffer()
Else
SetBuffer ImageBuffer(pimgSourceImage)
End If
lngBankPos% = 0
For bytLoop% = 0 To 1
If bytLoop = 1 Then
SetBuffer BackBuffer()
If pimgDestinationImage = 0 Then
ClsColor ColourR, ColourG, ColourB : Cls ; Fading to a blank screen (of specified colour)
Else
DrawBlock pimgDestinationImage, 0, 0 ; The image we're cross-fading in to
End If
End If
LockBuffer
bnkScreenFade% = LockedPixels()
intPitch% = LockedPitch()
bytFormat% = LockedFormat()
lngOffset% = 0
Select bytFormat
Case 1
intY% =0
Repeat
lngOffset% = (intY * intPitch)
intX% = 0
Repeat
lngPixel% = PeekShort(bnkScreenFade, lngOffset)
PokeByte(lngBank, lngBankPos, ((lngPixel And $F800) Shr 11) Shl 3)
PokeByte(lngBank, lngBankPos + 1, ((lngPixel And $7E0) Shr 5) Shl 2)
PokeByte(lngBank, lngBankPos + 2, (lngPixel And $1F) Shl 3)
lngOffset = lngOffset + 2
lngBankPos = lngBankPos + 3
intX = intX + 1
Until intX = 640
intY = intY + 1
Until intY = 480
Case 2
intY% =0
Repeat
lngOffset% = (intY * intPitch)
intX% = 0
Repeat
lngPixel = PeekShort(bnkScreenFade, lngOffset)
PokeByte(lngBank, lngBankPos, ((lngPixel And $7C00) Shr 10) Shl 3)
PokeByte(lngBank, lngBankPos + 1, ((lngPixel And $3E0) Shr 5) Shl 2)
PokeByte(lngBank, lngBankPos + 2, (lngPixel And $1F) Shl 3)
lngOffset = lngOffset + 2
lngBankPos = lngBankPos + 3
intX = intX + 1
Until intX = 640
intY = intY + 1
Until intY = 480
Case 3, 4
intY% =0
Repeat
lngOffset% = (intY * intPitch)
intX% = 0
Repeat
lngPixel = PeekInt(bnkScreenFade, lngOffset)
PokeByte(lngBank, lngBankPos, (lngPixel And $FF0000) Shr 16) ; r
PokeByte(lngBank, lngBankPos + 1, (lngPixel And $FF00) Shr 8) ; g
PokeByte(lngBank, lngBankPos + 2, lngPixel And $FF) ; b
lngOffset = lngOffset + bytFormat
lngBankPos = lngBankPos + 3
intX = intX + 1
Until intX = 640
intY = intY + 1
Until intY = 480
End Select
UnlockBuffer
Next
; Perform the fade using the appropriate "Poke"
SetBuffer BackBuffer()
dblAlpha# = 0.0
Repeat
dblAlpha = dblAlpha + pdblFadeSpeed
If dblAlpha > 1 Then dblAlpha = 1
LockBuffer
bnkScreenFade% = LockedPixels()
intPitch% = LockedPitch()
bytFormat% = LockedFormat()
lngBankPos1% = 0
lngBankPos2% = 921600
Select bytFormat
Case 1
intY% = 0
Repeat
lngOffset% = (intY * intPitch)
intX% = 0
Repeat
bytCache1% = PeekByte(lngBank, lngBankPos1)
bytCache2% = PeekByte(lngBank, lngBankPos1 + 1)
bytCache3% = PeekByte(lngBank, lngBankPos1 + 2)
PokeInt bnkScreenFade, lngOffset, (((bytCache1 + (PeekByte(lngBank, lngBankPos2) - bytCache1) * dblAlpha) / 8) Shl 11) Or (((bytCache2 + (PeekByte(lngBank, lngBankPos2 + 1) - bytCache2) * dblAlpha) / 4) Shl 5) Or ((bytCache3 + (PeekByte(lngBank, lngBankPos2 + 2) - bytCache3) * dblAlpha) / 8)
lngOffset = lngOffset + 2
lngBankPos1 = lngBankPos1 + 3
lngBankPos2 = lngBankPos2 + 3
intX = intX + 1
Until intX = 640
intY = intY + 1
Until intY = 480
Case 2
intY% = 0
Repeat
lngOffset% = (intY * intPitch)
intX% = 0
Repeat
bytCache1% = PeekByte(lngBank, lngBankPos1)
bytCache2% = PeekByte(lngBank, lngBankPos1 + 1)
bytCache3% = PeekByte(lngBank, lngBankPos1 + 2)
PokeInt bnkScreenFade, lngOffset, (((bytCache1 + (PeekByte(lngBank, lngBankPos2) - bytCache1) * dblAlpha) / 8) Shl 10) Or (((bytCache2 + (PeekByte(lngBank, lngBankPos2 + 1) - bytCache2) * dblAlpha) / 4) Shl 5) Or ((bytCache3 + (PeekByte(lngBank, lngBankPos2 + 2) - bytCache3) * dblAlpha) / 8)
lngOffset = lngOffset + 2
lngBankPos1 = lngBankPos1 + 3
lngBankPos2 = lngBankPos2 + 3
intX = intX + 1
Until intX = 640
intY = intY + 1
Until intY = 480
Case 3, 4
intY% = 0
Repeat
lngOffset% = (intY * intPitch)
intX% = 0
Repeat
bytCache1% = PeekByte(lngBank, lngBankPos1)
bytCache2% = PeekByte(lngBank, lngBankPos1 + 1)
bytCache3% = PeekByte(lngBank, lngBankPos1 + 2)
PokeInt bnkScreenFade, lngOffset, ((bytCache1 + (PeekByte(lngBank, lngBankPos2) - bytCache1) * dblAlpha) Shl 16) Or ((bytCache2 + (PeekByte(lngBank, lngBankPos2 + 1) - bytCache2) * dblAlpha) Shl 8) Or (bytCache3 + (PeekByte(lngBank, lngBankPos2 + 2) - bytCache3) * dblAlpha)
lngOffset = lngOffset + bytFormat
lngBankPos1 = lngBankPos1 + 3
lngBankPos2 = lngBankPos2 + 3
intX = intX + 1
Until intX = 640
intY = intY + 1
Until intY = 480
End Select
UnlockBuffer
VWait
Flip False
Until dblAlpha = 1
FreeBank lngBank
End Function |
| ||
| Without looking at your code very closely it looks very complicated for what you're doing. The basic function for the fade would be (off the top of my head coding no testing or nuffink) |
| ||
| That's basically what I'm doing but, as I'm using locked pixels, I need to take the LockedFormat() into account. The code could be condensed (and originally it was), but this was at the expense of some speed. Also, I'm effecively using two banks as I'm cross fading rather than just fading to/from black. LockPixel poking is faster than WritePixelFast, right? |
| ||
| LockPixel poking is faster than WritePixelFast, right? No idea! Test it! |
| ||
| It definitely is (from my tests at least), but you threw me for a moment there when you used the older WritePixel in your example :D |
| ||
| have you tried useing a seperate image stored in system memory as a backbuffer? erm... image1 = starting image you want to fade from (store in system memory as a bank or array) image2 = ending image you want to fade to (again store as a bank or array) virtualbuffer = createimage with the system memory flag (not stored in video memory) you then do your fade routine on the virtualbuffer and once done. drawimage virtualbuffer 0,0 flip although this might seem strange it's worth a try because it can maximise your use of the system to video bandwidth writepixelfast is slower then pokeing because the screen cords used in writepixelfast must be converted to a memory address...something already done when pokeing. But keep in mind that when pokeing pixels or useing writepixelfast you are wasteing your system to video bandwidth potential as , simply stated, both an video memory address as well as an pixel color value are sent across to the video card with each call...not so bad when you have only a few pixels to change... but when you are dealing with a full screen effect like this it can actualy slow things down because you are effectively sending twice the required information (a 32-bit color value + 32-bit memory address per pixel) however useing a system image could be faster as it only needs to sent a small amount of info over to the videocard telling it where to send the image, followed by the image itself...rather then a pixel at a time, the bandwidth useage can be maximised because the card knows where to put each pixel (and how many there are) ahead of time...so the whole image can be transfered in one batch rather then each pixel individualy (and the associated memory addressing for that pixel) at least thats the idea...try it and see if it helps. |