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-2013 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 "image_cache.h"
40 #include "image_list.h"
41 #include "image_actions.h"
42 #include "cpu_timer.h"
44 static GP_Pixel black_pixel
;
45 static GP_Pixel white_pixel
;
46 static GP_Pixel gray_pixel
;
48 static GP_Backend
*backend
= NULL
;
50 /* image loader thread */
51 static int abort_flag
= 0;
52 static int show_progress
= 0;
56 * Resize image to fit current size of the window.
61 * Use zoom set in zoom float and zoom offsets.
66 * Fixed zoom but spiv tries to change
67 * the window size to fit the image size
72 struct loader_params
{
73 /* current image path */
75 /* current resize ratio */
78 /* show loader progress */
80 long show_progress_once
:2;
81 /* show image info in topleft corner */
83 /* use nearest neighbour resampling first */
85 /* use dithering when blitting to display */
87 /* use low pass before resampling */
89 /* image orientation 0, 90, 180, 270 */
91 /* resampling method */
92 int resampling_method
;
94 /* offset in pixels */
95 unsigned int zoom_x_offset
;
96 unsigned int zoom_y_offset
;
99 enum zoom_type zoom_type
;
102 /* caches for loaded images */
103 struct image_cache
*img_resized_cache
;
104 struct image_cache
*img_orig_cache
;
106 /* image list structure */
107 struct image_list
*img_list
;
110 static int image_loader_callback(GP_ProgressCallback
*self
)
112 static GP_Size size
= 0;
113 GP_Context
*c
= backend
->context
;
123 snprintf(buf
, sizeof(buf
), "%s ... %-3.1f%%",
124 (const char*)self
->priv
, self
->percentage
);
126 int align
= GP_ALIGN_CENTER
|GP_VALIGN_ABOVE
;
128 size
= GP_TextWidth(NULL
, buf
);
130 int start
= c
->w
/2 - size
/2 - 10;
131 int end
= c
->w
/2 + size
/2 + 10;
132 int middle
= start
+ (end
- start
) * self
->percentage
/ 100;
133 int top
= c
->h
- GP_TextHeight(NULL
) - 11;
135 GP_FillRectXYXY(c
, start
, c
->h
- 1, middle
, top
, gray_pixel
);
136 GP_FillRectXYXY(c
, middle
, c
->h
- 1, end
, top
, black_pixel
);
138 GP_Text(c
, NULL
, c
->w
/2, c
->h
- 5, align
,
139 white_pixel
, black_pixel
, buf
);
141 GP_BackendUpdateRect(backend
, start
, c
->h
- 1, end
, top
);
146 static GP_Context
*load_image(struct loader_params
*params
, int elevate
);
149 * Ask backend to resize window may not be implemented or authorized. If
150 * backend (window) is resized we will get SYS_RESIZE event, see the main event
153 static void resize_backend(struct loader_params
*params
, float ratio
, int shift_flag
)
155 GP_Context
*img
= load_image(params
, 1);
158 ratio
= 1.00 / ratio
;
160 unsigned int w
= img
->w
* ratio
+ 0.5;
161 unsigned int h
= img
->h
* ratio
+ 0.5;
163 GP_BackendResize(backend
, w
, h
);
167 static float calc_img_size(struct loader_params
*params
,
168 uint32_t img_w
, uint32_t img_h
,
169 uint32_t src_w
, uint32_t src_h
)
174 switch (params
->zoom_type
) {
176 w_rat
= 1.00 * src_w
/ img_w
;
177 h_rat
= 1.00 * src_h
/ img_h
;
178 return GP_MIN(w_rat
, h_rat
);
182 resize_backend(params
, params
->zoom
, 0);
189 static const char *img_name(const char *img_path
)
191 int i
, len
= strlen(img_path
);
193 for (i
= len
- 1; i
> 0; i
--) {
194 if (img_path
[i
] == '/')
195 return &img_path
[i
+1];
201 static void set_caption(const char *path
, float rat
)
205 snprintf(buf
, sizeof(buf
), "Spiv ~ %s 1:%3.3f", img_name(path
), rat
);
207 GP_BackendSetCaption(backend
, buf
);
213 static GP_Context
*load_image(struct loader_params
*params
, int elevate
)
215 struct cpu_timer timer
;
216 GP_Context
*img
, *context
= backend
->context
;
218 GP_ProgressCallback callback
= {.callback
= image_loader_callback
,
219 .priv
= "Loading image"};
221 img
= image_cache_get(params
->img_orig_cache
, params
->img_path
, 0, 0, elevate
);
223 /* Image not cached, load it */
225 fprintf(stderr
, "Loading '%s'\n", params
->img_path
);
227 cpu_timer_start(&timer
, "Loading");
228 if ((img
= GP_LoadImage(params
->img_path
, &callback
)) == NULL
) {
230 if (errno
== ECANCELED
)
233 GP_Fill(context
, black_pixel
);
234 GP_Print(context
, NULL
, context
->w
/2, context
->h
/2 - 10,
235 GP_ALIGN_CENTER
|GP_VALIGN_CENTER
, white_pixel
, black_pixel
,
236 "'%s'", params
->img_path
);
237 GP_Print(context
, NULL
, context
->w
/2, context
->h
/2 + 10,
238 GP_ALIGN_CENTER
|GP_VALIGN_CENTER
, white_pixel
, black_pixel
,
239 "Failed to load image :( (%s)", strerror(errno
));
240 GP_BackendFlip(backend
);
245 // if (img->pixel_type != GP_PIXEL_RGB888) {
247 // tmp = GP_ContextConvertAlloc(img, GP_PIXEL_RGB888);
248 // GP_ContextFree(img);
252 image_cache_put(params
->img_orig_cache
, img
, params
->img_path
, 0, 0);
254 cpu_timer_stop(&timer
);
261 * Fill context with chessboard-like pattern.
263 static void pattern_fill(GP_Context
*ctx
, unsigned int x0
, unsigned int y0
, unsigned int w
, unsigned int h
)
267 GP_Pixel g1
= GP_RGBToContextPixel(0x44, 0x44, 0x44, ctx
);
268 GP_Pixel g2
= GP_RGBToContextPixel(0x33, 0x33, 0x33, ctx
);
270 for (y
= 0; y
< h
; y
++) {
271 for (x
= 0; x
< w
; x
++) {
274 if ((x
%(w
/10) < (w
/20)) ^ (y
%(h
/10) < (h
/20)))
279 GP_PutPixel(ctx
, x0
+ x
, y0
+ y
, pix
);
287 static void update_display(struct loader_params
*params
, GP_Context
*img
)
289 GP_Context
*context
= backend
->context
;
290 struct cpu_timer timer
;
291 GP_ProgressCallback callback
= {.callback
= image_loader_callback
};
293 switch (params
->rotate
) {
297 callback
.priv
= "Rotating image (90)";
298 img
= GP_FilterRotate90Alloc(img
, &callback
);
301 callback
.priv
= "Rotating image (180)";
302 img
= GP_FilterRotate180Alloc(img
, &callback
);
305 callback
.priv
= "Rotating image (270)";
306 img
= GP_FilterRotate270Alloc(img
, &callback
);
316 switch (params
->zoom_type
) {
318 cx
= (context
->w
- img
->w
)/2;
319 cy
= (context
->h
- img
->h
)/2;
323 cx
= params
->zoom_x_offset
;
324 cy
= params
->zoom_y_offset
;
328 GP_Context sub_display
;
330 cpu_timer_start(&timer
, "Blitting");
332 if (params
->use_dithering
) {
333 callback
.priv
= "Dithering";
334 GP_SubContext(context
, &sub_display
, cx
, cy
, img
->w
, img
->h
);
335 GP_FilterFloydSteinberg_RGB888(img
, &sub_display
, NULL
);
336 // GP_FilterHilbertPeano_RGB888(img, &sub_display, NULL);
338 if (GP_PixelHasFlags(img
->pixel_type
, GP_PIXEL_HAS_ALPHA
))
339 pattern_fill(context
, cx
, cy
, img
->w
, img
->h
);
341 GP_Blit_Clipped(img
, 0, 0, img
->w
, img
->h
, context
, cx
, cy
);
344 cpu_timer_stop(&timer
);
349 /* clean up the rest of the display */
350 GP_FillRectXYWH(context
, 0, 0, cx
, context
->h
, black_pixel
);
351 GP_FillRectXYWH(context
, 0, 0, context
->w
, cy
, black_pixel
);
353 int w
= context
->w
- img
->w
- cx
;
356 GP_FillRectXYWH(context
, img
->w
+ cx
, 0, w
, context
->h
, black_pixel
);
358 int h
= context
->h
- img
->h
- cy
;
361 GP_FillRectXYWH(context
, 0, img
->h
+ cy
, context
->w
, h
, black_pixel
);
363 set_caption(params
->img_path
, params
->rat
);
365 if (!params
->show_info
) {
366 GP_BackendFlip(backend
);
370 GP_Size th
= GP_TextHeight(NULL
);
372 GP_Print(context
, NULL
, 11, 11, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
373 black_pixel
, white_pixel
, "%ux%u", img
->w
, img
->h
);
375 GP_Print(context
, NULL
, 10, 10, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
376 white_pixel
, black_pixel
, "%ux%u", img
->w
, img
->h
);
378 GP_Print(context
, NULL
, 11, 13 + th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
379 black_pixel
, white_pixel
, "1:%3.3f", params
->rat
);
381 GP_Print(context
, NULL
, 10, 12 + th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
382 white_pixel
, black_pixel
, "1:%3.3f", params
->rat
);
384 GP_Print(context
, NULL
, 11, 15 + 2 * th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
385 black_pixel
, white_pixel
, "%s", img_name(params
->img_path
));
387 GP_Print(context
, NULL
, 10, 14 + 2 * th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
388 white_pixel
, black_pixel
, "%s", img_name(params
->img_path
));
390 GP_Print(context
, NULL
, 11, 17 + 3 * th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
391 black_pixel
, white_pixel
, "%s%s",
392 params
->use_low_pass
&& params
->rat
< 1 ? "Gaussian LP + " : "",
393 GP_InterpolationTypeName(params
->resampling_method
));
395 GP_Print(context
, NULL
, 10, 16 + 3 * th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
396 white_pixel
, black_pixel
, "%s%s",
397 params
->use_low_pass
&& params
->rat
< 1 ? "Gaussian LP + " : "",
398 GP_InterpolationTypeName(params
->resampling_method
));
400 unsigned int count
= image_list_count(params
->img_list
);
401 unsigned int pos
= image_list_pos(params
->img_list
) + 1;
403 GP_Print(context
, NULL
, 11, 19 + 4 * th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
404 black_pixel
, white_pixel
, "%u of %u", pos
, count
);
406 GP_Print(context
, NULL
, 10, 18 + 4 * th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
407 white_pixel
, black_pixel
, "%u of %u", pos
, count
);
409 unsigned int dir_count
= image_list_dir_count(params
->img_list
);
410 unsigned int dir_pos
= image_list_dir_pos(params
->img_list
) + 1;
412 if (dir_count
> 1 && dir_count
!= count
) {
413 GP_Print(context
, NULL
, 11, 21 + 5 * th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
414 black_pixel
, white_pixel
, "%u of %u in directory", dir_pos
, dir_count
);
416 GP_Print(context
, NULL
, 10, 20 + 5 * th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
417 white_pixel
, black_pixel
, "%u of %u in directory", dir_pos
, dir_count
);
420 GP_BackendFlip(backend
);
423 GP_Context
*load_resized_image(struct loader_params
*params
, GP_Size w
, GP_Size h
)
425 long cookie
= (w
& 0xffff) | (h
& 0xffff)<<16;
426 GP_Context
*img
, *res
= NULL
;
427 struct cpu_timer timer
;
428 GP_ProgressCallback callback
= {.callback
= image_loader_callback
};
430 int key
= (params
->resampling_method
<<1) | !!(params
->use_low_pass
);
432 /* Try to get resized cached image */
433 img
= image_cache_get(params
->img_resized_cache
, params
->img_path
, cookie
, key
, 1);
438 /* Otherwise load image and resize it */
439 if ((img
= load_image(params
, 1)) == NULL
)
442 if (params
->show_nn_first
) {
443 /* Do simple interpolation and blit the result */
444 GP_Context
*nn
= GP_FilterResizeNNAlloc(img
, w
, h
, NULL
);
446 update_display(params
, nn
);
451 /* Do low pass filter */
452 if (params
->use_low_pass
&& params
->rat
< 1) {
453 cpu_timer_start(&timer
, "Blur");
454 callback
.priv
= "Blurring Image";
456 res
= GP_FilterGaussianBlurAlloc(img
, 0.3/params
->rat
,
457 0.3/params
->rat
, &callback
);
464 cpu_timer_stop(&timer
);
467 // img->gamma = GP_GammaAcquire(img->pixel_type, 0.45);
469 cpu_timer_start(&timer
, "Resampling");
470 callback
.priv
= "Resampling Image";
471 GP_Context
*i1
= GP_FilterResize(img
, NULL
, params
->resampling_method
, w
, h
, &callback
);
472 // img->gamma = NULL;
473 // GP_Context *i2 = GP_FilterResize(img, NULL, params->resampling_method, w, h, &callback);
474 // img = GP_FilterDifferenceAlloc(i2, i1, NULL);
475 // img = GP_FilterInvert(img, NULL, NULL);
477 // if (params->resampling_method == GP_INTERP_CUBIC_INT)
478 // GP_FilterEdgeSharpening(img, img, 0.2, NULL);
479 cpu_timer_stop(&timer
);
482 if (params->rat > 1.5) {
483 cpu_timer_start(&timer, "Sharpening");
484 callback.priv = "Sharpening";
485 GP_FilterEdgeSharpening(i1, i1, 0.1, &callback);
486 cpu_timer_stop(&timer);
490 /* Free low passed context if needed */
496 image_cache_put(params
->img_resized_cache
, img
, params
->img_path
, cookie
, key
);
501 static void *image_loader(void *ptr
)
503 struct loader_params
*params
= ptr
;
504 struct cpu_timer sum_timer
;
505 GP_Context
*img
, *context
= backend
->context
;
507 cpu_timer_start(&sum_timer
, "sum");
509 show_progress
= params
->show_progress
|| params
->show_progress_once
;
510 params
->show_progress_once
= 0;
512 /* Figure out rotation */
515 switch (params
->rotate
) {
529 if ((img
= load_image(params
, 0)) == NULL
)
532 params
->rat
= calc_img_size(params
, img
->w
, img
->h
, w
, h
);
534 /* Special case => no need to resize */
535 if (params
->rat
== 1.0)
541 img
= load_resized_image(params
, w
* params
->rat
+ 0.5, h
* params
->rat
+ 0.5);
546 image_cache_print(params
->img_resized_cache
);
547 image_cache_print(params
->img_orig_cache
);
550 update_display(params
, img
);
551 cpu_timer_stop(&sum_timer
);
556 static pthread_t loader_thread
= (pthread_t
)0;
558 static void stop_loader(void)
562 pthread_join(loader_thread
, NULL
);
563 loader_thread
= (pthread_t
)0;
568 static void show_image(struct loader_params
*params
, const char *path
)
573 params
->img_path
= path
;
575 /* stop previous loader thread */
578 ret
= pthread_create(&loader_thread
, NULL
, image_loader
, (void*)params
);
581 fprintf(stderr
, "Failed to start thread: %s\n", strerror(ret
));
582 GP_BackendExit(backend
);
587 static void set_zoom_offset(struct loader_params
*params
, int dx
, int dy
)
589 params
->zoom_x_offset
+= dx
;
590 params
->zoom_y_offset
+= dy
;
591 show_image(params
, NULL
);
594 static void zoom_mul(struct loader_params
*params
, float mul
)
597 show_image(params
, NULL
);
600 static void sighandler(int signo
)
603 GP_BackendExit(backend
);
605 fprintf(stderr
, "Got signal %i\n", signo
);
610 static void init_backend(const char *backend_opts
)
612 backend
= GP_BackendInit(backend_opts
, "Spiv", stderr
);
614 if (backend
== NULL
) {
615 fprintf(stderr
, "Failed to initalize backend '%s'\n", backend_opts
);
620 static int alarm_fired
= 0;
622 static void alarm_handler(int signo
)
627 static int wait_for_event(int sleep_msec
)
629 static int sleep_msec_count
= 0;
630 static int alarm_set
= 0;
632 if (sleep_msec
< 0) {
633 GP_BackendPoll(backend
);
638 /* We can't sleep on backend fd, because the backend doesn't export it. */
639 if (backend
->fd
< 0) {
640 GP_BackendPoll(backend
);
643 sleep_msec_count
+= 10;
645 if (sleep_msec_count
>= sleep_msec
) {
646 sleep_msec_count
= 0;
653 /* Initalize select */
657 FD_SET(backend
->fd
, &rfds
);
660 signal(SIGALRM
, alarm_handler
);
661 alarm(sleep_msec
/ 1000);
666 struct timeval tv
= {.tv_sec
= sleep_msec
/ 1000,
667 .tv_usec
= (sleep_msec
% 1000) * 1000};
669 int ret
= select(backend
->fd
+ 1, &rfds
, NULL
, NULL
, &tv
);
676 GP_BackendExit(backend
);
688 GP_BackendPoll(backend
);
693 static void init_caches(struct loader_params
*params
)
695 size_t size
= image_cache_get_ram_size();
696 unsigned int resized_size
= (1024 * size
)/10;
697 unsigned int orig_size
= (1024 * size
)/50;
699 if (resized_size
> 100 * 1024 * 1024)
700 resized_size
= 100 * 1024 * 1024;
702 if (orig_size
> 40 * 1024 * 1024)
703 orig_size
= 40 * 1024 * 1024;
705 GP_DEBUG(1, "Cache sizes original = %u, resized = %u",
706 orig_size
, resized_size
);
708 params
->img_resized_cache
= image_cache_create(resized_size
);
709 params
->img_orig_cache
= image_cache_create(orig_size
);
711 // params->img_resized_cache = NULL;
712 // params->img_orig_cache = NULL;
715 static const char *keys_help
[] = {
718 "Esc, Enter, Q - quit spiv",
720 "< KP Minus - zoom out by 1.5",
721 ">, KP Plus - zoom in by 1.5",
722 "R - rotate by 90 degrees clockwise",
723 "Up, Down, Left, Right - move image by 1px",
724 " (by 10 with Shift)",
726 "Space - move to the next image",
727 "BackSpace - move to the prev image",
728 "PgDown - move to the start of directory",
729 "PgUp - move to the end of directory",
730 "Home - move to the first image",
731 "End - move to the last image",
733 "I - toggle show info box",
734 "P - toggle show progress",
736 "] - change to next resampling method",
737 "[ - change to prev resampling method",
738 " (current method is shown in info box)",
739 "L - toggle low pass filter",
740 "D - drop image cache",
743 "F1-F10 - execute action 0 - 9",
745 "1 - resize spiv window to the image size",
746 "1 - resize spiv window to the image size",
747 "2 - resize spiv window to the half of the image size",
748 "3 - resize spiv window to the third of the image size",
750 "9 - resize spiv window to the ninth of the image size",
752 "Shift 2 - resize spiv window twice of the image size",
753 "Shift 3 - resize spiv window three times of the image size",
757 static const int keys_help_len
= sizeof(keys_help
) / sizeof(char*);
759 static void print_help(void)
763 printf("Usage: spiv [opts] images\n\n");
764 printf("-I\n\tshow image info (filename and size)\n\n");
765 printf("-P\n\tshow loading progress\n\n");
766 printf("-f\n\tuse floyd-steinberg dithering\n\n");
767 printf("-s sec\n\tsleep interval in seconds\n\n");
768 printf("-c\n\tturns on bicubic resampling (experimental)\n\n");
769 printf("-d level\n\tsets GFXprim debug level\n\n");
770 printf("-e pixel_type\n\tturns on backend type emulation\n");
771 printf("\tfor example -e G1 sets 1-bit grayscale\n\n");
772 printf("-r angle\n\trotate display 90,180 or 270 degrees\n\n");
773 printf("-z sets zoom mode\n\t-zf zoom is set and modified by user\n");
774 printf("\t-zw zoom is fixed to window size (currently default)\n\n");
775 printf("-b\n\tpass backend init string to backend init\n");
776 printf("\tpass -b help for more info\n\n");
779 printf("-0 'cmd' sets first action\n");
780 printf("-1 'cmd' sets second action\n");
782 printf("-9 'cmd' sets tenth action\n");
784 printf(" actions are shell commands with following modifiers:\n");
785 printf(" %%f path to current image\n");
786 printf(" %%F shell escaped path to current image\n");
787 printf(" %%n current image filename without extension\n");
788 printf(" %%N shell escaped image filename without extension\n");
789 printf(" %%e current image file extension\n");
792 for (i
= 0; i
< keys_help_len
; i
++)
797 printf("Some cool options to try:\n\n");
798 printf("spiv -0 'cp %%F sorted/");
799 printf("\tcopies current image into directory 'sorted/' on F1\n");
800 printf("spiv -e G1 -f images\n");
801 printf("\truns spiv in 1-bit bitmap mode and turns on dithering\n\n");
802 printf("spiv -b 'X11:ROOT_WIN' images\n");
803 printf("\truns spiv using X root window as backend window\n\n");
804 printf("spiv -b 'X11:CREATE_ROOT' images\n");
805 printf("\tSame as abowe but works in KDE\n");
808 static void show_help(void)
810 GP_Context
*c
= backend
->context
;
813 GP_Fill(c
, black_pixel
);
815 for (i
= 0; i
< keys_help_len
; i
++) {
816 GP_Print(c
, NULL
, 20, 2 + i
* 15, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
817 white_pixel
, black_pixel
, "%s", keys_help
[i
]);
820 GP_BackendFlip(backend
);
823 int main(int argc
, char *argv
[])
825 GP_Context
*context
= NULL
;
826 const char *backend_opts
= "X11";
828 int opt
, debug_level
= 0;
829 int shift_flag
, help_flag
= 0;
830 GP_PixelType emul_type
= GP_PIXEL_UNKNOWN
;
832 struct loader_params params
= {
836 .show_progress_once
= 0,
841 .resampling_method
= GP_INTERP_LINEAR_LF_INT
,
843 .zoom_type
= ZOOM_FIT
,
846 .img_resized_cache
= NULL
,
847 .img_orig_cache
= NULL
,
850 while ((opt
= getopt(argc
, argv
, "b:cd:e:fhIPs:r:z:0:1:2:3:4:5:6:7:8:9:")) != -1) {
853 params
.show_info
= 1;
856 params
.show_progress
= 1;
859 params
.use_dithering
= 1;
862 sleep_sec
= atoi(optarg
);
865 params
.resampling_method
= GP_INTERP_CUBIC_INT
;
866 /* Cubic resampling needs low pass */
867 params
.use_low_pass
= 1;
868 /* Cubic resampling is slow, show nn first */
869 params
.show_nn_first
= 1;
872 debug_level
= atoi(optarg
);
875 emul_type
= GP_PixelTypeByName(optarg
);
877 if (emul_type
== GP_PIXEL_UNKNOWN
) {
878 fprintf(stderr
, "Invalid pixel type '%s'\n", optarg
);
883 if (!strcmp(optarg
, "90"))
885 else if (!strcmp(optarg
, "180"))
887 else if (!strcmp(optarg
, "270"))
890 backend_opts
= optarg
;
899 params
.zoom_type
= ZOOM_FIXED
;
902 params
.zoom_type
= ZOOM_FIXED_WIN
;
907 image_action_set(opt
- '0', optarg
);
910 fprintf(stderr
, "Invalid paramter '%c'\n", opt
);
916 if (optind
>= argc
) {
917 fprintf(stderr
, "Requires path to at least one image\n\n");
922 GP_SetDebugLevel(debug_level
);
924 signal(SIGINT
, sighandler
);
925 signal(SIGSEGV
, sighandler
);
926 signal(SIGBUS
, sighandler
);
927 signal(SIGABRT
, sighandler
);
929 init_caches(¶ms
);
931 init_backend(backend_opts
);
933 if (emul_type
!= GP_PIXEL_UNKNOWN
)
934 backend
= GP_BackendVirtualInit(backend
, emul_type
, GP_BACKEND_CALL_EXIT
);
936 context
= backend
->context
;
938 black_pixel
= GP_ColorToContextPixel(GP_COL_BLACK
, context
);
939 white_pixel
= GP_ColorToContextPixel(GP_COL_WHITE
, context
);
940 gray_pixel
= GP_RGBToContextPixel(0x33, 0x33, 0x33, context
);
942 GP_Fill(context
, black_pixel
);
943 GP_BackendFlip(backend
);
945 struct image_list
*list
= image_list_create((const char**)argv
+ optind
);
946 params
.img_list
= list
;
948 params
.show_progress_once
= 1;
949 show_image(¶ms
, image_list_img_path(list
));
952 /* wait for event or a timeout */
953 if (wait_for_event(sleep_sec
* 1000))
954 show_image(¶ms
, image_list_move(list
, 1));
956 /* Read and parse events */
959 while (GP_BackendGetEvent(backend
, &ev
)) {
961 shift_flag
= GP_EventGetKey(&ev
, GP_KEY_LEFT_SHIFT
) ||
962 GP_EventGetKey(&ev
, GP_KEY_RIGHT_SHIFT
);
967 case GP_EV_REL_WHEEL
:
975 if (GP_EventGetKey(&ev
, GP_BTN_LEFT
))
976 set_zoom_offset(¶ms
,
983 if (ev
.code
!= GP_EV_KEY_DOWN
)
986 switch (ev
.val
.key
.key
) {
989 show_image(¶ms
, NULL
);
993 //TODO: remove help_flag on any other action done
994 help_flag
= !help_flag
;
997 if (GP_BackendIsX11(backend
))
998 GP_BackendX11RequestFullscreen(backend
, 2);
1001 params
.show_info
= !params
.show_info
;
1003 params
.show_progress_once
= 1;
1004 show_image(¶ms
, NULL
);
1007 params
.show_progress
= !params
.show_progress
;
1010 params
.rotate
+= 90;
1011 if (params
.rotate
> 270)
1014 params
.show_progress_once
= 1;
1015 show_image(¶ms
, NULL
);
1017 case GP_KEY_RIGHT_BRACE
:
1018 params
.resampling_method
++;
1020 if (params
.resampling_method
> GP_INTERP_MAX
)
1021 params
.resampling_method
= 0;
1023 if (params
.resampling_method
== GP_INTERP_LINEAR_LF_INT
) {
1024 params
.use_low_pass
= 0;
1025 params
.show_nn_first
= 0;
1027 params
.use_low_pass
= 1;
1028 params
.show_nn_first
= 1;
1031 params
.show_progress_once
= 1;
1032 show_image(¶ms
, image_list_img_path(list
));
1034 case GP_KEY_LEFT_BRACE
:
1035 if (params
.resampling_method
== 0)
1036 params
.resampling_method
= GP_INTERP_MAX
;
1038 params
.resampling_method
--;
1040 if (params
.resampling_method
== GP_INTERP_LINEAR_LF_INT
) {
1041 params
.use_low_pass
= 0;
1042 params
.show_nn_first
= 0;
1044 params
.use_low_pass
= 1;
1045 params
.show_nn_first
= 1;
1048 params
.show_progress_once
= 1;
1049 show_image(¶ms
, image_list_img_path(list
));
1052 params
.use_low_pass
= !params
.use_low_pass
;
1054 params
.show_progress_once
= 1;
1055 show_image(¶ms
, image_list_img_path(list
));
1058 image_cache_drop(params
.img_resized_cache
);
1059 image_cache_drop(params
.img_orig_cache
);
1064 image_cache_drop(params
.img_resized_cache
);
1065 image_cache_drop(params
.img_orig_cache
);
1066 GP_BackendExit(backend
);
1069 case GP_KEY_PAGE_UP
:
1070 params
.show_progress_once
= 1;
1071 show_image(¶ms
, image_list_dir_move(list
, -1));
1073 case GP_KEY_PAGE_DOWN
:
1074 params
.show_progress_once
= 1;
1075 show_image(¶ms
, image_list_dir_move(list
, 1));
1078 params
.show_progress_once
= 1;
1079 show_image(¶ms
, image_list_first(list
));
1082 params
.show_progress_once
= 1;
1083 show_image(¶ms
, image_list_last(list
));
1087 set_zoom_offset(¶ms
, 1, 0);
1089 set_zoom_offset(¶ms
, 10, 0);
1094 set_zoom_offset(¶ms
, -1, 0);
1096 set_zoom_offset(¶ms
, -10, 0);
1100 set_zoom_offset(¶ms
, 0, -1);
1102 set_zoom_offset(¶ms
, 0, -10);
1106 set_zoom_offset(¶ms
, 0, 1);
1108 set_zoom_offset(¶ms
, 0, 10);
1112 params
.show_progress_once
= 1;
1114 show_image(¶ms
, image_list_move(list
, 10));
1116 show_image(¶ms
, image_list_move(list
, 1));
1119 case GP_KEY_BACKSPACE
:
1120 params
.show_progress_once
= 1;
1122 show_image(¶ms
, image_list_move(list
, -10));
1124 show_image(¶ms
, image_list_move(list
, -1));
1127 resize_backend(¶ms
, 1, shift_flag
);
1130 resize_backend(¶ms
, 2, shift_flag
);
1133 resize_backend(¶ms
, 3, shift_flag
);
1136 resize_backend(¶ms
, 4, shift_flag
);
1139 resize_backend(¶ms
, 5, shift_flag
);
1142 resize_backend(¶ms
, 6, shift_flag
);
1145 resize_backend(¶ms
, 7, shift_flag
);
1148 resize_backend(¶ms
, 8, shift_flag
);
1151 resize_backend(¶ms
, 9, shift_flag
);
1153 case GP_KEY_KP_PLUS
:
1155 params
.show_progress_once
= 1;
1156 zoom_mul(¶ms
, 1.5);
1158 case GP_KEY_KP_MINUS
:
1160 params
.show_progress_once
= 1;
1161 zoom_mul(¶ms
, 1/1.5);
1163 case GP_KEY_F1
... GP_KEY_F10
:
1164 image_action_run(ev
.val
.key
.key
- GP_KEY_F1
,
1165 image_list_img_path(list
));
1171 case GP_EV_SYS_RESIZE
:
1172 /* stop loader thread before resizing backend buffer */
1174 GP_BackendResizeAck(backend
);
1175 GP_Fill(backend
->context
, 0);
1176 params
.show_progress_once
= 1;
1177 show_image(¶ms
, NULL
);
1179 case GP_EV_SYS_QUIT
:
1180 GP_BackendExit(backend
);
1189 GP_BackendExit(backend
);