1 /* xoboot - A simple demonstration of what might be an XO boot animation
3 * gcc -Wall -g $(pkg-config --cflags --libs gtk+-2.0 cairo) xoboot.c -o xboot
5 * Copyright © 2007 Carl Worth
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3, or (at your option)
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software Foundation,
19 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA."
22 /* This is simply a proof-of-concept for using cairo to implement a
23 * design idea for an OLPC boot-time animation. The animation here is
24 * implemented to mimic an animation originally provided as a
25 * Quicktime movie by Rebecca Allen <rallen@arts.ucla.edu>.
27 * So far, this animation has been developed exclusively on an x86
28 * laptop with GTK+ targeting the X Window System. No guarantees are
29 * provided as to how this might perform on an OLPC XO machine. What's
30 * likely desired is to write the application to use cairo image
31 * surfaces, and augment cairo to support targeting the XO's 565 image
34 * And obviously, to serve as a real boot-time animation, code will
35 * have to be written to advance the animation according to various
36 * system events, instead of using the hard-coded timeline below.
54 typedef struct _timeline {
60 #define TIMELINE_ADVANCE_TIMER_PERIOD_MS 500
61 #define TIMELINE_TRANSITION_FPS 20
63 timeline_t timeline[] = {
64 { 0.5, ANIM_BLANK, 0.0},
65 { 1.0, ANIM_HEAD, 0.0},
66 { 2.0, ANIM_BODY, 0.0},
67 { 7.0, ANIM_SPIN, 6.0},
68 { 1.0, ANIM_SPIN_DONE, 0.0},
69 { 1.0, ANIM_OUTLINE, 0.1},
70 { 3.0, ANIM_FADE, 1.0},
74 typedef struct _state {
75 GtkWidget *drawing_area;
77 anim_stage_t anim_stage;
78 int stage_ticks_remaining;
79 /* frames are used during a transition */
85 typedef struct _color {
91 #define HEX_COLOR(r,g,b) {(r) / 255.0, (g) / 255.0, (b) / 255.0}
93 color_t background[2] = {
94 HEX_COLOR (0x75, 0x75, 0x75),
95 HEX_COLOR (0xdb, 0xdc, 0xdf)
98 color_t xo_green = HEX_COLOR (0x4f, 0xff, 0x12);
99 color_t xo_black = HEX_COLOR (0x30, 0x30, 0x30);
100 color_t dot_gray = HEX_COLOR (0xe5, 0xe5, 0xe5);
101 color_t ring_white = HEX_COLOR (0xf1, 0xf1, 0xf1);
103 #define LERP(a,b,t) ((a) + (t) * ((b) - (a)))
104 #define LERP_COLORS(c0, c1, t) LERP((c0).r, (c1).r, (t)), \
105 LERP((c0).g, (c1).g, (t)), \
106 LERP((c0).b, (c1).b, (t))
109 set_color (cairo_t *cr, color_t *color)
111 cairo_set_source_rgb (cr, color->r, color->g, color->b);
114 #define XO_HEAD_CENTER_X 320
115 #define XO_HEAD_CENTER_Y 211.5
116 #define XO_HEAD_RADIUS 12.25
117 #define XO_BODY_CENTER_X 320
118 #define XO_BODY_CENTER_Y 250
119 #define XO_BODY_DELTA 20.5
120 #define XO_BODY_LINE_WIDTH 12.25
121 #define XO_OUTLINE_EXTRA 2.5
122 #define XO_OUTLINE_SHRINK 1.1
123 #define XO_DOTS_CENTER_X 320
124 #define XO_DOTS_CENTER_Y 240
125 #define XO_DOTS_POSITION_RADIUS 112.5
126 #define XO_DOTS_DOT_RADIUS 5.5
127 #define XO_DOTS_NUM_DOTS 30
128 #define XO_RING_CENTER_X 320
129 #define XO_RING_CENTER_Y 240
130 #define XO_RING_INNER_RADIUS 63
131 #define XO_RING_OUTER_RADIUS 118
134 draw_head (cairo_t *cr, double extra)
139 XO_HEAD_RADIUS + extra,
145 draw_body (cairo_t *cr, double extra, double spin_transition)
150 XO_BODY_CENTER_X - XO_BODY_DELTA,
151 XO_BODY_CENTER_Y + XO_BODY_DELTA);
152 cairo_rel_line_to (cr, XO_BODY_DELTA, -XO_BODY_DELTA);
153 cairo_rel_line_to (cr, XO_BODY_DELTA, XO_BODY_DELTA);
155 cairo_translate (cr, XO_HEAD_CENTER_X, XO_HEAD_CENTER_Y);
156 cairo_rotate (cr, 2 * M_PI * spin_transition);
157 cairo_translate (cr, -XO_HEAD_CENTER_X, -XO_HEAD_CENTER_Y);
160 XO_BODY_CENTER_X - XO_BODY_DELTA,
161 XO_BODY_CENTER_Y - XO_BODY_DELTA);
162 cairo_rel_line_to (cr, XO_BODY_DELTA, XO_BODY_DELTA);
163 cairo_rel_line_to (cr, XO_BODY_DELTA, -XO_BODY_DELTA);
165 cairo_set_line_width (cr, XO_BODY_LINE_WIDTH + 2 * extra);
166 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
173 draw_dots (cairo_t *cr, double num_dots)
179 set_color (cr, &dot_gray);
181 cairo_translate (cr, XO_DOTS_CENTER_X, XO_DOTS_CENTER_Y);
182 cairo_rotate (cr, - 1.5 * M_PI);
184 for (i = 0; i < num_dots; i++) {
187 XO_DOTS_POSITION_RADIUS, 0,
192 cairo_rotate (cr, 2 * M_PI / XO_DOTS_NUM_DOTS);
199 draw_ring (cairo_t *cr)
206 XO_RING_INNER_RADIUS,
211 XO_RING_OUTER_RADIUS,
213 set_color (cr, &ring_white);
215 cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
222 xoboot_expose_event (GtkWidget *widget,
223 GdkEventExpose *event,
226 state_t *state = closure;
227 anim_stage_t anim_stage = state->anim_stage;
230 double spin_transition = 1.0;
232 cr = gdk_cairo_create (widget->window);
234 if (anim_stage < ANIM_FADE)
235 set_color (cr, &background[0]);
237 cairo_set_source_rgb (cr, LERP_COLORS (background[0],
243 if (anim_stage == ANIM_BLANK)
246 if (anim_stage == ANIM_SPIN)
247 spin_transition = state->transition;
250 if (anim_stage >= ANIM_OUTLINE) {
251 set_color (cr, &xo_black);
252 draw_head (cr, XO_OUTLINE_EXTRA);
253 draw_body (cr, XO_OUTLINE_EXTRA, spin_transition);
254 shrink = - XO_OUTLINE_SHRINK;
255 if (anim_stage == ANIM_OUTLINE)
256 shrink *= state->transition;
259 set_color (cr, &xo_green);
261 draw_head (cr, shrink);
262 if (anim_stage == ANIM_HEAD)
265 draw_body (cr, shrink, spin_transition);
266 if (anim_stage == ANIM_BODY)
269 if (anim_stage < ANIM_FADE) {
270 draw_dots (cr, (XO_DOTS_NUM_DOTS + 1) * spin_transition);
282 create_window (state_t *state)
285 GtkWidget *drawing_area;
287 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
288 gtk_window_set_title (GTK_WINDOW (window), "OLPC Boot Animation Demo");
290 g_signal_connect (window, "destroy",
291 G_CALLBACK (gtk_main_quit), &window);
293 drawing_area = gtk_drawing_area_new ();
295 /* We want to force 640x480 to emulate the OLPC display */
296 gtk_widget_set_size_request (drawing_area, 640, 480);
298 gtk_container_add (GTK_CONTAINER (window), drawing_area);
300 g_signal_connect (drawing_area, "expose_event",
301 G_CALLBACK (xoboot_expose_event), state);
307 transition_advance (gpointer closure)
309 state_t *state = closure;
311 if (state->frame >= state->num_frames - 1) {
312 state->frame = state->num_frames - 1;
317 state->transition = (double) state->frame / (state->num_frames - 1);
319 gtk_widget_queue_draw (state->drawing_area);
325 timeline_advance (gpointer closure)
327 state_t *state = closure;
330 if (state->anim_stage == ANIM_DONE)
333 if (--state->stage_ticks_remaining)
337 tl = &timeline[state->anim_stage];
338 state->stage_ticks_remaining = (tl->duration * 1000
339 / TIMELINE_ADVANCE_TIMER_PERIOD_MS);
340 gtk_widget_queue_draw (state->drawing_area);
342 if (tl->transition > 1.0 / TIMELINE_TRANSITION_FPS) {
343 state->num_frames = tl->transition * TIMELINE_TRANSITION_FPS;
345 state->transition = 0.0;
346 g_timeout_add (1000 / TIMELINE_TRANSITION_FPS, transition_advance, state);
348 state->num_frames = 1;
350 state->transition = 1.0;
357 main (int argc, char *argv[])
361 gtk_init (&argc, &argv);
363 state.drawing_area = create_window (&state);
364 state.progress = 0.0;
365 state.anim_stage = 0;
366 state.num_frames = 1;
368 state.transition = 1.0;
369 state.stage_ticks_remaining = (timeline[state.anim_stage].duration * 1000
370 / TIMELINE_ADVANCE_TIMER_PERIOD_MS);
372 gtk_widget_show_all (gtk_widget_get_toplevel (state.drawing_area));
374 g_timeout_add (TIMELINE_ADVANCE_TIMER_PERIOD_MS,
375 timeline_advance, &state);