Roku Developer Program

Join our online forum to talk to Roku developers and fellow channel creators. Ask questions, share tips with the community, and find helpful resources.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Komag
Roku Guru

Re: Brief summary of advantages of ditching classic Rokus?

Okay, I see what you mean. It almost sounds like the roCompositor example "Scrolling a bitmap". If I understand correctly, this also sounds like basically what NewAnimatedSprite does internally, just loads one wide picture containing all the "frames", and offsets the region when needed for the next frame.

How does this come into play with text menus though? Are you moving the menu around somewhere, or in what way do you reduce the slowdown effect of drawing a lot of text?
0 Kudos
NewManLiving
Visitor

Re: Brief summary of advantages of ditching classic Rokus?

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 )
0 Kudos
Komag
Roku Guru

Re: Brief summary of advantages of ditching classic Rokus?

On my Roku 2 XS there is almost no difference in this example, but man, on my new Roku 1 it's night and day, wow! :shock:
0 Kudos
NewManLiving
Visitor

Re: Brief summary of advantages of ditching classic Rokus?

Yeah same on my stick. Thought there would be better performance on the stick but it was just as bad as the LTE. Perfectly smooth though with region offsetting.
My Channels: 2D API Framework Presentation: https://owner.roku.com/add/2M9LCVC
Updated: 11-11-2015 - Completed Keyboard interface
The Joel Channel ( Final Beta )
0 Kudos