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
;
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];
112 static void set_caption(const char *path
, float rat
)
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
);
147 cpu_timer_stop(&timer
);
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);
178 float rat
= calc_img_size(img
->w
, img
->h
, w
, h
);
184 if (img
->pixel_type
!= GP_PIXEL_RGB888
) {
185 GP_Context
*tmp
= GP_ContextConvert(img
, GP_PIXEL_RGB888
);
192 /* Do low pass filter */
193 if (resampling_method
!= GP_INTERP_LINEAR_LF_INT
) {
195 cpu_timer_start(&timer
, "Blur");
196 callback
.priv
= "Blurring Image";
197 if (GP_FilterGaussianBlur(img
, img
, 0.4/rat
, 0.4/rat
,
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
);
208 cpu_timer_stop(&timer
);
217 callback
.priv
= "Rotating image (90)";
218 img
= GP_FilterRotate90(ret
, NULL
, &callback
);
221 callback
.priv
= "Rotating image (180)";
222 img
= GP_FilterRotate180(ret
, NULL
, &callback
);
225 callback
.priv
= "Rotating image (270)";
226 img
= GP_FilterRotate270(ret
, NULL
, &callback
);
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
);
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
);
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
);
286 static pthread_t loader_thread
= (pthread_t
)0;
288 static void show_image(struct loader_params
*params
)
292 /* stop previous loader thread */
295 pthread_join(loader_thread
, NULL
);
296 loader_thread
= (pthread_t
)0;
300 ret
= pthread_create(&loader_thread
, NULL
, image_loader
, (void*)params
);
303 fprintf(stderr
, "Failed to start thread: %s\n", strerror(ret
));
304 GP_BackendExit(backend
);
309 static void sighandler(int signo
)
312 GP_BackendExit(backend
);
314 fprintf(stderr
, "Got signal %i\n", signo
);
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
);
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";
336 struct loader_params params
= {NULL
, 0, 0, 0};
339 while ((opt
= getopt(argc
, argv
, "b:cIi:Ps:r:")) != -1) {
342 params
.show_info
= 1;
345 params
.show_progress
= 1;
351 sleep_sec
= atoi(optarg
);
354 resampling_method
= GP_INTERP_CUBIC_INT
;
357 if (!strcmp(optarg
, "90"))
359 else if (!strcmp(optarg
, "180"))
361 else if (!strcmp(optarg
, "270"))
364 backend_opts
= optarg
;
367 fprintf(stderr
, "Invalid paramter '%c'\n", opt
);
371 GP_SetDebugLevel(10);
373 if (input_dev
!= NULL
) {
374 drv
= GP_InputDriverLinuxOpen(input_dev
);
377 fprintf(stderr
, "Failed to initalize input device '%s'\n",
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
);
403 params
.show_progress_once
= 1;
404 params
.img_path
= argv
[argf
];
411 /* Initalize select */
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
;
424 GP_BackendExit(backend
);
432 params
.img_path
= argv
[argn
];
436 while (GP_InputDriverLinuxRead(drv
));
439 FD_SET(drv
->fd
, &rfds
);
441 if (sleep_sec
!= -1) {
448 params
.img_path
= argv
[argn
];
454 GP_BackendPoll(backend
);
458 /* Read and parse events */
461 while (GP_EventGet(&ev
)) {
467 if (ev
.code
!= GP_EV_KEY_DOWN
)
470 switch (ev
.val
.key
.key
) {
472 params
.show_info
= !params
.show_info
;
474 params
.show_progress_once
= 1;
478 params
.show_progress
= !params
.show_progress
;
485 params
.show_progress_once
= 1;
491 GP_BackendExit(backend
);
501 params
.show_progress_once
= 1;
502 params
.img_path
= argv
[argn
];
505 case GP_KEY_BACKSPACE
:
513 params
.show_progress_once
= 1;
514 params
.img_path
= argv
[argn
];
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;
528 GP_BackendExit(backend
);
537 GP_BackendExit(backend
);