Roku Developer Program

Developers and content creators—a complete solution for growing an audience directly.
cancel
Showing results for 
Search instead for 
Did you mean: 

RoCompositor

Hi,

Is there any example or sample code i can get where we used two seperate images combined into sprite using RoCompositor dynamically. I saw a sample with one big sprite image.

Or is it not possible two dynamically combine two images?

Is need to take a decision on what route to take based on the above example. Any help on this would be highly appreciated.

Thanks in advance.

Regards,
Prajwal
0 Kudos
8 Replies
Komag
Level 9

Re: RoCompositor

Not sure exactly what you mean by "combined" - do you mean right on top of one another? You can just draw them on top of each other, with alpha blending and have a "clear" color at least in the top picture so parts will be see-through as you wish. You can do it with or without the compositor.
0 Kudos
NewManLiving
Level 7

Re: RoCompositor

Not sure what you are trying to do but combining images is usually done by creating a host or parent bitmap big enough to contain both of them, drawing each of them into host the bitmap and then creating a sprite out of the host bitmap. You can also investigate changing the sprites region roSprite.SetRegion. Rule of thumb: Don't go sprite crazy - many little sprites are for game developers who need what they offer in the way of collision etc...Learn to use the method above where you combine images into a parent bitmap for grids, slideshows, and other interface. Then create a sprite from the larger bitmap and use region offsetting to move things around instead of doing a bunch a DrawObjects to reposition everything when needed. This is a great performance boost for the low-end boxes. As a side note you do not need a sprite to use a region. Sprites are a good way to perform complex, layered drawings that are more or less dynamic ( in asynchronous environments ) much the same as a game.
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

Re: RoCompositor

Hi,

I tried using the parent bitmap concept but the problem i am having is i have to create a layout similar to grid layout with scrolling but each image is of size around 600px and i have around 20 images to display. The issue i am facing the bitmap has a size restriction of 2048px. And also the other issue is even if i set setSwrap true it doesnot wrap but instead gets cut off.

Any help on this would be appreciated, thanks in advance.
screen=CreateObject("roScreen")
compositor=CreateObject("roCompositor")
compositor.SetDrawTo(screen, black)
parentCanvas = createobject("roBitmap", {width:2000, height:720, AlphaEnable: false})
dfDrawImage(parentCanvas, "image1.jpg",0,200)
dfDrawImage(parentCanvas, "image2.jpg",600,200)
dfDrawImage(parentCanvas, "image3.jpg",1200, 200)
dfDrawImage(parentCanvas, "image4.jpg",1800, 200)
region=CreateObject("roRegion", parentCanvas, 0, 0, screen.getwidth(), 325)
region.SetWrap(true)

view_sprite=compositor.NewSprite(0, 0, region)
compositor.draw()
screen.SwapBuffers()

Thanks in advance.

Prajwal
0 Kudos
NewManLiving
Level 7

Re: RoCompositor

With larger image sets, and to take full advantage of region-offsetting as opposed to bitmap coordinate offsetting, You need to understand the concept of a virtual buffer. The following example is a much earlier version of the grids I build today. But the concept of a virtual buffer is the same. For the final step, when you have more bitmaps then can fit it memory you can introduce the texturemanager into the grid. This adds a whole new level of complexity but it is doable.


' Ok this is an updated version that shows how to page swap the indexes. The concept here is
' 1 - When a page is written it is always going forward so both indexes are resting above
' the next index increment. So if you are incrementing forward all you have to do is increase
' by one position. However, if the user decides to move in the opposite direction then you
' have to flip everything by a pagesize (number of bitmaps visually seen in the dataindex)
' (regionsize for the virtual cell index). Picture a dog chasing it's tail. The bitmap is
' stationary but the region is moving frantically back and forth over the bitmap looking to catch and empty
' virtual cell. But the cell is smart and is always running in front or in back one step
' ahead. So when a transition is made from one direction to the other then you have to flip
' I did not comment too much I really have things I must get done. Any questions
' or mistakes let me know and I will fix them. All this stuff is already in my framework in a
' different but (functionally the same) format so it will be easy to see where the mistake is and correct it

Function Main() As Void

' standard stuff
Screen = CreateObject("roScreen", True)
Screen.Clear(&hE0DFDFFF)
Screen.SwapBuffers()

SimpleVGrid(Screen)

End Function


Function SimpleVGrid(Screen As Object)

' Generate 100 test bitmaps
bitmaps = GetBitmaps()

' Total number of bitmaps
bitmapCount = bitmaps.Count()
' Bitmap index
bitmapIndex = 0
' virtual column index
bufferIndex = 0

' width and height of our bitmaps. You can play with this if you want (change it to your size)
' but again this is an introduction so all that good stuff comes later
width = 224
height = 200
' If you don't want to use a margin set it to 0
margin = 15

