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_ProgressCallback
*self
)
83 static GP_Size size
= 0;
84 GP_Context
*c
= backend
->context
;
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_TextWidth(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_TextHeight(config
.style
) - 11;
106 GP_FillRectXYXY(c
, start
, c
->h
- 1, middle
, top
, gray_pixel
);
107 GP_FillRectXYXY(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_BackendUpdateRect(backend
, start
, c
->h
- 1, end
, top
);
117 static GP_Context
*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_BackendSetCaption(backend
, buf
);
143 static GP_Context
*load_image(int elevate
)
146 GP_ProgressCallback callback
= {.callback
= image_loader_callback
,
147 .priv
= "Loading image"};
149 img
= image_loader_get_image(&callback
, elevate
);
154 GP_Context
*ctx
= backend
->context
;
156 GP_Fill(ctx
, black_pixel
);
157 GP_Print(ctx
, config
.style
, ctx
->w
/2, ctx
->h
/2 - 10,
158 GP_ALIGN_CENTER
|GP_VALIGN_CENTER
, white_pixel
, black_pixel
,
159 "'%s'", image_loader_img_path());
160 GP_Print(ctx
, config
.style
, ctx
->w
/2, ctx
->h
/2 + 10,
161 GP_ALIGN_CENTER
|GP_VALIGN_CENTER
, white_pixel
, black_pixel
,
162 "Failed to load image :( (%s)", strerror(errno
));
163 GP_BackendFlip(backend
);
169 * Fill context with chessboard-like pattern.
171 static void pattern_fill(GP_Context
*ctx
, 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_RGBToContextPixel(0x64, 0x64, 0x64, ctx
);
178 col
[1] = GP_RGBToContextPixel(0x80, 0x80, 0x80, ctx
);
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_FillRectXYWH(ctx
, x0
+ x
, y0
+ y
, wm
, hm
, col
[i
]);
194 static void info_printf(GP_Context
*ctx
, GP_Coord x
, GP_Coord y
,
195 const char *fmt
, ...)
196 __attribute__ ((format (printf
, 4, 5)));
198 static void info_printf(GP_Context
*ctx
, GP_Coord x
, GP_Coord y
,
199 const char *fmt
, ...)
206 GP_VPrint(ctx
, 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(ctx
, 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_DataNode
*node
, GP_Context
*context
,
217 unsigned int th
, unsigned int y
, int level
)
222 for (i
= GP_DataDictFirst(node
); i
; i
= i
->next
) {
227 info_printf(context
, x
, y
, "%s : %li", i
->id
, i
->value
.i
);
230 info_printf(context
, x
, y
, "%s : %lf", i
->id
, i
->value
.d
);
233 info_printf(context
, x
, y
, "%s : %s", i
->id
, i
->value
.str
);
235 case GP_DATA_RATIONAL
:
236 info_printf(context
, x
, y
, "%s : %li/%li",
237 i
->id
, i
->value
.rat
.num
, i
->value
.rat
.den
);
240 info_printf(context
, x
, y
, "%s", i
->id
);
241 y
= print_meta_data(i
, context
, th
, y
, level
+1);
249 static void show_info(struct loader_params
*params
, GP_Context
*img
,
250 GP_Context
*orig_img
)
252 GP_Context
*context
= backend
->context
;
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_TextHeight(config
.style
), y
= 10;
262 info_printf(context
, 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_PixelTypeName(img
->pixel_type
));
267 info_printf(context
, 10, y
, "%s", img_name(img_path
));
271 if (params
->zoom_rat
!= 1.00) {
272 info_printf(context
, 10, y
, "%s%s",
273 params
->use_low_pass
&& params
->zoom_rat
< 1 ? "Gaussian LP + " : "",
274 GP_InterpolationTypeName(params
->resampling_method
));
278 unsigned int count
= image_loader_count();
279 unsigned int pos
= image_loader_pos() + 1;
281 info_printf(context
, 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(context
, 10, y
,
289 "%u of %u in directory", dir_pos
, dir_count
);
292 GP_DataStorage
*meta_data
= image_loader_get_meta_data();
297 GP_DataNode
*node
= GP_DataStorageRoot(meta_data
);
299 if (node
->type
!= GP_DATA_DICT
)
302 print_meta_data(node
, context
, th
+ 2, y
+ th
, 0);
305 static void update_display(struct loader_params
*params
, GP_Context
*img
,
306 GP_Context
*orig_img
)
308 GP_Context
*context
= backend
->context
;
309 struct cpu_timer timer
;
310 GP_ProgressCallback callback
= {.callback
= image_loader_callback
};
312 switch (config
.orientation
) {
316 callback
.priv
= "Rotating image (90)";
317 img
= GP_FilterRotate90Alloc(img
, &callback
);
320 callback
.priv
= "Rotating image (180)";
321 img
= GP_FilterRotate180Alloc(img
, &callback
);
324 callback
.priv
= "Rotating image (270)";
325 img
= GP_FilterRotate270Alloc(img
, &callback
);
336 * Center the image, if window size is fixed and
337 * the image is smaller than window.
339 if (config
.win_strategy
== ZOOM_WIN_FIXED
) {
341 if (img
->w
< context
->w
)
342 cx
= (context
->w
- img
->w
)/2;
344 if (img
->h
< context
->h
)
345 cy
= (context
->h
- img
->h
)/2;
348 if (params
->zoom_manual
) {
349 cx
= params
->zoom_x_offset
;
350 cy
= params
->zoom_y_offset
;
353 GP_Context sub_display
;
355 cpu_timer_start(&timer
, "Blitting");
357 if (config
.floyd_steinberg
) {
358 callback
.priv
= "Dithering";
359 GP_SubContext(context
, &sub_display
, cx
, cy
, img
->w
, img
->h
);
360 GP_FilterFloydSteinberg(img
, &sub_display
, NULL
);
361 // GP_FilterHilbertPeano(img, &sub_display, NULL);
363 if (GP_PixelHasFlags(img
->pixel_type
, GP_PIXEL_HAS_ALPHA
))
364 pattern_fill(context
, cx
, cy
, img
->w
, img
->h
);
365 GP_Blit_Clipped(img
, 0, 0, img
->w
, img
->h
, context
, cx
, cy
);
368 cpu_timer_stop(&timer
);
370 /* clean up the rest of the display */
371 GP_FillRectXYWH(context
, 0, 0, cx
, context
->h
, black_pixel
);
372 GP_FillRectXYWH(context
, 0, 0, context
->w
, cy
, black_pixel
);
375 int w
= context
->w
- img
->w
- cx
;
378 GP_FillRectXYWH(context
, img
->w
+ cx
, 0, w
, context
->h
, black_pixel
);
380 int h
= context
->h
- img
->h
- cy
;
383 GP_FillRectXYWH(context
, 0, img
->h
+ cy
, context
->w
, h
, black_pixel
);
385 show_info(params
, img
, orig_img
);
387 if (config
.orientation
)
390 GP_BackendFlip(backend
);
393 GP_Context
*load_resized_image(struct loader_params
*params
, GP_Size w
, GP_Size h
)
395 GP_Context
*img
, *res
= NULL
;
396 struct cpu_timer timer
;
397 GP_ProgressCallback callback
= {.callback
= image_loader_callback
};
399 const char *img_path
= image_loader_img_path();
401 /* Try to get resized cached image */
402 img
= image_cache_get2(params
->img_resized_cache
, 1, "%s %ux%u r%i l%i",
403 img_path
, w
, h
, params
->resampling_method
,
404 params
->use_low_pass
);
409 /* Otherwise load image and resize it */
410 if ((img
= load_image(1)) == NULL
)
413 if (params
->show_nn_first
) {
414 /* Do simple interpolation and blit the result */
415 GP_Context
*nn
= GP_FilterResizeNNAlloc(img
, w
, h
, NULL
);
417 update_display(params
, nn
, img
);
422 /* Do low pass filter */
423 if (params
->use_low_pass
&& params
->zoom_rat
< 1) {
424 cpu_timer_start(&timer
, "Blur");
425 callback
.priv
= "Blurring Image";
427 res
= GP_FilterGaussianBlurAlloc(img
, 0.4/params
->zoom_rat
,
428 0.4/params
->zoom_rat
, &callback
);
435 cpu_timer_stop(&timer
);
438 // img->gamma = GP_GammaAcquire(img->pixel_type, 0.45);
440 cpu_timer_start(&timer
, "Resampling");
441 callback
.priv
= "Resampling Image";
442 GP_Context
*i1
= GP_FilterResizeAlloc(img
, w
, h
, params
->resampling_method
, &callback
);
444 cpu_timer_stop(&timer
);
447 if (params->zoom_rat > 1.5) {
448 cpu_timer_start(&timer, "Sharpening");
449 callback.priv = "Sharpening";
450 GP_FilterEdgeSharpening(i1, i1, 0.1, &callback);
451 cpu_timer_stop(&timer);
455 /* Free low passed context if needed */
461 image_cache_put2(params
->img_resized_cache
, img
, NULL
, "%s %ux%u r%i l%i",
462 img_path
, w
, h
, params
->resampling_method
,
463 params
->use_low_pass
);
468 static float calc_img_size(struct loader_params
*params
,
469 uint32_t img_w
, uint32_t img_h
,
470 uint32_t win_w
, uint32_t win_h
)
472 float w_rat
, h_rat
, rat
;
473 unsigned int max_win_w
= config
.max_win_w
;
474 unsigned int max_win_h
= config
.max_win_h
;
476 switch (config
.orientation
) {
479 GP_SWAP(win_w
, win_h
);
480 GP_SWAP(max_win_w
, max_win_h
);
486 if (params
->zoom_manual
) {
487 if (config
.win_strategy
== ZOOM_WIN_RESIZABLE
) {
488 win_w
= GP_MIN(max_win_w
, img_w
* params
->zoom_rat
+ 0.5);
489 win_h
= GP_MIN(max_win_h
, img_h
* params
->zoom_rat
+ 0.5);
491 switch (config
.orientation
) {
494 GP_BackendResize(backend
, win_h
, win_w
);
497 GP_BackendResize(backend
, win_w
, win_h
);
500 return params
->zoom_rat
;
504 if (config
.win_strategy
== ZOOM_WIN_RESIZABLE
) {
505 win_w
= GP_MIN(max_win_w
, img_w
);
506 win_h
= GP_MIN(max_win_h
, img_h
);
509 * Image is larger than screen and downscale is enabled ->
510 * resize window to match image ratio.
512 if ((win_w
!= img_w
|| win_h
!= img_h
) &&
513 config
.zoom_strategy
& ZOOM_IMAGE_DOWNSCALE
) {
515 w_rat
= 1.00 * win_w
/ img_w
;
516 h_rat
= 1.00 * win_h
/ img_h
;
524 rat
= GP_MIN(h_rat
, w_rat
);
526 win_w
= rat
* img_w
+ 0.5;
527 win_h
= rat
* img_h
+ 0.5;
530 switch (config
.orientation
) {
533 GP_BackendResize(backend
, win_h
, win_w
);
536 GP_BackendResize(backend
, win_w
, win_h
);
540 if (img_w
<= win_w
&& img_h
<= win_h
) {
541 if (!(config
.zoom_strategy
& ZOOM_IMAGE_UPSCALE
))
544 if (!(config
.zoom_strategy
& ZOOM_IMAGE_DOWNSCALE
))
549 w_rat
= 1.00 * win_w
/ img_w
;
550 h_rat
= 1.00 * win_h
/ img_h
;
552 return GP_MIN(w_rat
, h_rat
);
555 static void *image_loader(void *ptr
)
557 struct loader_params
*params
= ptr
;
558 struct cpu_timer sum_timer
;
559 GP_Context
*img
, *orig_img
, *context
= backend
->context
;
561 cpu_timer_start(&sum_timer
, "sum");
563 show_progress
= config
.show_progress
|| params
->show_progress_once
;
564 params
->show_progress_once
= 0;
566 if ((orig_img
= load_image(0)) == NULL
) {
574 params
->zoom_rat
= calc_img_size(params
, orig_img
->w
, orig_img
->h
,
575 context
->w
, context
->h
);
577 w
= orig_img
->w
* params
->zoom_rat
+ 0.5;
578 h
= orig_img
->h
* params
->zoom_rat
+ 0.5;
580 /* Special case => no need to resize */
581 if (w
== orig_img
->w
&& h
== orig_img
->h
) {
586 img
= load_resized_image(params
, w
, h
);
594 update_display(params
, img
, orig_img
);
595 cpu_timer_stop(&sum_timer
);
602 static pthread_t loader_thread
= (pthread_t
)0;
604 static void stop_loader(void)
608 pthread_join(loader_thread
, NULL
);
609 loader_thread
= (pthread_t
)0;
614 static void show_image(struct loader_params
*params
)
618 /* stop previous loader thread */
623 ret
= pthread_create(&loader_thread
, NULL
, image_loader
, (void*)params
);
626 fprintf(stderr
, "Failed to start thread: %s\n", strerror(ret
));
627 GP_BackendExit(backend
);
632 static void image_seek(struct loader_params
*params
,
633 enum img_seek_offset offset
, int whence
)
636 * We need to stop loader first because image loader seek may free
637 * image we are currently resamling.
640 params
->zoom_manual
= 0;
641 image_loader_seek(offset
, whence
);
645 static void set_zoom_offset(struct loader_params
*params
, int dx
, int dy
)
647 params
->zoom_manual
= 1;
648 params
->zoom_x_offset
+= dx
;
649 params
->zoom_y_offset
+= dy
;
653 static void zoom_mul(struct loader_params
*params
, float mul
)
655 params
->zoom_manual
= 1;
656 params
->zoom_rat
*= mul
;
660 static void zoom_set(struct loader_params
*params
, float mul
)
662 params
->zoom_manual
= 1;
663 params
->zoom_rat
= mul
;
667 static void sighandler(int signo
)
670 GP_BackendExit(backend
);
672 fprintf(stderr
, "Got signal %i\n", signo
);
677 static void init_backend(const char *backend_opts
)
679 backend
= GP_BackendInit(backend_opts
, "Spiv");
681 if (backend
== NULL
) {
682 fprintf(stderr
, "Failed to initalize backend '%s'\n", backend_opts
);
686 if (config
.full_screen
) {
687 if (GP_BackendIsX11(backend
))
688 GP_BackendX11RequestFullscreen(backend
, 2);
692 #define RESIZED_CACHE_MAX 400 * 1024
693 #define ORIG_CACHE_MAX 80 * 1024
696 * Figure out cache size depending on the size of RAM.
698 * Initialize cache, image loader.
700 static int init_loader(struct loader_params
*params
, const char **argv
)
702 size_t size
= image_cache_get_ram_size();
703 size_t resized_size
= size
/10;
704 size_t orig_size
= size
/50;
706 if (resized_size
> RESIZED_CACHE_MAX
)
707 resized_size
= RESIZED_CACHE_MAX
;
709 if (orig_size
> ORIG_CACHE_MAX
)
710 orig_size
= ORIG_CACHE_MAX
;
712 GP_DEBUG(1, "Resized cache size = %zukB", resized_size
);
713 GP_DEBUG(1, "Orig cache size = %zukB", orig_size
);
715 if (image_loader_init(argv
, orig_size
))
718 params
->img_resized_cache
= image_cache_create(resized_size
);
723 static uint32_t timer_callback(GP_Timer
*self
)
725 struct loader_params
*params
= self
->priv
;
726 static int retries
= 0;
729 * If loader is still running, reschedule after 20ms
731 * If more than two seconds has passed, try load next
733 if (loader_running
&& retries
< 100) {
734 printf("Loader still running %ims\n", 20 * retries
);
742 * We need to stop loader first because image loader seek may free
743 * image we are currently resamling.
746 image_loader_seek(IMG_CUR
, 1);
749 return params
->sleep_ms
;
752 int main(int argc
, char *argv
[])
754 GP_Context
*context
= NULL
;
758 struct loader_params params
= {
759 .show_progress_once
= 0,
761 .resampling_method
= GP_INTERP_LINEAR_LF_INT
,
763 .img_resized_cache
= NULL
,
768 GP_TIMER_DECLARE(timer
, 0, 0, "Slideshow", timer_callback
, ¶ms
);
770 if (access("/etc/spiv.conf", R_OK
) == 0)
771 spiv_config_load("/etc/spiv.conf");
773 if (getenv("HOME")) {
776 snprintf(buf
, sizeof(buf
), "%s/%s", getenv("HOME"), ".spiv");
778 if (access(buf
, R_OK
) == 0)
779 spiv_config_load(buf
);
782 opts
= spiv_config_parse_args(argc
, argv
);
789 cpu_timer_switch(config
.timers
);
790 params
.sleep_ms
= 1000 * config
.slideshow_delay
+ 0.5;
794 if (!strcmp(config
.backend_init
, "help")) {
795 init_backend(config
.backend_init
);
799 fprintf(stderr
, "Requires path to at least one image\n\n");
804 signal(SIGINT
, sighandler
);
805 signal(SIGSEGV
, sighandler
);
806 signal(SIGBUS
, sighandler
);
807 signal(SIGABRT
, sighandler
);
809 if (init_loader(¶ms
, (const char **)argv
+ opts
))
812 init_backend(config
.backend_init
);
814 if (config
.emul_type
!= GP_PIXEL_UNKNOWN
) {
815 backend
= GP_BackendVirtualInit(backend
, config
.emul_type
,
816 GP_BACKEND_CALL_EXIT
);
819 context
= backend
->context
;
821 black_pixel
= GP_RGBToContextPixel(0x00, 0x00, 0x00, context
);
822 white_pixel
= GP_RGBToContextPixel(0xff, 0xff, 0xff, context
);
823 gray_pixel
= GP_RGBToContextPixel(0x33, 0x33, 0x33, context
);
825 GP_Fill(context
, black_pixel
);
826 GP_BackendFlip(backend
);
828 params
.show_progress_once
= 1;
831 if (params
.sleep_ms
) {
832 timer
.expires
= params
.sleep_ms
;
833 GP_BackendAddTimer(backend
, &timer
);
839 while (GP_BackendWaitEvent(backend
, &ev
)) {
841 shift_flag
= GP_EventGetKey(&ev
, GP_KEY_LEFT_SHIFT
) ||
842 GP_EventGetKey(&ev
, GP_KEY_RIGHT_SHIFT
);
847 case GP_EV_REL_WHEEL
:
855 if (GP_EventGetKey(&ev
, GP_BTN_LEFT
))
856 set_zoom_offset(¶ms
,
863 if (ev
.code
!= GP_EV_KEY_DOWN
)
866 switch (ev
.val
.key
.key
) {
872 if (GP_BackendIsX11(backend
))
873 GP_BackendX11RequestFullscreen(backend
, 2);
876 config
.show_info
= !config
.show_info
;
878 params
.show_progress_once
= 1;
882 config
.show_progress
= !config
.show_progress
;
885 config
.orientation
++;
887 if (config
.orientation
> ROTATE_270
)
888 config
.orientation
= 0;
890 params
.show_progress_once
= 1;
894 if (config
.orientation
> 0)
895 config
.orientation
--;
897 config
.orientation
= ROTATE_270
;
899 params
.show_progress_once
= 1;
903 if (params
.sleep_ms
) {
904 if (GP_BackendTimersInQueue(backend
)) {
905 GP_BackendRemTimer(backend
, &timer
);
907 timer
.expires
= params
.sleep_ms
;
908 GP_BackendAddTimer(backend
, &timer
);
912 case GP_KEY_RIGHT_BRACE
:
913 params
.resampling_method
++;
915 if (params
.resampling_method
> GP_INTERP_MAX
)
916 params
.resampling_method
= 0;
917 if (params
.resampling_method
== GP_INTERP_CUBIC
)
918 params
.resampling_method
++;
919 if (params
.resampling_method
== GP_INTERP_LINEAR_LF_INT
) {
920 params
.use_low_pass
= 0;
921 params
.show_nn_first
= 0;
923 params
.use_low_pass
= 1;
924 params
.show_nn_first
= 1;
927 params
.show_progress_once
= 1;
930 case GP_KEY_LEFT_BRACE
:
931 if (params
.resampling_method
== 0)
932 params
.resampling_method
= GP_INTERP_MAX
;
934 params
.resampling_method
--;
935 if (params
.resampling_method
== GP_INTERP_CUBIC
)
936 params
.resampling_method
--;
937 if (params
.resampling_method
== GP_INTERP_LINEAR_LF_INT
) {
938 params
.use_low_pass
= 0;
939 params
.show_nn_first
= 0;
941 params
.use_low_pass
= 1;
942 params
.show_nn_first
= 1;
945 params
.show_progress_once
= 1;
949 params
.use_low_pass
= !params
.use_low_pass
;
951 params
.show_progress_once
= 1;
955 image_cache_drop(params
.img_resized_cache
);
956 image_loader_drop_cache();
960 params
.show_progress_once
= 1;
964 config_upscale_toggle();
965 params
.show_progress_once
= 1;
969 config_downscale_toggle();
970 params
.show_progress_once
= 1;
977 image_cache_destroy(params
.img_resized_cache
);
978 image_loader_destroy();
979 GP_BackendExit(backend
);
983 params
.show_progress_once
= 1;
984 image_seek(¶ms
, IMG_DIR
, -1);
986 case GP_KEY_PAGE_DOWN
:
987 params
.show_progress_once
= 1;
988 image_seek(¶ms
, IMG_DIR
, 1);
991 params
.show_progress_once
= 1;
992 image_seek(¶ms
, IMG_FIRST
, 0);
995 params
.show_progress_once
= 1;
996 image_seek(¶ms
, IMG_LAST
, 0);
1000 set_zoom_offset(¶ms
, 1, 0);
1002 set_zoom_offset(¶ms
, 10, 0);
1007 set_zoom_offset(¶ms
, -1, 0);
1009 set_zoom_offset(¶ms
, -10, 0);
1013 set_zoom_offset(¶ms
, 0, -1);
1015 set_zoom_offset(¶ms
, 0, -10);
1019 set_zoom_offset(¶ms
, 0, 1);
1021 set_zoom_offset(¶ms
, 0, 10);
1025 params
.show_progress_once
= 1;
1027 image_seek(¶ms
, IMG_CUR
, 10);
1029 image_seek(¶ms
, IMG_CUR
, 1);
1032 case GP_KEY_BACKSPACE
:
1033 params
.show_progress_once
= 1;
1035 image_seek(¶ms
, IMG_CUR
, -10);
1037 image_seek(¶ms
, IMG_CUR
, -1);
1039 case GP_KEY_1
... GP_KEY_9
: {
1040 float val
= ev
.val
.key
.key
- GP_KEY_1
+ 1;
1045 zoom_set(¶ms
, val
);
1049 zoom_set(¶ms
, 10);
1051 zoom_set(¶ms
, 0.1);
1053 case GP_KEY_KP_PLUS
:
1055 params
.show_progress_once
= 1;
1057 zoom_mul(¶ms
, 1.1);
1059 zoom_mul(¶ms
, 1.5);
1061 case GP_KEY_KP_MINUS
:
1063 params
.show_progress_once
= 1;
1065 zoom_mul(¶ms
, 1/1.1);
1067 zoom_mul(¶ms
, 1/1.5);
1069 case GP_KEY_F1
... GP_KEY_F10
:
1070 image_action_run(ev
.val
.key
.key
- GP_KEY_F1
+ 1,
1071 image_loader_img_path());
1077 case GP_EV_SYS_RESIZE
:
1078 /* stop loader thread before resizing backend buffer */
1080 GP_BackendResizeAck(backend
);
1081 GP_Fill(backend
->context
, 0);
1082 params
.show_progress_once
= 1;
1083 show_image(¶ms
);
1085 case GP_EV_SYS_QUIT
:
1086 GP_BackendExit(backend
);