Roku Developer Program

Developers and content creators—a complete solution for growing an audience directly.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Highlighted
EnTerr
Level 9

Convert roByteArray -> roBitmap ?

For hours I have been pulling my hair (ok, metaphor; my hair is short so it was more of scratching/scalp massaging) trying to figure what's a way to efficiently display generated bitmap content on Roku and could not find it. Where "generated" means it is not some static image you can read from jpeg/png but changes dynamically and "efficient" is to signify fast - as magnitude, in the hundred times per second for small regions (say 1/100 of screen, so that's only a couple FPS in data throughput, not video). Poking that pixel by pixel won't work.

Seems to me that could be done well if there was a function to convert from roByteArray to roBitmap (or alike). There is already ifDraw2D.GetByteArray(x, y, w, h), which does roBitmap->roByteArray. I don't know whether the opposite direction should be createObject("roBitmap", {width: w, height: h, byteArray: ba}) or ifByteArray.toBitmap(...) or ifDraw2D.setByteArray(...). Well, the last one might be hairy, since other objects implement ifDraw2D and who knows if the assigned buffer can be replaced. Creating new bitmap given byte array should be pretty straightforward though - it's underlying to the already existing image file->bitmap loading.

Why do i need that, you ask. Well here is couple of ideas i have in mind to implement that won't work w/o it:

  • VNC client. VNC's RFB protocol is raster-based, it sends updates as rectangular sub-regions of the screen, in one of multiple encoding forms - raw, RLE, in 16x16 hextiles (or one of the newer compression++ methods but that's not important since negotiable). And that's the only way graphics is transferred, there are no vectors/text/sprites/alpha/lines/curves/arcs. Only bitmaps, those are matter of life and death for VNC.


  • A loupe (magnifying glass). This is more of a demo or test of the player abilities. Something that i did like twenty years ago, in college on PC/AT (6MHz 80286 CPU). The program was showing a moving circular-shaped magnifying glass over any image - by calculating properly the refraction by a semi-spherical lens (i.e. using Snell's law and refractive index of glass), including some "fisheye" effect (vs say dumb scaling combined with circular alpha cookie-cut). It's a special case of ray-tracing and sounds slow but i was able to come with some clever optimizations for the case so that it was working sufficiently fast: it was able to recalculate and redraw the loupe image multiple times per second, thus i made it animated with the lens bouncing around the screen. This was all written in high-level language (Turbo Pascal) poking directly in screen buffer, with no assembly nor graphic library (individual GetPixel/PutPixel too slow) - so i suspect i can get at least the same performance nowadays on Roku's BrS interpreter. If i can assemble bitmap's byte array, that is.
0 Kudos
13 Replies
Highlighted
TheEndless
Level 7

Re: Convert roByteArray -> roBitmap ?

The only way I've found to do what you're wanting to do is by using DrawRect() to draw 1x1 rectangles for each pixel, or by creating 1x1 roRegions and using Clear(), neither of which is efficient nor fast. You could potentially come up with some algorithm that calculates different size rects for larger color swatches, which could improve the speed, but the extra processing to calculate those would probably end up in a wash.

I think being able to call SetByteArray would be the best and most efficient way to implement the functionality, which has been discussed in the past, but nothing ever came of it. I've also tried implementing an uncompressed PNG routine that would convert a byte array into a PNG, but couldn't quite figure out the headers to pull it off without doing some amount of processing of the byte array, which ended up being just as inefficient.
My Channels: http://roku.permanence.com - Twitter: @TheEndlessDev
Instant Watch Browser (NetflixIWB), Aquarium Screensaver (AQUARIUM), Clever Clocks Screensaver (CLEVERCLOCKS), iTunes Podcasts (ITPC), My Channels (MYCHANNELS)
0 Kudos
Highlighted
EnTerr
Level 9

Re: Convert roByteArray -> roBitmap ?

I woke up today with the crazy idea that maybe - just maybe - getByteArray() already allows what i need by actually returning the inner byte array from roBitmap (i.e. repeated calls return the same object), so that i get to peek and poke directly at that while it still belongs to the bitmap. My hopes withered when i glanced the doc to be reminded it takes (x,y,w,h) as arguments, so no - it every time cuts you a new object that is not connected to the underlying bitmap.

Yes, i concluded that DrawRect(x,y,1,1,c) is about the only way to draw pixels. I understand why they did not include SetPixel() as such - because of possible autoscaling up, SetPixel(4,0,c) + SetPixel(5,0,c) is not the same as DrawRect(4,0,2,1,c). roRegion done per pixel would have huge overhead (yet cannot be faster). Generating/writing to/loading from disk hundreds of PNGs per second is a non-starter too.

After sleeping on it, i feel something like createObject("roBitmap", {width: w, height: h, byteArray: ba}) is probably the cleanest way to do it, since the functionality is already there for createObject("roBitmap", String filename) - it reads the file, uncompresses it to a buffer and then creates the ro-wrapper with params around that buffer, so same thing sans reading and uncompressing file; re-use the code. But it does not matter which approach - even an imagined ifDraw2D.getInnerByteArray() to let massage directly the data will do.

Would like to hear from RokuCo on this, please. This will have great pay-off to dev.effort ratio, wouldn't it? Unleash power that is already in the box.
0 Kudos
Highlighted
TheEndless
Level 7

Re: Convert roByteArray -> roBitmap ?

"EnTerr" wrote:
After sleeping on it, i feel something like createObject("roBitmap", {width: w, height: h, byteArray: ba}) is probably the cleanest way to do it, since the functionality is already there for createObject("roBitmap", String filename) - it reads the file, uncompresses it to a buffer and then creates the ro-wrapper with params around that buffer, so same thing sans reading and uncompressing file; re-use the code.

That wouldn't allow you to manipulate existing bitmaps, though. A SetByteArray() method would work no matter how the bitmap was created. Given your VNC example, you could create a full screen bitmap, and just call SetByteArray for every 16x16 section that changes on refresh, instead of having to create individual 16x16 bitmaps and draw them to the master. Better yet, you could set the byte array directly on the roScreen, further reducing the overhead... no bitmaps, DrawObjects, or DrawRects needed.
My Channels: http://roku.permanence.com - Twitter: @TheEndlessDev
Instant Watch Browser (NetflixIWB), Aquarium Screensaver (AQUARIUM), Clever Clocks Screensaver (CLEVERCLOCKS), iTunes Podcasts (ITPC), My Channels (MYCHANNELS)
0 Kudos
Highlighted
EnTerr
Level 9

Re: Convert roByteArray -> roBitmap ?

"TheEndless" wrote:
That wouldn't allow you to manipulate existing bitmaps, though. A SetByteArray() method would work no matter how the bitmap was created. Given your VNC example, you could create a full screen bitmap, and just call SetByteArray for every 16x16 section that changes on refresh, instead of having to create individual 16x16 bitmaps and draw them to the master. Better yet, you could set the byte array directly on the roScreen, further reducing the overhead... no bitmaps, DrawObjects, or DrawRects needed.

But i don't require to manipulate existing bitmaps. I am a modest man ("with much to be modest about") and can make do with only a little. In this case any of the 4 alternatives i described - or a 5th you propose.

What you describe is not what i thought of setByteArray(), it is more of DrawByteArray() - with behavior close to DrawObject(). If they implement it the way you imagine it - great, i'll use it - thankyouverymuch! I was trying to appreciate the difficulties in implementing the link roByteArray->roBitmap (tough job, thinking of a black box), under the presumption that the simpler something is, more likely it is to happen. Maybe i under-estimate the cost of creating new bitmap by feeding it byteArray (and imagining it won't be copied but only its ref.count will be bumped) and later disposing it. But anything will be better than the current nothing.
0 Kudos
Highlighted
TheEndless
Level 7