' This is how many cells in the grid are displayed you can set it from 1 to
' the number that will fit comfortably in the dimensions of your display. It is
' not necessary to go beyond the screen. The user cannot see any more than that
' If you want that effect, then offset in drawobject.
' And you risk making the grid bitmap too large and throwing an exception
' Screen.DrawObject( 75, 200, gridRegion ) increase the 75 to where you like it

' I would not go much beyond this. Also if your data is less than the page size
' you have to make adjustments yourself (see my virtual list view example in 2d api wisdom)
' This examle is fixed to show how to create a virtual cell. Error checking is your business
' I have tested page sizes from 1 to 5
pageSize = 5
' The actual column width is the width of the bitmap plus any margin
columnWidth = (width + margin)

' Ok now we are going to create the grid buffer. You notice that its width will be
' pagesize * columnwidth + columwidth the additional columnwidth is the virtuall column
bmBuffer = pageSize * columnWidth + columnWidth
' What the user sees is the same size - the extra column. that column is updated
' and then scrolled into view.
bmRegion = pageSize * columnWidth

' Create your bitmap and region based on the above calculations. The region will start at
' position 0,0 in the bitmap, extend the width of the bitmap minus one columnwidht
' and be the same heigh as the bitmap
gridBitmap = CreateObject("roBitmap", {width: bmBuffer, height: height, AlphaEnable: false})
gridRegion = CreateObject("roRegion", gridBitmap, 0, 0, bmRegion - margin, height)
' You must set the wrap to true for this to work, otherwise you have to do it another way
gridRegion.SetWrap(true)

' Now its time for the fun. Bitmap index is set to 0 . It is an index into the
' array of bitmaps . x is set to 0 then incremented by columnwidth to draw each
' page full of bitmaps
x = 0
bitmapIndex = 0

' Clear the bitmap
gridBitmap.Clear(&hE0DFDFFF)

' I prefer to do it this way (2 to pagesize) then after the loop exits
' draw the final column
for i = 2 to pageSize

' Now you want to draw a page full of bitmpas right into
' the bitmap buffer
gridBitmap.DrawObject(x, 0, bitmaps[bitmapIndex])

' Increment the x pos by columnwidth to draw the next cell
x = x + columnWidth

' Increment the bitmapindex by one to draw the next bitmap in the array
' If you have to wrap around then do so
bitmapIndex = bitmapIndex + 1
if bitmapIndex >= bitmapCount then bitmapIndex = 0

end for

' After we exit the loop you notice that x was incremented and bitmapindex was
' incremented in the loop but the loop itself failed because the limit was reached
' so all you need to do is write the last cell and set the virtual index at x
' NOTE: if you change this then you have to change the code below that increments
' these as well .
gridBitmap.DrawObject(x, 0, bitmaps[bitmapIndex])
bufferIndex = x
pagingRight = True

' Ok now put this bitmaps newly drawn region onto the screen
' Like is said you can change the x = 75 to whatever to make it
' appear like it is coming in from outside the screen
Screen.Clear(&hE0DFDFFF)
Screen.DrawObject( 50, 200, gridRegion )
Screen.SwapBuffers()

port = CreateObject("roMessagePort")
Screen.SetPort(port)

running = true
scrollSpeed = 100
lastKey = -1
kp_LT = 4
kp_RT = 5
kp_BK = 0
kp_FWD = 9
kp_REV = 8

while(running)

msg = wait(scrollSpeed, port)

if type(msg) = "roUniversalControlEvent"

index = msg.GetInt()
lastKey = index

if index = kp_LT or index = kp_REV
'Ok added a flag to indicate what direction the user is going in
'Since an adjustment needs to be made only once you want to keep that
'in the else portion of you statement not on the top (think speed, speed, speed)

' In this case the page adjustment was made (under the else) and the flag was set
' so if we are not pagingRight then we must be pagingLeft
if not pagingRight

bitmapIndex = bitmapIndex - 1
if bitmapIndex < 0 then bitmapIndex = bitmapCount - 1

bufferIndex = bufferIndex - columnWidth
if bufferIndex < 0 then bufferIndex = bmRegion

else
' Otherwise the user was formally paging right so we have to flip and set the flag
bitmapIndex = bitmapIndex - pageSize
if bitmapIndex < 0 then bitmapIndex = bitmapCount + bitmapIndex

bufferIndex = bufferIndex - bmRegion
if bufferIndex < 0 then bufferIndex = bufferIndex + bmRegion + columnWidth

pagingRight = False

end if

gridBitmap.DrawObject(bufferIndex, 0, bitmaps[bitmapIndex])
ScrollRegion(pagingRight, gridRegion, Screen, columnWidth)

else if index = kp_RT or index = kp_FWD

' Just read the top and apply the opposite
if pagingRight

