summaryrefslogtreecommitdiff
path: root/README
blob: 30f891786e9a7bcec274c2cbcc1a3a185c5c4bd7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
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.