]> git.cworth.org Git - xoboot/blobdiff - xoboot.c
Add transition animations
[xoboot] / 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 ();