bitmapIndex = bitmapIndex + 1
if bitmapIndex >= bitmapCount then bitmapIndex = 0

bufferIndex = bufferIndex + columnWidth
if bufferIndex > bmRegion then bufferIndex = 0

else

bitmapIndex = bitmapIndex + pageSize
if bitmapIndex >= bitmapCount then bitmapIndex = bitmapIndex - bitmapCount

bufferIndex = bufferIndex + bmRegion
if bufferIndex > bmRegion then bufferIndex = bufferIndex - bmRegion - columnWidth

pagingRight = True

end if

gridBitmap.DrawObject(bufferIndex, 0, bitmaps[bitmapIndex])
ScrollRegion(pagingRight, gridRegion, Screen, columnWidth)

else if index > 100 ' All key ups are value + 100

scrollSpeed = 100

else if index = kp_BK

running = False

end if

else
' The user is holding down the key. So you have to repeat all this
' nonsense again (THUS THE OBJECT). I mean you can put all this
' in a function but once you get used to the object you will never
' go back to the old way. You can play with the speeds here
' to get it to your liking. Once again just right for now
'if lastKey = kp_LT or lastKey = kp_RT

' I threw in a goodie here. If you use the page forward key you can
' see how fast this really is. I really don't expect much of a hit
' from loading one bitmap from temp each time or periodically
if lastKey = kp_LT or lastKey = kp_REV

bitmapIndex = bitmapIndex - 1
if bitmapIndex < 0 then bitmapIndex = bitmapCount - 1

bufferIndex = bufferIndex - columnWidth
if bufferIndex < 0 then bufferIndex = bmRegion

gridBitmap.DrawObject(bufferIndex, 0, bitmaps[bitmapIndex])

scrollSpeed = 5
if lastKey = kp_LT
ScrollRegion(pagingRight, gridRegion, Screen, columnWidth, 8)
else
ScrollRegion(pagingRight, gridRegion, Screen, columnWidth, 2)
end if

else if lastKey = kp_RT or lastKey = kp_FWD

bitmapIndex = bitmapIndex + 1
if bitmapIndex >= bitmapCount then bitmapIndex = 0

bufferIndex = bufferIndex + columnWidth
if bufferIndex > bmRegion then bufferIndex = 0

gridBitmap.DrawObject(bufferIndex, 0, bitmaps[bitmapIndex])

scrollSpeed = 5
if lastKey = kp_RT
ScrollRegion(pagingRight, gridRegion, Screen, columnWidth, 8)
else
ScrollRegion(pagingRight, gridRegion, Screen, columnWidth, 2)
end if

end if

end if


end while

End Function

' This is the where the nice effect occurs
' You can use many of the Robert Penner's easing functions (THANK YOU ROBERT)
' Bounce them off a tree if you want. This one is the simplest and most frequently used
' Play with the values and adjust them to your liking
Function ScrollRegion(goRight As Boolean, Region As Object, Screen As Object, width As Integer, frames = 16 As Integer) As Void

offset = 0
offdiff = 0
prevset = 0

if frames < 2 then frames = 2

for i = 1 to frames

offset = int(width * i / frames)
offdiff = offset - prevset

if offdiff > 0

if not goRight then offdiff = -offdiff

Region.Offset(offdiff, 0, 0, 0)

Screen.DrawObject( 50, 200, Region)
Screen.SwapBuffers()

prevset = offset

end if

end for

End Function


' Get some dummy bitmaps for the examle. Of course you would use your own
' Fix the thumb at 100 * 150 just so you can understand how it works
Function GetBitmaps() As Object

number = 100

width = 224
height = 200

clrs = [
&h222835FF,
&h203864FF,
&hAD4F0FFF,
&h324D1FFF,
&h7A0000FF,
]


fontRegistry = CreateObject("roFontRegistry")
font32 = fontRegistry.GetDefaultFont(32, True, False)
bitmaps = CreateObject("roArray", number, False)

j = 0
count = clrs.Count()
textHeight = font32.GetOneLineHeight()
y = int(height / 2 - textHeight / 2)

for i = 0 to number - 1

bitmap = CreateObject("roBitmap", {width: width, height: height, AlphaEnable: false})

bitmap.Clear( clrs[j] )

i_str = (i + 1).ToStr()
str_w = font32.GetOneLineWidth(i_str, width)
x = int(width / 2 - str_w / 2)
bitmap.DrawText(i_str, x, y, &hFFFFFFFF, font32)

bitmaps.Push(bitmap)

j = j + 1
if j >= count then j = 0

end for

return bitmaps

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

Re: RoCompositor

Hi,

Thanks that was really helpful. Is there any documentation i can refer to understand the bitmap cache more throwly. Like when does roku device clears the bitmap cache and what is the cache limit.

