core: use a monotonic clock for animation timing calculations
[fbsplash.git] / core / src / mng_render.c
blob268a98a8924868251eea971d5e7ffcf724cec976
1 /*
2 * mng_render.c - Functions for rendering MNG animations.
4 * Copyright (C) 2005 Bernard Blackham <bernard@blackham.com.au>
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License v2. See the file COPYING in the main directory of this archive for
8 * more details.
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <fcntl.h>
15 #include <libmng.h>
16 #include <unistd.h>
17 #include "common.h"
18 #include "render.h"
20 static int mng_readfile(mng_handle mngh, char *filename)
22 int fd, len;
23 char *file_data;
24 struct stat sb;
25 mng_anim *mng = mng_get_userdata(mngh);
27 if ((fd = open(filename, O_RDONLY)) < 0) {
28 perror("mng_readfile: open");
29 return 0;
31 if (fstat(fd, &sb) == -1) {
32 perror("mng_readfile: stat");
33 goto close_fail;
35 mng->len = sb.st_size;
37 if ((mng->data = malloc(mng->len)) == NULL) {
38 iprint(MSG_ERROR, "mng_readfile: Unable to allocate memory for MNG file\n");
39 goto close_fail;
42 len = 0;
43 file_data = mng->data;
44 while (len < mng->len) {
45 int ret;
46 int amt = 0x10000;
47 if (mng->len - len < amt)
48 amt = mng->len - len;
49 ret = read(fd, file_data, amt); /* read up to 64KB at a time */
50 switch (ret) {
51 case -1:
52 perror("mng_readfile: read");
53 goto close_fail;
54 case 0:
55 iprint(MSG_ERROR, "mng_readfile: Shorter file than expected!\n");
56 goto close_fail;
58 file_data += ret;
59 len += ret;
62 close(fd);
64 return 1;
66 close_fail:
67 close(fd);
68 return 0;
71 mng_handle mng_load(char* filename, int *width, int *height)
73 mng_handle mngh;
74 mng_anim *mng;
76 mng = (mng_anim*)malloc(sizeof(mng_anim));
77 if (!mng) {
78 iprint(MSG_ERROR, "%s: Unable to allocate memory for MNG data\n",
79 __FUNCTION__);
80 return MNG_NULL;
83 memset(mng, 0, sizeof(mng_anim));
84 mngh = mng_initialize(mng, fb_mng_memalloc, fb_mng_memfree,
85 MNG_NULL);
86 if (mngh == MNG_NULL) {
87 iprint(MSG_ERROR, "%s: mng_initialize failed\n", __FUNCTION__);
88 goto freemem_fail;
91 if (mng_init_callbacks(mngh)) {
92 print_mng_error(mngh, "mng_init_callbacks failed");
93 goto cleanup_fail;
96 /* Load the file into memory */
97 if (!mng_readfile(mngh, filename))
98 goto cleanup_fail;
100 /* Read and parse the file */
101 if (mng_read(mngh) != MNG_NOERROR) {
102 /* Do something about it. */
103 print_mng_error(mngh, "mng_read failed");
104 goto cleanup_fail;
107 mng->num_frames = mng_get_totalframes(mngh);
108 *width = mng->canvas_w;
109 *height = mng->canvas_h;
111 return mngh;
113 cleanup_fail:
114 mng_cleanup(&mngh);
115 freemem_fail:
116 free(mng);
117 return MNG_NULL;
120 void mng_done(mng_handle mngh)
122 mng_cleanup(&mngh);
125 mng_retcode mng_render_next(mng_handle mngh)
127 mng_anim *mng = mng_get_userdata(mngh);
128 mng_retcode ret;
129 int last_frame = 0;
131 /* last_frame = mng_get_currentframe(mngh) == mng->num_frames; FIXME */
132 if (!mng->displayed_first) {
133 ret = mng_display(mngh);
134 if (ret == MNG_NOERROR || ret == MNG_NEEDTIMERWAIT)
135 mng->displayed_first = 1;
136 } else
137 ret = mng_display_resume(mngh);
139 if (last_frame)
140 return MNG_NOERROR;
142 if (ret == MNG_NEEDTIMERWAIT || ret == MNG_NOERROR)
143 return ret;
145 print_mng_error(mngh, "mng_display failed");
147 return ret;
150 mng_retcode mng_render_proportional(mng_handle mngh, int progress)
152 mng_anim *mng = mng_get_userdata(mngh);
153 mng_retcode ret = MNG_NOERROR;
154 int frame_num, current_frame;
156 frame_num = ((progress * mng->num_frames) / FBSPL_PROGRESS_MAX) + 1;
157 if (!mng->displayed_first) {
158 ret = mng_display(mngh);
159 mng->displayed_first = 1;
162 if (mng->num_frames == 0 && mng->displayed_first)
163 return MNG_NOERROR;
165 if (frame_num > mng->num_frames)
166 frame_num = mng->num_frames;
168 current_frame = mng_get_currentframe(mngh);
169 if (current_frame == frame_num)
170 return ret;
172 /* Don't bother freezing if the next frame is just n+1. mng_display_resume
173 * will do this case by default, and it saves us from the horrid hack
174 * below.
176 if (frame_num != current_frame + 1) {
177 mng_display_freeze(mngh);
179 /* XXX: hack! workaround what seems to be a bug in libmng - it won't
180 * actually repaint the canvas if you wind an animation "forwards"
181 * using goframe, only backwards, so go to the end of the animation first.
184 mng_display_goframe(mngh, mng->num_frames);
185 if (frame_num != mng->num_frames)
186 mng_display_goframe(mngh, frame_num);
189 ret = mng_display_resume(mngh);
191 if (ret == MNG_NEEDTIMERWAIT || ret == MNG_NOERROR)
192 return ret;
194 print_mng_error(mngh, "mng_display failed");
196 return ret;
199 void anim_prerender(stheme_t *theme, anim *a, bool force)
201 obj *o = container_of(a);
203 if (!o->visible)
204 return;
206 if ((a->flags & F_ANIM_METHOD_MASK) == F_ANIM_PROPORTIONAL) {
207 if (a->curr_progress == config.progress && !force)
208 return;
210 a->curr_progress = config.progress;
212 int ret = mng_render_proportional(a->mng, config.progress);
214 if (ret == MNG_NEEDTIMERWAIT || ret == MNG_NOERROR) {
215 blit_add(theme, &o->bnd);
216 render_add(theme, o, &o->bnd);
218 } else {
219 blit_add(theme, &o->bnd);
220 render_add(theme, o, &o->bnd);
224 void anim_render(stheme_t *theme, anim *a, rect *re, u8* tg)
226 rgbacolor *src;
227 mng_anim *mng = mng_get_userdata(a->mng);
228 obj *o = container_of(a);
229 int line;
231 if (!o->visible)
232 return;
234 src = (rgbacolor*)mng->canvas;
236 src += mng->canvas_w * (re->y1 - a->y) + (re->x1 - a->x);
237 tg += ((theme->xres * re->y1) + re->x1) * fbd.bytespp;
239 for (line = re->y1; line <= re->y2; line++) {
240 rgba2fb(src, tg, tg, re->x2 - re->x1 + 1, line, 1, o->opacity);
241 tg += theme->xres * fbd.bytespp;
242 src += mng->canvas_w;
245 return;
248 mng_retcode mng_display_restart(mng_handle mngh)
250 mng_anim *mng = mng_get_userdata(mngh);
252 mng->displayed_first = 0;
253 return mng_display_reset(mngh);
257 * Renders an animation frame to the anim's canvas.
259 void anim_render_canvas(anim *a)
261 int ret;
262 mng_anim *mng;
263 obj *o;
265 mng = mng_get_userdata(a->mng);
266 mng->wait_msecs = 0;
267 memset(&mng->start_time, 0, sizeof(struct timeval));
269 /* XXX: This is a workaround for what seems to be a bug in libmng.
270 * Either we clear the canvas ourselves, or parts of the previous frame
271 * will remain in it after rendering the current one. */
272 memset(mng->canvas, 0, mng->canvas_h * mng->canvas_w * mng->canvas_bytes_pp);
273 ret = mng_render_next(a->mng);
274 if (ret == MNG_NOERROR) {
275 if (a->flags & F_ANIM_ONCE) {
276 a->status = F_ANIM_STATUS_DONE;
277 } else {
278 mng_display_restart(a->mng);
282 if (ret == MNG_NOERROR || ret == MNG_NEEDTIMERWAIT) {
283 o = container_of(a);
284 o->invalid = true;
288 int load_anims(stheme_t *theme)
290 int err = 0;
291 item *i;
293 for (i = theme->anims.head; i != NULL; i = i->next) {
294 anim *a = (anim*)i->p;
295 a->mng = mng_load(a->filename, &a->w, &a->h);
296 if (!a->mng) {
297 iprint(MSG_ERROR, "%s: failed to allocate memory for mng\n", __func__);
298 err = -1;
302 return err;