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-2014 Cyril Hrubis <metan@ucw.cz> *
21 *****************************************************************************/
25 SPIV -- Simple yet Powerful Image Viewer.
36 #include "image_cache.h"
37 #include "image_list.h"
38 #include "image_loader.h"
39 #include "image_actions.h"
40 #include "spiv_help.h"
41 #include "spiv_config.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;
53 static int loader_running
= 0;
55 struct loader_params
{
56 /* current resize ratio */
59 /* offset in pixels */
60 unsigned int zoom_x_offset
;
61 unsigned int zoom_y_offset
;
63 /* flag that is turned on when user changes zoom */
64 unsigned int zoom_manual
;
66 long show_progress_once
:2;
67 /* use nearest neighbour resampling first */
69 /* use low pass before resampling */
71 /* resampling method */
72 int resampling_method
;
77 /* caches for loaded images */
78 struct image_cache
*img_resized_cache
;
81 static int image_loader_callback(gp_progress_cb
*self
)
83 static gp_size size
= 0;
84 gp_pixmap
*c
= backend
->pixmap
;
94 snprintf(buf
, sizeof(buf
), "%s ... %-3.1f%%",
95 (const char*)self
->priv
, self
->percentage
);
97 int align
= GP_ALIGN_CENTER
|GP_VALIGN_ABOVE
;
99 size
= gp_text_width(config
.style
, buf
);
101 int start
= c
->w
/2 - size
/2 - 10;
102 int end
= c
->w
/2 + size
/2 + 10;
103 int middle
= start
+ (end
- start
) * self
->percentage
/ 100;
104 int top
= c
->h
- gp_text_height(config
.style
) - 11;
106 gp_fill_rect_xyxy(c
, start
, c
->h
- 1, middle
, top
, gray_pixel
);
107 gp_fill_rect_xyxy(c
, middle
, c
->h
- 1, end
, top
, black_pixel
);
109 gp_text(c
, config
.style
, c
->w
/2, c
->h
- 5, align
,
110 white_pixel
, black_pixel
, buf
);
112 gp_backend_update_rect(backend
, start
, c
->h
- 1, end
, top
);
117 static gp_pixmap
*load_image(int elevate
);
119 static const char *img_name(const char *img_path
)
121 int i
, len
= strlen(img_path
);
123 for (i
= len
- 1; i
> 0; i
--) {
124 if (img_path
[i
] == '/')
125 return &img_path
[i
+1];
131 static void set_caption(const char *path
, float rat
)
135 snprintf(buf
, sizeof(buf
), "Spiv ~ %s 1:%3.3f", img_name(path
), rat
);
137 gp_backend_set_caption(backend
, buf
);
143 static gp_pixmap
*load_image(int elevate
)
146 gp_progress_cb callback
= {.callback
= image_loader_callback
,
147 .priv
= "Loading image"};
149 img
= image_loader_get_image(&callback
, elevate
);
154 gp_pixmap
*pixmap
= backend
->pixmap
;
156 gp_fill(pixmap
, black_pixel
);
157 gp_print(pixmap
, config
.style
, pixmap
->w
/2, pixmap
->h
/2 - 10,
158 GP_ALIGN_CENTER
|GP_VALIGN_CENTER
, white_pixel
, black_pixel
,
159 "'%s'", image_loader_img_path());
160 gp_print(pixmap
, config
.style
, pixmap
->w
/2, pixmap
->h
/2 + 10,
161 GP_ALIGN_CENTER
|GP_VALIGN_CENTER
, white_pixel
, black_pixel
,
162 "Failed to load image :( (%s)", strerror(errno
));
163 gp_backend_flip(backend
);
169 * Fill pixmap with chessboard-like pattern.
171 static void pattern_fill(gp_pixmap
*pixmap
, unsigned int x0
, unsigned int y0
,
172 unsigned int w
, unsigned int h
)
174 unsigned int x
, y
, i
, j
= 0;
177 col
[0] = gp_rgb_to_pixmap_pixel(0x64, 0x64, 0x64, pixmap
);
178 col
[1] = gp_rgb_to_pixmap_pixel(0x80, 0x80, 0x80, pixmap
);
180 unsigned int wm
= w
/20 < 5 ? 5 : w
/20;
181 unsigned int hm
= h
/20 < 5 ? 5 : h
/20;
183 for (y
= 0; y
< h
; y
+= hm
) {
186 for (x
= 0; x
< w
; x
+= wm
) {
187 gp_fill_rect_xywh(pixmap
, x0
+ x
, y0
+ y
, wm
, hm
, col
[i
]);
194 static void info_printf(gp_pixmap
*pixmap
, gp_coord x
, gp_coord y
,
195 const char *fmt
, ...)
196 __attribute__ ((format (printf
, 4, 5)));
198 static void info_printf(gp_pixmap
*pixmap
, gp_coord x
, gp_coord y
,
199 const char *fmt
, ...)
206 gp_vprint(pixmap
, config
.style
, x
-1, y
-1, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
|GP_TEXT_NOBG
,
207 black_pixel
, white_pixel
, fmt
, vac
);
210 gp_vprint(pixmap
, config
.style
, x
, y
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
|GP_TEXT_NOBG
,
211 white_pixel
, black_pixel
, fmt
, va
);
216 static unsigned int print_meta_data(gp_data_node
*node
, gp_pixmap
*pixmap
,
217 unsigned int th
, unsigned int y
, int level
)
222 for (i
= gp_data_dict_first(node
); i
; i
= i
->next
) {
227 info_printf(pixmap
, x
, y
, "%s : %li", i
->id
, i
->value
.i
);
230 info_printf(pixmap
, x
, y
, "%s : %lf", i
->id
, i
->value
.d
);
233 info_printf(pixmap
, x
, y
, "%s : %s", i
->id
, i
->value
.str
);
235 case GP_DATA_RATIONAL
:
236 info_printf(pixmap
, x
, y
, "%s : %li/%li",
237 i
->id
, i
->value
.rat
.num
, i
->value
.rat
.den
);
240 info_printf(pixmap
, x
, y
, "%s", i
->id
);
241 y
= print_meta_data(i
, pixmap
, th
, y
, level
+1);
249 static void show_info(struct loader_params
*params
, gp_pixmap
*img
,
252 gp_pixmap
*pixmap
= backend
->pixmap
;
253 const char *img_path
= image_loader_img_path();
255 set_caption(img_path
, params
->zoom_rat
);
257 if (!config
.show_info
)
260 gp_size th
= gp_text_height(config
.style
), y
= 10;
262 info_printf(pixmap
, 10, y
, "%ux%u (%ux%u) 1:%3.3f %3.1f%% %s",
263 img
->w
, img
->h
, orig_img
->w
, orig_img
->h
, params
->zoom_rat
,
264 params
->zoom_rat
* 100, gp_pixel_type_name(img
->pixel_type
));
267 info_printf(pixmap
, 10, y
, "%s", img_name(img_path
));
271 if (params
->zoom_rat
!= 1.00) {
272 info_printf(pixmap
, 10, y
, "%s%s",
273 params
->use_low_pass
&& params
->zoom_rat
< 1 ? "Gaussian LP + " : "",
274 gp_interpolation_type_name(params
->resampling_method
));
278 unsigned int count
= image_loader_count();
279 unsigned int pos
= image_loader_pos() + 1;
281 info_printf(pixmap
, 10, y
, "%u of %u", pos
, count
);
284 if (image_loader_is_in_dir()) {
285 unsigned int dir_count
= image_loader_dir_count();
286 unsigned int dir_pos
= image_loader_dir_pos() + 1;
288 info_printf(pixmap
, 10, y
,
289 "%u of %u in directory", dir_pos
, dir_count
);
292 gp_storage
*meta_data
= image_loader_get_meta_data();
297 gp_data_node
*node
= gp_storage_root(meta_data
);
299 if (node
->type
!= GP_DATA_DICT
)
302 print_meta_data(node
, pixmap
, th
+ 2, y
+ th
, 0);
305 static void update_display(struct loader_params
*params
, gp_pixmap
*img
,
308 gp_pixmap
*pixmap
= backend
->pixmap
;
309 struct cpu_timer timer
;
310 gp_progress_cb callback
= {.callback
= image_loader_callback
};
312 switch (config
.combined_orientation
) {
316 callback
.priv
= "Rotating image (90)";
317 img
= gp_filter_rotate_90_alloc(img
, &callback
);
320 callback
.priv
= "Rotating image (180)";
321 img
= gp_filter_rotate_180_alloc(img
, &callback
);
324 callback
.priv
= "Rotating image (270)";
325 img
= gp_filter_rotate_270_alloc(img
, &callback
);
338 * Center the image, if window size is fixed and
339 * the image is smaller than window.
341 if (config
.win_strategy
== ZOOM_WIN_FIXED
) {
343 if (img
->w
< pixmap
->w
)
344 cx
= (pixmap
->w
- img
->w
)/2;
346 if (img
->h
< pixmap
->h
)
347 cy
= (pixmap
->h
- img
->h
)/2;
350 if (params
->zoom_manual
) {
351 cx
= params
->zoom_x_offset
;
352 cy
= params
->zoom_y_offset
;
355 gp_pixmap sub_display
;
357 cpu_timer_start(&timer
, "Blitting");
359 if (config
.floyd_steinberg
) {
360 callback
.priv
= "Dithering";
361 gp_sub_pixmap(pixmap
, &sub_display
, cx
, cy
, img
->w
, img
->h
);
362 gp_filter_floyd_steinberg(img
, &sub_display
, NULL
);
363 // gp_filter_hilbert_peano(img, &sub_display, NULL);
365 if (gp_pixel_has_flags(img
->pixel_type
, GP_PIXEL_HAS_ALPHA
))
366 pattern_fill(pixmap
, cx
, cy
, img
->w
, img
->h
);
367 gp_blit_clipped(img
, 0, 0, img
->w
, img
->h
, pixmap
, cx
, cy
);
370 cpu_timer_stop(&timer
);
372 /* clean up the rest of the display */
373 gp_fill_rect_xywh(pixmap
, 0, 0, cx
, pixmap
->h
, black_pixel
);
374 gp_fill_rect_xywh(pixmap
, 0, 0, pixmap
->w
, cy
, black_pixel
);
377 int w
= pixmap
->w
- img
->w
- cx
;
380 gp_fill_rect_xywh(pixmap
, img
->w
+ cx
, 0, w
, pixmap
->h
, black_pixel
);
382 int h
= pixmap
->h
- img
->h
- cy
;
385 gp_fill_rect_xywh(pixmap
, 0, img
->h
+ cy
, pixmap
->w
, h
, black_pixel
);
387 show_info(params
, img
, orig_img
);
389 if (config
.combined_orientation
)
392 gp_backend_flip(backend
);
395 gp_pixmap
*load_resized_image(struct loader_params
*params
, gp_size w
, gp_size h
)
397 gp_pixmap
*img
, *res
= NULL
;
398 struct cpu_timer timer
;
399 gp_progress_cb callback
= {.callback
= image_loader_callback
};
401 const char *img_path
= image_loader_img_path();
403 /* Try to get resized cached image */
404 img
= image_cache_get2(params
->img_resized_cache
, 1, "%s %ux%u r%i l%i",
405 img_path
, w
, h
, params
->resampling_method
,
406 params
->use_low_pass
);
411 /* Otherwise load image and resize it */
412 if ((img
= load_image(1)) == NULL
)
415 if (params
->show_nn_first
) {
416 /* Do simple interpolation and blit the result */
417 gp_pixmap
*nn
= gp_filter_resize_nn_alloc(img
, w
, h
, NULL
);
419 update_display(params
, nn
, img
);
424 /* Do low pass filter */
425 if (params
->use_low_pass
&& params
->zoom_rat
< 1) {
426 cpu_timer_start(&timer
, "Blur");
427 callback
.priv
= "Blurring Image";
429 res
= gp_filter_gaussian_blur_alloc(img
, 0.4/params
->zoom_rat
,
430 0.4/params
->zoom_rat
, &callback
);
437 cpu_timer_stop(&timer
);
440 // img->gamma = gp_gamma_acquire(img->pixel_type, 0.45);
442 cpu_timer_start(&timer
, "Resampling");
443 callback
.priv
= "Resampling Image";
444 gp_pixmap
*i1
= gp_filter_resize_alloc(img
, w
, h
, params
->resampling_method
, &callback
);
446 cpu_timer_stop(&timer
);
449 if (params->zoom_rat > 1.5) {
450 cpu_timer_start(&timer, "Sharpening");
451 callback.priv = "Sharpening";
452 gp_filter_edge_sharpening(i1, i1, 0.1, &callback);
453 cpu_timer_stop(&timer);
457 /* Free low passed pixmap if needed */
463 image_cache_put2(params
->img_resized_cache
, img
, NULL
, "%s %ux%u r%i l%i",
464 img_path
, w
, h
, params
->resampling_method
,
465 params
->use_low_pass
);
470 static void exif_autorotate(void)
472 gp_data_node
*orientation
;
474 config
.combined_orientation
= config
.orientation
;
476 if (!config
.exif_autorotate
)
479 orientation
= gp_storage_get_by_path(image_loader_get_meta_data(),
480 NULL
, "/Exif/Orientation");
484 switch (orientation
->value
.i
) {
485 case GP_EXIF_UPPER_LEFT
:
486 config
.combined_orientation
+= ROTATE_0
;
488 case GP_EXIF_LOWER_RIGHT
:
489 config
.combined_orientation
+= ROTATE_180
;
491 case GP_EXIF_UPPER_RIGHT
:
492 config
.combined_orientation
+= ROTATE_90
;
494 case GP_EXIF_LOWER_LEFT
:
495 config
.combined_orientation
+= ROTATE_270
;
499 if (config
.combined_orientation
> ROTATE_270
)
500 config
.combined_orientation
-= ROTATE_360
;
503 static float calc_img_size(struct loader_params
*params
,
504 uint32_t img_w
, uint32_t img_h
,
505 uint32_t win_w
, uint32_t win_h
)
507 float w_rat
, h_rat
, rat
;
508 unsigned int max_win_w
= config
.max_win_w
;
509 unsigned int max_win_h
= config
.max_win_h
;
513 switch (config
.combined_orientation
) {
516 GP_SWAP(win_w
, win_h
);
517 GP_SWAP(max_win_w
, max_win_h
);
523 if (params
->zoom_manual
) {
524 if (config
.win_strategy
== ZOOM_WIN_RESIZABLE
) {
525 win_w
= GP_MIN(max_win_w
, img_w
* params
->zoom_rat
+ 0.5);
526 win_h
= GP_MIN(max_win_h
, img_h
* params
->zoom_rat
+ 0.5);
528 switch (config
.combined_orientation
) {
531 gp_backend_resize(backend
, win_h
, win_w
);
534 gp_backend_resize(backend
, win_w
, win_h
);
537 return params
->zoom_rat
;
541 if (config
.win_strategy
== ZOOM_WIN_RESIZABLE
) {
542 win_w
= GP_MIN(max_win_w
, img_w
);
543 win_h
= GP_MIN(max_win_h
, img_h
);
546 * Image is larger than screen and downscale is enabled ->
547 * resize window to match image ratio.
549 if ((win_w
!= img_w
|| win_h
!= img_h
) &&
550 config
.zoom_strategy
& ZOOM_IMAGE_DOWNSCALE
) {
552 w_rat
= 1.00 * win_w
/ img_w
;
553 h_rat
= 1.00 * win_h
/ img_h
;
561 rat
= GP_MIN(h_rat
, w_rat
);
563 win_w
= rat
* img_w
+ 0.5;
564 win_h
= rat
* img_h
+ 0.5;
567 switch (config
.combined_orientation
) {
570 gp_backend_resize(backend
, win_h
, win_w
);
573 gp_backend_resize(backend
, win_w
, win_h
);
577 if (img_w
<= win_w
&& img_h
<= win_h
) {
578 if (!(config
.zoom_strategy
& ZOOM_IMAGE_UPSCALE
))
581 if (!(config
.zoom_strategy
& ZOOM_IMAGE_DOWNSCALE
))
586 w_rat
= 1.00 * win_w
/ img_w
;
587 h_rat
= 1.00 * win_h
/ img_h
;
589 return GP_MIN(w_rat
, h_rat
);
592 static void *image_loader(void *ptr
)
594 struct loader_params
*params
= ptr
;
595 struct cpu_timer sum_timer
;
596 gp_pixmap
*img
, *orig_img
, *pixmap
= backend
->pixmap
;
598 cpu_timer_start(&sum_timer
, "sum");
600 show_progress
= config
.show_progress
|| params
->show_progress_once
;
601 params
->show_progress_once
= 0;
603 if ((orig_img
= load_image(0)) == NULL
) {
611 params
->zoom_rat
= calc_img_size(params
, orig_img
->w
, orig_img
->h
,
612 pixmap
->w
, pixmap
->h
);
614 w
= orig_img
->w
* params
->zoom_rat
+ 0.5;
615 h
= orig_img
->h
* params
->zoom_rat
+ 0.5;
617 /* Special case => no need to resize */
618 if (w
== orig_img
->w
&& h
== orig_img
->h
) {
623 img
= load_resized_image(params
, w
, h
);
631 update_display(params
, img
, orig_img
);
632 cpu_timer_stop(&sum_timer
);
639 static pthread_t loader_thread
= (pthread_t
)0;
641 static void stop_loader(void)
645 pthread_join(loader_thread
, NULL
);
646 loader_thread
= (pthread_t
)0;
651 static void show_image(struct loader_params
*params
)
655 /* stop previous loader thread */
660 ret
= pthread_create(&loader_thread
, NULL
, image_loader
, (void*)params
);
663 fprintf(stderr
, "Failed to start thread: %s\n", strerror(ret
));
664 gp_backend_exit(backend
);
669 static void image_seek(struct loader_params
*params
,
670 enum img_seek_offset offset
, int whence
)
673 * We need to stop loader first because image loader seek may free
674 * image we are currently resamling.
677 params
->zoom_manual
= 0;
678 image_loader_seek(offset
, whence
);
682 static void set_zoom_offset(struct loader_params
*params
, int dx
, int dy
)
684 params
->zoom_manual
= 1;
685 params
->zoom_x_offset
+= dx
;
686 params
->zoom_y_offset
+= dy
;
690 static void zoom_mul(struct loader_params
*params
, float mul
)
692 params
->zoom_manual
= 1;
693 params
->zoom_rat
*= mul
;
697 static void zoom_set(struct loader_params
*params
, float mul
)
699 params
->zoom_manual
= 1;
700 params
->zoom_rat
= mul
;
704 static void sighandler(int signo
)
707 gp_backend_exit(backend
);
709 fprintf(stderr
, "Got signal %i\n", signo
);
714 static void init_backend(const char *backend_opts
)
716 backend
= gp_backend_init(backend_opts
, "Spiv");
718 if (backend
== NULL
) {
719 fprintf(stderr
, "Failed to initalize backend '%s'\n", backend_opts
);
723 if (config
.full_screen
) {
724 if (gp_backend_is_x11(backend
))
725 gp_x11_fullscreen(backend
, 2);
729 #define RESIZED_CACHE_MAX 400 * 1024
730 #define ORIG_CACHE_MAX 80 * 1024
733 * Figure out cache size depending on the size of RAM.
735 * Initialize cache, image loader.
737 static int init_loader(struct loader_params
*params
, const char **argv
)
739 size_t size
= image_cache_get_ram_size();
740 size_t resized_size
= size
/10;
741 size_t orig_size
= size
/50;
743 if (resized_size
> RESIZED_CACHE_MAX
)
744 resized_size
= RESIZED_CACHE_MAX
;
746 if (orig_size
> ORIG_CACHE_MAX
)
747 orig_size
= ORIG_CACHE_MAX
;
749 GP_DEBUG(1, "Resized cache size = %zukB", resized_size
);
750 GP_DEBUG(1, "Orig cache size = %zukB", orig_size
);
752 if (image_loader_init(argv
, orig_size
))
755 params
->img_resized_cache
= image_cache_create(resized_size
);
760 static uint32_t timer_callback(gp_timer
*self
)
762 struct loader_params
*params
= self
->priv
;
763 static int retries
= 0;
766 * If loader is still running, reschedule after 20ms
768 * If more than two seconds has passed, try load next
770 if (loader_running
&& retries
< 100) {
771 printf("Loader still running %ims\n", 20 * retries
);
779 * We need to stop loader first because image loader seek may free
780 * image we are currently resamling.
783 image_loader_seek(IMG_CUR
, 1);
786 return params
->sleep_ms
;
789 int main(int argc
, char *argv
[])
791 gp_pixmap
*pixmap
= NULL
;
795 struct loader_params params
= {
796 .show_progress_once
= 0,
798 .resampling_method
= GP_INTERP_LINEAR_LF_INT
,
800 .img_resized_cache
= NULL
,
805 GP_TIMER_DECLARE(timer
, 0, 0, "Slideshow", timer_callback
, ¶ms
);
807 if (access("/etc/spiv.conf", R_OK
) == 0)
808 spiv_config_load("/etc/spiv.conf");
810 if (getenv("HOME")) {
813 snprintf(buf
, sizeof(buf
), "%s/%s", getenv("HOME"), ".spiv");
815 if (access(buf
, R_OK
) == 0)
816 spiv_config_load(buf
);
819 opts
= spiv_config_parse_args(argc
, argv
);
826 cpu_timer_switch(config
.timers
);
827 params
.sleep_ms
= 1000 * config
.slideshow_delay
+ 0.5;
831 if (!strcmp(config
.backend_init
, "help")) {
832 init_backend(config
.backend_init
);
836 fprintf(stderr
, "Requires path to at least one image\n\n");
841 signal(SIGINT
, sighandler
);
842 signal(SIGSEGV
, sighandler
);
843 signal(SIGBUS
, sighandler
);
844 signal(SIGABRT
, sighandler
);
846 if (init_loader(¶ms
, (const char **)argv
+ opts
))
849 init_backend(config
.backend_init
);
851 if (config
.emul_type
!= GP_PIXEL_UNKNOWN
) {
852 backend
= gp_backend_virt_init(backend
, config
.emul_type
,
853 GP_BACKEND_CALL_EXIT
);
856 pixmap
= backend
->pixmap
;
858 black_pixel
= gp_rgb_to_pixmap_pixel(0x00, 0x00, 0x00, pixmap
);
859 white_pixel
= gp_rgb_to_pixmap_pixel(0xff, 0xff, 0xff, pixmap
);
860 gray_pixel
= gp_rgb_to_pixmap_pixel(0x33, 0x33, 0x33, pixmap
);
862 gp_fill(pixmap
, black_pixel
);
863 gp_backend_flip(backend
);
865 params
.show_progress_once
= 1;
868 if (params
.sleep_ms
) {
869 timer
.expires
= params
.sleep_ms
;
870 gp_backend_add_timer(backend
, &timer
);
876 while (gp_backend_wait_event(backend
, &ev
)) {
878 shift_flag
= gp_event_get_key(&ev
, GP_KEY_LEFT_SHIFT
) ||
879 gp_event_get_key(&ev
, GP_KEY_RIGHT_SHIFT
);
884 case GP_EV_REL_WHEEL
:
892 if (gp_event_get_key(&ev
, GP_BTN_LEFT
))
893 set_zoom_offset(¶ms
,
900 if (ev
.code
!= GP_EV_KEY_DOWN
)
903 switch (ev
.val
.key
.key
) {
909 if (gp_backend_is_x11(backend
))
910 gp_x11_fullscreen(backend
, 2);
913 config
.show_info
= !config
.show_info
;
915 params
.show_progress_once
= 1;
919 config
.show_progress
= !config
.show_progress
;
922 config
.orientation
++;
924 if (config
.orientation
> ROTATE_270
)
925 config
.orientation
= ROTATE_0
;
927 params
.show_progress_once
= 1;
931 if (config
.orientation
> ROTATE_0
)
932 config
.orientation
--;
934 config
.orientation
= ROTATE_270
;
936 params
.show_progress_once
= 1;
940 if (params
.sleep_ms
) {
941 if (gp_backend_timers_in_queue(backend
)) {
942 gp_backend_rem_timer(backend
, &timer
);
944 timer
.expires
= params
.sleep_ms
;
945 gp_backend_add_timer(backend
, &timer
);
949 case GP_KEY_RIGHT_BRACE
:
950 params
.resampling_method
++;
952 if (params
.resampling_method
> GP_INTERP_MAX
)
953 params
.resampling_method
= 0;
954 if (params
.resampling_method
== GP_INTERP_CUBIC
)
955 params
.resampling_method
++;
956 if (params
.resampling_method
== GP_INTERP_LINEAR_LF_INT
) {
957 params
.use_low_pass
= 0;
958 params
.show_nn_first
= 0;
960 params
.use_low_pass
= 1;
961 params
.show_nn_first
= 1;
964 params
.show_progress_once
= 1;
967 case GP_KEY_LEFT_BRACE
:
968 if (params
.resampling_method
== 0)
969 params
.resampling_method
= GP_INTERP_MAX
;
971 params
.resampling_method
--;
972 if (params
.resampling_method
== GP_INTERP_CUBIC
)
973 params
.resampling_method
--;
974 if (params
.resampling_method
== GP_INTERP_LINEAR_LF_INT
) {
975 params
.use_low_pass
= 0;
976 params
.show_nn_first
= 0;
978 params
.use_low_pass
= 1;
979 params
.show_nn_first
= 1;
982 params
.show_progress_once
= 1;
986 params
.use_low_pass
= !params
.use_low_pass
;
988 params
.show_progress_once
= 1;
992 image_cache_drop(params
.img_resized_cache
);
993 image_loader_drop_cache();
997 params
.show_progress_once
= 1;
1001 config_upscale_toggle();
1002 params
.show_progress_once
= 1;
1003 show_image(¶ms
);
1006 config_downscale_toggle();
1007 params
.show_progress_once
= 1;
1008 show_image(¶ms
);
1014 image_cache_destroy(params
.img_resized_cache
);
1015 image_loader_destroy();
1016 gp_backend_exit(backend
);
1019 case GP_KEY_PAGE_UP
:
1020 params
.show_progress_once
= 1;
1021 image_seek(¶ms
, IMG_DIR
, -1);
1023 case GP_KEY_PAGE_DOWN
:
1024 params
.show_progress_once
= 1;
1025 image_seek(¶ms
, IMG_DIR
, 1);
1028 params
.show_progress_once
= 1;
1029 image_seek(¶ms
, IMG_FIRST
, 0);
1032 params
.show_progress_once
= 1;
1033 image_seek(¶ms
, IMG_LAST
, 0);
1037 set_zoom_offset(¶ms
, 1, 0);
1039 set_zoom_offset(¶ms
, 10, 0);
1044 set_zoom_offset(¶ms
, -1, 0);
1046 set_zoom_offset(¶ms
, -10, 0);
1050 set_zoom_offset(¶ms
, 0, -1);
1052 set_zoom_offset(¶ms
, 0, -10);
1056 set_zoom_offset(¶ms
, 0, 1);
1058 set_zoom_offset(¶ms
, 0, 10);
1062 params
.show_progress_once
= 1;
1064 image_seek(¶ms
, IMG_CUR
, 10);
1066 image_seek(¶ms
, IMG_CUR
, 1);
1069 case GP_KEY_BACKSPACE
:
1070 params
.show_progress_once
= 1;
1072 image_seek(¶ms
, IMG_CUR
, -10);
1074 image_seek(¶ms
, IMG_CUR
, -1);
1076 case GP_KEY_1
... GP_KEY_9
: {
1077 float val
= ev
.val
.key
.key
- GP_KEY_1
+ 1;
1082 zoom_set(¶ms
, val
);
1086 zoom_set(¶ms
, 10);
1088 zoom_set(¶ms
, 0.1);
1090 case GP_KEY_KP_PLUS
:
1092 params
.show_progress_once
= 1;
1094 zoom_mul(¶ms
, 1.1);
1096 zoom_mul(¶ms
, 1.5);
1098 case GP_KEY_KP_MINUS
:
1100 params
.show_progress_once
= 1;
1102 zoom_mul(¶ms
, 1/1.1);
1104 zoom_mul(¶ms
, 1/1.5);
1106 case GP_KEY_F1
... GP_KEY_F10
:
1107 image_action_run(ev
.val
.key
.key
- GP_KEY_F1
+ 1,
1108 image_loader_img_path());
1114 case GP_EV_SYS_RESIZE
:
1115 /* stop loader thread before resizing backend buffer */
1117 gp_backend_resize_ack(backend
);
1118 gp_fill(backend
->pixmap
, 0);
1119 params
.show_progress_once
= 1;
1120 show_image(¶ms
);
1122 case GP_EV_SYS_QUIT
:
1123 gp_backend_exit(backend
);