]> git.cworth.org Git - xoboot/commitdiff
Add transition animations
authorCarl Worth <cworth@cworth.org>
Tue, 28 Aug 2007 07:41:54 +0000 (00:41 -0700)
committerCarl Worth <cworth@cworth.org>
Tue, 28 Aug 2007 07:41:54 +0000 (00:41 -0700)
At this point, things look _really_ close to the original Quicktime
animation. The most significant defect is that the circle of dots
and the rotating hands have different center points. Currently,
these rotate at basically the same angular speed, which means the
hands are not pointing nicely at the dots. We should be just a
touch more clever to make the dots appear exactly as they are
pointed to by the hands.

xoboot.c

index 14cfd07bc737ff80a3138996416f216d786abf9e..4daa763c14f94c0992bf9026719bcc0fab530372 100644 (file)
--- a/xoboot.c
+++ b/xoboot.c
@@ -21,14 +21,17 @@ typedef struct _timeline {
     double transition;
 } timeline_t;
 
+#define TIMELINE_ADVANCE_TIMER_PERIOD_MS       500
+#define TIMELINE_TRANSITION_FPS                        20
+
 timeline_t timeline[] = {
     { 0.5, ANIM_BLANK,      0.0},
     { 1.0, ANIM_HEAD,       0.0},
     { 2.0, ANIM_BODY,       0.0},
     { 7.0, ANIM_SPIN,       6.0},
     { 1.0, ANIM_SPIN_DONE,  0.0},
-    { 1.0, ANIM_OUTLINE,    0.0},
-    { 1.0, ANIM_FADE,       0.5},
+    { 1.0, ANIM_OUTLINE,    0.1},
+    { 3.0, ANIM_FADE,       1.0},
     { 0.0, ANIM_DONE }
 };
 
@@ -36,6 +39,11 @@ typedef struct _state {
     GtkWidget *drawing_area;
     double progress;
     anim_stage_t anim_stage;
+    int stage_ticks_remaining;
+    /* frames are used during a transition */
+    int num_frames;
+    int frame;
+    double transition;
 } state_t;
 
 typedef struct _color {
@@ -98,25 +106,35 @@ draw_head (cairo_t *cr, double extra)
 }
 
 static void
-draw_body (cairo_t *cr, double extra)
+draw_body (cairo_t *cr, double extra, double spin_transition)
 {
+    cairo_save (cr);
+
     cairo_move_to (cr,
                   XO_BODY_CENTER_X - XO_BODY_DELTA,
-                  XO_BODY_CENTER_Y - XO_BODY_DELTA);
-    cairo_rel_line_to (cr, XO_BODY_DELTA, XO_BODY_DELTA);
+                  XO_BODY_CENTER_Y + XO_BODY_DELTA);
     cairo_rel_line_to (cr, XO_BODY_DELTA, -XO_BODY_DELTA);
+    cairo_rel_line_to (cr, XO_BODY_DELTA, XO_BODY_DELTA);
+
+    cairo_translate (cr, XO_HEAD_CENTER_X, XO_HEAD_CENTER_Y);
+    cairo_rotate (cr, 2 * M_PI * spin_transition);
+    cairo_translate (cr, -XO_HEAD_CENTER_X, -XO_HEAD_CENTER_Y);
+       
     cairo_move_to (cr,
                   XO_BODY_CENTER_X - XO_BODY_DELTA,
-                  XO_BODY_CENTER_Y + XO_BODY_DELTA);
-    cairo_rel_line_to (cr, XO_BODY_DELTA, -XO_BODY_DELTA);
+                  XO_BODY_CENTER_Y - XO_BODY_DELTA);
     cairo_rel_line_to (cr, XO_BODY_DELTA, XO_BODY_DELTA);
+    cairo_rel_line_to (cr, XO_BODY_DELTA, -XO_BODY_DELTA);
+
     cairo_set_line_width (cr, XO_BODY_LINE_WIDTH + 2 * extra);
     cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
     cairo_stroke (cr);
+    
+    cairo_restore (cr);
 }
 
 static void
