Code archives/Graphics/Picture Viewer with scoll and zoom
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
| I wrote this as a prototype for a viewer of large (image-based) maps that can be easily scrolled and zoomed via mouse buttons/wheel. As such, it has several techniques that might be useful when starting a similar project of your own. It uses Tesuji's routines to divide a large image file up into smaller tiles to efficiently use video memory. It also integrates a hooked redraw event within the main type rather than as a stand-alone function. | |||||
Rem
A sample picture viewer, mostly to explore type-enclosed event handling and
Tesuji's 256x256 tiled handling of large images. Also an experiment of
mouse dragging and right-click popup menu (and mouse wheel) for scaling.
by Wendell Martin, December 27, 2005
End Rem
Framework BRL.D3D7Max2D
Import BRL.System
Import BRL.FileSystem
Import BRL.Pixmap
Import BRL.MaxGUI
Import BRL.Win32MaxGUI
Import BRL.PNGLoader
Import BRL.BMPLoader
Import BRL.JPGLoader
SetGraphicsDriver D3D7Max2DDriver()
AppTitle = "Picture Viewer (Drag with left mouse, zoom with wheel or right click)"
' Load an image and tile in 256x256 chunks:
Const FRAGSIZE = 256 ' maximum image fragment size
Local imageUrl:String
Local pixmap:TPixmap
Local filter$="Picture Files (*.bmp,jpeg,jpg,png):bmp,jpeg,jpg,png;All Files:*"
imageUrl = RequestFile( "Load Picture",filter$, False, CurrentDir()+"/" )
pixmap:TPixmap = LoadPixmap(imageUrl)
If pixmap = Null Then End ' user didn't choose an image, so quit
Global img:BigImage = BigImage.Create(pixmap)
img.scale = 1
' Set up window and viewer:
Local winW# = GadgetWidth( Desktop() ) * .75
Local winH# = GadgetHeight( Desktop() ) * .75
Local offsetX# = ( GadgetWidth( Desktop() ) - winW ) / 2.0
Local offsetY# = ( GadgetHeight( Desktop() ) - winH ) / 2.0
Local style = WINDOW_TITLEBAR|WINDOW_RESIZABLE|WINDOW_CLIENTCOORDS ' ClientCoords for Maximize
Global Win:TGadget=CreateWindow( AppTitle, offsetX,offsetY, WinW,WinH, Null, style)
SetMinWindowSize Win,200,200
Local Viewer:TViewer = New TViewer.create( 0,0, winW,winH, Win )
Local Quit:Int = False
' Main loop:
Repeat
WaitEvent()
Select EventSource()
Case Win
Select EventID()
Case EVENT_WINDOWCLOSE
Quit=True
End Select
End Select
Until Quit=True
End
Type TViewer
Field can:TGadget
Global drag
Global startx, starty
Global endx, endy
Field Popup_Gadget:TGadget
Field Popup_Menu:TGadget
Method Create:TViewer( x,y, w,h, group:TGadget )
can = CreateCanvas( x,y, w,h, group )
SetGadgetLayout can, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED ' Lock the size
Popup_Menu=CreateMenu( "", Null, Popup_Gadget )
CreateMenu "10%", 10, popup_menu
CreateMenu "25%", 25, popup_menu
CreateMenu "50%", 50, popup_menu
CreateMenu "75%", 75, popup_menu
CreateMenu "100%", 100, popup_menu
CreateMenu "150%", 150, popup_menu
CreateMenu "200%", 200, popup_menu
CreateMenu "400%", 400, popup_menu
CreateMenu "1000%", 1000, popup_menu
AddHook EmitEventHook, EventHook, Self
Return Self
End Method
Function EventHook:Object( id, data:Object, context:Object )
If data <> Null Then Return TViewer(context).EventHandler( TEvent(data) )
End Function
Method EventHandler:Object ( event:TEvent )
If event.source = can And event.id = EVENT_GADGETPAINT Then
SetGraphics CanvasGraphics(can)
SetViewport 0,0,GadgetWidth(can),GadgetHeight(can)
SetBlend maskblend
Cls
img.render
Flip
ElseIf event.id = EVENT_MOUSEDOWN
If event.data = MOUSE_LEFT Then
drag = True
SetPointer POINTER_HAND
startx = event.x
starty = event.y
ElseIf event.data = MOUSE_RIGHT Then
PopupWindowMenu( Win, popup_menu )
EndIf
ElseIf event.id = EVENT_MOUSEUP Then
drag = False
SetPointer POINTER_DEFAULT
ElseIf drag And (event.id = EVENT_MOUSEMOVE) Then
endx = event.x
endy = event.y
img.x :+ endx - startx
img.y :+ endy - starty
startx = endx
starty = endy
Cls
img.render
Flip
ElseIf event.id = EVENT_MOUSEWHEEL Then
If event.data >0 Then
img.scale :* 1.1
If img.scale > 10 Then
img.scale = 10
Else
img.x :* 1.1
img.y :* 1.1
EndIf
End If
If event.data <0 Then
img.scale :/ 1.1
If img.scale < 0.1 Then
img.scale = 0.1
Else
img.x :/ 1.1
img.y :/ 1.1
EndIf
EndIf
Cls
img.render
Flip
ElseIf event.id = EVENT_MENUACTION Then
Local scale:Float = event.data / 100.0
Local deltascale:Float = scale / img.scale
img.scale :* deltascale
img.x :* deltascale
img.y :* deltascale
Cls
img.render
Flip
Else
Return event ' not handled here, so pass along
EndIf
End Method
End Type
' Tesuji's tile functions below:
' =============================
' Image Fragment
' =============================
Type ImageFragment
Field img:TImage
Field x,y
Field rotation:Float = 0
Field angle:Double
Field distance:Double
' ----------------------------------
' constructor
' ----------------------------------
Function create:ImageFragment(pmap:TPixmap,x:Float,y:Float,w,h)
Local frag:ImageFragment = New ImageFragment
frag.img = LoadImage(PixmapWindow(pmap,x,y,w,h),0|FILTEREDIMAGE)
x = (pmap.width*.5) - x
y = (pmap.height*.5) - y
frag.x = x
frag.y = y
frag.angle = ATan2(y,x)-180
frag.distance = Sqr(x*x + y*y)
Return frag
End Function
' --------------------
' Draw individual tile
' --------------------
Method render(scale:Float,xoff:Float=0,yoff:Float=0,rot:Float=0)
SetRotation rot
Local d:Float = Self.distance*scale
SetScale(scale,scale)
DrawImage(Self.img,(Cos(rot+Self.angle)*d)+xoff,(Sin(rot+Self.angle)*d)+yoff )
End Method
End Type
' ==================================
' Big Image
' ==================================
Type BigImage
Field pixmap:TPixmap
Field px,py
Field fragments:TList
Field scale:Float = 1
Field width
Field height
Field x:Float = 0
Field y:Float = 0
Field rotation:Float = 0
' ----------------------------------
' constructor
' ----------------------------------
Function create:BigImage(p:TPixmap)
Local bi:BigImage = New BigImage
bi.pixmap = p
bi.width = p.width
bi.height = p.height
bi.fragments = CreateList()
bi.load()
Return bi
End Function
' -------------------------------------
' convert pixmap into image fragments
' -------------------------------------
Method load()
Local px = 0
Local py = 0
Local loading = True
While (loading)
'FlushMem
Local w = FRAGSIZE
If Self.pixmap.width - px < FRAGSIZE w = Self.pixmap.width - px
Local h = FRAGSIZE
If Self.pixmap.height - py < FRAGSIZE h = Self.pixmap.height - py
Local f1:ImageFragment = ImageFragment.create(Self.pixmap,px,py,w,h)
ListAddLast Self.fragments,f1
px:+FRAGSIZE
If px >= Self.pixmap.width
px = 0
py:+FRAGSIZE
If py >= Self.pixmap.height loading = False
End If
Wend
End Method
' -----------------
' Draw entire image
' -----------------
Method render()
SetOrigin(GraphicsWidth()*.5,GraphicsHeight()*.5)
For Local f:ImageFragment = EachIn Self.fragments
f.render(Self.scale,Self.x,Self.y,Self.rotation)
Next
SetOrigin(0,0)
End Method
End Type |
Comments
None.
Code Archives Forum