Re: Convert roByteArray -> roBitmap ?

Just so this doesn't get lost...
"EnTerr" wrote:
Would like to hear from RokuCo on this, please. This will have great pay-off to dev.effort ratio, wouldn't it? Unleash power that is already in the box.
My Channels: http://roku.permanence.com - Twitter: @TheEndlessDev
Instant Watch Browser (NetflixIWB), Aquarium Screensaver (AQUARIUM), Clever Clocks Screensaver (CLEVERCLOCKS), iTunes Podcasts (ITPC), My Channels (MYCHANNELS)
0 Kudos
Highlighted
Roku Employee
Roku Employee

Re: Convert roByteArray -> roBitmap ?

I can resuscitate this feature request for this and see what happens.

- Joel
0 Kudos
Highlighted
EnTerr
Level 9

Re: Convert roByteArray -> roBitmap ?

"RokuJoel" wrote:
I can resuscitate this feature request for this and see what happens.

Please do - and add the reasoning/discussion from this thread.
And as always, requesting to please track-back to this thread any developments, i am sure >1 will be eagerly waiting.
0 Kudos
Highlighted
GPF
Level 7

Re: Convert roByteArray -> roBitmap ?

Here is a little simple pixel plotter I wrote awhile ago, using an uncompressed gif this only supports 4 index colors(trivial to change to support up to 127 colors). When I was trying to find something faster then doing DrawRect(x,y,1,1,c) . Maybe someone can find a use, my 8080 space invaders emulator port was still way to slow Smiley Sad

