Realtime Pizmap Pan/zoom
BlitzMax Forums/BlitzMax Programming/Realtime Pizmap Pan/zoom
| ||
Hi All, I present below my code for realtime pixmap panning/zooming: Type PixmapZoomer Field bg:TPixmap ' Background to render into Field pm:TPixmap ' Loaded pixmap Field width:Int,height:Int Field aspectRatio:Float Field iWidth:Int,iHeight:Int Field scale:Float Const FPS=60 Function Create:PixmapZoomer(width:Int,height:Int,filename:String="") Local pz:PixmapZoomer=New PixmapZoomer pz.width=width pz.height=height pz.bg=CreatePixmap(width,height,PF_RGB888) If filename<>"" pz.Load fileName EndIf Return pz End Function Method Load(fileName:String) Local q:TPixmap=LoadPixmap(fileName) pm=ConvertPixmap(q,PF_RGB888) iWidth=PixmapWidth(pm) iHeight=PixmapHeight(pm) aspectRatio=Float(width)/Float(height) scale=Float(width)/Float(iWidth) End Method Method GetCorrectWidth:Int(height:Int) Return Int(Float(height)*aspectRatio) End Method Method GetCorrectHeight:Int(width:Int) Return Int(Float(width)/aspectRatio) End Method Method Perform(r1:rect,r2:rect,t:Int) ' Zoom pm from r1 to r2 in time t(millisecs) Local nSteps:Float=(t*FPS)/1000 Local xStep:Float=Float(r2.x-r1.x)/nSteps Local yStep:Float=Float(r2.y-r1.y)/nSteps Local wStep:Float=Float(r2.width-r1.width)/nSteps Local hStep:Float=Float(r2.height-r1.height)/nSteps Local x:Float,y:Float,w:Float,h:Float x=Float(r1.x) y=Float(r1.y) w=Float(r1.width) h=Float(r1.height) For Local i:Int=0 To nSteps GetRectFast x,y,w,h DrawPixmap bg,0,0 Flip x:+xStep y:+yStep w:+wStep h:+hStep Next End Method Method GetRect(sx:Int,sy:Int,sWidth:Int,sHeight:Int) If pm=Null Then Return If sWidth>iWidth And sHeight>iHeight sWidth=iWidth sHeight=GetCorrectHeight(sWidth) If sHeight>iHeight Then sHeight=iHeight sWidth=GetCorrectWidth(sHeight) EndIf ElseIf sWidth>iWidth Or sHeight=0 sWidth=iWidth sHeight=GetCorrectHeight(sWidth) If sHeight>iHeight Then sHeight=iHeight sWidth=GetCorrectWidth(sHeight) EndIf ElseIf sHeight>iHeight Or sWidth=0 sHeight=iHeight sWidth=GetCorrectWidth(sHeight) If sWidth>iWidth Then sWidth=iWidth sHeight=GetCorrectHeight(sWidth) EndIf EndIf GetRectFast sx,sy,sWidth,sHeight End Method Method GetRectFast(sx:Int,sy:Int,sWidth:Int,sHeight:Int) Local endY:Int=sy+sHeight-1 Local endX:Int=sx+sWidth-1 Local yStep:Float=sHeight/Float(height) Local xStep:Float=sWidth/Float(width) Local pixel:Byte Ptr Local pixel2:Byte Ptr Local pitch2:Int=bg.pitch Local bgXStart:Byte Ptr Local y:Float=sy Local x:Float bgXStart=bg.PixelPtr(0,0) pixel2=bgXStart While y<=endY x=sx While x<=endX pixel=pm.PixelPtr(x,y) pixel2[0]=pixel[0] pixel2[1]=pixel[1] pixel2[2]=pixel[2] x:+xStep pixel2:+3 Wend y:+yStep Wend End Method End Type Essentially, I needed to implement a "Ken Burns" effect on arbitarily lage bitmaps, and render them to a pixmap which is them dumped to the screen. The code is not without some limitations. The maximum zoom is to a 1:1 pixel level (by design), and there is absolutly no error checking at run time (as I use my authoring application to ensure that I don't go over the bounds of the pixmap). Speed is achieved by reading directly from one pixmap and writing to the other. Since the destination pixmap (bg) has a direct 1:1 relation to the screen, I can speed up by getting the pointer to (0,0) and only ever increasing it. GetRectFast() takes it's parameters as (x,y,width,height) from within the source bitmap, which can be any arbitary size, and scales the bitmap to fit on the screen by pixel skipping. Since the image is in motion, there is no real need to perform some sort of pixel averaging over the skipped pixels. GetRect() is used by the authoring application to ensure that pixmap coordinates are valid and have the correct aspect ratio. This code could stand futher refinement, and a few more options, but as it stands it suits my needs well. Enjoy! Neil |
| ||
Thanks for sharing, but it's better to put stuff like this in the code archives as they will just get buried and eventually lost in the forums. |
| ||
'rect' type isn't defined. and why you don't use RESIZEPIXMAP? Method Perform(r1:rect,r2:rect,t:Int) |
| ||
Probably would be faster to write into an Image and draw the image. |
| ||
@BlackSp1der, 'rect' type isn't defined. rect is a simple x,y,width,height structure. and why you don't use RESIZEPIXMAP? One reason: speed. Actually, there are a few reasons, but it ultimately comes down to speed. ResizePixmap() stretches / shrinks a bitmap, but only an entire bitmap. I could use PixmapWindow() to create the window on the original image, but my speed tests indicated that this was slower. My function, while somewhat limited, is quite fast - 67ms to resize any given bitmap while in debug, and much, much faster when running without. @AngelDaniel, Probably would be faster to write into an Image and draw the image. Not according to my speed tests. I spent a day writing, testing and rewriting the core routine, and working out the math needed for direct byte access. Neil |