Skip to content

Commit 6cfdf56

Browse files
committed
improve zooming feature with logarithmic scale
Zooming now uses a logarithmic scale, and the events for zooming in/out gravitate to powers of 2, such as 50%, 100%, 200%, etc. The option `--zoom-step` has been changed to represent the number of zoom steps between powers of 2, with the default being 3 steps.
1 parent 421273d commit 6cfdf56

7 files changed

Lines changed: 65 additions & 50 deletions

File tree

man/feh.pre

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,12 +1003,12 @@ zoom the image like the
10031003
.Cm --bg-fill
10041004
mode.
10051005
.
1006-
.It Cm --zoom-step Ar percent
1006+
.It Cm --zoom-step Ar index
10071007
.
1008-
Zoom images in and out by
1009-
.Ar percent
1010-
.Pq default: 25
1011-
when using the zoom keys and buttons.
1008+
Zoom images in and out by a factor of the nth root
1009+
.Pq Ar index
1010+
of 2 when using the zoom keys and buttons. The default is the 3rd root,
1011+
which means that there will be 3 steps between powers of 2.
10121012
.
10131013
.El
10141014
.

src/events.c

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ fehkey *feh_str_to_kb(char *action);
4040

4141
feh_event_handler *ev_handler[LASTEvent];
4242

43+
static const double zoom_rate = M_LN2 / 128.0;
44+
4345
static void feh_event_handle_ButtonPress(XEvent * ev);
4446
static void feh_event_handle_ButtonRelease(XEvent * ev);
4547
static void feh_event_handle_LeaveNotify(XEvent * ev);
@@ -242,29 +244,31 @@ static void feh_event_handle_ButtonPress(XEvent * ev)
242244
D(("click offset is %d,%d\n", ev->xbutton.x, ev->xbutton.y));
243245
winwid->click_offset_x = ev->xbutton.x;
244246
winwid->click_offset_y = ev->xbutton.y;
245-
winwid->old_zoom = winwid->zoom;
247+
winwid->old_step = round(log(winwid->zoom) / zoom_rate);
246248

247249
/* required to adjust the image position in zoom mode */
248250
winwid->im_click_offset_x = (winwid->click_offset_x
249-
- winwid->im_x) / winwid->old_zoom;
251+
- winwid->im_x) / winwid->zoom;
250252
winwid->im_click_offset_y = (winwid->click_offset_y
251-
- winwid->im_y) / winwid->old_zoom;
253+
- winwid->im_y) / winwid->zoom;
252254

253255
} else if (feh_is_bb(EVENT_zoom_in, button, state)) {
254256
D(("Zoom_In Button Press event\n"));
255257
D(("click offset is %d,%d\n", ev->xbutton.x, ev->xbutton.y));
258+
if (winwid->zoom >= ZOOM_MAX || opt.step_rate <= 0)
259+
return;
260+
256261
winwid->click_offset_x = ev->xbutton.x;
257262
winwid->click_offset_y = ev->xbutton.y;
258-
winwid->old_zoom = winwid->zoom;
259263

260264
/* required to adjust the image position in zoom mode */
261265
winwid->im_click_offset_x = (winwid->click_offset_x
262-
- winwid->im_x) / winwid->old_zoom;
266+
- winwid->im_x) / winwid->zoom;
263267
winwid->im_click_offset_y = (winwid->click_offset_y
264-
- winwid->im_y) / winwid->old_zoom;
268+
- winwid->im_y) / winwid->zoom;
265269

266270
/* copied from zoom_in, keyevents.c */
267-
winwid->zoom = winwid->zoom * opt.zoom_rate;
271+
winwid->zoom = exp(++winwid->zoom_step * opt.step_rate);
268272

269273
if (winwid->zoom > ZOOM_MAX)
270274
winwid->zoom = ZOOM_MAX;
@@ -281,18 +285,20 @@ static void feh_event_handle_ButtonPress(XEvent * ev)
281285
} else if (feh_is_bb(EVENT_zoom_out, button, state)) {
282286
D(("Zoom_Out Button Press event\n"));
283287
D(("click offset is %d,%d\n", ev->xbutton.x, ev->xbutton.y));
288+
if (winwid->zoom <= ZOOM_MIN || opt.step_rate <= 0)
289+
return;
290+
284291
winwid->click_offset_x = ev->xbutton.x;
285292
winwid->click_offset_y = ev->xbutton.y;
286-
winwid->old_zoom = winwid->zoom;
287293

288294
/* required to adjust the image position in zoom mode */
289295
winwid->im_click_offset_x = (winwid->click_offset_x
290-
- winwid->im_x) / winwid->old_zoom;
296+
- winwid->im_x) / winwid->zoom;
291297
winwid->im_click_offset_y = (winwid->click_offset_y
292-
- winwid->im_y) / winwid->old_zoom;
298+
- winwid->im_y) / winwid->zoom;
293299

