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. |