Sorry for troubling you, since i just started working on device trying to understand the capability.

Regards,
Prajwal
0 Kudos
NewManLiving
Level 7

Re: RoCompositor

There are two ways to control larger amounts of bitmaps that may exceed available memory. roUrlTransfer and roTextureManager. I prefer to use roTextureManager for grids, although a channel I'm working on now uses roUrlTransfer for the grid since there is no need for asynchronous retrieval. The great thing about the TextureManager is its LRU cache which is undocumented, debated as to its actual existence, but can be readily observed by those that use it. It provides a great speed advantage over roURLTransfer for fast scrolling grids. To use it well you need to be able to build your application to handle asynchronous communication in a single-threaded environment. You generally request a number of textures (fetch ahead so to speak ) while the user is scrolling in, what the user is scrolling out is unloaded from the manager so that memory stays at a constant. Since you do not wait on asynchronous calls you have to be always listening on a dedicated port, especially in loops that may block for some time. With asynchronous calls the function does not wait but returns immediately and you are notified via it's event that a texture has arrived. One pitfall that many developers encounter when beginning to use asynchronous calls is not persisting their request object. The roTextureRequest for example cannot go out of scope until the texture is received. So you need to build a structure which contains information about what to do with the bitmap, a handle to the roTextureRequest. You keep a dictionary of these objects in an associative array. You can use the ID that is returned to you as a key. Keep this list at a global level. When you receive your texture event get, the ID and retrieve your structure from your dictionary. You then have all the information you need (if you designed it that way) to use draw it where you like. You can put parent bitmap in there, the x, y coordinates etc. Just be sure to invalidate all references when done with it.

To get around things that insuring sprites are removed and bitmaps are invalidated I designed a simple framework that uses the roAssociativeArray as an object. With this you can assign function pointers to operate on any member of the associative array using the m pointer. Works good for me
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

Re: RoCompositor

Hi,

Thanks for the reply that was really helpful. Had a small doubt i might be misunderstanding the concept but when if i unload a scrolled out images when the user comes back again to scrolled out image it will ended up downloading that remote image again right sir.

But i see lot of advantage of using TextureManager so that we can be sure to clear the cache when the user goes out to a new page.

Thanks again for the help.

Regards,
Prajwal
0 Kudos
NewManLiving
Level 7

Re: RoCompositor

Yes, the texturemanager is a great tool when you need an undefined number of textures. Its pretty quick, well almost, the videoplayer trashes the lru but you can jump start it back by a few retrieves and a pause, only need about 200-300 ms pauses, in your pause loop (create with a timer) just poll the texture manager's queue as textures arrive, pull them out of your list with the id. Have everything in that structure in the list. The parent bitmap, the coordinates of the bitmap. When you do this your event handler can just write the bitmap to the x,y coordinates of the bitmap in the structure. Call Compositor.DrawAll, swapbuffers and you see it pop right into the parent bitmap. It is important again that you use a parent bitmap and region offsetting. Not only does this guarantee better scrolling performance but since you are offsetting the region and not the bitmaps when the user scrolls, your bitmap's x,y coordinates are fixed. The only one that changes is the one that is written into the virtual portion of the buffer. Otherwise if you use coordinate offsetting, you would have to go through all your structures and change the x,y positions before the texture was received by your texturemanager event handler. If you did not, obviously they would be popping all over the place. The best way to to this that I have found, which insures an even performance across the scope of supported models is to just display your place holder as the user is scrolling. On the button up event unload the bitmaps that are no longer in the display area ( I use an internal buffer lru that has x number on either side of the display, so im always throwing out and requesting a margin of bitmaps on each side of the display area ) So say I have a buffer of 10 bitmaps, then there will always be 10, no matter when the user stops. The 10 that are existing will fast-scroll by and the rest will be placeholders. When the button is up I compare and retrieve at the index the user has stopped at. On the faster boxes you can just unload and request each texture as the user scrolls and its performance is excellent, most arrive before the virtual area of the buffer is scrolled into view, their is a straggler every now and then but its nice to know that your system is working as it pops into the correct position even as the user is in a fast scroll. On the slower boxes unfortunately its too much for the processor and you notice a studdering. But like I said not so on the 3 the new 2 and possibly the tv. WIth those devices you don't need anything but to unload and request your single bitmap or if you use a fetch ahead cache as I do. Works out the same just a couple of textures ahead of the display on either side. But frankly, if the user is scrolling they are not interested and want to get somewhere else, a grid counter indicates the position and a button up brings in almost immediately the page scrolled to so you can leave this for all devices and get excellent scrolling performance

Quite frankly it is probably the most difficult thing I have done so far so their is quite a learning curve. I spent some considerable time totally on the 2D, I could not even tell you what to do with a roPosterScreen, but I can tell something about the 2D API.
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