Cairosdl: convenience functions for setting up drawing to SDL surfaces using cairo. Using cairo to draw on SDL functions is very easy. The only gotcha is that it's only possible for surfaces whose pixel format is supported by cairo. * Quick start to cairosdl.c --------------------------- For one-off rendering using cairo only: sdlsurf = SDL_CreateRBGSurface(flags, width, height, 32, CAIROSDL_RMASK, CAIROSDL_GMASK, CAIROSDL_BMASK, 0 /* or CAIROSDL_AMASK*/); cairo_t *cairosdl_create(sdlsurf); /* ... normal cairo calls ... */ cairosdl_destroy(sdlsurf); For mixed cairo/non-cairo rendering: sdlsurf = ... as before ...; cairo_surface_t *cairosurf = cairosdl_surface_create(sdlsurf); /* ... normal cairo calls ... and then before switching to * non-cairo rendering: */ cairosdl_surface_flush_rects (cairosurf, rects, num_rects); /* ... non-cairo rendering to the sdlsurf ... and then before * resuming cairo rendering mark the changed areas: */ cairosdl_surface_mark_dirty_rects (cairosurf, rects, num_rects); So the short of it is: Use cairosdl_surface_create() or cairosdl_create() to bind your SDL_Surface to a cairo_surface_t or cairo_t context, and do normal cairo calls to render to it. Replace calls to cairo_surface_flush() and cairo_surface_mark_dirty() with the corresponding cairosdl.c calls. All the cairo functions expect the SDL_Surface to be locked or not need locking. * Amask = 0 surfaces -------------------- Technically there is only one common pixel format in common between cairo and SDL: 32 bit pixels which do NOT use per pixel alpha. Cairo calls that format CAIRO_FORMAT_RGB24. In SDL terms such surfaces are created by: SDL_CreateRGBSurface (flags, width, height, 32, CAIROSDL_RMASK, // 0x00FF0000 CAIROSDL_GMASK, // 0x0000FF00 CAIROSDL_BMASK, // 0x000000FF 0); Using the cairosdl_surface_create(SDL_Surface*) function one can get a cairo_surface_t* that can be used to get a cairo_t drawing context, just as normal. As a convenience, cairosdl_create(SDL_Surface*) will make the cairo_surface_t and then bind it to a cairo_t drawing context. If your SDL_Surface has this pixel format and does not need locking, that's all you need to do. * Amask = 0xFF000000 surfaces ----------------------------- Unfortunately SDL and cairo have an inconsolable difference of opinion on the interpretation of 32 bit pixels with per pixel alpha. SDL uses unpremultiplied colour components, while cairo uses premultiplied colours: an SDL pixel with components (r,g,b,a) would be represented in a cairo as (a*r/255, a*g/255, a*b/255, a). (Here a=255 means fully opaque and a=0 means fully transparent.) The first option to consider is whether you really need such surfaces to be SDL_Surfaces or whether cairo's image surfaces would do just as well. After all, the screen surface almost never has per-pixel alpha, so you could create an Amask=0 surface for it and use cairo to blit to it from a normal cairo image surface. This is likely a little faster than SDL's per-pixel-alpha blits even when SDL can't accelerate them. Regardless, sometimes you really want to draw to an Amask=0xFF000000 surface. The good news is that cairosdl.c supports such surfaces anyway via a backing buffer and will format shift between them on demand. In the simple one-off rendering case you just need to remember to call cairosdl_destroy() rather than cairo_destroy(). That will flush the backing buffer at the end to the SDL_Surface. If you plan to do mixed cairo/non-cairo rendering to the surface, cairosdl needs some coordination from you so that it can format shift between it's backing buffer and the SDL_Surface at the right times. The functions to call to make the cairo-drawn stuff appear in the SDL_Surface are: cairosdl_surface_flush() ; writes to the entire SDL_Surface cairosdl_surface_flush_rect(); writes to just a region. cairosdl_surface_flush_rects(); writes via a list of SDL_Rects. These functions do not call SDL_UpdateRect or anything like that to make the output visible on screen. The functions to call to import non-cairo drawn changes from the SDL_Surface to the backing surface are: cairosdl_surface_mark_dirty () ; reads the entire SDL_Surface. cairosdl_surface_mark_dirty_rect () ; reads just a region. cairosdl_surface_mark_dirty_rects () ; reads via a list of SDL_Rects. If you're doing mixed rendering and are already using the flush/mark_dirty functions, then you don't want to call cairosdl_destroy() at the end since that does an implicit final flush. * Palette indexed surfaces -------------------------- You could create a cairo image surface using the CAIRO_FORMAT_A8 pixel format. Cairo will ignore your palette though. cairosdl.c doesn't support this at the moment. * SDL_Surface flags, locking and other caveats ---------------------------------------------- Cairo really needs direct access to the pixel data of an SDL surface, but it doesn't know anything about locking them. Since the SDL locking rules can be a little inconvenient, the cairosdl_* functions assume that you're passing in an SDL_Surface which does not need locking or is already locked. In particular, the surface->pixels member must be valid and not change for the lifetime of a cairo_surface_t created by cairosdl_surface_create() or cairosdl_create(). It must also be safe to call malloc and whatever platform specific mutex operations inside cairo while the surface is locked. For most platforms I believe this means that SDL_SWSURFACEs are always safe to use with cairo. If they don't use RLEACCEL, they don't even need any locking at all. One can also use SDL_HWSURFACEs if it's okay to call malloc and other OS facilities while they're locked. In this case the the lifetime of the cairo_surface_t created for the SDL_Surface must be contained within a single SDL_LockSurface/UnlockSurface pair. Again, for many platforms this isn't a problem. I haven't tested GL enabled SDL surfaces, but expect there shouldn't be a problem if GL can be configured to accept cairo's pixel formats. Cairo ignores SDL_Surface colorkey and treats every 32 bit surface with alpha as if it had SDL_SRCALPHA. * Tips and tricks ----------------- Cairo's software blits seem to be slightly faster than SDL's software blits. The fastest combination seems to be using a 32 bit SDL_Surface with Amask = 0, wrap that up in a cairo_surface_t, and use normal cairo image surfaces for the per-pixel-alpha surfaces. The cairosdl_surface_get_target() and cairosdl_get_target() functions return the SDL_Surface bound to the given cairo_surface_t or cairo_t.