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)
152 XO_BODY_CENTER_X - XO_BODY_DELTA,
153 XO_BODY_CENTER_Y + XO_BODY_DELTA);
154 cairo_rel_line_to (cr, XO_BODY_DELTA, -XO_BODY_DELTA);
155 cairo_rel_line_to (cr, XO_BODY_DELTA, XO_BODY_DELTA);
157 cairo_translate (cr, XO_HEAD_CENTER_X, XO_HEAD_CENTER_Y);
159 angle = 2 * M_PI * spin_transition;
160 y = cos (angle) * XO_DOTS_POSITION_RADIUS;
161 x = sin (angle) * XO_DOTS_POSITION_RADIUS;
162 angle = atan2 (x, y + XO_BODY_CENTER_Y - XO_HEAD_CENTER_Y);
164 cairo_rotate (cr, angle);
165 cairo_translate (cr, -XO_HEAD_CENTER_X, -XO_HEAD_CENTER_Y);
168 XO_BODY_CENTER_X - XO_BODY_DELTA,
169 XO_BODY_CENTER_Y - XO_BODY_DELTA);
170 cairo_rel_line_to (cr, XO_BODY_DELTA, XO_BODY_DELTA);
171 cairo_rel_line_to (cr, XO_BODY_DELTA, -XO_BODY_DELTA);
173 cairo_set_line_width (cr, XO_BODY_LINE_WIDTH + 2 * extra);
174 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
181 draw_dots (cairo_t *cr, double num_dots)
187 set_color (cr, &dot_gray);
189 cairo_translate (cr, XO_DOTS_CENTER_X, XO_DOTS_CENTER_Y);
190 cairo_rotate (cr, - 1.5 * M_PI);
192 for (i = 0; i <= num_dots; i++) {
195 XO_DOTS_POSITION_RADIUS, 0,
200 cairo_rotate (cr, 2 * M_PI / XO_DOTS_NUM_DOTS);
207 draw_ring (cairo_t *cr)
214 XO_RING_INNER_RADIUS,
219 XO_RING_OUTER_RADIUS,
221 set_color (cr, &ring_white);
223 cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
230 xoboot_expose_event (GtkWidget *widget,
231 GdkEventExpose *event,
234 state_t *state = closure;
235 anim_stage_t anim_stage = state->anim_stage;
238 double spin_transition = 1.0;
240 cr = gdk_cairo_create (widget->window);
242 if (anim_stage < ANIM_FADE)
243 set_color (cr, &background[0]);
245 cairo_set_source_rgb (cr, LERP_COLORS (background[0],
251 if (anim_stage == ANIM_BLANK)
254 if (anim_stage == ANIM_SPIN)
255 spin_transition = state->transition;
258 if (anim_stage >= ANIM_OUTLINE) {
259 set_color (cr, &xo_black);
260 draw_head (cr, XO_OUTLINE_EXTRA);
261 draw_body (cr, XO_OUTLINE_EXTRA, spin_transition);
262 shrink = - XO_OUTLINE_SHRINK;
263 if (anim_stage == ANIM_OUTLINE)
264 shrink *= state->transition;
267 set_color (cr, &xo_green);
269 draw_head (cr, shrink);
270 if (anim_stage == ANIM_HEAD)
273 draw_body (cr, shrink, spin_transition);
274 if (anim_stage == ANIM_BODY)
277 if (anim_stage < ANIM_FADE) {
278 draw_dots (cr, (XO_DOTS_NUM_DOTS) * spin_transition);
290 create_window (state_t *state)
293 GtkWidget *drawing_area;
295 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
296 gtk_window_set_title (GTK_WINDOW (window), "OLPC Boot Animation Demo");
298 g_signal_connect (window, "destroy",
299 G_CALLBACK (gtk_main_quit), &window);
301 drawing_area = gtk_drawing_area_new ();
303 /* We want to force 640x480 to emulate the OLPC display */
304 gtk_widget_set_size_request (drawing_area, 640, 480);
306 gtk_container_add (GTK_CONTAINER (window), drawing_area);
308 g_signal_connect (drawing_area, "expose_event",
309 G_CALLBACK (xoboot_expose_event), state);
315 transition_advance (gpointer closure)
317 state_t *state = closure;
319 if (state->frame >= state->num_frames - 1) {
320 state->frame = state->num_frames - 1;
325 state->transition = (double) state->frame / (state->num_frames - 1);
327 gtk_widget_queue_draw (state->drawing_area);
333 timeline_advance (gpointer closure)
335 state_t *state = closure;
338 if (state->anim_stage == ANIM_DONE)
341 if (--state->stage_ticks_remaining)
345 tl = &timeline[state->anim_stage];
346 state->stage_ticks_remaining = (tl->duration * 1000
347 / TIMELINE_ADVANCE_TIMER_PERIOD_MS);
348 gtk_widget_queue_draw (state->drawing_area);
350 if (tl->transition > 1.0 / TIMELINE_TRANSITION_FPS) {
351 state->num_frames = tl->transition * TIMELINE_TRANSITION_FPS;
353 state->transition = 0.0;
354 g_timeout_add (1000 / TIMELINE_TRANSITION_FPS, transition_advance, state);
356 state->num_frames = 1;
358 state->transition = 1.0;
365 main (int argc, char *argv[])
369 gtk_init (&argc, &argv);
371 state.drawing_area = create_window (&state);
372 state.progress = 0.0;
373 state.anim_stage = 0;
374 state.num_frames = 1;
376 state.transition = 1.0;
377 state.stage_ticks_remaining = (timeline[state.anim_stage].duration * 1000
378 / TIMELINE_ADVANCE_TIMER_PERIOD_MS);
380 gtk_widget_show_all (gtk_widget_get_toplevel (state.drawing_area));
382 g_timeout_add (TIMELINE_ADVANCE_TIMER_PERIOD_MS,
383 timeline_advance, &state);