Written and run on the LT 2700X which is my lowest support model at this moment. Use up/dn, fwd, rev, and ok
Test it on your Lower models. Will not run on legacy I believe
Demonstrates the difference between region and coordinate offsetting. I pulled this out of my framework and stripped out a
bunch of code including the compositor so please forgive any mistakes. It is not necessary to use a compositor for region offsetting.
To keep it quick and simple for myself I have kept it in object format. I know this is hard for some to understand right now but all you really
need to do is look at the scrolling functions to see the differences. The scrolling_list_scroll_down_offset, scrolling_list_scroll_up_offset
are the functions that perform y coordinate changes and extraneous drawtext calls. The scrolling functions ending in region
perform the same function using far less code and only one drawtext call. To toggle between the two, press the OK button.
The program starts out with region offsetting. The true value of region offsetting is with older devices, especially those without
access to Open GL. Even the slower devices with Open GL perform far better. As you go up the performance scale there is not
really much of a perceptible difference. Unfortunately, until all under-performing devices are phased out there is the need
to be more optimal with your coding if you want to get the best results. In more complex applications which overlay selectors
have fades, or have multiple columns for a spreadsheet style, the performance really takes a hit. For me right now this seems
to be the best way. But I'm still learning myself. I have commented only where It pertains to the subject. It may well be that the
real difference is the amount of code involved that causes the most significant performance hit
Sub Main()
' Global Channel object - This is where I keep globals
m.APPChannel = NewTestChannel()
m.APPChannel.Create()
' Instance of scrolling list
l_list = NewScrollingList()
if not l_list.Create( GetData() )
print "l_list.Create Failed"
return
end if
' The list's modal event loop
l_list.EventLoop()
' Cleanup
l_list.Release( True )
l_list.Clear()
l_list = Invalid
m.APPChannel.Release()
m.APPChannel.Clear()
m.APPChannel = Invalid
End Sub
' Get some test data
Function GetData() As Object
l_data = CreateObject( "roArray", 1000, False )
for l_i = 1 to 100
l_str = ( 100 - l_i ).ToStr() + " Test Menu Display Line " + l_i.ToStr()
l_data.Push( l_str )
end for
return l_data
End Function
' Channel object used for globals and theme definitions
Function NewTestChannel() As Object
return {
BackgroundClr: Invalid
Device: Invalid
DisplayWidth: 0
DisplayHeight: 0
Screen: Invalid
Port: Invalid
FontRegistry: Invalid
DefalutFontR: Invalid
Create: test_channel_create
Initialize: test_channel_initialize
Release: test_channel_release
}
End Function
Function test_channel_create() As Boolean
if m.Initialize() then return True
m.Release()
return False
End Function
Function test_channel_release() As Void
m.FontRegistry = Invalid
m.DefaultFontR = Invalid
m.Screen = Invalid
m.Port = Invalid
m.Device = Invalid
End Function
Function test_channel_initialize() As Boolean
m.BackgroundClr = &hE4D0B7FF
m.TextColor = &h7A0000FF
m.Device = CreateObject( "roDeviceInfo" )
l_ds = m.Device.GetDisplaySize()
m.DisplayWidth = l_ds.w
m.DisplayHeight = l_ds.h
m.FontRegistry = CreateObject( "roFontRegistry" )
m.DefaultFontR = m.FontRegistry.GetDefaultFont( 26, False, False )
m.Screen = CreateObject( "roScreen", True, m.DisplayWidth, m.DisplayHeight )
m.Port = CreateObject( "roMessagePort" )
m.Screen.SetMessagePort( m.Port )
m.Screen.Clear( m.BackgroundClr )
m.Screen.SwapBuffers()
return True
End Function
' Scrolling list object
Function NewScrollingList() As Object
return {
APPChannel: m.APPChannel
Screen: m.APPChannel.Screen
Port: m.APPChannel.Port
IsEase: True
Data: Invalid
DataCount: 0
DisplayIndexTop: 0
DisplayIndexBot: 0
X: 0
Y: 0
RowHeight: 0
PageSize: 0
PageHeight: 0
BkgClr: Invalid
TextClr: Invalid
TextFont: Invalid
BufferBitmap: Invalid
BufferRegion: Invalid
BufferSprite: Invalid
BufferWidth: 0
BufferHeight: 0
Create: scrolling_list_create
Initialize: scrolling_list_initialize
Release: scrolling_list_release
DrawPage: scrolling_list_draw_page
ScrollDown: scrolling_list_scroll_down_offset
ScrollUp: scrolling_list_scroll_up_offset
EventLoop: scrolling_list_event_loop
}
End Function
Function scrolling_list_create( a_data As Object ) As Boolean
if m.Initialize( a_data ) then return True
m.Release( True )
return False
End Function
Function scrolling_list_initialize( a_data As Object ) As Boolean
m.Release()
' Assign the configuration data
m.TextFont = m.APPChannel.DefaultFontR
m.TextClr = m.APPChannel.TextColor
m.BkgClr = m.APPChannel.BackgroundClr
m.PageSize = 20
m.DataCount = a_data.Count()
' Row Height
m.TextHeight = m.TextFont.GetOneLineHeight()
m.RowHeight = m.TextHeight
m.PageHeight = m.PageSize * m.RowHeight
' Determine buffer width
l_maxWidth = m.APPChannel.DisplayWidth
m.BufferWidth = 0
m.Data = NewContainer( m.DataCount )
l_i = 0
for each l_str in a_data
l_d = m.Data[ l_i ]
l_d.SText = l_str
l_d.TextClr = m.TextClr
l_d.W = m.TextFont.GetOneLineWidth( l_str, l_maxWidth )
if m.BufferWidth < l_d.W then m.BufferWidth = l_d.W
l_i = l_i + 1
end for
' Buffer Height is the page height + a rowHeight virtual row. With region offseting you use additional
' memory to maintain an off-region portion of the bitmap. With coordinate offsetting this is not
' needed as you are not wrapping around the region and can start your drawing outside the bounds of
' the bitmap (see scrolling functions for more detail )
m.BufferHeight = m.PageHeight + m.RowHeight
'Finally, create the bitmap buffer
m.BufferBitmap = CreateObject( "roBitmap", { width: m.BufferWidth, height: m.BufferHeight , AlphaEnable: True } )
if m.BufferBitmap = Invalid
print "Unable To Create m.BufferBitmap"
return False
end if
' The buffer region is only what will be displayed on the screen, not the virtual area
' Once the virtual area is written to, then the region is offsetted into that area thus
' scrolling out one row and scrolling in another
m.BufferRegion = CreateObject( "roRegion", m.BufferBitmap, 0, 0, m.BufferWidth, m.PageHeight )
if m.BufferRegion = Invalid
print "Unable To Create m.BufferRegion"
return False
end if
m.BufferRegion.SetWrap( True )
m.X = int( m.APPChannel.DisplayWidth / 2 - m.BufferWidth / 2 )
m.Y = int( m.APPChannel.DisplayHeight / 2 - m.PageHeight / 2 )
m.DisplayIndexTop = 0
m.DisplayIndexBot = m.DisplayIndexTop
m.ScrollDown = scrolling_list_scroll_down_region
m.ScrollUp = scrolling_list_scroll_up_region
m.IsEase = True
m.DrawPage()
return True
End Function
Function scrolling_list_release( a_isReleaseAll = False As Boolean ) As Void
m.BufferBitmap = Invalid
m.BufferRegion = Invalid
if m.Data <> Invalid
m.Data.Clear()
m.Data = Invalid
m.DataCount = 0
end if
m.TextFont = Invalid
if a_isReleaseAll
m.APPChannel = Invalid
m.Screen = Invalid
m.Port = Invalid
end if
return
End Function
' Draw page function draws are page on an as needed bases
Function scrolling_list_draw_page( a_isDrawAll = True As Boolean ) As Boolean
if m.BufferBitmap = Invalid then return False
m.BufferBitmap.Clear( m.BkgClr )
l_y = 0
l_index = m.DisplayIndexTop
for l_i = 1 to m.PageSize
l_d = m.Data[ l_index ]
l_d.Y = l_y
m.BufferBitmap.DrawText( l_d.SText, l_d.X, l_d.Y, l_d.TextClr, m.TextFont )
l_y = l_y + m.RowHeight
l_index = l_index + 1
if l_index >= m.DataCount then l_index = 0
end for
m.BufferBitmap.Finish()
l_index = l_index - 1
if l_index < 0 then l_index = m.DataCount - 1
m.DisplayIndexBot = l_index
if a_isDrawAll then
m.Screen.Clear( m.BkgClr )
m.Screen.DrawObject( m.X, m.Y, m.BufferRegion )
m.Screen.SwapBuffers()
end if
return True
End Function
' Offsetting a region
Function scrolling_list_scroll_down_region( a_frames = 16 As Integer ) As Void
' Calculate our indexes into the list. You should avoid using an array as a dispaly stack
' but more on that if your interested. Suffice to say use a top and bottom index
l_bd = m.Data[ m.DisplayIndexBot ]
m.DisplayIndexBot = m.DisplayIndexBot + 1
if m.DisplayIndexBot >= m.DataCount then m.DisplayIndexBot = 0
l_d = m.Data[ m.DisplayIndexBot ]
l_d.Y = l_bd.Y + m.RowHeight
if l_d.Y >= m.BufferHeight then l_d.Y = 0
m.DisplayIndexTop = m.DisplayIndexTop + 1
if m.DisplayIndexTop >= m.DataCount then m.DisplayIndexTop = 0
' So here there is only one call to the drawing functions and the row
' is always written to an area of the bitmap outside the position of the region
' the region is then scrolled in via offsetting
m.BufferBitmap.DrawRect( 0, l_d.Y, m.BufferWidth, m.RowHeight, m.BkgClr )
m.BufferBitmap.DrawText( l_d.SText, l_d.X, l_d.Y, l_d.TextClr, m.TextFont )
m.BufferBitmap.Finish()
' Ease the row into view
l_offset = 0
l_prevset = 0
l_offdiff = 0
for l_i = 1 to a_frames
l_offset = int( m.RowHeight * l_i / a_frames )
l_offdiff = l_offset - l_prevset
l_prevset = l_offset
' So here we have a simple offsetting of the y coordinate of the region
' Think of it as a shopping list of items on a paper and you are my age and
' need a magnifying glass to look at it. You only add an item to the bottom of
' your shopping list and then use your magnifying glass to move down the list. The list
' does not move around or change positions on the page. It's only the magnifying
' glass that is moving thus leaving the previous row and bringing in the next row
m.BufferRegion.Offset( 0, l_offdiff, 0, 0 )
m.BufferRegion.Finish()
m.Screen.Clear( m.BkgClr )
m.Screen.DrawObject( m.X, m.Y, m.BufferRegion )
m.Screen.SwapBuffers()
end for
End Function
' Same as above only the opposite direction
Function scrolling_list_scroll_up_region( a_frames = 16 As Integer ) As Void
l_td = m.Data[ m.DisplayIndexTop ]
m.DisplayIndexTop = m.DisplayIndexTop - 1
if m.DisplayIndexTop < 0 then m.DisplayIndexTop = m.DataCount - 1
l_d = m.Data[ m.DisplayIndexTop ]
l_d.Y = l_td.Y - m.RowHeight
if l_d.Y < 0 then l_d.Y = m.BufferHeight - m.RowHeight
m.DisplayIndexBot = m.DisplayIndexBot - 1
if m.DisplayIndexBot < 0 then m.DisplayIndexBot = m.DataCount - 1
m.BufferBitmap.DrawRect( 0, l_d.Y, m.BufferWidth, m.RowHeight, m.BkgClr )
m.BufferBitmap.DrawText( l_d.SText, l_d.X, l_d.Y, l_d.TextClr, m.TextFont )
m.BufferBitmap.Finish()
' Ease the row into view
l_offset = 0
l_prevset = 0
l_offdiff = 0
for l_i = 1 to a_frames
l_offset = int( m.RowHeight * l_i / a_frames )
l_offdiff = l_offset - l_prevset
l_prevset = l_offset
m.BufferRegion.Offset( 0, -l_offdiff, 0, 0 )
m.BufferRegion.Finish()
m.Screen.Clear( m.BkgClr )
m.Screen.DrawObject( m.X, m.Y, m.BufferRegion )
m.Screen.SwapBuffers()
end for
End Function
' Offseting the coordinates
Function scrolling_list_scroll_down_offset( a_frames = 16 As Integer ) As Void
l_bd = m.Data[ m.DisplayIndexBot ]
m.DisplayIndexBot = m.DisplayIndexBot + 1
if m.DisplayIndexBot >= m.DataCount then m.DisplayIndexBot = 0
' Since you are not using region offseting here you only need to start outside
' the boundry of your bitmap depending upon which direction you are going in
l_d = m.Data[ m.DisplayIndexBot ]
l_d.Y = m.BufferHeight - m.RowHeight
m.Screen.Clear( m.BkgClr )
' Ease the row into view
l_offset = 0
l_prevset = 0
l_offdiff = 0
' Now you have to ease offset each line in your display
for l_i = 1 to a_frames
l_offset = int( m.RowHeight * l_i / a_frames )
l_offdiff = l_offset - l_prevset
l_prevset = l_offset
m.BufferBitmap.Clear( m.BkgClr )
' Here we must use a loop to iterate or display indexes, change their coordinates
' and redraw them. More calls, more loops and more math
l_index = m.DisplayIndexTop
for l_j = 1 to m.PageSize
l_d = m.Data[ l_index ]
l_d.Y = l_d.Y - l_offdiff
m.BufferRegion.DrawText( l_d.SText, l_d.X, l_d.Y, l_d.TextClr, m.TextFont )
l_index = l_index + 1
if l_index >= m.DataCount then l_index = 0
end for
' Then you also have to bring in the new line
l_d = m.Data[ m.DisplayIndexBot ]
l_d.Y = l_d.Y - l_offdiff
m.BufferRegion.DrawText( l_d.SText, l_d.X, l_d.Y, l_d.TextClr, m.TextFont )
m.BufferRegion.Finish()
m.Screen.DrawObject( m.X, m.Y, m.BufferRegion )
m.Screen.SwapBuffers()
end for
m.DisplayIndexTop = m.DisplayIndexTop + 1
if m.DisplayIndexTop >= m.DataCount then m.DisplayIndexTop = 0
End Function
Function scrolling_list_scroll_up_offset( a_frames = 16 As Integer ) As Void
l_td = m.Data[ m.DisplayIndexTop ]
m.DisplayIndexTop = m.DisplayIndexTop - 1
if m.DisplayIndexTop < 0 then m.DisplayIndexTop = m.DataCount - 1
l_d = m.Data[ m.DisplayIndexTop ]
l_d.Y = -m.RowHeight
m.Screen.Clear( m.BkgClr )
' Ease the row into view
l_offset = 0
l_prevset = 0
l_offdiff = 0
for l_i = 1 to a_frames
l_offset = int( m.RowHeight * l_i / a_frames )
l_offdiff = l_offset - l_prevset
l_prevset = l_offset
m.BufferBitmap.Clear( m.BkgClr )
l_index = m.DisplayIndexTop
for l_j = 1 to m.PageSize
l_d = m.Data[ l_index ]
l_d.Y = l_d.Y + l_offdiff
m.BufferRegion.DrawText( l_d.SText, l_d.X, l_d.Y, l_d.TextClr, m.TextFont )
l_index = l_index + 1
if l_index >= m.DataCount then l_index = 0
end for
l_d = m.Data[ m.DisplayIndexBot ]
l_d.Y = l_d.Y + l_offdiff
m.BufferRegion.DrawText( l_d.SText, l_d.X, l_d.Y, l_d.TextClr, m.TextFont )
m.BufferRegion.Finish()
m.Screen.DrawObject( m.X, m.Y, m.BufferRegion )
m.Screen.SwapBuffers()
end for
m.DisplayIndexBot = m.DisplayIndexBot - 1
if m.DisplayIndexBot < 0 then m.DisplayIndexBot = m.DataCount - 1
End Function
Function scrolling_list_event_loop() As Boolean
if m.BufferBitmap = Invalid
print "m.BufferBitmap Is Invalid"
return False
end if
l_scrollFrameRate = 8
l_pageFrameRate = 2
l_scrollSpeed = 200
l_bp_BK = 0
l_bp_UP = 2
l_bp_DN = 3
l_bp_OK = 6
l_bp_REV = 8
l_bp_FWD = 9
l_index = 0
l_lastKey = -1
l_running = True
l_timer = CreateObject( "roTimeSpan" )
while( l_running )
l_msg = m.Port.GetMessage()
if type( l_msg ) = "roUniversalControlEvent"
l_index = l_msg.GetInt()
l_lastKey = l_index
if l_index = l_bp_DN or l_index = l_bp_FWD
m.ScrollDown( l_scrollFrameRate )
else if l_index = l_bp_UP or l_index = l_bp_REV
m.ScrollUp( l_scrollFrameRate )
else if l_index = l_bp_OK
m.Screen.Clear( m.BkgClr )
if m.IsEase
m.Screen.DrawText( "Switching To Coordinate Offsetting...", 100, 100, m.TextClr, m.TextFont )
m.ScrollDown = scrolling_list_scroll_down_offset
m.ScrollUp = scrolling_list_scroll_up_offset
else
m.Screen.DrawText( "Switching To Region Offsetting...", 100, 100, m.TextClr, m.TextFont )
m.ScrollDown = scrolling_list_scroll_down_region
m.ScrollUp = scrolling_list_scroll_up_region
end if
m.Screen.SwapBuffers()
Sleep( 1500 )
m.BufferRegion.Offset( 0, -m.BufferRegion.GetY(), 0, 0 )
m.bufferregion.Finish()
m.DisplayIndexTop = 0
m.DrawPage()
m.IsEase = not m.IsEase
l_lastKey = -1
else if l_index = l_bp_BK
l_running = False
else if l_index >= 100 ' All key ups are value + 100
l_lastKey = -1
end if
l_timer.Mark()
else if ( l_lastKey >= 0 and l_timer.TotalMilliseconds() >= l_scrollSpeed )
if l_lastKey = l_bp_DN
m.ScrollDown( l_scrollFrameRate )
else if l_lastKey = l_bp_FWD
m.ScrollDown( l_pageFrameRate )
else if l_lastKey = l_bp_UP
m.ScrollUp( l_scrollFrameRate )
else if l_lastKey = l_bp_REV
m.ScrollUp( l_pageFrameRate )
end if
end if
end while
return True
End Function
Function NewContainer( a_count As Integer ) As Object
dc = CreateObject( "roArray", a_count, False )
for l_i = 0 to a_count - 1 : dc.Push( NewData( l_i ) ) : end for
return dc
End Function
Function NewData( a_index As Integer ) As Object
return {
Index: a_index
X: 0
Y: 0
W: 0
H: 0
SText: Invalid
TextClr: Invalid
}
End Function
My Channels: 2D API Framework Presentation: https://owner.roku.com/add/2M9LCVC
Updated: 11-11-2015 - Completed Keyboard interface
The Joel Channel ( Final Beta )