diff options
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | gears.c | 439 |
2 files changed, 443 insertions, 1 deletions
@@ -1,7 +1,7 @@ CFLAGS += -W -Wall -g -O3 CFLAGS += `pkg-config --cflags sdl cairo` -TARGETS=test-cairosdl fuzzy-balls sdl-clock +TARGETS=test-cairosdl fuzzy-balls sdl-clock gears all: $(TARGETS) @@ -11,6 +11,9 @@ fuzzy-balls: fuzzy-balls.o cairosdl.o sdl-clock: sdl-clock.o cairosdl.o $(CC) -o $@ $+ `pkg-config --libs sdl cairo` +gears: gears.o cairosdl.o + $(CC) -o $@ $+ `pkg-config --libs sdl cairo` + test-cairosdl: test-cairosdl.o cairosdl.o $(CC) -o $@ $+ `pkg-config --libs sdl cairo` @@ -0,0 +1,439 @@ +/* + * Copyright © 2004 David Reveman, Peter Nilsson + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the names of + * David Reveman and Peter Nilsson not be used in advertising or + * publicity pertaining to distribution of the software without + * specific, written prior permission. David Reveman and Peter Nilsson + * makes no representations about the suitability of this software for + * any purpose. It is provided "as is" without express or implied warranty. + * + * DAVID REVEMAN AND PETER NILSSON DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DAVID REVEMAN AND + * PETER NILSSON BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA + * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * Authors: David Reveman <c99drn@cs.umu.se> + * Peter Nilsson <c99pnn@cs.umu.se> + */ +#include <stdlib.h> +#include <cairo.h> +#include <math.h> + +#define LINEWIDTH 3.0 + +#define FILL_R 0.1 +#define FILL_G 0.1 +#define FILL_B 0.75 +#define FILL_OPACITY 0.5 + +#define STROKE_R 0.1 +#define STROKE_G 0.75 +#define STROKE_B 0.1 +#define STROKE_OPACITY 1.0 + +#define NUMPTS 6 + +#define CHEAT_SHADOWS 1 /* 1: use opaque gear shadows, + * 0: semitransparent shadows like qgears2 */ + +static double animpts[NUMPTS * 2]; +static double deltas[NUMPTS * 2]; + +static int fill_gradient = 0; + +static void +gear (cairo_t *cr, + double inner_radius, + double outer_radius, + int teeth, + double tooth_depth) +{ + int i; + double r0, r1, r2; + double angle, da; + + r0 = inner_radius; + r1 = outer_radius - tooth_depth / 2.0; + r2 = outer_radius + tooth_depth / 2.0; + + da = 2.0 * M_PI / (double) teeth / 4.0; + + cairo_new_path (cr); + + angle = 0.0; + cairo_move_to (cr, r1 * cos (angle + 3 * da), r1 * sin (angle + 3 * da)); + + for (i = 1; i <= teeth; i++) { + angle = i * 2.0 * M_PI / (double) teeth; + + cairo_line_to (cr, r1 * cos (angle), r1 * sin (angle)); + cairo_line_to (cr, r2 * cos (angle + da), r2 * sin (angle + da)); + cairo_line_to (cr, r2 * cos (angle + 2 * da), r2 * sin (angle + 2 * da)); + + if (i < teeth) + cairo_line_to (cr, r1 * cos (angle + 3 * da), + r1 * sin (angle + 3 * da)); + } + + cairo_close_path (cr); + + cairo_move_to (cr, r0 * cos (angle + 3 * da), r0 * sin (angle + 3 * da)); + + for (i = 1; i <= teeth; i++) { + angle = i * 2.0 * M_PI / (double) teeth; + + cairo_line_to (cr, r0 * cos (angle), r0 * sin (angle)); + } + + cairo_close_path (cr); +} + +void +trap_setup (cairo_surface_t *target, int w, int h) +{ + int i; + + (void)target; + //cairo_scale (cr, 3.0, 1.0); + + for (i = 0; i < (NUMPTS * 2); i += 2) { + animpts[i + 0] = (float) (drand48 () * w); + animpts[i + 1] = (float) (drand48 () * h); + deltas[i + 0] = (float) (drand48 () * 6.0 + 4.0); + deltas[i + 1] = (float) (drand48 () * 6.0 + 4.0); + if (animpts[i + 0] > w / 2.0) { + deltas[i + 0] = -deltas[i + 0]; + } + if (animpts[i + 1] > h / 2.0) { + deltas[i + 1] = -deltas[i + 1]; + } + } +} + +static void +stroke_and_fill_animate (double *pts, + double *deltas, + int index, + int limit) +{ + double newpt = pts[index] + deltas[index]; + + if (newpt <= 0) { + newpt = -newpt; + deltas[index] = (double) (drand48 () * 4.0 + 2.0); + } else if (newpt >= (double) limit) { + newpt = 2.0 * limit - newpt; + deltas[index] = - (double) (drand48 () * 4.0 + 2.0); + } + pts[index] = newpt; +} + +static void +stroke_and_fill_step (int w, int h) +{ + int i; + + for (i = 0; i < (NUMPTS * 2); i += 2) { + stroke_and_fill_animate (animpts, deltas, i + 0, w); + stroke_and_fill_animate (animpts, deltas, i + 1, h); + } +} + +static double gear1_rotation = 0.35; +static double gear2_rotation = 0.33; +static double gear3_rotation = 0.50; + +void +trap_render (cairo_t *cr, int w, int h) +{ + double *ctrlpts = animpts; + int len = (NUMPTS * 2); + double prevx = ctrlpts[len - 2]; + double prevy = ctrlpts[len - 1]; + double curx = ctrlpts[0]; + double cury = ctrlpts[1]; + double midx = (curx + prevx) / 2.0; + double midy = (cury + prevy) / 2.0; + int i; + int pass; + + cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); + + cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_rectangle (cr, 0, 0, w, h); + cairo_fill (cr); + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + cairo_set_source_rgba (cr, 0.75, 0.75, 0.75, 1.0); + cairo_set_line_width (cr, 1.0); + + cairo_save (cr); { + cairo_scale (cr, (double) w / 512.0, (double) h / 512.0); + + cairo_save (cr); { + cairo_translate (cr, -10.0, -10.0); + cairo_translate (cr, 170.0, 330.0); + cairo_rotate (cr, gear1_rotation); + gear (cr, 30.0, 120.0, 20, 20.0); + cairo_set_source_rgba (cr, 0.70, 0.70, 0.70, 0.70 + CHEAT_SHADOWS); + cairo_fill (cr); + cairo_restore (cr); + } + cairo_save (cr); { + cairo_translate (cr, -10.0, -10.0); + cairo_translate (cr, 369.0, 330.0); + cairo_rotate (cr, gear2_rotation); + gear (cr, 15.0, 75.0, 12, 20.0); + cairo_set_source_rgba (cr, 0.70, 0.70, 0.70, 0.70 + CHEAT_SHADOWS); + cairo_fill (cr); + cairo_restore (cr); + } + cairo_save (cr); { + cairo_translate (cr, -10.0, -10.0); + cairo_translate (cr, 170.0, 116.0); + cairo_rotate (cr, gear3_rotation); + gear (cr, 20.0, 90.0, 14, 20.0); + cairo_set_source_rgba (cr, 0.70, 0.70, 0.70, 0.70 + CHEAT_SHADOWS); + cairo_fill (cr); + cairo_restore (cr); + } + + cairo_save (cr); { + cairo_translate (cr, 170.0, 330.0); + cairo_rotate (cr, gear1_rotation); + gear (cr, 30.0, 120.0, 20, 20.0); + cairo_set_source_rgb (cr, 0.75, 0.75, 0.75); + cairo_fill_preserve (cr); + cairo_set_source_rgb (cr, 0.25, 0.25, 0.25); + cairo_stroke (cr); + cairo_restore (cr); + } + cairo_save (cr); { + cairo_translate (cr, 369.0, 330.0); + cairo_rotate (cr, gear2_rotation); + gear (cr, 15.0, 75.0, 12, 20.0); + cairo_set_source_rgb (cr, 0.75, 0.75, 0.75); + cairo_fill_preserve (cr); + cairo_set_source_rgb (cr, 0.25, 0.25, 0.25); + cairo_stroke (cr); + cairo_restore (cr); + } + cairo_save (cr); { + cairo_translate (cr, 170.0, 116.0); + cairo_rotate (cr, gear3_rotation); + gear (cr, 20.0, 90.0, 14, 20.0); + cairo_set_source_rgb (cr, 0.75, 0.75, 0.75); + cairo_fill_preserve (cr); + cairo_set_source_rgb (cr, 0.25, 0.25, 0.25); + cairo_stroke (cr); + cairo_restore (cr); + } + + cairo_restore (cr); + } + + gear1_rotation += 0.01; + gear2_rotation -= (0.01 * (20.0 / 12.0)); + gear3_rotation -= (0.01 * (20.0 / 14.0)); + + stroke_and_fill_step (w, h); + + cairo_translate (cr, -10, -10); + for (pass = 1; pass <= 2; pass++) { + cairo_new_path (cr); + cairo_move_to (cr, midx, midy); + + for (i = 2; i <= (NUMPTS * 2); i += 2) { + double x2, x1 = (midx + curx) / 2.0; + double y2, y1 = (midy + cury) / 2.0; + + prevx = curx; + prevy = cury; + if (i < (NUMPTS * 2)) { + curx = ctrlpts[i + 0]; + cury = ctrlpts[i + 1]; + } else { + curx = ctrlpts[0]; + cury = ctrlpts[1]; + } + midx = (curx + prevx) / 2.0; + midy = (cury + prevy) / 2.0; + x2 = (prevx + midx) / 2.0; + y2 = (prevy + midy) / 2.0; + cairo_curve_to (cr, x1, y1, x2, y2, midx, midy); + } + cairo_close_path (cr); + + if (pass == 1) { + cairo_set_source_rgba (cr, 0,0,0,77/255.0); + cairo_fill (cr); + cairo_translate (cr, 10, 10); + } + } + + if (fill_gradient) { + double x1, y1, x2, y2; + cairo_pattern_t *pattern; + + cairo_fill_extents (cr, &x1, &y1, &x2, &y2); + + pattern = cairo_pattern_create_linear (x1, y1, x2, y2); + cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0.0, 0.0, 1.0, 0.75); + cairo_pattern_add_color_stop_rgba (pattern, 1.0, 1.0, 0.0, 0.0, 1.0); + cairo_pattern_set_filter (pattern, CAIRO_FILTER_BILINEAR); + + cairo_move_to (cr, 0, 0); + cairo_set_source (cr, pattern); + cairo_pattern_destroy (pattern); + } else { + cairo_set_source_rgba (cr, FILL_R, FILL_G, FILL_B, FILL_OPACITY); + } + + cairo_fill_preserve (cr); + cairo_set_source_rgba (cr, STROKE_R, STROKE_G, STROKE_B, STROKE_OPACITY); + cairo_set_line_width (cr, LINEWIDTH); + cairo_stroke (cr); +} + + +/* SDL code */ +#include "cairosdl.h" + +static Uint32 +print_fps_timer (Uint32 interval, void *param) +{ + unsigned *frame_counter = (unsigned *)param; + unsigned num_frames = *frame_counter; + *frame_counter = 0; + + printf("%u frames in %u ms = %.3f fps\n", + num_frames, interval, 1000.0*num_frames / interval); + + return interval; +} + + +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 +event_loop (unsigned flags, int width, int height) +{ + unsigned num_frames_rendered = 0; + SDL_Event event[1]; + event->resize.type = SDL_VIDEORESIZE; + event->resize.w = width; + event->resize.h = height; + SDL_PushEvent (event); + + SDL_AddTimer (5000, print_fps_timer, &num_frames_rendered); + + 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); + } + width = event->resize.w; + height = event->resize.h; + /* fallthrough */ + + case SDL_VIDEOEXPOSE: { + SDL_Surface *screen = SDL_GetVideoSurface (); + cairo_t *cr; + cairo_status_t status; + + while (SDL_LockSurface (screen) != 0) + SDL_Delay (1); + + cr = cairosdl_create (screen); + trap_render (cr, width, height); + status = cairo_status (cr); + cairosdl_destroy (cr); + + SDL_UnlockSurface (screen); + SDL_Flip (screen); + + ++num_frames_rendered; + + if (status != CAIRO_STATUS_SUCCESS) { + fprintf (stderr, "Failed to render: %s\n", + cairo_status_to_string (status)); + } + + push_expose (); /* Schedule another expose soon, */ + break; + } + case SDL_KEYDOWN: + if (event->key.keysym.sym == SDLK_q) + return; + } + } + fprintf (stderr, "WaitEvent failed: %s\n", SDL_GetError ()); +} + +int +main (int argc, char **argv) +{ + int width = 512; + int height = 512; + int flags = SDL_SWSURFACE; + int init_flags = SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE; + int i; + + if (SDL_Init (init_flags) < 0) { + fprintf (stderr, "Failed to initialise SDL: %s\n", + SDL_GetError ()); + exit (1); + } + atexit (SDL_Quit); + + for (i=1; i<argc; i++) { + if (0 == strcmp(argv[i], "-gradient")) { + fill_gradient = 1; + } + else if (0 == strcmp(argv[i], "-fullscreen")) { + flags |= SDL_HWSURFACE | SDL_FULLSCREEN; + } + else if (0 == strcmp(argv[i], "-hwsurface")) { + flags |= SDL_HWSURFACE; + } + else if (0 == strcmp(argv[i], "-swsurface")) { + flags |= SDL_SWSURFACE; + } + else if (0 == strcmp(argv[i], "-doublebuf")) { + flags |= SDL_DOUBLEBUF; + } + else if (0 == strcmp(argv[i], "-resizable")) { + flags |= SDL_RESIZABLE; + } + else { + fprintf(stderr, "usage: [-gradient] [-fullscreen] [-resizable]\n"); + } + } + + event_loop (flags, width, height); + return 0; +} |