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 yet Powerful Image Viewer.
37 #include "image_cache.h"
38 #include "image_list.h"
39 #include "image_loader.h"
40 #include "image_actions.h"
41 #include "spiv_help.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;
57 * Resize image to fit current size of the window.
62 * Use zoom set in zoom float and zoom offsets.
67 * Fixed zoom but spiv tries to change
68 * the window size to fit the image size
73 * Do not upscale images but downscale them
74 * if they are too big.
79 struct loader_params
{
80 /* current resize ratio */
83 /* show loader progress */
85 long show_progress_once
:2;
86 /* show image info in topleft corner */
88 /* use nearest neighbour resampling first */
90 /* use dithering when blitting to display */
92 /* use low pass before resampling */
94 /* image orientation 0, 90, 180, 270 */
96 /* resampling method */
97 int resampling_method
;
102 /* offset in pixels */
103 unsigned int zoom_x_offset
;
104 unsigned int zoom_y_offset
;
107 enum zoom_type zoom_type
;
110 /* caches for loaded images */
111 struct image_cache
*img_resized_cache
;
114 static int image_loader_callback(GP_ProgressCallback
*self
)
116 static GP_Size size
= 0;
117 GP_Context
*c
= backend
->context
;
127 snprintf(buf
, sizeof(buf
), "%s ... %-3.1f%%",
128 (const char*)self
->priv
, self
->percentage
);
130 int align
= GP_ALIGN_CENTER
|GP_VALIGN_ABOVE
;
132 size
= GP_TextWidth(NULL
, buf
);
134 int start
= c
->w
/2 - size
/2 - 10;
135 int end
= c
->w
/2 + size
/2 + 10;
136 int middle
= start
+ (end
- start
) * self
->percentage
/ 100;
137 int top
= c
->h
- GP_TextHeight(NULL
) - 11;
139 GP_FillRectXYXY(c
, start
, c
->h
- 1, middle
, top
, gray_pixel
);
140 GP_FillRectXYXY(c
, middle
, c
->h
- 1, end
, top
, black_pixel
);
142 GP_Text(c
, NULL
, c
->w
/2, c
->h
- 5, align
,
143 white_pixel
, black_pixel
, buf
);
145 GP_BackendUpdateRect(backend
, start
, c
->h
- 1, end
, top
);
150 static GP_Context
*load_image(int elevate
);
153 * Ask backend to resize window may not be implemented or authorized. If
154 * backend (window) is resized we will get SYS_RESIZE event, see the main event
157 static void resize_backend(float ratio
, int shift_flag
)
159 GP_Context
*img
= load_image(1);
162 ratio
= 1.00 / ratio
;
164 unsigned int w
= img
->w
* ratio
+ 0.5;
165 unsigned int h
= img
->h
* ratio
+ 0.5;
167 GP_BackendResize(backend
, w
, h
);
171 static float calc_img_size(struct loader_params
*params
,
172 uint32_t img_w
, uint32_t img_h
,
173 uint32_t src_w
, uint32_t src_h
)
178 switch (params
->zoom_type
) {
179 case ZOOM_FIT_DOWNSCALE
:
180 if (img_w
<= src_w
&& img_h
<= src_h
)
183 w_rat
= 1.00 * src_w
/ img_w
;
184 h_rat
= 1.00 * src_h
/ img_h
;
185 return GP_MIN(w_rat
, h_rat
);
189 resize_backend(params
->zoom
, 0);
196 static const char *img_name(const char *img_path
)
198 int i
, len
= strlen(img_path
);
200 for (i
= len
- 1; i
> 0; i
--) {
201 if (img_path
[i
] == '/')
202 return &img_path
[i
+1];
208 static void set_caption(const char *path
, float rat
)
212 snprintf(buf
, sizeof(buf
), "Spiv ~ %s 1:%3.3f", img_name(path
), rat
);
214 GP_BackendSetCaption(backend
, buf
);
220 static GP_Context
*load_image(int elevate
)
223 GP_ProgressCallback callback
= {.callback
= image_loader_callback
,
224 .priv
= "Loading image"};
226 img
= image_loader_get_image(&callback
, elevate
);
231 GP_Context
*ctx
= backend
->context
;
233 GP_Fill(ctx
, black_pixel
);
234 GP_Print(ctx
, NULL
, ctx
->w
/2, ctx
->h
/2 - 10,
235 GP_ALIGN_CENTER
|GP_VALIGN_CENTER
, white_pixel
, black_pixel
,
236 "'%s'", image_loader_img_path());
237 GP_Print(ctx
, NULL
, ctx
->w
/2, ctx
->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
);
246 * Fill context with chessboard-like pattern.
248 static void pattern_fill(GP_Context
*ctx
, unsigned int x0
, unsigned int y0
,
249 unsigned int w
, unsigned int h
)
253 GP_Pixel g1
= GP_RGBToContextPixel(0x64, 0x64, 0x64, ctx
);
254 GP_Pixel g2
= GP_RGBToContextPixel(0x80, 0x80, 0x80, ctx
);
256 unsigned int wm
= w
/10 < 10 ? 10 : w
/10;
257 unsigned int hm
= h
/10 < 10 ? 10 : h
/10;
258 unsigned int wt
= w
/20 < 5 ? 5 : w
/20;
259 unsigned int ht
= h
/20 < 5 ? 5 : h
/20;
261 for (y
= 0; y
< h
; y
++) {
262 for (x
= 0; x
< w
; x
++) {
265 if ((x
% wm
< wt
) ^ (y
% hm
< ht
))
270 GP_PutPixel(ctx
, x0
+ x
, y0
+ y
, pix
);
275 static void update_display(struct loader_params
*params
, GP_Context
*img
,
276 GP_Context
*orig_img
)
278 GP_Context
*context
= backend
->context
;
279 struct cpu_timer timer
;
280 GP_ProgressCallback callback
= {.callback
= image_loader_callback
};
282 switch (params
->rotate
) {
286 callback
.priv
= "Rotating image (90)";
287 img
= GP_FilterRotate90Alloc(img
, &callback
);
290 callback
.priv
= "Rotating image (180)";
291 img
= GP_FilterRotate180Alloc(img
, &callback
);
294 callback
.priv
= "Rotating image (270)";
295 img
= GP_FilterRotate270Alloc(img
, &callback
);
305 switch (params
->zoom_type
) {
306 case ZOOM_FIT_DOWNSCALE
:
308 cx
= (context
->w
- img
->w
)/2;
309 cy
= (context
->h
- img
->h
)/2;
313 cx
= params
->zoom_x_offset
;
314 cy
= params
->zoom_y_offset
;
318 GP_Context sub_display
;
320 cpu_timer_start(&timer
, "Blitting");
322 if (params
->use_dithering
) {
323 callback
.priv
= "Dithering";
324 GP_SubContext(context
, &sub_display
, cx
, cy
, img
->w
, img
->h
);
325 GP_FilterFloydSteinberg(img
, &sub_display
, NULL
);
326 // GP_FilterHilbertPeano(img, &sub_display, NULL);
328 if (GP_PixelHasFlags(img
->pixel_type
, GP_PIXEL_HAS_ALPHA
))
329 pattern_fill(context
, cx
, cy
, img
->w
, img
->h
);
331 GP_Blit_Clipped(img
, 0, 0, img
->w
, img
->h
, context
, cx
, cy
);
334 cpu_timer_stop(&timer
);
336 /* clean up the rest of the display */
337 GP_FillRectXYWH(context
, 0, 0, cx
, context
->h
, black_pixel
);
338 GP_FillRectXYWH(context
, 0, 0, context
->w
, cy
, black_pixel
);
340 int w
= context
->w
- img
->w
- cx
;
343 GP_FillRectXYWH(context
, img
->w
+ cx
, 0, w
, context
->h
, black_pixel
);
345 int h
= context
->h
- img
->h
- cy
;
348 GP_FillRectXYWH(context
, 0, img
->h
+ cy
, context
->w
, h
, black_pixel
);
350 const char *img_path
= image_loader_img_path();
352 set_caption(img_path
, params
->rat
);
354 if (!params
->show_info
)
357 GP_Size th
= GP_TextHeight(NULL
);
359 GP_Print(context
, NULL
, 11, 11, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
360 black_pixel
, white_pixel
, "%ux%u (%ux%u) 1:%3.3f",
361 img
->w
, img
->h
, orig_img
->w
, orig_img
->h
, params
->rat
);
363 GP_Print(context
, NULL
, 10, 10, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
364 white_pixel
, black_pixel
, "%ux%u (%ux%u) 1:%3.3f",
365 img
->w
, img
->h
, orig_img
->w
, orig_img
->h
, params
->rat
);
367 GP_Print(context
, NULL
, 11, 13 + th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
368 black_pixel
, white_pixel
, "%s", img_name(img_path
));
370 GP_Print(context
, NULL
, 10, 12 + th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
371 white_pixel
, black_pixel
, "%s", img_name(img_path
));
373 GP_Print(context
, NULL
, 11, 13 + 2 * th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
374 black_pixel
, white_pixel
, "%s%s",
375 params
->use_low_pass
&& params
->rat
< 1 ? "Gaussian LP + " : "",
376 GP_InterpolationTypeName(params
->resampling_method
));
378 GP_Print(context
, NULL
, 10, 14 + 2 * th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
379 white_pixel
, black_pixel
, "%s%s",
380 params
->use_low_pass
&& params
->rat
< 1 ? "Gaussian LP + " : "",
381 GP_InterpolationTypeName(params
->resampling_method
));
383 unsigned int count
= image_loader_count();
384 unsigned int pos
= image_loader_pos() + 1;
386 GP_Print(context
, NULL
, 11, 17 + 3 * th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
387 black_pixel
, white_pixel
, "%u of %u", pos
, count
);
389 GP_Print(context
, NULL
, 10, 16 + 3 * th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
390 white_pixel
, black_pixel
, "%u of %u", pos
, count
);
392 if (!image_loader_is_in_dir())
395 unsigned int dir_count
= image_loader_dir_count();
396 unsigned int dir_pos
= image_loader_dir_pos() + 1;
398 GP_Print(context
, NULL
, 11, 19 + 4 * th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
399 black_pixel
, white_pixel
, "%u of %u in directory", dir_pos
, dir_count
);
401 GP_Print(context
, NULL
, 10, 18 + 4* th
, GP_ALIGN_RIGHT
|GP_VALIGN_BOTTOM
,
402 white_pixel
, black_pixel
, "%u of %u in directory", dir_pos
, dir_count
);
407 GP_BackendFlip(backend
);
410 GP_Context
*load_resized_image(struct loader_params
*params
, GP_Size w
, GP_Size h
)
412 GP_Context
*img
, *res
= NULL
;
413 struct cpu_timer timer
;
414 GP_ProgressCallback callback
= {.callback
= image_loader_callback
};
416 const char *img_path
= image_loader_img_path();
418 /* Try to get resized cached image */
419 img
= image_cache_get2(params
->img_resized_cache
, 1, "%s %ux%u r%i l%i",
420 img_path
, w
, h
, params
->resampling_method
,
421 params
->use_low_pass
);
426 /* Otherwise load image and resize it */
427 if ((img
= load_image(1)) == NULL
)
430 if (params
->show_nn_first
) {
431 /* Do simple interpolation and blit the result */
432 GP_Context
*nn
= GP_FilterResizeNNAlloc(img
, w
, h
, NULL
);
434 update_display(params
, nn
, img
);
439 /* Do low pass filter */
440 if (params
->use_low_pass
&& params
->rat
< 1) {
441 cpu_timer_start(&timer
, "Blur");
442 callback
.priv
= "Blurring Image";
444 res
= GP_FilterGaussianBlurAlloc(img
, 0.4/params
->rat
,
445 0.4/params
->rat
, &callback
);
452 cpu_timer_stop(&timer
);
455 // img->gamma = GP_GammaAcquire(img->pixel_type, 0.45);
457 cpu_timer_start(&timer
, "Resampling");
458 callback
.priv
= "Resampling Image";
459 GP_Context
*i1
= GP_FilterResizeAlloc(img
, w
, h
, params
->resampling_method
, &callback
);
460 // img->gamma = NULL;
461 // GP_Context *i2 = GP_FilterResizeAlloc(img, w, h, params->resampling_method, &callback);
462 // img = GP_FilterDifferenceAlloc(i2, i1, NULL);
463 // img = GP_FilterInvert(img, NULL, NULL);
465 // if (params->resampling_method == GP_INTERP_CUBIC_INT)
466 // GP_FilterEdgeSharpening(img, img, 0.2, NULL);
467 cpu_timer_stop(&timer
);
470 if (params->rat > 1.5) {
471 cpu_timer_start(&timer, "Sharpening");
472 callback.priv = "Sharpening";
473 GP_FilterEdgeSharpening(i1, i1, 0.1, &callback);
474 cpu_timer_stop(&timer);
478 /* Free low passed context if needed */
484 image_cache_put2(params
->img_resized_cache
, img
, "%s %ux%u r%i l%i",
485 img_path
, w
, h
, params
->resampling_method
,
486 params
->use_low_pass
);
491 static void *image_loader(void *ptr
)
493 struct loader_params
*params
= ptr
;
494 struct cpu_timer sum_timer
;
495 GP_Context
*img
, *orig_img
, *context
= backend
->context
;
497 cpu_timer_start(&sum_timer
, "sum");
499 show_progress
= params
->show_progress
|| params
->show_progress_once
;
500 params
->show_progress_once
= 0;
502 /* Figure out rotation */
505 switch (params
->rotate
) {
519 if ((orig_img
= load_image(0)) == NULL
) {
524 params
->rat
= calc_img_size(params
, orig_img
->w
, orig_img
->h
, w
, h
);
526 /* Special case => no need to resize */
527 if (params
->rat
== 1.0) {
532 w
= orig_img
->w
* params
->rat
+ 0.5;
533 h
= orig_img
->h
* params
->rat
+ 0.5;
535 img
= load_resized_image(params
, w
, h
);
542 //image_cache_print(params->img_resized_cache);
544 update_display(params
, img
, orig_img
);
545 cpu_timer_stop(&sum_timer
);
552 static pthread_t loader_thread
= (pthread_t
)0;
554 static void stop_loader(void)
558 pthread_join(loader_thread
, NULL
);
559 loader_thread
= (pthread_t
)0;
564 static void show_image(struct loader_params
*params
)
568 /* stop previous loader thread */
573 ret
= pthread_create(&loader_thread
, NULL
, image_loader
, (void*)params
);
576 fprintf(stderr
, "Failed to start thread: %s\n", strerror(ret
));
577 GP_BackendExit(backend
);
582 static void image_seek(struct loader_params
*params
,
583 enum img_seek_offset offset
, int whence
)
586 * We need to stop loader first because image loader seek may free
587 * image we are currently resamling.
590 image_loader_seek(offset
, whence
);
594 static void set_zoom_offset(struct loader_params
*params
, int dx
, int dy
)
596 params
->zoom_x_offset
+= dx
;
597 params
->zoom_y_offset
+= dy
;
601 static void zoom_mul(struct loader_params
*params
, float mul
)
607 static void sighandler(int signo
)
610 GP_BackendExit(backend
);
612 fprintf(stderr
, "Got signal %i\n", signo
);
617 static void init_backend(const char *backend_opts
)
619 backend
= GP_BackendInit(backend_opts
, "Spiv", stderr
);
621 if (backend
== NULL
) {
622 fprintf(stderr
, "Failed to initalize backend '%s'\n", backend_opts
);
628 * Figure out cache size depending on the size of RAM.
630 * Initialize cache, image loader.
632 static int init_loader(struct loader_params
*params
, const char **argv
)
634 size_t size
= image_cache_get_ram_size();
635 unsigned int resized_size
= (1024 * size
)/10;
636 unsigned int orig_size
= (1024 * size
)/50;
638 if (resized_size
> 100 * 1024 * 1024)
639 resized_size
= 100 * 1024 * 1024;
641 GP_DEBUG(1, "Resized cache size = %u", resized_size
);
643 if (image_loader_init(argv
, orig_size
))
646 params
->img_resized_cache
= image_cache_create(resized_size
);
651 static uint32_t timer_callback(GP_Timer
*self
)
653 struct loader_params
*params
= self
->priv
;
654 static int retries
= 0;
657 * If loader is still running, reschedule after 20ms
659 * If more than two seconds has passed, try load next
661 if (loader_running
&& retries
< 100) {
662 printf("Loader still running %ims\n", 20 * retries
);
670 * We need to stop loader first because image loader seek may free
671 * image we are currently resamling.
674 image_loader_seek(IMG_CUR
, 1);
677 return params
->sleep_ms
;
680 int main(int argc
, char *argv
[])
682 GP_Context
*context
= NULL
;
683 const char *backend_opts
= "X11";
686 GP_PixelType emul_type
= GP_PIXEL_UNKNOWN
;
688 struct loader_params params
= {
690 .show_progress_once
= 0,
695 .resampling_method
= GP_INTERP_LINEAR_LF_INT
,
697 .zoom_type
= ZOOM_FIT
,
700 .img_resized_cache
= NULL
,
705 GP_TIMER_DECLARE(timer
, 0, 0, "Slideshow", timer_callback
, ¶ms
);
707 while ((opt
= getopt(argc
, argv
, "b:ce:fhIPr:s:tz:0:1:2:3:4:5:6:7:8:9:")) != -1) {
710 params
.show_info
= 1;
713 params
.show_progress
= 1;
716 params
.use_dithering
= 1;
719 params
.sleep_ms
= 1000 * atof(optarg
) + 0.5;
722 params
.resampling_method
= GP_INTERP_CUBIC_INT
;
723 /* Cubic resampling needs low pass */
724 params
.use_low_pass
= 1;
725 /* Cubic resampling is slow, show nn first */
726 params
.show_nn_first
= 1;
729 emul_type
= GP_PixelTypeByName(optarg
);
731 if (emul_type
== GP_PIXEL_UNKNOWN
) {
732 fprintf(stderr
, "Invalid pixel type '%s'\n", optarg
);
737 if (!strcmp(optarg
, "90"))
739 else if (!strcmp(optarg
, "180"))
741 else if (!strcmp(optarg
, "270"))
744 backend_opts
= optarg
;
756 params
.zoom_type
= ZOOM_FIXED
;
759 params
.zoom_type
= ZOOM_FIXED_WIN
;
764 /* -0 is mapped to action 10 */
765 image_action_set(9, optarg
);
768 image_action_set(opt
- '1', optarg
);
771 fprintf(stderr
, "Invalid paramter '%c'\n", opt
);
777 if (optind
>= argc
) {
778 fprintf(stderr
, "Requires path to at least one image\n\n");
783 signal(SIGINT
, sighandler
);
784 signal(SIGSEGV
, sighandler
);
785 signal(SIGBUS
, sighandler
);
786 signal(SIGABRT
, sighandler
);
788 if (init_loader(¶ms
, (const char **)argv
+ optind
))
791 init_backend(backend_opts
);
793 if (emul_type
!= GP_PIXEL_UNKNOWN
)
794 backend
= GP_BackendVirtualInit(backend
, emul_type
, GP_BACKEND_CALL_EXIT
);
796 context
= backend
->context
;
798 black_pixel
= GP_ColorToContextPixel(GP_COL_BLACK
, context
);
799 white_pixel
= GP_ColorToContextPixel(GP_COL_WHITE
, context
);
800 gray_pixel
= GP_RGBToContextPixel(0x33, 0x33, 0x33, context
);
802 GP_Fill(context
, black_pixel
);
803 GP_BackendFlip(backend
);
805 params
.show_progress_once
= 1;
808 if (params
.sleep_ms
) {
809 timer
.expires
= params
.sleep_ms
;
810 GP_BackendAddTimer(backend
, &timer
);
816 while (GP_BackendWaitEvent(backend
, &ev
)) {
818 shift_flag
= GP_EventGetKey(&ev
, GP_KEY_LEFT_SHIFT
) ||
819 GP_EventGetKey(&ev
, GP_KEY_RIGHT_SHIFT
);
824 case GP_EV_REL_WHEEL
:
832 if (GP_EventGetKey(&ev
, GP_BTN_LEFT
))
833 set_zoom_offset(¶ms
,
840 if (ev
.code
!= GP_EV_KEY_DOWN
)
843 switch (ev
.val
.key
.key
) {
849 if (GP_BackendIsX11(backend
))
850 GP_BackendX11RequestFullscreen(backend
, 2);
853 params
.show_info
= !params
.show_info
;
855 params
.show_progress_once
= 1;
859 params
.show_progress
= !params
.show_progress
;
863 if (params
.rotate
> 270)
866 params
.show_progress_once
= 1;
870 if (params
.sleep_ms
) {
871 if (GP_BackendTimersInQueue(backend
)) {
872 GP_BackendRemTimer(backend
, &timer
);
874 timer
.expires
= params
.sleep_ms
;
875 GP_BackendAddTimer(backend
, &timer
);
879 case GP_KEY_RIGHT_BRACE
:
880 params
.resampling_method
++;
882 if (params
.resampling_method
> GP_INTERP_MAX
)
883 params
.resampling_method
= 0;
884 if (params
.resampling_method
== GP_INTERP_CUBIC
)
885 params
.resampling_method
++;
886 if (params
.resampling_method
== GP_INTERP_LINEAR_LF_INT
) {
887 params
.use_low_pass
= 0;
888 params
.show_nn_first
= 0;
890 params
.use_low_pass
= 1;
891 params
.show_nn_first
= 1;
894 params
.show_progress_once
= 1;
897 case GP_KEY_LEFT_BRACE
:
898 if (params
.resampling_method
== 0)
899 params
.resampling_method
= GP_INTERP_MAX
;
901 params
.resampling_method
--;
902 if (params
.resampling_method
== GP_INTERP_CUBIC
)
903 params
.resampling_method
--;
904 if (params
.resampling_method
== GP_INTERP_LINEAR_LF_INT
) {
905 params
.use_low_pass
= 0;
906 params
.show_nn_first
= 0;
908 params
.use_low_pass
= 1;
909 params
.show_nn_first
= 1;
912 params
.show_progress_once
= 1;
916 params
.use_low_pass
= !params
.use_low_pass
;
918 params
.show_progress_once
= 1;
922 image_cache_drop(params
.img_resized_cache
);
923 image_loader_drop_cache();
929 image_cache_destroy(params
.img_resized_cache
);
930 image_loader_destroy();
931 GP_BackendExit(backend
);
935 params
.show_progress_once
= 1;
936 image_seek(¶ms
, IMG_DIR
, -1);
938 case GP_KEY_PAGE_DOWN
:
939 params
.show_progress_once
= 1;
940 image_seek(¶ms
, IMG_DIR
, 1);
943 params
.show_progress_once
= 1;
944 image_seek(¶ms
, IMG_FIRST
, 0);
947 params
.show_progress_once
= 1;
948 image_seek(¶ms
, IMG_LAST
, 0);
952 set_zoom_offset(¶ms
, 1, 0);
954 set_zoom_offset(¶ms
, 10, 0);
959 set_zoom_offset(¶ms
, -1, 0);
961 set_zoom_offset(¶ms
, -10, 0);
965 set_zoom_offset(¶ms
, 0, -1);
967 set_zoom_offset(¶ms
, 0, -10);
971 set_zoom_offset(¶ms
, 0, 1);
973 set_zoom_offset(¶ms
, 0, 10);
977 params
.show_progress_once
= 1;
979 image_seek(¶ms
, IMG_CUR
, 10);
981 image_seek(¶ms
, IMG_CUR
, 1);
984 case GP_KEY_BACKSPACE
:
985 params
.show_progress_once
= 1;
987 image_seek(¶ms
, IMG_CUR
, -10);
989 image_seek(¶ms
, IMG_CUR
, -1);
991 case GP_KEY_1
... GP_KEY_9
: {
992 int val
= ev
.val
.key
.key
- GP_KEY_1
+ 1;
993 resize_backend(val
, shift_flag
);
996 resize_backend(10, shift_flag
);
1000 params
.show_progress_once
= 1;
1001 zoom_mul(¶ms
, 1.5);
1003 case GP_KEY_KP_MINUS
:
1005 params
.show_progress_once
= 1;
1006 zoom_mul(¶ms
, 1/1.5);
1008 case GP_KEY_F1
... GP_KEY_F10
:
1009 image_action_run(ev
.val
.key
.key
- GP_KEY_F1
,
1010 image_loader_img_path());
1016 case GP_EV_SYS_RESIZE
:
1017 /* stop loader thread before resizing backend buffer */
1019 GP_BackendResizeAck(backend
);
1020 GP_Fill(backend
->context
, 0);
1021 params
.show_progress_once
= 1;
1022 show_image(¶ms
);
1024 case GP_EV_SYS_QUIT
:
1025 GP_BackendExit(backend
);