spiv: Show progress when window was resized.
[gfxprim.git] / demos / spiv / spiv.c
blob18cfabc29b3ed715ddd5d2bc12321ad81a11ac4c
1 /*****************************************************************************
2 * This file is part of gfxprim library. *
3 * *
4 * Gfxprim is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU Lesser General Public *
6 * License as published by the Free Software Foundation; either *
7 * version 2.1 of the License, or (at your option) any later version. *
8 * *
9 * Gfxprim is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * Lesser General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with gfxprim; if not, write to the Free Software *
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
17 * Boston, MA 02110-1301 USA *
18 * *
19 * Copyright (C) 2009-2012 Cyril Hrubis <metan@ucw.cz> *
20 * *
21 *****************************************************************************/
25 SPIV -- Simple but Powerfull Image Viewer.
29 #include <unistd.h>
30 #include <errno.h>
31 #include <signal.h>
32 #include <string.h>
33 #include <pthread.h>
35 #include <GP.h>
36 #include <backends/GP_Backends.h>
37 #include <input/GP_InputDriverLinux.h>
39 #include "cpu_timer.h"
41 static GP_Pixel black_pixel;
42 static GP_Pixel white_pixel;
44 static GP_Backend *backend = NULL;
46 /* image loader thread */
47 static int abort_flag = 0;
48 static int rotate = 0;
49 static int show_progress = 0;
50 static int resampling_method = GP_INTERP_LINEAR_LF_INT;
52 static int image_loader_callback(GP_ProgressCallback *self)
54 static GP_Size size = 0;
55 GP_Context *c = backend->context;
57 if (abort_flag)
58 return 1;
60 if (!show_progress)
61 return 0;
63 char buf[100];
65 snprintf(buf, sizeof(buf), "%s ... %-3.1f%%",
66 (const char*)self->priv, self->percentage);
68 int align = GP_ALIGN_CENTER|GP_VALIGN_ABOVE;
70 GP_TextClear(c, NULL, c->w/2, c->h - 4, align,
71 black_pixel, GP_MAX(size, GP_TextWidth(NULL, buf)));
73 GP_Text(c, NULL, c->w/2, c->h - 4, align,
74 white_pixel, black_pixel, buf);
76 size = GP_TextWidth(NULL, buf);
78 GP_BackendUpdateRect(backend, c->w/2 - size/2 - 1, c->h - 4,
79 c->w/2 + size/2 + 1, c->h - 4 - GP_TextHeight(NULL));
81 return 0;
84 struct loader_params {
85 const char *img_path;
86 int show_progress;
87 int show_progress_once;
88 int show_info;
91 static float calc_img_size(uint32_t img_w, uint32_t img_h,
92 uint32_t src_w, uint32_t src_h)
94 float w_rat = 1.00 * src_w / img_w;
95 float h_rat = 1.00 * src_h / img_h;
97 return GP_MIN(w_rat, h_rat);
100 static const char *img_name(const char *img_path)
102 int i, len = strlen(img_path);
104 for (i = len - 1; i > 0; i--) {
105 if (img_path[i] == '/')
106 return &img_path[i+1];
109 return img_path;
112 static void set_caption(const char *path, float rat)
114 char buf[256];
116 snprintf(buf, sizeof(buf), "Spiv ~ %s 1:%3.3f", img_name(path), rat);
118 GP_BackendSetCaption(backend, buf);
121 static void *image_loader(void *ptr)
123 struct loader_params *params = ptr;
124 GP_ProgressCallback callback = {.callback = image_loader_callback};
125 struct cpu_timer timer;
126 struct cpu_timer sum_timer;
127 GP_Context *img, *context = backend->context;
129 cpu_timer_start(&sum_timer, "sum");
131 show_progress = params->show_progress || params->show_progress_once;
132 params->show_progress_once = 0;
134 fprintf(stderr, "Loading '%s'\n", params->img_path);
136 callback.priv = "Loading image";
138 cpu_timer_start(&timer, "Loading");
139 if ((img = GP_LoadImage(params->img_path, &callback)) == NULL) {
140 GP_Fill(context, black_pixel);
141 GP_Print(context, NULL, context->w/2, context->h/2,
142 GP_ALIGN_CENTER|GP_VALIGN_CENTER, white_pixel, black_pixel,
143 "Failed to load image :( (%s)", strerror(errno));
144 GP_BackendFlip(backend);
145 return NULL;
147 cpu_timer_stop(&timer);
149 GP_Size w, h;
151 switch (rotate) {
152 case 0:
153 case 180:
154 default:
155 w = context->w;
156 h = context->h;
157 break;
158 case 90:
159 case 270:
160 w = context->h;
161 h = context->w;
162 break;
165 /* Try to resize window */
167 if (!GP_BackendResize(backend, img->w, img->h)) {
168 context = backend->context;
170 GP_Blit_Raw(img, 0, 0, img->w, img->h, context, 0, 0);
171 GP_BackendFlip(backend);
172 set_caption(params->img_path, 1);
174 return NULL;
178 float rat = calc_img_size(img->w, img->h, w, h);
180 w = img->w;
181 h = img->h;
183 /* Workaround */
184 if (img->pixel_type != GP_PIXEL_RGB888) {
185 GP_Context *tmp = GP_ContextConvert(img, GP_PIXEL_RGB888);
186 GP_ContextFree(img);
187 img = tmp;
190 GP_Context *ret;
192 /* Do low pass filter */
193 if (resampling_method != GP_INTERP_LINEAR_LF_INT) {
194 if (rat < 1) {
195 cpu_timer_start(&timer, "Blur");
196 callback.priv = "Blurring Image";
197 if (GP_FilterGaussianBlur(img, img, 0.4/rat, 0.4/rat,
198 &callback) == NULL)
199 return NULL;
200 cpu_timer_stop(&timer);
204 cpu_timer_start(&timer, "Resampling");
205 callback.priv = "Resampling Image";
206 ret = GP_FilterResize(img, NULL, resampling_method, img->w * rat, img->h * rat, &callback);
207 GP_ContextFree(img);
208 cpu_timer_stop(&timer);
210 if (ret == NULL)
211 return NULL;
213 switch (rotate) {
214 case 0:
215 break;
216 case 90:
217 callback.priv = "Rotating image (90)";
218 img = GP_FilterRotate90(ret, NULL, &callback);
219 break;
220 case 180:
221 callback.priv = "Rotating image (180)";
222 img = GP_FilterRotate180(ret, NULL, &callback);
223 break;
224 case 270:
225 callback.priv = "Rotating image (270)";
226 img = GP_FilterRotate270(ret, NULL, &callback);
227 break;
230 if (rotate) {
231 GP_ContextFree(ret);
232 ret = img;
235 if (img == NULL)
236 return NULL;
238 uint32_t cx = (context->w - ret->w)/2;
239 uint32_t cy = (context->h - ret->h)/2;
241 cpu_timer_start(&timer, "Blitting");
242 GP_Blit_Raw(ret, 0, 0, ret->w, ret->h, context, cx, cy);
243 cpu_timer_stop(&timer);
244 GP_ContextFree(ret);
246 /* clean up the rest of the display */
247 GP_FillRectXYWH(context, 0, 0, cx, context->h, black_pixel);
248 GP_FillRectXYWH(context, 0, 0, context->w, cy, black_pixel);
249 GP_FillRectXYWH(context, ret->w+cx, 0, context->w - ret->w - cx, context->h, black_pixel);
250 GP_FillRectXYWH(context, 0, ret->h+cy, context->w, context->h - ret->h - cy, black_pixel);
252 cpu_timer_stop(&sum_timer);
254 set_caption(params->img_path, rat);
256 if (!params->show_info) {
257 GP_BackendFlip(backend);
258 return NULL;
261 GP_Size th = GP_TextHeight(NULL);
263 GP_Print(context, NULL, 11, 11, GP_ALIGN_RIGHT|GP_VALIGN_BOTTOM,
264 black_pixel, white_pixel, "%ux%u", w, h);
266 GP_Print(context, NULL, 10, 10, GP_ALIGN_RIGHT|GP_VALIGN_BOTTOM,
267 white_pixel, black_pixel, "%ux%u", w, h);
269 GP_Print(context, NULL, 11, 13 + th, GP_ALIGN_RIGHT|GP_VALIGN_BOTTOM,
270 black_pixel, white_pixel, "1:%3.3f", rat);
272 GP_Print(context, NULL, 10, 12 + th, GP_ALIGN_RIGHT|GP_VALIGN_BOTTOM,
273 white_pixel, black_pixel, "1:%3.3f", rat);
275 GP_Print(context, NULL, 11, 15 + 2 * th, GP_ALIGN_RIGHT|GP_VALIGN_BOTTOM,
276 black_pixel, white_pixel, "%s", img_name(params->img_path));
278 GP_Print(context, NULL, 10, 14 + 2 * th, GP_ALIGN_RIGHT|GP_VALIGN_BOTTOM,
279 white_pixel, black_pixel, "%s", img_name(params->img_path));
281 GP_BackendFlip(backend);
283 return NULL;
286 static pthread_t loader_thread = (pthread_t)0;
288 static void show_image(struct loader_params *params)
290 int ret;
292 /* stop previous loader thread */
293 if (loader_thread) {
294 abort_flag = 1;
295 pthread_join(loader_thread, NULL);
296 loader_thread = (pthread_t)0;
297 abort_flag = 0;
300 ret = pthread_create(&loader_thread, NULL, image_loader, (void*)params);
302 if (ret) {
303 fprintf(stderr, "Failed to start thread: %s\n", strerror(ret));
304 GP_BackendExit(backend);
305 exit(1);
309 static void sighandler(int signo)
311 if (backend != NULL)
312 GP_BackendExit(backend);
314 fprintf(stderr, "Got signal %i\n", signo);
316 exit(1);
319 static void init_backend(const char *backend_opts)
321 backend = GP_BackendInit(backend_opts, "Spiv", stderr);
323 if (backend == NULL) {
324 fprintf(stderr, "Failed to initalize backend '%s'\n", backend_opts);
325 exit(1);
329 int main(int argc, char *argv[])
331 GP_InputDriverLinux *drv = NULL;
332 GP_Context *context = NULL;
333 const char *input_dev = NULL;
334 const char *backend_opts = "SDL";
335 int sleep_sec = -1;
336 struct loader_params params = {NULL, 0, 0, 0};
337 int opt;
339 while ((opt = getopt(argc, argv, "b:cIi:Ps:r:")) != -1) {
340 switch (opt) {
341 case 'I':
342 params.show_info = 1;
343 break;
344 case 'P':
345 params.show_progress = 1;
346 break;
347 case 'i':
348 input_dev = optarg;
349 break;
350 case 's':
351 sleep_sec = atoi(optarg);
352 break;
353 case 'c':
354 resampling_method = GP_INTERP_CUBIC_INT;
355 break;
356 case 'r':
357 if (!strcmp(optarg, "90"))
358 rotate = 90;
359 else if (!strcmp(optarg, "180"))
360 rotate = 180;
361 else if (!strcmp(optarg, "270"))
362 rotate = 270;
363 case 'b':
364 backend_opts = optarg;
365 break;
366 default:
367 fprintf(stderr, "Invalid paramter '%c'\n", opt);
371 GP_SetDebugLevel(10);
373 if (input_dev != NULL) {
374 drv = GP_InputDriverLinuxOpen(input_dev);
376 if (drv == NULL) {
377 fprintf(stderr, "Failed to initalize input device '%s'\n",
378 input_dev);
379 return 1;
383 signal(SIGINT, sighandler);
384 signal(SIGSEGV, sighandler);
385 signal(SIGBUS, sighandler);
386 signal(SIGABRT, sighandler);
388 init_backend(backend_opts);
390 context = backend->context;
392 GP_EventSetScreenSize(context->w, context->h);
394 black_pixel = GP_ColorToContextPixel(GP_COL_BLACK, context);
395 white_pixel = GP_ColorToContextPixel(GP_COL_WHITE, context);
397 GP_Fill(context, black_pixel);
398 GP_BackendFlip(backend);
400 int argf = optind;
401 int argn = argf;
403 params.show_progress_once = 1;
404 params.img_path = argv[argf];
405 show_image(&params);
407 for (;;) {
408 int ret;
410 if (drv != NULL) {
411 /* Initalize select */
412 fd_set rfds;
413 FD_ZERO(&rfds);
414 FD_SET(drv->fd, &rfds);
415 struct timeval tv = {.tv_sec = sleep_sec, .tv_usec = 0};
416 struct timeval *tvp = sleep_sec != -1 ? &tv : NULL;
418 ret = select(drv->fd + 1, &rfds, NULL, NULL, tvp);
420 tv.tv_sec = sleep_sec;
422 switch (ret) {
423 case -1:
424 GP_BackendExit(backend);
425 return 0;
426 break;
427 case 0:
428 argn++;
429 if (argn >= argc)
430 argn = argf;
432 params.img_path = argv[argn];
433 show_image(&params);
434 break;
435 default:
436 while (GP_InputDriverLinuxRead(drv));
439 FD_SET(drv->fd, &rfds);
440 } else {
441 if (sleep_sec != -1) {
442 sleep(sleep_sec);
444 argn++;
445 if (argn >= argc)
446 argn = argf;
448 params.img_path = argv[argn];
449 show_image(&params);
453 if (backend->Poll)
454 GP_BackendPoll(backend);
456 usleep(1000);
458 /* Read and parse events */
459 GP_Event ev;
461 while (GP_EventGet(&ev)) {
463 GP_EventDump(&ev);
465 switch (ev.type) {
466 case GP_EV_KEY:
467 if (ev.code != GP_EV_KEY_DOWN)
468 continue;
470 switch (ev.val.key.key) {
471 case GP_KEY_I:
472 params.show_info = !params.show_info;
474 params.show_progress_once = 1;
475 show_image(&params);
476 break;
477 case GP_KEY_P:
478 params.show_progress = !params.show_progress;
479 break;
480 case GP_KEY_R:
481 rotate += 90;
482 if (rotate > 270)
483 rotate = 0;
485 params.show_progress_once = 1;
486 show_image(&params);
487 break;
488 case GP_KEY_ESC:
489 case GP_KEY_ENTER:
490 case GP_KEY_Q:
491 GP_BackendExit(backend);
492 return 0;
493 break;
494 case GP_KEY_RIGHT:
495 case GP_KEY_UP:
496 case GP_KEY_SPACE:
497 argn++;
498 if (argn >= argc)
499 argn = argf;
501 params.show_progress_once = 1;
502 params.img_path = argv[argn];
503 show_image(&params);
504 break;
505 case GP_KEY_BACKSPACE:
506 case GP_KEY_LEFT:
507 case GP_KEY_DOWN:
508 argn--;
510 if (argn < argf)
511 argn = argc - 1;
513 params.show_progress_once = 1;
514 params.img_path = argv[argn];
515 show_image(&params);
516 break;
518 break;
519 case GP_EV_SYS:
520 switch (ev.code) {
521 case GP_EV_SYS_RESIZE:
522 GP_BackendResize(backend, ev.val.sys.w, ev.val.sys.h);
523 GP_Fill(backend->context, 0);
524 params.show_progress_once = 1;
525 show_image(&params);
526 break;
527 case GP_EV_SYS_QUIT:
528 GP_BackendExit(backend);
529 return 0;
530 break;
532 break;
537 GP_BackendExit(backend);
539 return 0;