]> git.neil.brown.name Git - edlib.git/blob - render-imageview.c
TODO: clean out done items.
[edlib.git] / render-imageview.c
1 /*
2  * Copyright Neil Brown ©2023 <neil@brown.name>
3  * May be distributed under terms of GPLv2 - see file:COPYING
4  *
5  * Display an image and allow it to be scaled and panned.
6  */
7
8 #include <stdio.h>
9 #define PANE_DATA_TYPE struct imageview_data
10 #include "core.h"
11
12 struct imageview_data {
13         char *image;
14         int w,h;        /* size of image */
15         int scale;      /* 1024 * displayed-size / actual size */
16         int cx,cy;      /* image co-ordinates at center of pane.
17                          * Kept stable during zoom
18                          */
19         short px,py;    /* number of pixels in each pane cell */
20         bool integral;  /* Use integral scales */
21 };
22 #include "core-pane.h"
23
24 DEF_CMD_CLOSED(imageview_close)
25 {
26         struct imageview_data *ivd = ci->home->data;
27
28         free(ivd->image);
29         ivd->image = NULL;
30         return 1;
31 }
32
33 static int fix_scale(struct imageview_data *ivd safe, int scale)
34 {
35         if (!ivd->integral)
36                 return scale;
37
38         if (scale >= 1024)
39                 return scale & ~1023;
40         if (scale > 0)
41                 return 1024 / (1024 / scale);
42         return scale;
43 }
44
45 DEF_CMD(imageview_refresh)
46 {
47         struct imageview_data *ivd = ci->home->data;
48         char *img = ivd->image;
49         int x,y;
50         int pw = ci->home->w * ivd->px;
51         int ph = ci->home->h * ivd->py;
52
53         call("Draw:clear", ci->focus, 0, NULL, "bg:black");
54
55         if (!img)
56                 img = pane_attr_get(ci->focus, "imageview:image-source");
57         if (!img)
58                 img = "comm:doc:get-bytes";
59         if (!ivd->image)
60                 ivd->image = strdup(img);
61
62         if (ivd->w <= 0) {
63                 char *i;
64                 struct call_return cr = call_ret(all, "Draw:image-size",
65                                                  ci->focus,
66                                                  0, NULL, img);
67                 ivd->w = cr.x;
68                 ivd->h = cr.y;
69
70                 i = pane_attr_get(ci->focus, "imageview:integral");
71                 ivd->integral = (i && strcmp(i, "yes") == 0);
72         }
73         if (ivd->w <= 0 || ivd->h <= 0)
74                 return 1;
75
76         if (ivd->scale <= 0) {
77                 int xs = pw * 1024 / ivd->w;
78                 int ys = ph * 1024 / ivd->h;
79                 ivd->scale = fix_scale(ivd, xs > ys ? ys : xs);
80         }
81
82         x = (ivd->cx * ivd->scale) - pw * 1024 / 2;
83         y = (ivd->cy * ivd->scale) - ph * 1024 / 2;
84
85         if (ivd->scale * ivd->w < pw * 1024)
86                 /* Doesn't use full width, so centre */
87                 x = -(pw * 1024 - ivd->scale * ivd->w) / 2;
88         else {
89                 /* Does use full width, so avoid margins */
90                 if (x < 0)
91                         x = 0;
92                 if (x > ivd->w * ivd->scale - pw * 1024)
93                         x = ivd->w * ivd->scale - pw * 1024;
94         }
95         if (ivd->scale * ivd->h < ph * 1024)
96                 y = -(ph * 1024 - ivd->scale * ivd->h) / 2;
97         else {
98                 if (y < 0)
99                         y = 0;
100                 if (y > ivd->h * ivd->scale - ph * 1024)
101                         y = ivd->h * ivd->scale - ph * 1024;
102         }
103
104         ivd->cx = (pw * 1024 / 2 + x) / ivd->scale;
105         ivd->cy = (ph * 1024 / 2 + y) / ivd->scale;
106
107         call("Draw:image", ci->focus, ivd->scale, NULL, img,
108              0, NULL, NULL, x / 1024, y / 1024);
109
110         return 1;
111 }
112
113 DEF_CMD(imageview_refresh_size)
114 {
115         struct imageview_data *ivd = ci->home->data;
116         int pw = ci->home->w * ivd->px;
117         int ph = ci->home->h * ivd->py;
118
119         if (ivd->scale * ivd->w < pw * 1024 &&
120             ivd->scale * ivd->h < ph * 1024)
121                 /* Scale it too small to make use of space - reset */
122                 ivd->scale = 0;
123         pane_damaged(ci->home, DAMAGED_REFRESH);
124
125         return Efallthrough;
126 }
127
128 DEF_CMD(imageview_zoom)
129 {
130         /* Keep the centre of the pane at the same pixel when
131          * zooming.
132          */
133         struct imageview_data *ivd = ci->home->data;
134         int scale = ivd->scale;
135
136         if (strcmp(ci->key, "K-+") == 0) {
137                 /* zoom up */
138                 ivd->scale = fix_scale(ivd, scale + scale / 10);
139                 if (ivd->scale == scale)
140                         ivd->scale += 1024;
141         } else {
142                 /* zoom down */
143                 ivd->scale = fix_scale(ivd, scale - scale / 11);
144                 if (ivd->scale == scale && scale > 1 && scale <= 1024)
145                         ivd->scale = 1024 / (1024 / scale + 1);
146         }
147
148         pane_damaged(ci->home, DAMAGED_REFRESH);
149         return 1;
150 }
151
152 DEF_CMD(imageview_pan)
153 {
154         struct imageview_data *ivd = ci->home->data;
155         int pw = ci->home->w * ivd->px;
156         int ph = ci->home->h * ivd->py;
157
158         switch (ci->key[2]) {
159         case 'L':
160                 ivd->cx -= pw * 1024 / ivd->scale / 10;
161                 break;
162         case 'R':
163                 ivd->cx += pw * 1024 / ivd->scale / 10;
164                 break;
165         case 'U':
166                 ivd->cy -= ph * 1024 / ivd->scale / 10;
167                 break;
168         case 'D':
169                 ivd->cy += ph * 1024 / ivd->scale / 10;
170                 break;
171         }
172         pane_damaged(ci->home, DAMAGED_REFRESH);
173         return 1;
174 }
175
176 DEF_CMD(imageview_reset)
177 {
178         struct imageview_data *ivd = ci->home->data;
179
180         ivd->scale = 0;
181
182         pane_damaged(ci->home, DAMAGED_REFRESH);
183         return 1;
184 }
185
186 DEF_CMD(imageview_quit)
187 {
188         call("Tile:close", ci->focus);
189         return 1;
190 }
191
192 static struct map *iv_map;
193 DEF_LOOKUP_CMD(iv_handle, iv_map);
194
195 DEF_CMD(imageview_attach)
196 {
197         struct pane *p;
198         struct imageview_data *ivd;
199         char *pxl;
200
201         p = pane_register(ci->focus, 0, &iv_handle.c);
202         if (!p)
203                 return Efail;
204         ivd = p->data;
205         if (ci->str)
206                 ivd->image = strdup(ci->str);
207         ivd->scale = 0;
208         ivd->integral = False;
209         pxl = pane_attr_get(p, "Display:pixels");
210         if (sscanf(pxl ?: "1x1", "%hdx%hx", &ivd->px, &ivd->py) != 2)
211                 ivd->px = ivd->py = 1;
212
213         pane_damaged(p, DAMAGED_REFRESH);
214
215         return comm_call(ci->comm2, "cb", p);
216 }
217
218 void edlib_init(struct pane *ed safe)
219 {
220         call_comm("global-set-command", ed, &imageview_attach, 0, NULL,
221                   "attach-render-imageview");
222         iv_map = key_alloc();
223         key_add(iv_map, "Close", &imageview_close);
224         key_add(iv_map, "Refresh", &imageview_refresh);
225         key_add(iv_map, "Refresh:size", &imageview_refresh_size);
226
227         key_add(iv_map, "K-+", &imageview_zoom);
228         key_add(iv_map, "K--", &imageview_zoom);
229
230         key_add(iv_map, "K:Left", &imageview_pan);
231         key_add(iv_map, "K:Right", &imageview_pan);
232         key_add(iv_map, "K:Up", &imageview_pan);
233         key_add(iv_map, "K:Down", &imageview_pan);
234         key_add(iv_map, "K:Home", &imageview_reset);
235         key_add(iv_map, "K-.", &imageview_reset);
236
237         key_add(iv_map, "K:ESC", &imageview_quit);
238         key_add(iv_map, "K-q", &imageview_quit);
239 }