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
12 #include <sys/types.h>
20 static int mng_readfile(mng_handle mngh
, char *filename
)
25 mng_anim
*mng
= mng_get_userdata(mngh
);
27 if ((fd
= open(filename
, O_RDONLY
)) < 0) {
28 perror("mng_readfile: open");
31 if (fstat(fd
, &sb
) == -1) {
32 perror("mng_readfile: stat");
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");
43 file_data
= mng
->data
;
44 while (len
< mng
->len
) {
47 if (mng
->len
- len
< amt
)
49 ret
= read(fd
, file_data
, amt
); /* read up to 64KB at a time */
52 perror("mng_readfile: read");
55 iprint(MSG_ERROR
, "mng_readfile: Shorter file than expected!\n");
71 mng_handle
mng_load(char* filename
, int *width
, int *height
)
76 mng
= (mng_anim
*)malloc(sizeof(mng_anim
));
78 iprint(MSG_ERROR
, "%s: Unable to allocate memory for MNG data\n",
83 memset(mng
, 0, sizeof(mng_anim
));
84 mngh
= mng_initialize(mng
, fb_mng_memalloc
, fb_mng_memfree
,
86 if (mngh
== MNG_NULL
) {
87 iprint(MSG_ERROR
, "%s: mng_initialize failed\n", __FUNCTION__
);
91 if (mng_init_callbacks(mngh
)) {
92 print_mng_error(mngh
, "mng_init_callbacks failed");
96 /* Load the file into memory */
97 if (!mng_readfile(mngh
, filename
))
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");
107 mng
->num_frames
= mng_get_totalframes(mngh
);
108 *width
= mng
->canvas_w
;
109 *height
= mng
->canvas_h
;
120 void mng_done(mng_handle mngh
)
125 mng_retcode
mng_render_next(mng_handle mngh
)
127 mng_anim
*mng
= mng_get_userdata(mngh
);
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;
137 ret
= mng_display_resume(mngh
);
142 if (ret
== MNG_NEEDTIMERWAIT
|| ret
== MNG_NOERROR
)
145 print_mng_error(mngh
, "mng_display failed");
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
)
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
)
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
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
)
194 print_mng_error(mngh
, "mng_display failed");
199 void anim_prerender(stheme_t
*theme
, anim
*a
, bool force
)
201 obj
*o
= container_of(a
);
206 if ((a
->flags
& F_ANIM_METHOD_MASK
) == F_ANIM_PROPORTIONAL
) {
207 if (a
->curr_progress
== config
.progress
&& !force
)
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
);
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
)
227 mng_anim
*mng
= mng_get_userdata(a
->mng
);
228 obj
*o
= container_of(a
);
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
;
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
)
265 mng
= mng_get_userdata(a
->mng
);
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
;
278 mng_display_restart(a
->mng
);
282 if (ret
== MNG_NOERROR
|| ret
== MNG_NEEDTIMERWAIT
) {
288 int load_anims(stheme_t
*theme
)
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
);
297 iprint(MSG_ERROR
, "%s: failed to allocate memory for mng\n", __func__
);