294300
/* copied from zoom_out, keyevents.c */
295-
winwid->zoom = winwid->zoom / opt.zoom_rate;
301+
winwid->zoom = exp(--winwid->zoom_step * opt.step_rate);
296302

297303
if (winwid->zoom < ZOOM_MIN)
298304
winwid->zoom = ZOOM_MIN;
@@ -400,11 +406,16 @@ static void feh_event_handle_ButtonRelease(XEvent * ev)
400406
opt.mode = MODE_NORMAL;
401407
winwid->mode = MODE_NORMAL;
402408

403-
if ((feh_is_bb(EVENT_zoom, button, state))
404-
&& (ev->xbutton.x == winwid->click_offset_x)
405-
&& (ev->xbutton.y == winwid->click_offset_y)) {
406-
winwid->zoom = 1.0;
407-
winwidget_center_image(winwid);
409+
if ((feh_is_bb(EVENT_zoom, button, state))) {
410+
if ((ev->xbutton.x == winwid->click_offset_x)
411+
&& (ev->xbutton.y == winwid->click_offset_y)) {
412+
winwid->zoom = 1.0;
413+
winwid->zoom_step = 0;
414+
winwidget_center_image(winwid);
415+
} else {
416+
winwid->zoom_step = round(log(winwid->zoom) / opt.step_rate);
417+
winwidget_sanitise_offsets(winwid);
418+
}
408419
} else
409420
winwidget_sanitise_offsets(winwid);
410421

@@ -543,14 +554,8 @@ static void feh_event_handle_MotionNotify(XEvent * ev)
543554

544555
winwid = winwidget_get_from_window(ev->xmotion.window);
545556
if (winwid) {
546-
if (ev->xmotion.x > winwid->click_offset_x)
547-
winwid->zoom = winwid->old_zoom + (
548-
((double) ev->xmotion.x - (double) winwid->click_offset_x)
549-
/ 128.0);
550-
else
551-
winwid->zoom = winwid->old_zoom - (
552-
((double) winwid->click_offset_x - (double) ev->xmotion.x)
553-
/ 128.0);
557+
int step = winwid->old_step + ev->xmotion.x - winwid->click_offset_x;
558+
winwid->zoom = exp(step * zoom_rate);
554559

555560
if (winwid->zoom < ZOOM_MIN)
556561
winwid->zoom = ZOOM_MIN;

src/keyevents.c

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -602,35 +602,42 @@ void feh_event_handle_generic(winwidget winwid, unsigned int state, KeySym keysy
602602
feh_event_invoke_action(winwid, 9);
603603
}
604604
else if (feh_is_kp(EVENT_zoom_in, state, keysym, button)) {
605-
winwid->old_zoom = winwid->zoom;
606-
winwid->zoom = winwid->zoom * opt.zoom_rate;
605+
if (winwid->zoom >= ZOOM_MAX || opt.step_rate <= 0)
606+
return;
607+
608+
double old_zoom = winwid->zoom;
609+
winwid->zoom = exp(++winwid->zoom_step * opt.step_rate);
607610

608611
if (winwid->zoom > ZOOM_MAX)
609612
winwid->zoom = ZOOM_MAX;
610613

611614
winwid->im_x = (winwid->w / 2) - (((winwid->w / 2) - winwid->im_x) /
612-
winwid->old_zoom * winwid->zoom);
615+
old_zoom * winwid->zoom);
613616
winwid->im_y = (winwid->h / 2) - (((winwid->h / 2) - winwid->im_y) /
614-
winwid->old_zoom * winwid->zoom);
617+
old_zoom * winwid->zoom);
615618
winwidget_sanitise_offsets(winwid);
616619
winwidget_render_image(winwid, 0, 0);
617620
}
618621
else if (feh_is_kp(EVENT_zoom_out, state, keysym, button)) {
619-
winwid->old_zoom = winwid->zoom;
620-
winwid->zoom = winwid->zoom / opt.zoom_rate;
622+
if (winwid->zoom <= ZOOM_MIN || opt.step_rate <= 0)
623+
return;
624+
625+
double old_zoom = winwid->zoom;
626+
winwid->zoom = exp(--winwid->zoom_step * opt.step_rate);
621627

622628
if (winwid->zoom < ZOOM_MIN)
623629
winwid->zoom = ZOOM_MIN;
624630

625631
winwid->im_x = (winwid->w / 2) - (((winwid->w / 2) - winwid->im_x) /
626-
winwid->old_zoom * winwid->zoom);
632+
old_zoom * winwid->zoom);
627633
winwid->im_y = (winwid->h / 2) - (((winwid->h / 2) - winwid->im_y) /
628-
winwid->old_zoom * winwid->zoom);
634+
old_zoom * winwid->zoom);
629635
winwidget_sanitise_offsets(winwid);
630636
winwidget_render_image(winwid, 0, 0);
631637
}
632638
else if (feh_is_kp(EVENT_zoom_default, state, keysym, button)) {
633639
winwid->zoom = 1.0;
640+
winwid->zoom_step = 0;
634641
winwidget_center_image(winwid);
635642
winwidget_render_image(winwid, 0, 0);
636643
}

