Simple video filter...

Blitz3D Forums/Blitz3D Programming/Simple video filter...

mearrin69(Posted 2003) [#1]
***Code Updated***

Hi all,
I was going to ask if someone had any code laying around that would simulate a grainy video window but I decided 'what the heck' and did it myself. It's not the fastest, smallest, or best but I think it looks pretty darn cool. Anyway, it's good enough for my purposes now but I humbly offer this snippet for any that want to use it/improve it.

You'll need to include your own image file...just keep it pretty small because this per pixel stuff is slow as Christmas to do.

Anyway, this is going to be used to overlay a small 3D window to make it look like really crummy web-cam-type video. Think 'Aliens'. Remember those little helmet-cam feeds they were watching in the APC? You'll get the point if you see them.

Hope someone can use it.
Cheers,
M

[CODE]
; vidfilter.bb
; Experiment to create video-like scanlines and static
; by Michael Arrington (mearrin69 on BlitzCoder.com)
; michaela@...

; Set up
Graphics 320, 200, 16, 2
SetBuffer BackBuffer()
AppTitle "Video Filter by Michael Arrington (mearrin69)"
SeedRnd MilliSecs()

; Load and create test images
Global imgFeed = LoadImage("camtest2.bmp")
Global imgVideo = CreateImage(ImageWidth(imgFeed), ImageHeight(imgFeed))

; Loop until esc is hit
While Not KeyHit(1)
Cls

; **************************************
; Uncomment one to see different effects
; **************************************
; -----------------------------------------
; Straight from the feed, no filter effects
; imgVideo = VideoFilter(imgFeed, 1.0, 1.0, 1.0, 0, 0, 0, 0)
; -----------------------------------------
; Just green shift
; imgVideo = VideoFilter(imgFeed, 0.5, 1.0, 0.5, 0, 0, 0, 0)
; -----------------------------------------
; Just scan lines
; imgVideo = VideoFilter(imgFeed, 1.0, 1.0, 1.0, 40, 0, 0, 0)
; -----------------------------------------
; Just some minor static
; imgVideo = VideoFilter(imgFeed, 1.0, 1.0, 1.0, 0, 25, -5, 25)
; -----------------------------------------
; Just major static
; imgVideo = VideoFilter(imgFeed, 1.0, 1.0, 1.0, 0, 75, -25, 75)
; -----------------------------------------
; Light green shift, light scan lines, light static
; imgVideo = VideoFilter(imgFeed, 0.8, 1.0, 0.8, 15, 25, -5, 25)
; -----------------------------------------
; Heavy green shift, heavy scan lines, heavy static
imgVideo = VideoFilter(imgFeed, 0.3, 1.0, 0.3, 40, 75, -25, 75)
; -----------------------------------------
; White out
; imgVideo = VideoFilter(imgFeed, 1.1, 1.1, 1.1, 40, 90, 75, 150)
; -----------------------------------------
; **************************************

DrawBlock imgVideo, (GraphicsWidth() / 2) - (ImageWidth(imgVideo) / 2), (GraphicsHeight() / 2) - (ImageHeight(imgVideo) / 2)
Flip
Wend

; Exit
End


; ====================================================================
; VideoFilter(imgIn%, pRGain#, pGGain#, pBGain#, pSLGain%, pSTPct%,
; pSTLow%, pSTHigh%)
; ====================================================================
; Apply video effects to passed image. Returns image of same size.
; ====================================================================
; Parameter Descriptions:
; imgIn = Image handle - Watch the size, slows way down on large imgs.
; pRGain# = Red Gain - Float value controlling red shift. Use <1.0 for
; less red in image, >1.0 for more red in image.
; pGGain# = Green Gain - See pRGain
; pBGain# = Blue Gain - see pRGain
; pSLGain% = Scan Line Gain - Int value controlling boost to every other
; line, simulating video scan lines. This is just additive.
; pSTPct% = Static Percentage - Int value from 0 - 100 specifying how
; much of the screen will be covered by static each frame.
; pSTLow% = Static Low Gain - Int value specifying the bottom of the
; static gain range. Use negative numbers to get darker static.
; Should be lower than pSTHigh.
; pSTHigh% = Static High Gain - Int value specifying the top of the
; static gain range. Higher gives lighter static. Should be
; higher than pSTLow.
; ====================================================================
Function VideoFilter(imgIn%, pRGain#, pGGain#, pBGain#, pSLGain%, pSTPct%, pSTLow%, pSTHigh%)
; Set up local variables
Local lWidth% = ImageWidth(imgIn), lHeight% = ImageHeight(imgIn), lSrc% = ImageBuffer(imgIn)
Local imgOut% = CreateImage(lWidth, lHeight), lDst% = ImageBuffer(imgOut)
Local lARGB%, lR%, lG%, lB%, lCGain%, lSLGain%, lSTGain%, l
; See if we are doing any color shifting to optimize a bit
If pRGain <> 1.0 Or pGGain <> 1.0 Or pBGain <> 1.0 Then lCGain = 1 Else lCGain = 0
; Set to draw to composite and lock it
SetBuffer lDst
LockBuffer lDst
; Lock buffer on video feed
LockBuffer lSrc
; Do image processing
lSTGain = 0
; Loop through each row
For lY = 0 To (lHeight - 1)
; Calc scanline color shift, if any, by bumping gain every other line
If pSLGain Then lSLGain = (lY Mod 2) * pSLGain
; Loop through each column
For lX = 0 To (lWidth - 1)
; Do we need to place static? Skip if not
lSTGain = 0
If pStPct Then
; Calc static for this pixel?
If Rand(0, 100) <= pSTPct Then lSTGain = Rand(pSTLow, pSTHigh)
End If
; Precalc static and scan line gain to save a couple mS
lTmp = lSLGain + lSTGain
; Get pixel value
lARGB = ReadPixelFast(lX, lY, lSrc)
; If there's no color gain then we can skip some multiplies
If lCGain Then
lR = (((lARGB Shr 16) And $ff) * pRGain) + lTmp
lG = (((lARGB Shr 8) And $ff) * pGGain) + lTmp
lB = (((lARGB) And $ff) * pBGain) + lTmp
Else
lR = ((lARGB Shr 16) And $ff) + lTmp
lG = ((lARGB Shr 8) And $ff) + lTmp
lB = ((lARGB) And $ff) + lTmp
End If
; Check bounds
If lR < 0 Then lR = 0 Else If lR > 255 Then lR = 255
If lG < 0 Then lG = 0 Else If lG > 255 Then lG = 255
If lB < 0 Then lB = 0 Else If lB > 255 Then lB = 255
; Write the pixel
WritePixelFast lX, lY, (lB Or (lG Shl 8) Or (lR Shl 16) Or ($ff000000))
Next
Next
; Unlock buffers and draw to back buffer again
UnlockBuffer lDst
UnlockBuffer lSrc
SetBuffer BackBuffer()
; Return the image
Return imgOut
End Function
[/CODE]


jfk EO-11110(Posted 2003) [#2]
cool! looks line it's coded pretty optimized. other than the imagebuffer() call which could be precalculated to a variable I don't see any optimation options atm. - oh yes, precalc lWidht-1 and lHeight-1 in the For-commands. But it's peanuts.


mearrin69(Posted 2003) [#3]
Hey thanks. BTW, I didn't even think about pre-calcing the imagebuffer() call. That might indeed save a few mS, especially on large images, since it's in the inner loop. Thanks for the tip.
M


[edit]Heh, guess you inspired me. I took your advice on pre-calcing the ImageBuffer() call - it made a noticeable savings. I also tightened up a few other areas. If you're not doing color shifting, or static, or scanlines then you save a few mS on each call. I need all three effects so I hadn't thought about making them optional.[/edit]


gpete(Posted 2003) [#4]
I haven't tried your code yet....but this is what I use to make rain or scanlines.... (excerpt from my program-"Nautilus") ->

rain=CreateCube(camera)
EntityParent rain,camera
raintex=LoadTexture("scanl.png") ;or rain.png bitmap
ScaleEntity rain,.1,10,10
ScaleTexture raintex,.2,.2
EntityAlpha rain,.7 ;or set texture to mask ie. "2"
EntityTexture rain,raintex
TurnEntity rain,0,90,0
PositionEntity rain,0,0,2 ;in front of camera

set rainpos=0.0 before Main loop, then after UpdateWorld in prog: ->

PositionTexture raintex,0,rainpos ;rotates texture!!!
rainpos=rainpos+.1

So simple, and yet so cool!

gpete


mearrin69(Posted 2003) [#5]
Whoops. There was an error in the code above...fixed now. I switched around the x and y loops to optimize on drawing scanline gain but forgot to change the loop end conditions. Didn't show itself until I tried a non-square window.