Mercurial > hg > forks > libbpg
annotate bpgview.c @ 5:524eae707ba4
Cosmetics: Remove trailing whitespace.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Wed, 07 Dec 2016 10:38:38 +0200 |
parents | 772086c29cc7 |
children |
rev | line source |
---|---|
0 | 1 /* |
2 * BPG viewer | |
3 * | |
4 * Copyright (c) 2014-2015 Fabrice Bellard | |
5 * | |
6 * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 * of this software and associated documentation files (the "Software"), to deal | |
8 * in the Software without restriction, including without limitation the rights | |
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 * copies of the Software, and to permit persons to whom the Software is | |
11 * furnished to do so, subject to the following conditions: | |
12 * | |
13 * The above copyright notice and this permission notice shall be included in | |
14 * all copies or substantial portions of the Software. | |
15 * | |
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 * THE SOFTWARE. | |
23 */ | |
24 | |
25 #include <stdlib.h> | |
26 #include <stdio.h> | |
27 #include <math.h> | |
28 #include <getopt.h> | |
29 #include <inttypes.h> | |
30 #ifdef WIN32 | |
31 #include <windows.h> | |
32 #endif | |
33 #include <SDL/SDL.h> | |
34 #include <SDL/SDL_image.h> | |
35 | |
36 #include "libbpg.h" | |
37 | |
38 typedef enum { | |
39 BG_BLACK, | |
40 BG_TILED, | |
41 } BackgroundTypeEnum; | |
42 | |
43 typedef struct { | |
44 SDL_Surface *img; | |
45 int delay; /* in ms */ | |
46 } Frame; | |
47 | |
48 typedef struct { | |
49 int screen_w, screen_h; | |
50 int win_w, win_h; | |
51 SDL_Surface *screen; | |
52 | |
53 int img_w, img_h; | |
54 int frame_count; | |
55 Frame *frames; | |
56 int frame_index; /* index of the current frame */ | |
57 int loop_counter; | |
58 int loop_count; | |
59 SDL_TimerID frame_timer_id; | |
60 | |
61 int is_full_screen; | |
62 int pos_x, pos_y; | |
63 BackgroundTypeEnum background_type; | |
64 } DispContext; | |
65 | |
66 static uint32_t timer_cb(uint32_t interval, void *param); | |
67 | |
68 static inline int clamp_int(int val, int min_val, int max_val) | |
69 { | |
70 if (val < min_val) | |
71 return min_val; | |
72 else if (val > max_val) | |
73 return max_val; | |
74 else | |
75 return val; | |
76 } | |
77 | |
78 Frame *bpg_load(FILE *f, int *pframe_count, int *ploop_count) | |
79 { | |
80 BPGDecoderContext *s; | |
81 BPGImageInfo bi_s, *bi = &bi_s; | |
82 uint8_t *buf; | |
83 int len, y; | |
84 SDL_Surface *img; | |
85 Frame *frames; | |
86 uint32_t rmask, gmask, bmask, amask; | |
87 int frame_count, i, delay_num, delay_den; | |
88 | |
89 fseek(f, 0, SEEK_END); | |
90 len = ftell(f); | |
91 fseek(f, 0, SEEK_SET); | |
92 if (len < 0) | |
93 return NULL; | |
94 buf = malloc(len); | |
95 if (!buf) | |
96 return NULL; | |
97 if (fread(buf, 1, len, f) != len) | |
98 return NULL; | |
5
524eae707ba4
Cosmetics: Remove trailing whitespace.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
99 |
0 | 100 frames = NULL; |
101 frame_count = 0; | |
102 | |
103 s = bpg_decoder_open(); | |
5
524eae707ba4
Cosmetics: Remove trailing whitespace.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
104 if (bpg_decoder_decode(s, buf, len) < 0) |
0 | 105 goto fail; |
106 bpg_decoder_get_info(s, bi); | |
107 #if SDL_BYTEORDER == SDL_BIG_ENDIAN | |
108 rmask = 0xff000000; | |
109 gmask = 0x00ff0000; | |
110 bmask = 0x0000ff00; | |
111 amask = 0x000000ff; | |
112 #else | |
113 rmask = 0x000000ff; | |
114 gmask = 0x0000ff00; | |
115 bmask = 0x00ff0000; | |
116 amask = 0xff000000; | |
117 #endif | |
118 for(;;) { | |
119 if (bpg_decoder_start(s, BPG_OUTPUT_FORMAT_RGBA32) < 0) | |
120 break; | |
121 bpg_decoder_get_frame_duration(s, &delay_num, &delay_den); | |
122 frames = realloc(frames, sizeof(frames[0]) * (frame_count + 1)); | |
123 img = SDL_CreateRGBSurface(SDL_HWSURFACE, bi->width, bi->height, 32, | |
124 rmask, gmask, bmask, amask); | |
5
524eae707ba4
Cosmetics: Remove trailing whitespace.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
125 if (!img) |
0 | 126 goto fail; |
5
524eae707ba4
Cosmetics: Remove trailing whitespace.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
127 |
0 | 128 SDL_LockSurface(img); |
129 for(y = 0; y < bi->height; y++) { | |
130 bpg_decoder_get_line(s, (uint8_t *)img->pixels + y * img->pitch); | |
131 } | |
132 SDL_UnlockSurface(img); | |
133 frames[frame_count].img = img; | |
134 frames[frame_count].delay = (delay_num * 1000) / delay_den; | |
135 frame_count++; | |
136 } | |
137 bpg_decoder_close(s); | |
138 *pframe_count = frame_count; | |
139 *ploop_count = bi->loop_count; | |
140 return frames; | |
141 fail: | |
142 bpg_decoder_close(s); | |
143 for(i = 0; i < frame_count; i++) { | |
144 SDL_FreeSurface(frames[i].img); | |
145 } | |
146 free(frames); | |
147 *pframe_count = 0; | |
148 return NULL; | |
149 } | |
150 | |
151 static void restart_frame_timer(DispContext *dc) | |
152 { | |
153 if (dc->frame_timer_id) { | |
154 /* XXX: the SDL timer API is not safe, so we remove the timer even if it already expired */ | |
155 SDL_RemoveTimer(dc->frame_timer_id); | |
156 dc->frame_timer_id = 0; | |
157 } | |
5
524eae707ba4
Cosmetics: Remove trailing whitespace.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
158 dc->frame_timer_id = |
0 | 159 SDL_AddTimer(dc->frames[dc->frame_index].delay, timer_cb, NULL); |
160 } | |
161 | |
162 int load_image(DispContext *dc, const char *filename) | |
163 { | |
164 SDL_Surface *img; | |
165 Frame *frames; | |
166 FILE *f; | |
167 uint8_t buf[BPG_DECODER_INFO_BUF_SIZE]; | |
168 int len, i, frame_count, loop_count; | |
169 BPGImageInfo bi; | |
170 | |
171 f = fopen(filename, "rb"); | |
172 if (!f) | |
173 goto fail; | |
174 len = fread(buf, 1, sizeof(buf), f); | |
175 if (bpg_decoder_get_info_from_buf(&bi, NULL, buf, len) >= 0) { | |
176 fseek(f, 0, SEEK_SET); | |
177 frames = bpg_load(f, &frame_count, &loop_count); | |
178 if (!frames) | |
179 goto fail; | |
180 fclose(f); | |
181 } else { | |
182 /* use SDL image loader */ | |
183 img = IMG_Load(filename); | |
184 if (!img) { | |
185 fail: | |
186 fprintf(stderr, "Could not load '%s'\n", filename); | |
187 return -1; | |
188 } | |
189 frame_count = 1; | |
190 frames = malloc(sizeof(dc->frames[0]) * frame_count); | |
191 frames[0].img = img; | |
192 frames[0].delay = 0; | |
193 loop_count = 1; | |
194 } | |
195 | |
196 for(i = 0; i < dc->frame_count; i++) { | |
197 SDL_FreeSurface(dc->frames[i].img); | |
198 } | |
199 free(dc->frames); | |
200 if (dc->frame_timer_id) { | |
201 SDL_RemoveTimer(dc->frame_timer_id); | |
202 dc->frame_timer_id = 0; | |
203 } | |
5
524eae707ba4
Cosmetics: Remove trailing whitespace.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
204 |
0 | 205 dc->frame_count = frame_count; |
206 dc->frames = frames; | |
207 dc->frame_index = 0; | |
208 dc->loop_counter = 0; | |
209 dc->loop_count = loop_count; | |
210 dc->img_w = dc->frames[0].img->w; | |
211 dc->img_h = dc->frames[0].img->h; | |
212 | |
213 /* start the animation timer if needed */ | |
214 if (dc->frame_count > 1) { | |
215 restart_frame_timer(dc); | |
216 } | |
217 return 0; | |
218 } | |
219 | |
220 void center_image(DispContext *dc) | |
221 { | |
222 dc->pos_x = clamp_int((dc->screen->w - dc->img_w) / 2, -32767, 32768); | |
223 dc->pos_y = clamp_int((dc->screen->h - dc->img_h) / 2, -32767, 32768); | |
224 } | |
225 | |
226 void draw_image(DispContext *dc) | |
227 { | |
228 SDL_Rect r; | |
229 | |
230 r.x = 0; | |
231 r.y = 0; | |
232 r.w = dc->screen->w; | |
233 r.h = dc->screen->h; | |
234 SDL_FillRect(dc->screen, &r, SDL_MapRGB(dc->screen->format, 0x00, 0x00, 0x00)); | |
235 | |
236 if (dc->background_type == BG_TILED) { | |
237 int x, y, tw, w, h, x2, y2, w1, h1, x1, y1; | |
238 uint32_t bgcolors[2]; | |
239 | |
240 tw = 16; | |
241 w = dc->img_w; | |
242 h = dc->img_h; | |
243 w1 = (w + tw - 1) / tw; | |
244 h1 = (h + tw - 1) / tw; | |
245 bgcolors[0] = SDL_MapRGB(dc->screen->format, 100, 100, 100); | |
246 bgcolors[1] = SDL_MapRGB(dc->screen->format, 150, 150, 150); | |
247 for(y = 0; y < h1; y++) { | |
248 for(x = 0; x < w1; x++) { | |
249 x1 = x * tw; | |
250 y1 = y * tw; | |
251 x2 = x1 + tw; | |
252 y2 = y1 + tw; | |
253 if (x2 > w) | |
254 x2 = w; | |
255 if (y2 > h) | |
256 y2 = h; | |
257 r.x = x1 + dc->pos_x; | |
258 r.y = y1 + dc->pos_y; | |
259 r.w = x2 - x1; | |
260 r.h = y2 - y1; | |
261 SDL_FillRect(dc->screen, &r, bgcolors[(x ^ y) & 1]); | |
262 } | |
263 } | |
264 } | |
265 | |
266 r.x = dc->pos_x; | |
267 r.y = dc->pos_y; | |
268 r.w = 0; | |
269 r.h = 0; | |
270 SDL_BlitSurface (dc->frames[dc->frame_index].img, NULL, dc->screen, &r); | |
271 | |
272 SDL_Flip(dc->screen); | |
273 } | |
274 | |
275 void pan_image(DispContext *dc, int dx, int dy) | |
276 { | |
277 int dw, dh; | |
278 | |
279 dw = dc->img_w - dc->screen->w; | |
280 dh = dc->img_h - dc->screen->h; | |
281 if (dw > 0) { | |
282 dc->pos_x += dx; | |
283 if (dc->pos_x < -dw) | |
284 dc->pos_x = -dw; | |
285 else if (dc->pos_x > 0) | |
286 dc->pos_x = 0; | |
287 } | |
288 if (dh > 0) { | |
289 dc->pos_y += dy; | |
290 if (dc->pos_y < -dh) | |
291 dc->pos_y = -dh; | |
292 else if (dc->pos_y > 0) | |
293 dc->pos_y = 0; | |
294 } | |
295 draw_image(dc); | |
296 } | |
297 | |
298 static void set_caption(DispContext *dc, char **argv, | |
299 int image_index, int image_count) | |
300 { | |
301 char buf[1024]; | |
302 const char *filename; | |
303 filename = argv[image_index]; | |
304 snprintf(buf, sizeof(buf), "bpgview [%d of %d] - %s", | |
305 image_index + 1, image_count, filename); | |
306 SDL_WM_SetCaption(buf, buf); | |
307 } | |
308 | |
309 static void open_window(DispContext *dc, int w, int h, int is_full_screen) | |
310 { | |
311 int flags; | |
312 | |
313 flags = SDL_DOUBLEBUF | SDL_HWSURFACE | SDL_HWACCEL; | |
314 if (is_full_screen) | |
315 flags |= SDL_FULLSCREEN; | |
316 else | |
317 flags |= SDL_RESIZABLE; | |
318 | |
319 dc->screen = SDL_SetVideoMode(w, h, 32, flags); | |
320 if (!dc->screen) { | |
321 fprintf(stderr, "Could not init screen\n"); | |
322 exit(1); | |
323 } | |
324 } | |
325 | |
326 static uint32_t timer_cb(uint32_t interval, void *param) | |
327 { | |
328 SDL_Event event; | |
329 SDL_UserEvent userevent; | |
330 | |
331 userevent.type = SDL_USEREVENT; | |
332 userevent.code = 0; | |
333 userevent.data1 = NULL; | |
334 userevent.data2 = NULL; | |
335 | |
336 event.type = SDL_USEREVENT; | |
337 event.user = userevent; | |
338 | |
339 SDL_PushEvent(&event); | |
340 return 0; | |
341 } | |
342 | |
343 #define DEFAULT_W 640 | |
344 #define DEFAULT_H 480 | |
345 | |
346 static void help(void) | |
347 { | |
348 const char *str; | |
349 str = "BPG Image Viewer version " CONFIG_BPG_VERSION "\n" | |
350 "usage: bpgview infile...\n" | |
351 "\n" | |
352 "Keys:\n" | |
353 "q, ESC quit\n" | |
354 "n, SPACE next image\n" | |
355 "p previous image\n" | |
356 "arrows pan\n" | |
357 "c center\n" | |
358 "b toggle background type\n"; | |
359 #ifdef WIN32 | |
360 MessageBox(NULL, str, "Error", MB_ICONERROR | MB_OK); | |
361 exit(1); | |
362 #else | |
363 printf("%s", str); | |
364 exit(1); | |
365 #endif | |
366 } | |
367 | |
368 int main(int argc, char **argv) | |
369 { | |
370 int c, image_index, image_count, incr, i; | |
371 SDL_Event event; | |
372 DispContext dc_s, *dc = &dc_s; | |
373 const SDL_VideoInfo *vi; | |
374 | |
375 for(;;) { | |
376 c = getopt(argc, argv, "h"); | |
377 if (c == -1) | |
378 break; | |
379 switch(c) { | |
380 case 'h': | |
381 show_help: | |
382 help(); | |
383 break; | |
384 default: | |
385 exit(1); | |
386 } | |
387 } | |
388 | |
389 if (optind >= argc) | |
390 goto show_help; | |
391 | |
392 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) { | |
393 fprintf(stderr, "Could not init SDL\n"); | |
394 exit(1); | |
395 } | |
396 memset(dc, 0, sizeof(*dc)); | |
397 | |
398 vi = SDL_GetVideoInfo(); | |
399 dc->screen_w = vi->current_w; | |
400 dc->screen_h = vi->current_h; | |
401 dc->is_full_screen = 0; | |
402 | |
403 image_count = argc - optind; | |
404 image_index = 0; | |
405 if (load_image(dc, argv[optind + image_index]) < 0) | |
406 exit(1); | |
407 dc->background_type = BG_TILED; | |
408 | |
409 { | |
410 int w, h; | |
411 | |
412 if (image_count > 1 || (dc->img_w < 256 || dc->img_h < 256)) { | |
413 w = DEFAULT_W; | |
414 h = DEFAULT_H; | |
415 } else { | |
416 w = clamp_int(dc->img_w, 32, dc->screen_w); | |
417 h = clamp_int(dc->img_h, 32, dc->screen_h); | |
418 } | |
419 open_window(dc, w, h, 0); | |
420 set_caption(dc, argv + optind, image_index, image_count); | |
421 } | |
422 | |
423 center_image(dc); | |
424 draw_image(dc); | |
425 | |
426 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); | |
427 | |
428 for(;;) { | |
429 if (!SDL_WaitEvent(&event)) | |
430 continue; | |
431 switch(event.type) { | |
432 case SDL_KEYDOWN: | |
433 switch (event.key.keysym.sym) { | |
434 case SDLK_ESCAPE: | |
435 case SDLK_q: | |
436 goto done; | |
437 case SDLK_SPACE: /* next image */ | |
438 case SDLK_n: | |
439 incr = 1; | |
440 goto prev_next; | |
441 case SDLK_p: /* previous image */ | |
442 incr = -1; | |
443 prev_next: | |
444 if (image_count > 1) { | |
445 for(i = 0; i < image_count; i++) { | |
446 image_index += incr; | |
447 if (image_index < 0) | |
448 image_index = image_count - 1; | |
449 else if (image_index >= image_count) | |
450 image_index = 0; | |
451 if (load_image(dc, argv[optind + image_index]) == 0) | |
452 break; | |
453 } | |
454 if (i == image_count) | |
455 exit(1); | |
456 set_caption(dc, argv + optind, image_index, image_count); | |
457 center_image(dc); | |
458 draw_image(dc); | |
459 } | |
460 break; | |
461 case SDLK_LEFT: | |
462 pan_image(dc, 32, 0); | |
463 break; | |
464 case SDLK_RIGHT: | |
465 pan_image(dc, -32, 0); | |
466 break; | |
467 case SDLK_UP: | |
468 pan_image(dc, 0, 32); | |
469 break; | |
470 case SDLK_DOWN: | |
471 pan_image(dc, 0, -32); | |
472 break; | |
473 case SDLK_c: | |
474 center_image(dc); | |
475 draw_image(dc); | |
476 break; | |
477 case SDLK_b: | |
478 dc->background_type ^= 1; | |
479 draw_image(dc); | |
480 break; | |
481 case SDLK_f: | |
482 dc->is_full_screen ^= 1; | |
483 if (dc->is_full_screen) { | |
484 /* save old windows size */ | |
485 dc->win_w = dc->screen->w; | |
486 dc->win_h = dc->screen->h; | |
487 open_window(dc, dc->screen_w, dc->screen_h, 1); | |
488 } else { | |
489 open_window(dc, dc->win_w, dc->win_h, 0); | |
490 } | |
491 center_image(dc); | |
492 draw_image(dc); | |
493 break; | |
494 default: | |
495 break; | |
496 } | |
497 break; | |
498 case SDL_VIDEORESIZE: | |
499 { | |
500 open_window(dc, event.resize.w, event.resize.h, 0); | |
501 center_image(dc); | |
502 draw_image(dc); | |
503 } | |
504 break; | |
505 case SDL_QUIT: | |
506 goto done; | |
507 case SDL_MOUSEMOTION: | |
508 if (event.motion.state) { | |
509 pan_image(dc, event.motion.xrel, event.motion.yrel); | |
510 } | |
511 break; | |
512 case SDL_USEREVENT: | |
513 if (dc->frame_count > 1) { | |
514 /* show next frame */ | |
515 if (dc->frame_index == (dc->frame_count - 1)) { | |
516 if (dc->loop_count == 0 || | |
517 dc->loop_counter < (dc->loop_count - 1)) { | |
518 dc->frame_index = 0; | |
519 dc->loop_counter++; | |
520 } else { | |
521 break; | |
522 } | |
523 } else { | |
524 dc->frame_index++; | |
525 } | |
526 draw_image(dc); | |
527 restart_frame_timer(dc); | |
528 } | |
529 break; | |
530 default: | |
531 break; | |
532 } | |
533 } | |
5
524eae707ba4
Cosmetics: Remove trailing whitespace.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
534 done: |
0 | 535 |
536 SDL_FreeSurface(dc->screen); | |
537 return 0; | |
538 } |