src/options.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ void init_parse_options(int argc, char **argv)
6363
opt.font = NULL;
6464
opt.max_height = opt.max_width = UINT_MAX;
6565

66-
opt.zoom_rate = 1.25;
66+
opt.step_rate = M_LN2 / 3.0;
6767

6868
opt.start_list_at = NULL;
6969
opt.jump_on_resort = 1;
@@ -848,12 +848,11 @@ static void feh_parse_option_array(int argc, char **argv, int finalrun)
848848
opt.x11_windowid = strtol(optarg, NULL, 0);
849849
break;
850850
case OPTION_zoom_step:
851-
opt.zoom_rate = atof(optarg);
852-
if ((opt.zoom_rate <= 0)) {
853-
weprintf("Zooming disabled due to --zoom-step=%f", opt.zoom_rate);
854-
opt.zoom_rate = 1.0;
851+
opt.step_rate = atof(optarg);
852+
if ((opt.step_rate <= 0)) {
853+
weprintf("Zooming disabled due to --zoom-step=%f", opt.step_rate);
855854
} else {
856-
opt.zoom_rate = 1 + ((float)opt.zoom_rate / 100);
855+
opt.step_rate = M_LN2 / opt.step_rate;
857856
}
858857
break;
859858
default:

src/options.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ struct __fehoptions {
127127
int offset_y;
128128
int default_zoom;
129129
int zoom_mode;
130-
double zoom_rate;
130+
double step_rate;
131131
unsigned char adjust_reload;
132132
int xinerama_index;
133133
char *x11_class;

src/winwidget.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ static winwidget winwidget_allocate(void)
7777
ret->im_x = 0;
7878
ret->im_y = 0;
7979
ret->zoom = 1.0;
80-
ret->old_zoom = 1.0;
80+
ret->zoom_step = 0;
81+
ret->old_step = 0;
8182

8283
ret->click_offset_x = 0;
8384
ret->click_offset_y = 0;
@@ -494,6 +495,8 @@ void winwidget_render_image(winwidget winwid, int resize, int force_alias)
494495
&& (!opt.default_zoom || required_zoom < winwid->zoom))
495496
winwid->zoom = required_zoom;
496497

498+
winwid->zoom_step = round(log(winwid->zoom) / opt.step_rate);
499+
497500
if (opt.offset_flags & XValue) {
498501
if (opt.offset_flags & XNegative) {
499502
winwid->im_x = winwid->w - (winwid->im_w * winwid->zoom) - opt.offset_x;
@@ -1065,20 +1068,20 @@ void feh_debug_print_winwid(winwidget w)
10651068
"h = %d\n" "im_w = %d\n" "im_h = %d\n" "im_angle = %f\n"
10661069
"type = %d\n" "had_resize = %d\n" "im = %p\n" "GC = %p\n"
10671070
"pixmap = %ld\n" "name = %s\n" "file = %p\n" "mode = %d\n"
1068-
"im_x = %d\n" "im_y = %d\n" "zoom = %f\n" "old_zoom = %f\n"
1069-
"click_offset_x = %d\n" "click_offset_y = %d\n"
1071+
"im_x = %d\n" "im_y = %d\n" "zoom = %f\n" "zoom_step = %d\n"
1072+
"old_step = %d" "click_offset_x = %d\n" "click_offset_y = %d\n"
10701073
"has_rotated = %d\n", (void *)w, w->win, w->w, w->h, w->im_w,
10711074
w->im_h, w->im_angle, w->type, w->had_resize, w->im, (void *)w->gc,
10721075
w->bg_pmap, w->name, (void *)w->file, w->mode, w->im_x, w->im_y,
1073-
w->zoom, w->old_zoom, w->click_offset_x, w->click_offset_y,
1076+
w->zoom, w->zoom_step, w->old_step, w->click_offset_x, w->click_offset_y,
10741077
w->has_rotated);
10751078
}
10761079

10771080
void winwidget_reset_image(winwidget winwid)
10781081
{
10791082
if (!opt.keep_zoom_vp) {
10801083
winwid->zoom = 1.0;
1081-
winwid->old_zoom = 1.0;
1084+
winwid->zoom_step = 0;
10821085
winwid->im_x = 0;
10831086
winwid->im_y = 0;
10841087
}

src/winwidget.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ struct __winwidget {
107107
* all the way up to INT_MAX (eww)
108108
*/
109109
double zoom;
110-
double old_zoom;
110+
int zoom_step;
111+
int old_step;
111112

112113
int click_offset_x;
113114
int click_offset_y;

0 commit comments

Comments
 (0)