Skip to content

Instantly share code, notes, and snippets.

@nobuyukinyuu
Last active January 2, 2016 19:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nobuyukinyuu/8348382 to your computer and use it in GitHub Desktop.
Save nobuyukinyuu/8348382 to your computer and use it in GitHub Desktop.
Automatic scaling and letterboxing, plus matrix-compensated cursor and scissor functions.
'Copyright 2011-2014 Nobuyuki (nobu[AT]subsoap.com).
'No warranties expressed or implied.
Import mojo
Private
Global lastDeviceWidth:Float, lastDeviceHeight:Float
Public
Global PreserveAspectRatio:Bool = True 'Aspect ratio preserving
Global ScreenWidth:Float = 800 'Native size of your app
Global ScreenHeight:Float = 480
Global ScaleFactor:Float 'Amount to scale window by preserving aspect ratio; calculated at runtime
Global SlideAxis:Bool '0: X-Axis, 1: Y-Axis
Global SlideAmount:Int 'amount to slide the window to center the screen
Global ScaleX:Float, ScaleY:Float 'Individual scale amounts for sloppy stretch to window
Function initAutoScale:Void(w:Float = 800, h:Float = 480, preserveAspectRatio:Bool = True)
ScreenWidth = w; ScreenHeight = h; PreserveAspectRatio = preserveAspectRatio
lastDeviceWidth = DeviceWidth(); lastDeviceHeight = DeviceHeight()
ScaleX = DeviceWidth() / ScreenWidth
ScaleY = DeviceHeight() / ScreenHeight
'shorter distance determines scale factor
If ScaleX < ScaleY then ScaleFactor = ScaleX else ScaleFactor = ScaleY
'calculate slide distance based on derived size
Local DiffX:Int= DeviceWidth() - ScreenWidth * ScaleFactor
Local DiffY:Int= DeviceHeight() - ScreenHeight * ScaleFactor
If ScaleX < ScaleY then
SlideAxis = True 'Set axis to Y
SlideAmount = DiffY / 2
else
SlideAxis = False 'Set axis to X
SlideAmount = DiffX / 2
End if
'Scale the slide amount by the scale factor to get the real slide amount after scaling
SlideAmount *= (1/ScaleFactor)
End Function
Function CheckScreenDimensions:Void()
If lastDeviceWidth <> DeviceWidth() Or lastDeviceHeight <> DeviceHeight() Then
initAutoScale(ScreenWidth, ScreenHeight, PreserveAspectRatio)
'Print "Screen dimensions changed. " + lastDeviceWidth + "=" + DeviceWidth() + ", " +
' lastDeviceHeight + "=" + DeviceHeight()
End If
End Function
'Summary: Scales the screen. It is recommended that you call this at beginning of OnRender.
Function ScaleScreen:Void(checkForResize:Bool = True)
If checkForResize = True Then CheckScreenDimensions()
'SetMatrix (1,0,0,1,0,0) 'Identity Matrix
If PreserveAspectRatio = True Then
Scale(ScaleFactor, ScaleFactor)
If SlideAxis = False Then Translate(SlideAmount, 0) Else Translate(0, SlideAmount)
Else
Scale(ScaleX, ScaleY)
End If
End Function
'Derived mouse positions
Function dMouseX:Int()
If PreserveAspectRatio
If SlideAxis = False Then Return MouseX() / ScaleFactor - SlideAmount Else Return MouseX() / ScaleFactor
Else
Return MouseX() / ScaleX
End If
End Function
Function dMouseY:Int()
If PreserveAspectRatio
If SlideAxis = True Then Return MouseY() / ScaleFactor - SlideAmount Else Return MouseY() / ScaleFactor
Else
Return MouseY() / ScaleY
End If
End Function
'Derived multitouch positions
Function dTouchX:Int(index:Int=0)
If PreserveAspectRatio
If SlideAxis = False Then Return TouchX(index) / ScaleFactor - SlideAmount Else Return TouchX(index) / ScaleFactor
Else
Return TouchX(index) / ScaleX
End If
End Function
Function dTouchY:Int(index:Int=0)
If PreserveAspectRatio
If SlideAxis = True Then Return TouchY(index) / ScaleFactor - SlideAmount Else Return TouchY(index) / ScaleFactor
Else
Return TouchY(index) / ScaleY
End If
End Function
'Derived device screen positions. Returns position based on percent from 0-1.
Function DeviceX:Float(percent:Float=1)
If ScaleX = 1 Then Return DeviceWidth() * percent
If SlideAxis = False Then Return (DeviceWidth() / ScaleFactor) * percent - SlideAmount Else Return (DeviceWidth()/ScaleFactor) * percent
End Function
Function DeviceY:Float(percent:Float=1)
If ScaleY = 1 Then Return DeviceHeight() * percent
If SlideAxis = True Then Return (DeviceHeight() / ScaleFactor) * percent - SlideAmount Else Return (DeviceHeight()/ScaleFactor) * percent
End Function
'Derived Scissor
Function SetScaledScissor:Void(x:Float, y:Float, width:Float, height:Float)
Local sx:Float, sy:Float
If PreserveAspectRatio = True Then
If SlideAxis = False Then sx = (x + SlideAmount) * ScaleFactor Else sx = x * ScaleFactor
If SlideAxis = True Then sy = (y + SlideAmount) * ScaleFactor Else sy = y * ScaleFactor
SetScissor(sx, sy, ScaleFactor * width, ScaleFactor * height)
Else
sx = x * ScaleX
sy = y * ScaleY
SetScissor(sx, sy, ScaleX * width, ScaleY * height)
End If
End Function
'Summary: Renders a letterbox over the top of the surface.
Function RenderLetterBox:Void(lb:LetterBoxer)
If DeviceWidth() = ScreenWidth And DeviceHeight() = ScreenHeight Then Return
Local x:Float, y:Float, w:Float, h:Float
If SlideAxis = False Then 'X-Axis
w = SlideAmount * ScaleFactor 'Letterbox size
h = DeviceHeight()
x = DeviceWidth() -w 'SideB origin
Else 'Y-Axis
w = DeviceWidth()
h = SlideAmount * ScaleFactor
y = DeviceHeight() -h 'SideB origin
End If
SetMatrix(1, 0, 0, 1, 0, 0) 'Identity matrix.
lb.Render(0, 0, w, h, False)
lb.Render(x, y, Ceil(w), Ceil(h), True) 'Side B
End Function
'Summary: Base LetterBoxer class. Extend this with your own to make custom letterboxes.
Class LetterBoxer Abstract
Method Render:Void(x:Float, y:Float, w:Float, h:Float, SideB:Bool = False) Abstract
End Class
'Summary: The most basic type of letterbox; colored bars.
Class ColoredLetterBox Extends LetterBoxer
Field r:Int, g:Int, b:Int
Method New(r:Int, g:Int, b:Int)
Self.r = r; Self.g = g; Self.b = b
End Method
Method Render:Void(x:Float, y:Float, w:Float, h:Float, SideB:Bool)
SetColor(r, g, b)
DrawRect(x, y, w, h)
SetColor(255, 255, 255)
End Method
End Class
'Summary: Displays an image on either side of the letterbox.
Class BookEndLetterBox Extends LetterBoxer
Field img:Image, img2:Image
Method New(A:Image, B:Image)
img = A; img2 = B
End Method
Method Render:Void(x:Float, y:Float, w:Float, h:Float, SideB:Bool)
If SideB
DrawImage(img2, x, y, 0, w / img2.Width, h / img2.Height)
Else
DrawImage(img, x, y, 0, w / img.Width, h / img.Height)
End If
End Method
End Class
'Summary: Displays a tiled image on both sides of the letterbox.
Class TiledLetterBox Extends LetterBoxer
Field img:Image
Method New(img:Image)
Self.img = img
End Method
Method Render:Void(x:Float, y:Float, w:Float, h:Float, SideB:Bool)
Local s:Float[] = GetScissor()
SetScissor(x, y, w, h)
For Local yy:Int = 0 To Ceil(h / img.Height)
For Local xx:Int = 0 To Ceil(w / img.Width)
DrawImage(img, x + xx * img.Width, y + yy * img.Height)
Next
Next
SetScissor(s[0], s[1], s[2], s[3])
End Method
End Class
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment