summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorM Joonas Pihlaja <jpihlaja@cc.helsinki.fi>2009-02-15 15:16:52 +0200
committerM Joonas Pihlaja <jpihlaja@cc.helsinki.fi>2009-02-15 15:16:52 +0200
commit6dfa5a65bfa6009a3a6df8f1ceccfda5f09d36b6 (patch)
treef171b93942565c316a40d55e0c04d58c4ac80d21
parent11ea8fbcc8634b130a22237dadf338c0d735adb1 (diff)
Added cairo/SDL demo.
-rw-r--r--Makefile15
-rw-r--r--fuzzy-balls.c460
-rw-r--r--test-cairosdl.c119
3 files changed, 594 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..52aa244
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,15 @@
+CFLAGS += -W -Wall -g -O3
+CFLAGS += `pkg-config --cflags sdl cairo`
+
+all: test-cairosdl fuzzy-balls
+
+fuzzy-balls: fuzzy-balls.o cairosdl.o cairosdl-premultiply.o
+ $(CC) -o $@ $+ `pkg-config --libs sdl cairo`
+
+test-cairosdl: test-cairosdl.o cairosdl.o cairosdl-premultiply.o
+ $(CC) -o $@ $+ `pkg-config --libs sdl cairo`
+
+clean:
+ $(RM) test-cairosdl fuzzy-balls
+ $(RM) *.o
+ $(RM) *~
diff --git a/fuzzy-balls.c b/fuzzy-balls.c
new file mode 100644
index 0000000..b52e624
--- /dev/null
+++ b/fuzzy-balls.c
@@ -0,0 +1,460 @@
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "cairosdl.h"
+
+#define dprintf(args)
+
+#define BLIT_BOBS_USING_CAIRO 1
+
+struct vector {
+ double x, y;
+};
+
+#define MAX_BOBS 20
+
+struct bob {
+ struct vector pos;
+ struct vector vel;
+ struct vector accel;
+ double mass;
+ double radius;
+ double focus;
+ cairo_surface_t *surface;
+};
+
+static void
+init_bobs (struct bob *bobs, size_t num_bobs)
+{
+ size_t i;
+ double fill_ratio = 0.95;
+ double n = sqrt(num_bobs);
+
+ for (i=0; i<num_bobs; i++) {
+ struct bob *bob = bobs + i;
+ double theta = 1.0 - (i+0.5) / MAX_BOBS;
+ double r = fill_ratio*(0.5 + theta*0.0)/n;
+ r = r < 0.2 ? r : 0.2;
+ bob->pos.x = rand () * 1.0 / RAND_MAX;
+ bob->pos.y = rand () * 1.0 / RAND_MAX;
+ bob->vel.x = bob->vel.y = 0;
+ bob->accel.x = bob->accel.y = 0;
+ bob->mass = (1+theta)*(1+theta);
+ bob->mass = 1.0; /* equal mass bobs have a smoother ride */
+ bob->mass /= num_bobs;
+ bob->radius = r;
+ bob->focus = 1.0;
+ bob->surface = NULL;
+ }
+}
+
+static void
+alloc_bobs (struct bob *bobs, size_t num_bobs)
+{
+ size_t i;
+ SDL_Surface *screen = SDL_GetVideoSurface ();
+
+ for (i=0; i<num_bobs; i++) {
+ struct bob *bob = bobs + i;
+ int width = screen->w * 2*bob->radius + 1;
+ int height = screen->h * 2*bob->radius + 1;
+
+ if (bob->surface) {
+ cairo_surface_destroy (bob->surface);
+ bob->surface = NULL;
+ }
+
+ if (BLIT_BOBS_USING_CAIRO) {
+ bob->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ width, height);
+ }
+ else {
+ SDL_Surface *sdl_surface = SDL_CreateRGBSurface (
+ SDL_SWSURFACE | SDL_SRCALPHA,
+ width, height, 32,
+ CAIROSDL_RMASK,
+ CAIROSDL_GMASK,
+ CAIROSDL_BMASK,
+ CAIROSDL_AMASK);
+ if (sdl_surface == NULL) {
+ fprintf (stderr, "Failed allocating bob sdl surfaces: %s\n",
+ SDL_GetError ());
+ exit (1);
+ }
+ assert (!SDL_MUSTLOCK (sdl_surface));
+ bob->surface = cairosdl_surface_create (sdl_surface);
+ SDL_FreeSurface (sdl_surface);
+ }
+
+ if (cairo_surface_status (bob->surface) != CAIRO_STATUS_SUCCESS) {
+ cairo_status_t status = cairo_surface_status (bob->surface);
+ fprintf (stderr, "Failed making a cairo surface for a bob: %s\n",
+ cairo_status_to_string (status));
+ exit (1);
+ }
+
+ }
+}
+
+static void
+render_bob (struct bob *bob, int i)
+{
+ int width = cairo_image_surface_get_width (bob->surface);
+ int height = cairo_image_surface_get_height (bob->surface);
+ cairo_t *cr = cairo_create (bob->surface);
+ double theta = (i+0.5) / MAX_BOBS;
+ double dx = bob->pos.x - 0.5;
+ double dy = bob->pos.y - 0.5;
+
+ cairo_scale (cr, 0.5*width, 0.5*height);
+ cairo_translate (cr, 1.0, 1.0);
+
+ cairo_set_tolerance (cr, 0.5);
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+ cairo_paint (cr);
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+ cairo_set_source_rgba (cr,
+ 1-fabs(dx),
+ 1-fabs(dy),
+ theta,
+ (1-2*fabs(dx))*(1-2*fabs(dy)));
+ if (1) {
+ cairo_pattern_t *pat;
+ pat = cairo_pattern_create_radial (
+ -dx, -dy, 0.0,
+ 0, 0, 1.0);
+ cairo_pattern_add_color_stop_rgba (pat, 0.0,
+ fabs(dy),
+ 0,
+ fabs(dx),
+ 0);
+
+ cairo_pattern_add_color_stop_rgba (pat, bob->focus,
+ fabs(dx+dy),
+ i&1 ? 1 : 1,
+ i&1 ? fabs(dx) : 1-fabs(dx),
+ 0.6);
+
+ cairo_pattern_add_color_stop_rgba (pat, 1.0,
+ 1 - fabs(dy),
+ 1 - fabs(dx),
+ theta,
+ 0.0);
+
+ cairo_set_source (cr, pat);
+ cairo_pattern_destroy (pat);
+ }
+
+ cairo_arc (cr, 0,0, 1.0, 0, 6.29);
+ cairo_close_path (cr);
+ cairo_fill (cr);
+
+ cairosdl_destroy (cr);
+}
+
+static void
+render_bobs (struct bob *bobs, size_t num_bobs)
+{
+ size_t i;
+ for (i=0; i<num_bobs; i++) {
+ render_bob (bobs+i, i);
+ }
+}
+
+static void
+blit_bobs_using_sdl (struct bob *bobs, size_t num_bobs)
+{
+ size_t i;
+ SDL_Surface *screen = SDL_GetVideoSurface ();
+
+ for (i=0; i<num_bobs; i++) {
+ struct bob *bob = bobs + i;
+ SDL_Surface *sdl_surface = cairosdl_surface_get_target (bob->surface);
+ SDL_Rect src_rect[1];
+ SDL_Rect dst_rect[1];
+
+ src_rect->x = 0;
+ src_rect->y = 0;
+ src_rect->w = sdl_surface->w;
+ src_rect->h = sdl_surface->h;
+
+ dst_rect->x = (bob->pos.x - bob->radius) * screen->w;
+ dst_rect->y = (bob->pos.y - bob->radius) * screen->h;
+ dst_rect->w = sdl_surface->w;
+ dst_rect->h = sdl_surface->h;
+
+ SDL_BlitSurface (sdl_surface, src_rect, screen, dst_rect);
+ }
+}
+
+static void
+blit_bobs_using_cairo (struct bob *bobs, size_t num_bobs)
+{
+ size_t i;
+ SDL_Surface *screen = SDL_GetVideoSurface ();
+ cairo_t *cr;
+
+ while (SDL_LockSurface (screen) != 0) {
+ SDL_Delay (1);
+ }
+
+ cr = cairosdl_create (screen);
+
+ for (i=0; i<num_bobs; i++) {
+ struct bob *bob = bobs + i;
+ int x = (int)((bob->pos.x - bob->radius) * screen->w);
+ int y = (int)((bob->pos.y - bob->radius) * screen->h);
+ int width = cairo_image_surface_get_width (bob->surface);
+ int height = cairo_image_surface_get_height (bob->surface);
+
+ cairo_set_source_surface (cr, bob->surface, x,y);
+ cairo_rectangle (cr,
+ x, y,
+ width, height);
+ cairo_fill (cr);
+ }
+
+ cairo_destroy (cr);
+
+ SDL_UnlockSurface (screen);
+}
+
+#define SQR(x) ((x)*(x))
+
+static double
+sim_bobs (struct bob *bobs, size_t num_bobs,
+ double t0, double t1)
+{
+ double dt = 0.002;
+ double G = 0.5;
+ double t = t0;
+
+ if (dt*10 < (t1 - t0) && 0)
+ dt = (t1 - t0) / 10.0;
+
+ while (t < t1) {
+ size_t i, j;
+
+ for (i=0; i<num_bobs; i++) {
+ double theta = (i+0.5) / MAX_BOBS;
+ double f = 0.3;
+ bobs[i].focus = f + (0.96-f)*0.5*(1 + cos(3.141*t*(1-theta)));
+ bobs[i].accel.x = (0.5-bobs[i].pos.x)*0.0;
+ bobs[i].accel.y = (0.5-bobs[i].pos.y)*0.0;
+ }
+
+ /* Basic mass attraction forces. */
+ for (i=0; i<num_bobs; i++) {
+ struct bob *p = bobs + i;
+ for (j=i+1; j<num_bobs; j++) {
+ struct bob *q = bobs + j;
+ double dx = q->pos.x - p->pos.x;
+ double dy = q->pos.y - p->pos.y;
+ double dist2 = SQR(dx) + SQR(dy);
+ double f = G / dist2;
+
+ if ((i^j) & 1) {
+ f *= -1;
+ }
+
+ p->accel.x += dx*f*q->mass;
+ p->accel.y += dy*f*q->mass;
+ q->accel.x -= dx*f*p->mass;
+ q->accel.y -= dy*f*p->mass;
+ }
+ }
+
+
+ /* Integrate one step forwards. */
+ for (i=0; i<num_bobs; i++) {
+ struct bob *p = bobs + i;
+
+ p->accel.x = p->accel.x;
+ p->accel.y = p->accel.y;
+
+ p->vel.x += dt*p->accel.x;
+ p->vel.y += dt*p->accel.y;
+
+ p->vel.x = p->vel.x;
+ p->vel.y = p->vel.y;
+
+ p->pos.x = p->pos.x + dt*p->vel.x;
+ p->pos.y = p->pos.y + dt*p->vel.y;
+
+ }
+
+ /* Apply position constraints. */
+ for (i=0; i<num_bobs; i++) {
+ double eps = 0.0;
+ struct bob *p = bobs + i;
+
+ /* Bounce off each other after allowed overlap. */
+ for (j=i+1; j<num_bobs; j++) {
+ struct bob *q = bobs + j;
+ double dx = q->pos.x - p->pos.x;
+ double dy = q->pos.y - p->pos.y;
+ double dist = sqrt(SQR(dx) + SQR(dy));
+ double overlap = p->radius*p->focus + q->radius*q->focus - dist - 0.02;
+
+ if (overlap < 0.0)
+ continue;
+ p->pos.x -= dx*overlap/dist;
+ p->pos.y -= dy*overlap/dist;
+ q->pos.x += dx*overlap/dist;
+ q->pos.y += dy*overlap/dist;
+
+ /* Swap velocity vectors, preserve momentum. */
+ {
+ double scale;
+ struct vector tmp = p->vel;
+ p->vel = q->vel;
+ q->vel = tmp;
+
+ scale = q->mass/p->mass;
+ p->vel.x *= scale;
+ p->vel.y *= scale;
+ scale = p->mass/q->mass;
+ q->vel.x *= scale;
+ q->vel.y *= scale;
+ }
+ }
+
+ /* Bounce off walls */
+ if (p->pos.x > 1+eps - p->radius*p->focus) {
+ p->pos.x = 1+eps - p->radius*p->focus;
+ p->vel.x *= -1;
+ }
+ if (p->pos.x < 0-eps + p->radius*p->focus) {
+ p->pos.x = 0-eps + p->radius*p->focus;
+ p->vel.x *= -1;
+ }
+ if (p->pos.y > 1+eps - p->radius*p->focus) {
+ p->pos.y = 1+eps - p->radius*p->focus;
+ p->vel.y *= -1;
+ }
+ if (p->pos.y < 0-eps + p->radius*p->focus) {
+ p->pos.y = 0-eps + p->radius*p->focus;
+ p->vel.y *= -1;
+ }
+
+ }
+
+
+ t += dt;
+ if (t + dt >= t1)
+ dt = t1 - t + 1e-6;
+ }
+
+ return t1;
+}
+
+static void
+push_expose ()
+{
+ SDL_Event event[1];
+ event->type = SDL_VIDEOEXPOSE;
+ if (SDL_PushEvent (event) != 0) {
+ fprintf (stderr, "Failed to push an expose event: %s\n",
+ SDL_GetError ());
+ }
+}
+
+static void
+on_expose (struct bob *bobs, size_t num_bobs)
+{
+ SDL_Surface *screen = SDL_GetVideoSurface ();
+
+ SDL_FillRect (screen, NULL,
+ SDL_MapRGBA (screen->format,
+ 0,0,0,SDL_ALPHA_OPAQUE));
+
+ if (BLIT_BOBS_USING_CAIRO)
+ blit_bobs_using_cairo (bobs, num_bobs);
+ else
+ blit_bobs_using_sdl (bobs, num_bobs);
+
+ SDL_Flip (screen);
+
+ push_expose (); /* Schedule another expose soon, */
+ SDL_Delay (1); /* but don't hog the cpu. */
+}
+
+static void
+event_loop (unsigned flags, int width, int height)
+{
+ struct bob bobs[MAX_BOBS];
+ size_t num_bobs = MAX_BOBS;
+
+ double t0 = SDL_GetTicks () / 1000.0; /* Current simulation time.. */
+ double t1; /* Next simulation time. */
+ SDL_Event event[1];
+
+ init_bobs (bobs, num_bobs);
+
+ event->resize.type = SDL_VIDEORESIZE;
+ event->resize.w = width;
+ event->resize.h = height;
+ SDL_PushEvent (event);
+
+ while (SDL_WaitEvent (event)) {
+ switch (event->type) {
+ case SDL_VIDEORESIZE:
+ if (SDL_SetVideoMode (event->resize.w,
+ event->resize.h,
+ 32, flags) == NULL)
+ {
+ fprintf (stderr, "Failed to set video mode: %s\n",
+ SDL_GetError ());
+ exit (1);
+ }
+ alloc_bobs (bobs, num_bobs);
+ render_bobs (bobs, num_bobs);
+ /* fallthrough */
+
+ case SDL_VIDEOEXPOSE:
+ t1 = SDL_GetTicks () / 1000.0;
+ render_bobs (bobs, num_bobs);
+ t0 = sim_bobs (bobs, num_bobs, t0, t1);
+ on_expose (bobs, num_bobs);
+ break;
+
+ case SDL_KEYDOWN:
+ if (event->key.keysym.sym == SDLK_q)
+ return;
+ }
+ }
+ fprintf (stderr, "WaitEvent failed: %s\n", SDL_GetError ());
+}
+
+int
+main()
+{
+ int width = 600;
+ int height = 600;
+ int flags = SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
+
+ if (SDL_Init (flags) < 0) {
+ fprintf (stderr, "Failed to initialise SDL: %s\n",
+ SDL_GetError ());
+ exit (1);
+ }
+ atexit (SDL_Quit);
+
+ if (1) {
+ event_loop (
+ SDL_SWSURFACE | SDL_RESIZABLE,
+ width, height);
+ }
+ else {
+ event_loop (
+ SDL_HWSURFACE |
+ SDL_FULLSCREEN |
+ SDL_DOUBLEBUF,
+ width, height);
+ }
+ return 0;
+}
diff --git a/test-cairosdl.c b/test-cairosdl.c
new file mode 100644
index 0000000..f6583c0
--- /dev/null
+++ b/test-cairosdl.c
@@ -0,0 +1,119 @@
+#include <assert.h>
+#include <string.h>
+#include "cairosdl.h"
+
+static int
+sdl_surface_eq(SDL_Surface *a, SDL_Surface *b)
+{
+ if (a->format->BitsPerPixel != 32) return 0;
+ if (a->format->BytesPerPixel != 4) return 0;
+ if (SDL_MUSTLOCK(a)) return 0;
+ if (SDL_MUSTLOCK(b)) return 0;
+
+#define check_field(name) if (a->name != b->name) return 0
+ check_field(format->BitsPerPixel);
+ check_field(format->BytesPerPixel);
+ check_field(format->Rmask);
+ check_field(format->Gmask);
+ check_field(format->Bmask);
+ check_field(format->Amask);
+ check_field(format->colorkey);
+ check_field(format->alpha);
+ check_field(w);
+ check_field(h);
+
+ {
+ unsigned char *a_bytes = a->pixels;
+ unsigned char *b_bytes = b->pixels;
+ size_t row_size = a->format->BytesPerPixel * a->w;
+ int i;
+ for (i=0; i<a->h; i++) {
+ if (memcmp(a_bytes, b_bytes, row_size))
+ return 0;
+ a_bytes += a->pitch;
+ b_bytes += b->pitch;
+ }
+ return 1;
+ }
+}
+
+static SDL_Surface *
+dup_sdl_surface(SDL_Surface *a)
+{
+ SDL_Surface *b = SDL_CreateRGBSurface(SDL_SWSURFACE,
+ a->w, a->h,
+ a->format->BitsPerPixel,
+ a->format->Rmask,
+ a->format->Gmask,
+ a->format->Bmask,
+ a->format->Amask);
+ unsigned char *b_bytes = b->pixels;
+ unsigned char *a_bytes = a->pixels;
+ size_t row_size = a->format->BytesPerPixel * a->w;
+ int i;
+ assert(! SDL_MUSTLOCK(a) );
+ assert(! SDL_MUSTLOCK(b) );
+
+ for (i=0; i<a->h; i++) {
+ memcpy(b_bytes, a_bytes, row_size);
+ a_bytes += a->pitch;
+ b_bytes += b->pitch;
+ }
+ return b;
+}
+
+static int
+test_argb32()
+{
+ SDL_Surface *ref;
+ SDL_Surface *sdlsurf = SDL_CreateRGBSurface(
+ SDL_SWSURFACE,
+ 100, 100, 32,
+ CAIROSDL_RMASK,
+ CAIROSDL_GMASK,
+ CAIROSDL_BMASK,
+ CAIROSDL_AMASK);
+
+ /* Prefilling the surface tests the mark_dirty functions. */
+ SDL_FillRect(sdlsurf, NULL,
+ SDL_MapRGBA(sdlsurf->format,255,0,0,128));
+
+ /* Make the reference image. */
+ {
+ SDL_Rect r;
+ ref = dup_sdl_surface (sdlsurf);
+ r.x = r.y = 25;
+ r.w = r.h = 50;
+ SDL_FillRect(ref, &r,
+ SDL_MapRGBA(ref->format,255,170,0,192));
+ }
+
+ /* Draw with cairo. cairosdl_destroy() calls the cairosdl surface
+ * flush. */
+ {
+ cairo_t *cr = cairosdl_create(sdlsurf);
+
+ cairo_set_source_rgba(cr, 1,1,0,0.5);
+ cairo_rectangle(cr, 25,25,50,50);
+ cairo_fill(cr);
+
+ cairosdl_destroy(cr);
+ }
+
+ /* Check vs the reference. */
+ {
+ int ok = sdl_surface_eq(ref,sdlsurf);
+ SDL_FreeSurface(ref);
+ SDL_FreeSurface(sdlsurf);
+ return ok;
+ }
+}
+
+int
+main()
+{
+ SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);
+ atexit(SDL_Quit);
+
+ return test_argb32() ? 0 : 1;
+}