Skip to content

Instantly share code, notes, and snippets.

@incinirate
Last active March 1, 2017 01:23
Show Gist options
  • Save incinirate/a73bffb10eae31a6763d1658ec9eb3b9 to your computer and use it in GitHub Desktop.
Save incinirate/a73bffb10eae31a6763d1658ec9eb3b9 to your computer and use it in GitHub Desktop.

Riko 4

Graphics Interface Specs

Good practices

    Generally, the fastest way to handle graphics in Riko 4 is to rasterize everything into an image (see below) to reduce calls. However this is not always applicable as creating images is not super fast. Meaning, its not a good idea to create new images every frame. You can modify images which is a good way to make minor changes to them, however the ideal condition is to have a spritesheet with all possible forms of your graphics present.

    As the Riko 4 screen is double buffered, the ideal way to draw graphics is to clear the screen, draw your graphics, then swap.


Display Info

The Riko 4 has a display with a resolution of 340 pixels wide and 200 pixels tall All graphics functions have their origin at 0, 0 which is the top left pixel

The Riko 4 also uses vsync, so if you have a 144hz monitor, and your graphics card can do it, it will run at 144hz, it is important to create applications and games with this in mind, don't assume that every cycle will be 1/60th of a second long.


Colors

Colors as mapped as a 4 bit integer (0 - 15) which are (will be) reprogrammable (rgb wise) through a palette

Reprogrammable functionality interface is currently TBD

Pixels

While not ideal, it is sometimes necessary to draw individual pixels, you can do this with the following interface.

gpu.drawPixel(x, y, color)

Code Example

gpu.drawPixel(1, 2, 7)
-- Draws a pixel at 1, 2, with color 7 from the color palette

Rectangles

Rectangles are better than writing individual pixels, and are faster than using images if your intended result is very basic, the interface is as follows

gpu.drawRectangle(x, y, w, h, color)

Code Example

gpu.drawRectangle(15, 30, 45, 50, 8)

Images

Images are the ideal way to deal with graphics, the Image interface natively supports the RIF (see below) graphics format; however, you can also just create a blank image and draw to it if you like, which is useful for baking textures that you generate run-time.


Creating images

You can create an image datatype with the following constructor

image.createImage(imagedata)
-- Or
image.createImage(width, height)

imagedata must be in RIF (subject to change, support for more graphics types may come in the future) If nothing is supplied to the function, it will error.

Code Example

local handle = io.open("myImage", "r")
local imagedata = handle:read("*a")
handle:close()

local myImage = image.createImage(imagedata)

Or

local myBlankImage = image.createImage(30, 30)

Drawing images

You can render an image to the screen with the following function

image:render(x, y)

However, for performance reasons, the actual texture uploaded to your computer's GPU isn't updated until you flush the image buffer with the following function.

image:flush()

Code Example

local myImage = image.createImage(32, 32)
myImage:drawRectangle(0, 0, 16, 16, 4)
myImage:drawRectangle(16, 16, 16, 16, 4)
myImage:flush()
myImage:render(50, 50)

Drawing to images

If you have a blank image (or not), you can draw to it using any of the aforementioned functions, but replace the gpu namespace with the image, for example:

local myImage = image.createImage(30, 30)

myImage:drawRectangle(0, 0, 30, 15, 4) 

Disposing of images

The actual Images exposed by image userdata aren't are now garbage collected, so when you're done with an image, Lua takes care of everything for you! :D


RIF (Riko Image Format)

The RIF graphics format is very simple; each color is a 4 bit integer, so we can take advantage of this by storing two pixels in each byte. There is also a header at the beginning to provide metadata about the image. The full specs are as follows:

File format:

Signature (3 bytes) -> Header (4 bytes) -> Pixel data (? bytes)

The signature is "RIF" in ascii, mapping to 82 73 and 70 as bytes The header is comprised of 4 bytes, specifying the image's dimensions, 2 for each dimension. The first byte represents the 256s place of the x dimension and the second byte represents the 1s place of the x dimension. For example, if the headers first two bytes were 3 and 7 the width of the image would be (3 * 256) + 7. This is a very generous amount of space and the maximum width (and height) is 256^2, realistically speaking, an image should never have to be larger than this. The same logic applies to the height.

For the pixel data, each byte represents 2 pixels, with the second half of the last byte being padded with 0s if the total pixel count is odd. Going like this:

For two consecutive pixels having the colors 3 and 5 respectively this would be the corresponding byte:
0 0 1 1 0 1 0 1
\-----/ \-----/
   3       5

The final value of the byte would be 53, in ASCII this is the number 5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment