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."
37 typedef struct _timeline {
43 #define TIMELINE_ADVANCE_TIMER_PERIOD_MS 500
44 #define TIMELINE_TRANSITION_FPS 20
46 timeline_t timeline[] = {
47 { 0.5, ANIM_BLANK, 0.0},
48 { 1.0, ANIM_HEAD, 0.0},
49 { 2.0, ANIM_BODY, 0.0},
50 { 7.0, ANIM_SPIN, 6.0},
51 { 1.0, ANIM_SPIN_DONE, 0.0},
52 { 1.0, ANIM_OUTLINE, 0.1},
53 { 3.0, ANIM_FADE, 1.0},
57 typedef struct _state {
58 GtkWidget *drawing_area;
60 anim_stage_t anim_stage;
61 int stage_ticks_remaining;
62 /* frames are used during a transition */
68 typedef struct _color {
74 #define HEX_COLOR(r,g,b) {(r) / 255.0, (g) / 255.0, (b) / 255.0}
76 color_t background[2] = {
77 HEX_COLOR (0x75, 0x75, 0x75),
78 HEX_COLOR (0xdb, 0xdc, 0xdf)
81 color_t xo_green = HEX_COLOR (0x4f, 0xff, 0x12);
82 color_t xo_black = HEX_COLOR (0x30, 0x30, 0x30);
83 color_t dot_gray = HEX_COLOR (0xe5, 0xe5, 0xe5);
84 color_t ring_white = HEX_COLOR (0xf1, 0xf1, 0xf1);
86 #define LERP(a,b,t) ((a) + (t) * ((b) - (a)))
87 #define LERP_COLORS(c0, c1, t) LERP((c0).r, (c1).r, (t)), \
88 LERP((c0).g, (c1).g, (t)), \
89 LERP((c0).b, (c1).b, (t))
92 set_color (cairo_t *cr, color_t *color)
94 cairo_set_source_rgb (cr, color->r, color->g, color->b);
97 #define XO_HEAD_CENTER_X 320
98 #define XO_HEAD_CENTER_Y 211.5
99 #define XO_HEAD_RADIUS 12.25
100 #define XO_BODY_CENTER_X 320
101 #define XO_BODY_CENTER_Y 250
102 #define XO_BODY_DELTA 20.5
103 #define XO_BODY_LINE_WIDTH 12.25
104 #define XO_OUTLINE_EXTRA 2.5
105 #define XO_OUTLINE_SHRINK 1.1
106 #define XO_DOTS_CENTER_X 320
107 #define XO_DOTS_CENTER_Y 240
108 #define XO_DOTS_POSITION_RADIUS 112.5
109 #define XO_DOTS_DOT_RADIUS 5.5
110 #define XO_DOTS_NUM_DOTS 30
111 #define XO_RING_CENTER_X 320
112 #define XO_RING_CENTER_Y 240
113 #define XO_RING_INNER_RADIUS 63
114 #define XO_RING_OUTER_RADIUS 118
117 draw_head (cairo_t *cr, double extra)
122 XO_HEAD_RADIUS + extra,
128 draw_body (cairo_t *cr, double extra, double spin_transition)
133 XO_BODY_CENTER_X - XO_BODY_DELTA,
134 XO_BODY_CENTER_Y + XO_BODY_DELTA);
135 cairo_rel_line_to (cr, XO_BODY_DELTA, -XO_BODY_DELTA);
136 cairo_rel_line_to (cr, XO_BODY_DELTA, XO_BODY_DELTA);
138 cairo_translate (cr, XO_HEAD_CENTER_X, XO_HEAD_CENTER_Y);
139 cairo_rotate (cr, 2 * M_PI * spin_transition);
140 cairo_translate (cr, -XO_HEAD_CENTER_X, -XO_HEAD_CENTER_Y);
143 XO_BODY_CENTER_X - XO_BODY_DELTA,
144 XO_BODY_CENTER_Y - XO_BODY_DELTA);
145 cairo_rel_line_to (cr, XO_BODY_DELTA, XO_BODY_DELTA);
146 cairo_rel_line_to (cr, XO_BODY_DELTA, -XO_BODY_DELTA);
148 cairo_set_line_width (cr, XO_BODY_LINE_WIDTH + 2 * extra);
149 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
156 draw_dots (cairo_t *cr, double num_dots)
162 set_color (cr, &dot_gray);
164 cairo_translate (cr, XO_DOTS_CENTER_X, XO_DOTS_CENTER_Y);
165 cairo_rotate (cr, - 1.5 * M_PI);
167 for (i = 0; i < num_dots; i++) {
170 XO_DOTS_POSITION_RADIUS, 0,
175 cairo_rotate (cr, 2 * M_PI / XO_DOTS_NUM_DOTS);
182 draw_ring (cairo_t *cr)
189 XO_RING_INNER_RADIUS,
194 XO_RING_OUTER_RADIUS,
196 set_color (cr, &ring_white);
198 cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
205 xoboot_expose_event (GtkWidget *widget,
206 GdkEventExpose *event,
209 state_t *state = closure;
210 anim_stage_t anim_stage = state->anim_stage;
213 double spin_transition = 1.0;
215 cr = gdk_cairo_create (widget->window);
217 if (anim_stage < ANIM_FADE)
218 set_color (cr, &background[0]);
220 cairo_set_source_rgb (cr, LERP_COLORS (background[0],
226 if (anim_stage == ANIM_BLANK)
229 if (anim_stage == ANIM_SPIN)
230 spin_transition = state->transition;
233 if (anim_stage >= ANIM_OUTLINE) {
234 set_color (cr, &xo_black);
235 draw_head (cr, XO_OUTLINE_EXTRA);
236 draw_body (cr, XO_OUTLINE_EXTRA, spin_transition);
237 shrink = - XO_OUTLINE_SHRINK;
238 if (anim_stage == ANIM_OUTLINE)
239 shrink *= state->transition;
242 set_color (cr, &xo_green);
244 draw_head (cr, shrink);
245 if (anim_stage == ANIM_HEAD)
248 draw_body (cr, shrink, spin_transition);
249 if (anim_stage == ANIM_BODY)
252 if (anim_stage < ANIM_FADE) {
253 draw_dots (cr, (XO_DOTS_NUM_DOTS + 1) * spin_transition);
265 create_window (state_t *state)
268 GtkWidget *drawing_area;
270 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
271 gtk_window_set_title (GTK_WINDOW (window), "OLPC Boot Animation Demo");
273 g_signal_connect (window, "destroy",
274 G_CALLBACK (gtk_main_quit), &window);
276 drawing_area = gtk_drawing_area_new ();
278 /* We want to force 640x480 to emulate the OLPC display */
279 gtk_widget_set_size_request (drawing_area, 640, 480);
281 gtk_container_add (GTK_CONTAINER (window), drawing_area);
283 g_signal_connect (drawing_area, "expose_event",
284 G_CALLBACK (xoboot_expose_event), state);
290 transition_advance (gpointer closure)
292 state_t *state = closure;
294 if (state->frame >= state->num_frames - 1) {
295 state->frame = state->num_frames - 1;
300 state->transition = (double) state->frame / (state->num_frames - 1);
302 gtk_widget_queue_draw (state->drawing_area);
308 timeline_advance (gpointer closure)
310 state_t *state = closure;
313 if (state->anim_stage == ANIM_DONE)
316 if (--state->stage_ticks_remaining)
320 tl = &timeline[state->anim_stage];
321 state->stage_ticks_remaining = (tl->duration * 1000
322 / TIMELINE_ADVANCE_TIMER_PERIOD_MS);
323 gtk_widget_queue_draw (state->drawing_area);
325 if (tl->transition > 1.0 / TIMELINE_TRANSITION_FPS) {
326 state->num_frames = tl->transition * TIMELINE_TRANSITION_FPS;
328 state->transition = 0.0;
329 g_timeout_add (1000 / TIMELINE_TRANSITION_FPS, transition_advance, state);
331 state->num_frames = 1;
333 state->transition = 1.0;
340 main (int argc, char *argv[])
344 gtk_init (&argc, &argv);
346 state.drawing_area = create_window (&state);
347 state.progress = 0.0;
348 state.anim_stage = 0;
349 state.num_frames = 1;
351 state.transition = 1.0;
352 state.stage_ticks_remaining = (timeline[state.anim_stage].duration * 1000
353 / TIMELINE_ADVANCE_TIMER_PERIOD_MS);
355 gtk_widget_show_all (gtk_widget_get_toplevel (state.drawing_area));
357 g_timeout_add (TIMELINE_ADVANCE_TIMER_PERIOD_MS,
358 timeline_advance, &state);