1 /*****************************************************************************
2 * This file is part of gfxprim library. *
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. *
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. *
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 *
19 * Copyright (C) 2009-2012 Cyril Hrubis <metan@ucw.cz> *
21 *****************************************************************************/
25 SPIV -- Simple but Powerfull Image Viewer.
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
;
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
));
84 struct loader_params
{
87 int show_progress_once
;
90 /* cached loaded image */
94 static float calc_img_size(uint32_t img_w
, uint32_t img_h
,
95 uint32_t src_w
, uint32_t src_h
)
97 float w_rat
= 1.00 * src_w
/ img_w
;
98 float h_rat
= 1.00 * src_h
/ img_h
;
100 return GP_MIN(w_rat
, h_rat
);
103 static const char *img_name(const char *img_path
)
105 int i
, len
= strlen(img_path
);
107 for (i
= len
- 1; i
> 0; i
--) {
108 if (img_path
[i
] == '/')
109 return &img_path
[i
+1];
115 static void set_caption(const char *path
, float rat
)
119 snprintf(buf
, sizeof(buf
), "Spiv ~ %s 1:%3.3f", img_name(path
), rat
);
121 GP_BackendSetCaption(backend
, buf
);
127 int load_image(struct loader_params
*params
)
129 struct cpu_timer timer
;
130 GP_Context
*img
, *context
= backend
->context
;
132 if (params
->img
!= NULL
) {
133 fprintf(stderr
, "Image cached!\n");
137 GP_ProgressCallback callback
= {.callback
= image_loader_callback
,
138 .priv
= "Loading image"};
140 show_progress
= params
->show_progress
|| params
->show_progress_once
;
141 params
->show_progress_once
= 0;
143 fprintf(stderr
, "Loading '%s'\n", params
->img_path
);
145 cpu_timer_start(&timer
, "Loading");
146 if ((img
= GP_LoadImage(params
->img_path
, &callback
)) == NULL
) {
147 GP_Fill(context
, black_pixel
);
148 GP_Print(context
, NULL
, context
->w
/2, context
->h
/2,
149 GP_ALIGN_CENTER
|GP_VALIGN_CENTER
, white_pixel
, black_pixel
,
150 "Failed to load image :( (%s)", strerror(errno
));
151 GP_BackendFlip(backend
);
154 cpu_timer_stop(&timer
);
162 * This function tries to resize spiv window
163 * and if succedes blits the image directly to the screen.
165 static int resize_backend_and_blit(GP_Context
*img
,
166 struct loader_params
*params
)
168 if (GP_BackendResize(backend
, img
->w
, img
->h
))
171 GP_Blit_Raw(img
, 0, 0, img
->w
, img
->h
, backend
->context
, 0, 0);
172 GP_BackendFlip(backend
);
173 set_caption(params
->img_path
, 1);
178 static void *image_loader(void *ptr
)
180 struct loader_params
*params
= ptr
;
181 struct cpu_timer timer
;
182 struct cpu_timer sum_timer
;
183 GP_Context
*img
, *context
= backend
->context
;
184 GP_ProgressCallback callback
= {.callback
= image_loader_callback
};
186 cpu_timer_start(&sum_timer
, "sum");
189 if (load_image(params
))
195 if (img->w < 320 && img->h < 240) {
196 if (!resize_backend_and_blit(img, params))
201 /* Figure out rotation */
218 float rat
= calc_img_size(img
->w
, img
->h
, w
, h
);
224 if (img
->pixel_type
!= GP_PIXEL_RGB888
) {
225 GP_Context
*tmp
= GP_ContextConvert(img
, GP_PIXEL_RGB888
);
232 /* Do low pass filter */
233 if (resampling_method
!= GP_INTERP_LINEAR_LF_INT
) {
235 cpu_timer_start(&timer
, "Blur");
236 callback
.priv
= "Blurring Image";
237 if (GP_FilterGaussianBlur(img
, img
, 0.4/rat
, 0.4/rat
,
240 cpu_timer_stop(&timer
);
244 cpu_timer_start(&timer
, "Resampling");
245 callback
.priv
= "Resampling Image";
246 ret
= GP_FilterResize(img
, NULL
, resampling_method
, img
->w
* rat
, img
->h
* rat
, &callback
);
247 cpu_timer_stop(&timer
);
256 callback
.priv
= "Rotating image (90)";
257 img
= GP_FilterRotate90Alloc(ret
, &callback
);
260 callback
.priv
= "Rotating image (180)";
261 img
= GP_FilterRotate180Alloc(ret
, &callback
);
264 callback
.priv
= "Rotating image (270)";
265 img
= GP_FilterRotate270Alloc(ret
, &callback
);
277 uint32_t cx
= (context
->w
- ret
->w
)/2;
278 uint32_t cy
= (context
->h
- ret
->h
)/2;
280 cpu_timer_start(&timer
, "Blitting");
281 GP_Blit_Raw(ret
, 0, 0, ret
->w
, ret
->h
, context
, cx
, cy
);
282 cpu_timer_stop(&timer
);
285 /* clean up the rest of the display */
286 GP_FillRectXYWH(context
, 0, 0, cx
, context
->h
, black_pixel
);
287 GP_FillRectXYWH(context
, 0, 0, context
->w
, cy
, black_pixel
);
288 GP_FillRectXYWH(context
, ret
->w
+cx
, 0, context
->w
- ret
->w
- cx
, context
->h
, black_pixel
);
289 GP_FillRectXYWH(context
, 0, ret
->h
+cy
, context
->w
, context
->h
- ret
->h
- cy
, black_pixel
);
291 cpu_timer_stop(&sum_timer
);
293 set_caption(params
->img_path
, rat
);
295 if (!params
->show_info
) {
296 GP_BackendFlip(backend
);
300 GP_Size th
= GP_TextHeight(NULL
);
302 GP_Print(context
, NULL
, 11, 11, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
303 black_pixel
, white_pixel
, "%ux%u", w
, h
);
305 GP_Print(context
, NULL
, 10, 10, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
306 white_pixel
, black_pixel
, "%ux%u", w
, h
);
308 GP_Print(context
, NULL
, 11, 13 + th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
309 black_pixel
, white_pixel
, "1:%3.3f", rat
);
311 GP_Print(context
, NULL
, 10, 12 + th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
312 white_pixel
, black_pixel
, "1:%3.3f", rat
);
314 GP_Print(context
, NULL
, 11, 15 + 2 * th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
315 black_pixel
, white_pixel
, "%s", img_name(params
->img_path
));
317 GP_Print(context
, NULL
, 10, 14 + 2 * th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
318 white_pixel
, black_pixel
, "%s", img_name(params
->img_path
));
320 GP_BackendFlip(backend
);
325 static pthread_t loader_thread
= (pthread_t
)0;
327 static void show_image(struct loader_params
*params
, const char *path
)
331 /* stop previous loader thread */
334 pthread_join(loader_thread
, NULL
);
335 loader_thread
= (pthread_t
)0;
339 /* invalidate cached image if path has changed */
340 if (params
->img_path
== NULL
||
341 (path
!= NULL
&& strcmp(params
->img_path
, path
))) {
342 GP_ContextFree(params
->img
);
344 params
->img_path
= path
;
347 ret
= pthread_create(&loader_thread
, NULL
, image_loader
, (void*)params
);
350 fprintf(stderr
, "Failed to start thread: %s\n", strerror(ret
));
351 GP_BackendExit(backend
);
356 static void sighandler(int signo
)
359 GP_BackendExit(backend
);
361 fprintf(stderr
, "Got signal %i\n", signo
);
366 static void init_backend(const char *backend_opts
)
368 backend
= GP_BackendInit(backend_opts
, "Spiv", stderr
);
370 if (backend
== NULL
) {
371 fprintf(stderr
, "Failed to initalize backend '%s'\n", backend_opts
);
376 int main(int argc
, char *argv
[])
378 GP_InputDriverLinux
*drv
= NULL
;
379 GP_Context
*context
= NULL
;
380 const char *input_dev
= NULL
;
381 const char *backend_opts
= "X11";
383 struct loader_params params
= {NULL
, 0, 0, 0, .img
= NULL
};
384 int opt
, debug_level
= 0;
386 while ((opt
= getopt(argc
, argv
, "b:cd:Ii:Ps:r:")) != -1) {
389 params
.show_info
= 1;
392 params
.show_progress
= 1;
398 sleep_sec
= atoi(optarg
);
401 resampling_method
= GP_INTERP_CUBIC_INT
;
404 debug_level
= atoi(optarg
);
407 if (!strcmp(optarg
, "90"))
409 else if (!strcmp(optarg
, "180"))
411 else if (!strcmp(optarg
, "270"))
414 backend_opts
= optarg
;
417 fprintf(stderr
, "Invalid paramter '%c'\n", opt
);
421 GP_SetDebugLevel(debug_level
);
423 if (input_dev
!= NULL
) {
424 drv
= GP_InputDriverLinuxOpen(input_dev
);
427 fprintf(stderr
, "Failed to initalize input device '%s'\n",
433 signal(SIGINT
, sighandler
);
434 signal(SIGSEGV
, sighandler
);
435 signal(SIGBUS
, sighandler
);
436 signal(SIGABRT
, sighandler
);
438 init_backend(backend_opts
);
440 context
= backend
->context
;
442 GP_EventSetScreenSize(context
->w
, context
->h
);
444 black_pixel
= GP_ColorToContextPixel(GP_COL_BLACK
, context
);
445 white_pixel
= GP_ColorToContextPixel(GP_COL_WHITE
, context
);
447 GP_Fill(context
, black_pixel
);
448 GP_BackendFlip(backend
);
453 params
.show_progress_once
= 1;
454 show_image(¶ms
, argv
[argf
]);
460 /* Initalize select */
463 FD_SET(drv
->fd
, &rfds
);
464 struct timeval tv
= {.tv_sec
= sleep_sec
, .tv_usec
= 0};
465 struct timeval
*tvp
= sleep_sec
!= -1 ? &tv
: NULL
;
467 ret
= select(drv
->fd
+ 1, &rfds
, NULL
, NULL
, tvp
);
469 tv
.tv_sec
= sleep_sec
;
473 GP_BackendExit(backend
);
481 show_image(¶ms
, argv
[argn
]);
484 while (GP_InputDriverLinuxRead(drv
));
487 FD_SET(drv
->fd
, &rfds
);
489 if (sleep_sec
!= -1) {
496 show_image(¶ms
, argv
[argn
]);
501 GP_BackendPoll(backend
);
505 /* Read and parse events */
508 while (GP_EventGet(&ev
)) {
514 if (ev
.code
!= GP_EV_KEY_DOWN
)
517 switch (ev
.val
.key
.key
) {
519 params
.show_info
= !params
.show_info
;
521 params
.show_progress_once
= 1;
522 show_image(¶ms
, NULL
);
525 params
.show_progress
= !params
.show_progress
;
532 params
.show_progress_once
= 1;
533 show_image(¶ms
, NULL
);
538 GP_BackendExit(backend
);
548 params
.show_progress_once
= 1;
549 show_image(¶ms
, argv
[argn
]);
551 case GP_KEY_BACKSPACE
:
559 params
.show_progress_once
= 1;
560 show_image(¶ms
, argv
[argn
]);
566 case GP_EV_SYS_RESIZE
:
567 GP_BackendResize(backend
, ev
.val
.sys
.w
, ev
.val
.sys
.h
);
568 GP_Fill(backend
->context
, 0);
569 params
.show_progress_once
= 1;
570 show_image(¶ms
, NULL
);
573 GP_BackendExit(backend
);
582 GP_BackendExit(backend
);