-draw_dots (cairo_t *cr)
+draw_dots (cairo_t *cr, double num_dots)
 {
     int i;
 
@@ -125,14 +143,16 @@ draw_dots (cairo_t *cr)
     set_color (cr, &dot_gray);
 
     cairo_translate (cr, XO_DOTS_CENTER_X, XO_DOTS_CENTER_Y);
-    cairo_rotate (cr, - M_PI_2);
-
-    for (i = 0; i < XO_DOTS_NUM_DOTS; i++) {
-       cairo_arc (cr,
-                  XO_DOTS_POSITION_RADIUS, 0,
-                  XO_DOTS_DOT_RADIUS,
-                  0, 2 * M_PI);
-       cairo_fill (cr);
+    cairo_rotate (cr, - 1.5 * M_PI);
+
+    for (i = 0; i < num_dots; i++) {
+       if (i != 0) {
+           cairo_arc (cr,
+                      XO_DOTS_POSITION_RADIUS, 0,
+                      XO_DOTS_DOT_RADIUS,
+                      0, 2 * M_PI);
+           cairo_fill (cr);
+       }
        cairo_rotate (cr, 2 * M_PI / XO_DOTS_NUM_DOTS);
     }
     
@@ -171,25 +191,33 @@ xoboot_expose_event (GtkWidget      *widget,
     anim_stage_t anim_stage = state->anim_stage;
     cairo_t *cr;
     double shrink;
+    double spin_transition = 1.0;
 
     cr = gdk_cairo_create (widget->window);
 
     if (anim_stage < ANIM_FADE)
        set_color (cr, &background[0]);
     else
-       set_color (cr, &background[1]);
+       cairo_set_source_rgb (cr, LERP_COLORS (background[0],
+                                              background[1],
+                                              state->transition));
 
     cairo_paint (cr);
 
     if (anim_stage == ANIM_BLANK)
        goto DONE;
 
+    if (anim_stage == ANIM_SPIN)
+       spin_transition = state->transition;
+
     shrink = 0.0;
     if (anim_stage >= ANIM_OUTLINE) {
        set_color (cr, &xo_black);
        draw_head (cr, XO_OUTLINE_EXTRA);
-       draw_body (cr, XO_OUTLINE_EXTRA);
+       draw_body (cr, XO_OUTLINE_EXTRA, spin_transition);
        shrink = - XO_OUTLINE_SHRINK;
+       if (anim_stage == ANIM_OUTLINE)
+           shrink *= state->transition;
     }
 
     set_color (cr, &xo_green);
@@ -198,14 +226,15 @@ xoboot_expose_event (GtkWidget      *widget,
     if (anim_stage == ANIM_HEAD)
        goto DONE;
 
-    draw_body (cr, shrink);
+    draw_body (cr, shrink, spin_transition);
     if (anim_stage == ANIM_BODY)
        goto DONE;
 
-    if (anim_stage < ANIM_FADE)
-       draw_dots (cr);
-    else
+    if (anim_stage < ANIM_FADE) {
+       draw_dots (cr, (XO_DOTS_NUM_DOTS + 1) * spin_transition);
+    } else {
        draw_ring (cr);
+    }
 
   DONE:
     cairo_destroy (cr);
@@ -238,17 +267,53 @@ create_window (state_t *state)
     return drawing_area;
 }
 
+static gint
+transition_advance (gpointer closure)
+{
+    state_t *state = closure;
+
+    if (state->frame >= state->num_frames - 1) {
+       state->frame = state->num_frames - 1;
+       return FALSE;
+    }
+
+    state->frame++;
+    state->transition = (double) state->frame / (state->num_frames - 1);
+
+    gtk_widget_queue_draw (state->drawing_area);
+
+    return TRUE;
+}
+
 static gint
 timeline_advance (gpointer closure)
 {
     state_t *state = closure;
+    timeline_t *tl;
 
     if (state->anim_stage == ANIM_DONE)
        return FALSE;
 
+    if (--state->stage_ticks_remaining)
+       return TRUE;
+
     state->anim_stage++;
+    tl = &timeline[state->anim_stage];
+    state->stage_ticks_remaining = (tl->duration * 1000
+                                   / TIMELINE_ADVANCE_TIMER_PERIOD_MS);
     gtk_widget_queue_draw (state->drawing_area);
 
+    if (tl->transition > 1.0 / TIMELINE_TRANSITION_FPS) {
+       state->num_frames = tl->transition * TIMELINE_TRANSITION_FPS;
+       state->frame = 0;
+       state->transition = 0.0;
+       g_timeout_add (1000 / TIMELINE_TRANSITION_FPS, transition_advance, state);
+    } else {
+       state->num_frames = 1;
+       state->frame = 0;
+       state->transition = 1.0;
+    }
+
     return TRUE;
 }
 
@@ -262,10 +327,16 @@ main (int argc, char *argv[])
     state.drawing_area = create_window (&state);
     state.progress = 0.0;
     state.anim_stage = 0;
+    state.num_frames = 1;
+    state.frame = 0;
+    state.transition = 1.0;
+    state.stage_ticks_remaining = (timeline[state.anim_stage].duration * 1000
+                                  / TIMELINE_ADVANCE_TIMER_PERIOD_MS);
 
     gtk_widget_show_all (gtk_widget_get_toplevel (state.drawing_area));
 
-    g_timeout_add (1000, timeline_advance, &state);
+    g_timeout_add (TIMELINE_ADVANCE_TIMER_PERIOD_MS,
+                  timeline_advance, &state);
 
     gtk_main ();