In many of our recent articles, we have looked at how to use the screen and other UI templates offered by the BrightScript SDK. These components are great for getting your channels developed quickly and for giving them the standard Roku look and feel. There may be times, however, when you want to build something that doesn’t fit into this template model. You might want to implement your own custom grid for displaying poster art, display text in a non-standard way, or create a simple casual game. For many of these situations, the BrightScript 2D Graphics API may be just the thing you are looking for. The 2D API provides a number of fundamental drawing functions for drawing basic shapes, rendering and rotating images, and the like.
roScreen and the ifDraw2D Interface
The first thing to know about using the 2D API is that all rendering with the API is done via functions defined on the ifDraw2D interface. This interface is in turn implemented by the roScreen component. Therefore, in order to use the 2D API, your channel needs to create an instance of roScreen. Creating an roScreen instance is as simple as creating any other screen type in BrightScript:
screen = CreateObject(“roScreen”)
When creating an roScreen instance, CreateObject can accept a second optional boolean parameter that indicates whether the screen is double buffered.
screen = CreateObject(“roScreen”, true) ‘This creates a double buffered screen
The 2D API supports alpha blending. To turn on alpha blending call SetAlphaEnable(true). To disable alpha blending call SetAlphaEnable(false). API calls that take a color value include an alpha value that is used when alpha blending is enabled.
Like other BrightScript screen types, roScreen should be assigned a message port so that it can listen for application events such as remote control events. Once the screen is created, you can start calling 2D API functions. We will get to some of these shortly. First though we need to talk about a very key step in drawing 2D graphics. To guarantee that graphic operations are performed in single buffered screens, your channel needs to call the ifDraw2D method Finish(). Finish is a synchronous API that forces all queued graphics calls to complete. Before calling Finish, graphics that your channel renders may not be visible to the user. Keep this in mind as you are experimenting with the 2D API. If things that you are expecting are not drawing on the screen, check to see if Finish is being called. Taking the Finish function into account, the typical single buffered 2D API channel will have code that looks like this:
screen = CreateObject("roScreen") port = CreateObject("roMessagePort") screen.SetMessagePort(port) ‘Perform various drawing, graphics, etc. operations screen.Finish()
The majority of the work of course is done by the specific 2D API methods which we introduce next.
Drawing Lines and Rectangles
Perhaps the simplest graphics operations that you can perform with the 2D API are drawing lines and rectangles. The corresponding API functions are
Boolean DrawLine(Integer xStart, Integer yStart, Integer xEnd, Integer yEnd, Integer rgba) Boolean DrawRect(Integer x, Integer y, Integer width, Integer height, Integer rgba)
These functions are pretty obvious, except perhaps for the return values and the rgba argument. Many of the 2D API drawing functions return a Boolean value indicating whether the call completed successfully. The rgba argument is also common to many of the functions, and specifies the RGB color value as well as the alpha channel value to use when rendering the associated object. For example, to draw a filled yellow filled rectangle at location (0,0) that is 100 pixels wide and 200 pixels high with 100% alpha, you would call DrawRect as follows:
screen.DrawRect(0, 0, 100, 200, &hFFFF00FF)
In our tic tac toe game, we use DrawRect and DrawLine calls to render the game grid, the focus rectangle that is drawn when a square is highlighted, and the ‘X’ used to mark the player’s move selections:
Function mark_selected_move(index as integer, useX as Boolean) as void … ‘Code removed for simplicity x = ((horz_line_sep) * index)+inset_x+line_width y = inset_y + 10 w = horz_line_sep h = vert_line_sep screen.DrawLine(x, y, w+x-10, h+y-10, &h808080FF) screen.DrawLine(x+horz_line_sep, y, x, h+y-10, &h808080FF) … End Function
This function defines the start and end points for two diagonal lines centered in the square in the game grid selected by the player. These values are defined in terms of things like the vertical and horizontal distances between the game grid lines, as well as the width of those lines. The essential point here is that the DrawLine function is used twice to mark the player’s move on the game screen.
The BrightScript 2D API can also be used to draw images. Images in JPG or PNG format can be rendered using the ifDraw2D interface DrawObject function:
Boolean DrawObject(Integer x, Integer y, Object src)
DrawObject renders the object specified by the src argument at the (x, y) location on the screen specified by the first two arguments. src can be an roBitmap or an roRegion (a section of a bitmap.) For example, to draw the image packaged with a channel in the location pkg:/images/myimage.png, you could use the following BrightScript code:
screen = CreateObject("roScreen") img = CreateObject(“roBitmap”, “pkg:/images/myimage.png”) screen.DrawObject(0, 0, img) screen.Finish()
Our tic tac toe game uses this image rendering technique to draw a circle in game grid squares when the Roku Player makes a move. The 2D API does not include a function for drawing circles similar to DrawLine or DrawRect. Therefore in order to draw a circle to represent your opponent’s moves, we draw a PNG file packaged with the channel.
The 2d API can also be used to draw text. The function used for this purpose is, you guessed it, DrawText:
Boolean DrawText(String text, Integer x, Integer y, Integer rgba, Object font)
text contains the string to draw, and font contains the font to use when rendering the text. rgba contains the color and alpha values to use. In our tic tac toe game, we use DrawText to announce the winner of the game, or to indicate that there was a tie:
As an example of how to use the DrawText API, here is how you could draw the text “Hello Roku” using the default device font:
fontRegistry = CreateObject("roFontRegistry") font = fontRegistry.GetDefaultFont() screen.DrawText(txt, 0, 0, &h00FFFFFF, font) screen.Finish()
Note that drawing text is a very expensive operation and should be done sparingly.
More information about the 2D API can be found starting from the SDK documentation for the roScreen component at this link. The tic tac to sample game can be downloaded by clicking this link. Stay tuned for more articles on additional 2D graphics topics. In the meantime, have fun experimenting with Roku’s graphics features.