Thanks,
Troy

main.brs
' ******
' ****** Create a GIF For Roku by Troy Davis(GPF)
' ******

Library "v30/bslDefender.brs"

Function IsHD()
di = CreateObject("roDeviceInfo")
if di.GetDisplayType() = "HDTV" then return true
return false
End Function

function main()

width%=160
height%=144
bckcolrindx%=0
trnspclrindx%=0

theGIF = CreateObject("roByteArray")
tmpfilename = "tmp:/1.gif"
print "Temp File "+tmpfilename

backstop = CreateObject("roParagraphScreen")
backstop.show()

if IsHD()
screen=CreateObject("roScreen", true, 854, 480) '
else
screen=CreateObject("roScreen", true)
endif
msgport = CreateObject("roMessagePort")
screen.SetPort(msgport)

initgif(width%,height%,bckcolrindx%,trnspclrindx%,theGIF)

print "Gif Init done"

for y% = 0 to (height%-1)
for x%=0 to (width%-1)

plotgif(x%,y%,width%, fix( (x%+y%) MOD 3),theGIF )

end for
end for

print "Gif Plotting done"

savegif(tmpfilename,theGIF)


print "Gif saved done"


newGIF=CreateObject("roBitmap", tmpfilename)

print "Gif loading done"

if newGIF = invalid
print "newGIF create failed"
stop
endif

screen.drawobject(50, 50, newGIF)

screen.SwapBuffers()

print "Gif drawing done"

DeleteFile(tmpfilename)

codes = bslUniversalControlEventCodes()

pressedState = -1 ' If > 0, is the button currently in pressed state
while true
if pressedState = -1 then
msg=wait(0, msgport) ' wait for a button press
else
msg=wait(1, msgport) ' wait for a button release or move in current pressedState direction
endif
if type(msg)="roUniversalControlEvent" then
keypressed = msg.GetInt()
print "keypressed=";keypressed
if keypressed=codes.BUTTON_BACK_PRESSED then
pressedState = -1
exit while
end if
else if msg = invalid then
print "eventLoop timeout pressedState = "; pressedState

end if
end while

end function


gifplotter.brs
' ****************************************************************
' * Generates a GIF with 4 colors that can be updated *
' * For Roku by Troy Davis(GPF) *
' ****************************************************************

function initgif (width% as integer, height% as integer, backgroundcolorindex% as integer, transparentcolorindex% as integer, theGIF as object)' as object', colorpal as object, numcolors as integer) as

bodylength%=fix((width%*height%)/126)
bodyremain%=(width%*height%) MOD 126

'theGIF.setresize( 416+(bodylength%*128)+bodyremain%+6,false)

