How to use Cairo 2D graphics library
| Language: | English |
|---|
In this tutorial you will learn how to build a simple homebrew that uses Cairo 2D graphics library.
With Cairo you will able to load texts and make geometric figures and also you will be able to make screenshots (you will need to check Cairo graphics documentation to do that).
What you can't do : you can't display any type of images, because Cairo wont work properly with the RSX. if you need to display some image you should use SDL instead of Cairo.
If you start to not understand where you are or where something needs to be pasted inside your source code, please go to the end of this tutorial and download the source code and take a look at it.
Contents |
Headers and RSX
First of all, you will need to add this header inside the main.c\main.cpp file :
#include <ppu-lv2.h> #include <stdio.h> #include <malloc.h> #include <string.h> #include <unistd.h> #include <math.h> #include <time.h> #include <io/pad.h>
then, add the Cairo library header :
#include <cairo/cairo.h>Once you have added these headers, you will need to initialize the RSX manually, this means that you will need the rsxutil.c and rsxutil.h (if you are building something with c++, you can simply change the extension of rsxutil.c to rsxutil.cpp and it will work).
Now, the RSX is not yet initialized, so you will need to add these lines inside of the main.c\main.cpp.
Add these lines after the Cairo header :
#include "rsxutil.h" #define MAX_BUFFERS 2
And these inside the int main() :
gcmContextData *context; void *host_addr = NULL; rsxBuffer buffers[MAX_BUFFERS]; int currentBuffer = 0; u16 width; u16 height; host_addr = memalign (1024*1024, HOST_SIZE); context = initScreen (host_addr, HOST_SIZE); getResolution(&width, &height); for (i = 0; i < MAX_BUFFERS; i++) makeBuffer (&buffers[i], width, height, i); flip(context, MAX_BUFFERS - 1);
Now you have correctly initialized the RSX (if you want to define a resolution, you can call the function getResolution() and define yourself the width and height parameter).
Draw with Cairo
We need to make a new thread before int main() (I call it void drawFrame() but you can simply call it void draw(), void paint(), or however you may prefer)
So we have this new thread :
void drawFrame () {
We need to add this parameter (rsxBuffer *buffer) to allow you to correctly initialize the cairo_surface function, inside of the parenthesis, so we will have this :
void drawFrame (rsxBuffer *buffer) {
In the example you will see that there is another parameter (int frame); this parameter is used to calculate the FPS. Now we will start to work with the Cairo functions.
First of all we need to define the drawing context so we will call this function :
cairo_t *cr;
This is the Cairo drawing context function. Without this you can't draw with Cairo. We also need to set the surface where we can draw. To do this we need to call the following function :
cairo_surface_t *surface = NULL;
Now we need to define the surface. To do that we can use this :
surface = cairo_image_surface_create_for_data ((u8 *) buffer->ptr, CAIRO_FORMAT_RGB24, buffer->width, buffer->height, buffer->width * 4);
Now we can draw what we want (check some Cairo samples, if you want to use some interesting draw functions)
How to clear the screen
First thing we need to do is to set the RGB color. To do that we can use this function :
cairo_set_source_rgb (cr, RED, GREEN, BLUE); White
Now cr is the name of the draw context (we called it before) the RED, GREEN, BLUE parameters work between 0.00 and 1.00. now 1.00 is the max and it is equal to 0xFF or 255 on the RGB scale; 0.00 is the minimum and it's equal to 0x00 or 0 on the RGB scale.
Here's a simple example :
White
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
Black
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
Red
cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
Green
cairo_set_source_rgb (cr, 0.0, 1.0, 0.0);
Blue
cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
Now we need to draw it, to do that we need to use this tima this function :
cairo_paint (cr);
Display text
First thing that you should do is to understand how Cairo works with coordinates (X,Y). We use this coordinate system Human Coordinate System but Cairo don't.
Human Coordinate System :
Cairo works with this coordinate system Cairo Coordinate System, we need to use this.
Cairo Coordinate System :
If you want to see text on your screen then you must first define the fonts :
cairo_select_font_face(cr, "Purisa", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
Now Purisa is the name of the font, but you can use Sans or other fonts.
All of the other parameters are used to define how you want the text to be formatted. For this example, we will use a bold text. Now we need to define the font size, we can use this function :
cairo_set_font_size(cr, 15);
obviously the number 15 is the size of our font.
We need to define where we want to see the text; this function will allow you to choose the X, Y coordinate (remember that we need to use the Cairo Coordinate System) :
cairo_move_to(cr, X, Y);
This is an example :
cairo_move_to(cr, 200, 90);
then we can define the color of our text using the cairo_set_source_rgb() function.
cairo_set_source_rgb (cr, 0.0, 1.0, 0.0); //Green
Now we need to display our Text on the surface, this is the function that we can use :
cairo_show_text (cr, "Text example. you will see this on your screen");
Now there are a lot of functions inside the Cairo 2D graphics library. You can learn more about them here.
Clear the draw surface to free memory
After Drawing on the surface we will need to destroy it in order to free some memory and to allow the RSX to draw onto the TV screen. To do that we need to release the surface :
cairo_destroy (cr);
Then flush and destroy the surface:
cairo_surface_finish (surface); cairo_surface_destroy (surface);
and now you can close the void drawFrame() thread. Draw the surface on the Screen and Uninitialize RSX.
Now let's go back to the main thread. We will first need to initialize the pad (just to allow you to exit from the homebrew) to do that we call these functions :
padInfo padinfo; padData paddata; ioPadInit (7);
and open a while :
while(1){ ioPadGetInfo (&padinfo); for(i = 0; i < MAX_PADS; i++) { if(padinfo.status[i]) { ioPadGetData (i, &paddata); if(paddata.BTN_START) { goto end; } } }
(I explain this in detail, in the How to integrate controller support tutorial)
Now we set that when we will press the START button, we will goto to the end function (we need to set end outside the while; if you don't know what i'm talking, do not care about it).
First thing we need to use these functions setRenderTarget() and waitFlip() to set the render target and set a waitFlip (always inside the while). The FLIP function is the function that draws everything onto the TV screen, you don't need to understand what waitFlip is.
setRenderTarget(context, &buffers[currentBuffer]); waitFlip ();
Then we need to call the drawFrame() function in order to load the Cairo function that we wrote before, into the RSX memory (drawFrame was the name i gave to the thread that i called in the Draw with Cairo paragraph, if you called it with a different name you need to call with the name that you gave) :
drawFrame (&buffers[currentBuffer]);
Now we need to draw the buffer onto the screen :
flip (context, buffers[currentBuffer].id); currentBuffer++; if (currentBuffer >= MAX_BUFFERS) currentBuffer = 0;
Now we can close the while and set the last part of the code. First thing we need to call is the goto end :
end:Here we will uninitialize the RSX and uninitialize the joypad to allow the user to return to the XMB without problems :
gcmSetWaitFlip(context); for (i = 0; i < MAX_BUFFERS; i++) rsxFree (buffers[i].ptr); rsxFinish (context, 1); free (host_addr); ioPadEnd(); return 0;
Now we can close the main() thread and compile it, but to to that we need to add some flags to the Makefile. These are the Flags that we need in order to compile it correctly.
You will need to add them inside of the Makefile after LIBS := :
-lgcm_sys -lrsx -lsysutil -lio -lcairo -lm -lfreetype -lz -lpixman-1 -lrt -llv2
Samples/Exercises
Authors/Sources/References
Deroad/Wargio; Team PS3Dev;