theGIF.push(asc("G"))
theGIF.push(asc("I"))
theGIF.push(asc("F"))
theGIF.push(asc("8"))
theGIF.push(asc("9"))
theGIF.push(asc("a"))
theGIF.push(0) ' Width
theGIF.push(1) ' Width
theGIF.push(0) ' Height
theGIF.push(1) ' Height
theGIF.push(&HF1) ' GCT follows for 4 colors
theGIF.push(0) ' Background color
theGIF.push(0) ' default pixel aspect ratio
theGIF.push(0) ' Color 0 Black
theGIF.push(0) ' Color 0 Black
theGIF.push(0) ' Color 0 Black
theGIF.push(&HFF) ' Color 1 White
theGIF.push(&HFF) ' Color 1 White
theGIF.push(&HFF) ' Color 1 White
theGIF.push(&HFF) ' Color 2 Red
theGIF.push(0) ' Color 2 Red
theGIF.push(0) ' Color 2 Red
theGIF.push(0) ' Color 3 Green
theGIF.push(&HFF) ' Color 3 Green
theGIF.push(0) ' Color 3 Green
theGIF.push(&H21) ' Graphic Control Extension
theGIF.push(&HF9) ' Graphic Control Extension
theGIF.push(4) ' 4 bytes of GCE data follow
theGIF.push(1) ' there is a transparent background color
theGIF.push(0) ' delay for animation: not used
theGIF.push(0) ' there is a transparent background color
theGIF.push(0) ' color #0 is transparent
theGIF.push(0) ' end of GCE block
theGIF.push(&H2C) ' Image Descriptor
theGIF.push(0) ' NW corner position of image in logical screen
theGIF.push(0) ' NW corner position of image in logical screen
theGIF.push(0) ' NW corner position of image in logical screen
theGIF.push(0) ' NW corner position of image in logical screen
theGIF.push(0) ' Width
theGIF.push(1) ' Width
theGIF.push(0) ' Height
theGIF.push(1) ' Height
theGIF.push(0) ' no local color table
theGIF.push(7) ' Start of image - LZW minimum code size



'theGIF.ReadFile("pkg:/data/gifhead.bin")

'm.width%=width%
'm.height%=height%

tempw%=(width% and &HFF)
theGIF[6]=tempw%
theGIF[38]=tempw%
tempw%=((width%/256) and &HFF)
theGIF[7]=tempw%
theGIF[39]=tempw%

temph%=(height% and &HFF)
theGIF[8]=temph%
theGIF[40]=temph%
temph%=((height%/256) and &HFF)
theGIF[9]=temph%
theGIF[41]=temph%

theGIF[11]=backgroundcolorindex%
theGIF[30]=transparentcolorindex%

gifpart = CreateObject("roByteArray")
'gifpart.ReadFile("pkg:/data/gifpart.bin")
gifpart.fromhexstring(String(252,"0"))

For i%=0 to bodylength%-1

if (i% <> bodylength%)
theGIF.push(127)
theGIF.push(128)
end if

theGIF.append(gifpart)

end for
theGIF.push(3+bodyremain%)
theGIF.push(128)
For i%=0 to bodyremain%-1
theGIF.push(0)
end for
theGIF.push(129)
theGIF.push(0)
theGIF.push(0)
theGIF.push(59)

end function

function plotgif (x% as integer, y% as integer, width% as integer, colorindex% as integer, theGIF as object)' as object

theGIF[(46+(((width%)*y%+x%))+( fix(((width%)*y%+x%)/126)*2))] = colorindex%

'return theGIF
end function

function savegif (filename as string, theGIF as object)


'?theGIF.count()
'print "File "+filename
theGIF.writefile(filename)


'myurl=CreateObject("roUrlTransfer")
'myurl.SetUrl("http://192.168.0.2/roku/upload.php")
'myurl.AddHeader("Content-Type", "image/png")
'myurl.PostFromFile(filename)

return 1
end function
0 Kudos
Highlighted
EnTerr
Level 9

Re: Convert roByteArray -> roBitmap ?

"EnTerr" wrote:
"RokuJoel" wrote:
I can resuscitate this feature request for this and see what happens.

Please do - and add the reasoning/discussion from this thread.
And as always, requesting to please track-back to this thread any developments, i am sure >1 will be eagerly waiting.

Any news on this?

I was reading http://www.cnet.com/news/get-a-google-c ... -for-29-99 - which on a complete tangent mentions and compliments Roku stick - and was reminded the difficulties of making non-video apps on the platform. Does RokuCo still want developers to write casual games and apps for Roku? Perhaps RokuPatrick (if he is still the one behind dev.program) can shed some light for us?
0 Kudos