9367eca67d2856c4326fec25eff5a51e8dafedda
[dos_imgv] / src / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <conio.h>
5 #include "video.h"
6 #include "mouse.h"
7 #include "imago2.h"
8
9 int switch_mode(int m);
10 int find_best_mode(int minx, int miny);
11 void drop_equiv_modes(void);
12
13 void display8(void);
14 void display16(void);
15 void display24(void);
16 void display32(void);
17 void (*display)(void);
18 void xor_cursor8(int x, int y);
19 void xor_cursor16(int x, int y);
20 void xor_cursor24(int x, int y);
21 void xor_cursor32(int x, int y);
22 void (*xor_cursor)(int x, int y);
23
24 int equiv_bpp(int a, int b);
25 int vmcmp(const void *a, const void *b);
26 int parse_args(int argc, char **argv);
27
28 static struct video_mode *vmode;
29 static struct video_mode *vmlist;
30 static int vmlist_size, cur_vm = -1;
31 static void *backbuf;
32
33 static int opt_width, opt_height;
34 static int opt_bpp = 16;
35 static const char *opt_fname;
36
37 static int redraw_pending = 1;
38 static int pan_x, pan_y, max_pan_x, max_pan_y;
39
40 static struct img_pixmap img;
41
42
43 int main(int argc, char **argv)
44 {
45         int i, num, modeidx;
46         struct video_mode *vmodes;
47
48         if(parse_args(argc, argv) == -1) {
49                 return 1;
50         }
51         if(!opt_fname) {
52                 fprintf(stderr, "please specify an image file to open\n");
53                 return 1;
54         }
55
56         if(init_video() == -1) {
57                 fprintf(stderr, "failed to initialize video\n");
58                 return 1;
59         }
60
61         /* populate and sort the usable video mode list */
62         num = num_video_modes();
63         vmodes = video_modes();
64
65         if(!(vmlist = malloc(num * sizeof *vmlist))) {
66                 fprintf(stderr, "failed to allocate video mode list\n");
67                 return 1;
68         }
69         vmlist_size = 0;
70
71         for(i=0; i<num; i++) {
72                 if(equiv_bpp(opt_bpp, vmodes[i].bpp)) {
73                         vmlist[vmlist_size++] = vmodes[i];
74                 }
75         }
76         if(!vmlist_size) {
77                 fprintf(stderr, "no usable video modes found for %d bpp\n", opt_bpp);
78                 goto end;
79         }
80         qsort(vmlist, vmlist_size, sizeof *vmlist, vmcmp);
81         drop_equiv_modes();
82
83         img_init(&img);
84         if(img_load(&img, opt_fname) == -1) {
85                 fprintf(stderr, "failed to load image: %s\n", opt_fname);
86                 goto end;
87         }
88
89         switch(opt_bpp) {
90         case 15:
91         case 16:
92                 img_convert(&img, IMG_FMT_RGB565);
93                 break;
94         case 24:
95                 img_convert(&img, IMG_FMT_RGB24);
96                 break;
97         case 32:
98                 img_convert(&img, IMG_FMT_RGBA32);
99                 break;
100         }
101
102         init_mouse();
103
104         if(!opt_width) {
105                 opt_width = img.width;
106                 opt_height = img.height;
107         }
108         modeidx = find_best_mode(opt_width, opt_height);
109         if(switch_mode(modeidx) == -1) {
110                 goto end;
111         }
112
113         for(;;) {
114                 if(kbhit()) {
115                         int c = getch();
116                         switch(c) {
117                         case 27:
118                                 goto end;
119
120                         case '=':
121                                 if(cur_vm < vmlist_size - 1) {
122                                         switch_mode(cur_vm + 1);
123                                         redraw_pending = 1;
124                                 }
125                                 break;
126
127                         case '-':
128                                 if(cur_vm > 0) {
129                                         switch_mode(cur_vm - 1);
130                                         redraw_pending = 1;
131                                 }
132                                 break;
133                         }
134                 }
135
136                 if(mouse.active) {
137                         proc_mouse();
138                         if((mouse.dx | mouse.dy) && mouse.bn & 1) {
139                                 pan_x -= mouse.dx;
140                                 pan_y -= mouse.dy;
141                                 if(pan_x < 0) pan_x = 0;
142                                 if(pan_y < 0) pan_y = 0;
143                                 if(pan_x > max_pan_x) pan_x = max_pan_x;
144                                 if(pan_y > max_pan_y) pan_y = max_pan_y;
145                                 redraw_pending = 1;
146                         }
147                 }
148
149                 if(redraw_pending) {
150                         redraw_pending = 0;
151                         display();
152                         blit_frame(backbuf, FLIP_NOW);
153                 }
154         }
155 end:
156
157         img_destroy(&img);
158         free(backbuf);
159         free(vmlist);
160         if(cur_vm >= 0) {
161                 set_text_mode();
162         }
163         cleanup_video();
164         return 0;
165 }
166
167 int switch_mode(int m)
168 {
169         vmode = vmlist + m;
170         cur_vm = m;
171
172         free(backbuf);
173         if(!(backbuf = malloc(vmode->ysz * vmode->pitch))) {
174                 fprintf(stderr, "failed to allocate back buffer\n");
175                 return -1;
176         }
177
178         set_video_mode(find_video_mode(vmode->mode), 0);
179
180         switch(vmode->bpp) {
181         case 8:
182                 display = display8;
183                 xor_cursor = xor_cursor8;
184                 break;
185         case 15:
186         case 16:
187                 display = display16;
188                 xor_cursor = xor_cursor16;
189                 break;
190         case 24:
191                 display = display24;
192                 xor_cursor = xor_cursor24;
193                 break;
194         case 32:
195                 display = display32;
196                 xor_cursor = xor_cursor32;
197         }
198
199         if(mouse.active) {
200                 reset_mouse();
201                 set_mouse_bounds(0, 0, vmode->xsz, vmode->ysz);
202         }
203
204         max_pan_x = img.width - vmode->xsz;
205         max_pan_y = img.height - vmode->ysz;
206         if(max_pan_x < 0) max_pan_x = 0;
207         if(max_pan_y < 0) max_pan_y = 0;
208
209         if(pan_x > max_pan_x) pan_x = max_pan_x;
210         if(pan_y > max_pan_y) pan_y = max_pan_y;
211
212         return 0;
213 }
214
215 int find_best_mode(int minx, int miny)
216 {
217         int i;
218
219         for(i=0; i<vmlist_size; i++) {
220                 if(vmlist[i].xsz >= minx && vmlist[i].ysz >= miny) {
221                         return i;
222                 }
223         }
224         return vmlist_size - 1;
225 }
226
227 void drop_equiv_modes(void)
228 {
229         struct video_mode *vm, *end;
230
231         /* modes are sorted, so just keeping the last equiv of a pair, keeps the 
232          * preferred one
233          */
234         vm = vmlist + 1;
235         end = vmlist + vmlist_size;
236         while(vm < end) {
237                 if(vm->xsz == vm[-1].xsz && vm->ysz == vm[-1].ysz &&
238                                 equiv_bpp(vm->bpp, vm[-1].bpp)) {
239                         memmove(vm - 1, vm, (end - vm) * sizeof *vm);
240                         end--;
241                 } else {
242                         vm++;
243                 }
244         }
245         vmlist_size = end - vmlist;
246 }
247
248 void display8(void)
249 {
250 }
251
252 void display16(void)
253 {
254         int i, minx, miny;
255         uint16_t *dptr = backbuf;
256         uint16_t *sptr = (uint16_t*)img.pixels + pan_y * img.width + pan_x;
257
258         minx = img.width - pan_x;
259         if(minx > vmode->xsz) minx = vmode->xsz;
260         miny = img.height - pan_y;
261         if(miny > vmode->ysz) miny = vmode->ysz;
262
263         for(i=0; i<miny; i++) {
264                 memcpy(dptr, sptr, minx << 1);
265                 if(minx < vmode->xsz) {
266                         memset(dptr + minx, 0, (vmode->xsz - minx) << 1);
267                 }
268                 dptr += vmode->pitch >> 1;
269                 sptr += img.width;
270         }
271         if(miny < vmode->ysz) {
272                 memset(dptr, 0, (vmode->ysz - miny) * vmode->pitch);
273         }
274 }
275
276 void display24(void)
277 {
278 }
279
280 void display32(void)
281 {
282 }
283
284 void xor_cursor8(int x, int y)
285 {
286 }
287
288 void xor_cursor16(int x, int y)
289 {
290         unsigned short *sptr = (unsigned short*)backbuf + y * (vmode->pitch >> 1);
291         int i, x0, x1, y0, y1;
292
293         x0 = x - 7;
294         x1 = x + 7;
295         if(x0 < 0) x0 = 0;
296         if(x1 >= vmode->xsz) x1 = vmode->xsz - 1;
297         y0 = y - 7;
298         y1 = y + 7;
299         if(y0 < 0) y0 = 0;
300         if(y1 >= vmode->ysz) y1 = vmode->ysz - 1;
301
302         for(i=x0; i<=x1; i++) {
303                 sptr[i] ^= 0xffff;
304         }
305
306         sptr = (unsigned short*)backbuf + y0 * (vmode->pitch >> 1) + x;
307         for(i=y0; i<=y1; i++) {
308                 if(i != y) {
309                         *sptr ^= 0xffff;
310                 }
311                 sptr += vmode->pitch >> 1;
312         }
313 }
314
315 void xor_cursor24(int x, int y)
316 {
317 }
318
319 void xor_cursor32(int x, int y)
320 {
321 }
322
323 int equiv_bpp(int a, int b)
324 {
325         if(a == 15 || a == 16) {
326                 return b == 15 || b == 16 ? 1 : 0;
327         }
328         if(a == 24 || a == 32) {
329                 return b == 24 || b == 32 ? 1 : 0;
330         }
331         return a == b ? 1 : 0;
332 }
333
334 int vmcmp(const void *a, const void *b)
335 {
336         const struct video_mode *vma = a;
337         const struct video_mode *vmb = b;
338
339         return vma->ysz - vmb->ysz;
340 }
341
342 int parse_args(int argc, char **argv)
343 {
344         int i;
345         static const char *usage_fmt = "Usage: %s [options] <image file>\n"
346                 "Options:\n"
347                 " -s <WxH>: choose screen resolution if available\n"
348                 " -bpp <n>: video mode color depth (8, 15, 16, 24, 32)\n"
349                 " -h,-help: print usage information and exit\n";
350
351         for(i=1; i<argc; i++) {
352                 if(argv[i][0] == '-') {
353                         if(strcmp(argv[i], "-s") == 0) {
354                                 if(!argv[++i] || sscanf(argv[i], "%dx%d", &opt_width, &opt_height) != 2 ||
355                                                 opt_width <= 0 || opt_height <= 0) {
356                                         fprintf(stderr, "invalid resolution\n");
357                                         return -1;
358                                 }
359                         } else if(strcmp(argv[i], "-bpp") == 0) {
360                                 if(!argv[++i] || (opt_bpp = atoi(argv[i])) <= 0) {
361                                         fprintf(stderr, "invalid -bpp\n");
362                                         return -1;
363                                 }
364                         } else if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-help") == 0) {
365                                 printf(usage_fmt, argv[0]);
366                                 exit(0);
367                         } else {
368                                 fprintf(stderr, "invalid option: %s\n", argv[i]);
369                                 printf(usage_fmt, argv[0]);
370                                 return -1;
371                         }
372                 } else {
373                         if(opt_fname) {
374                                 fprintf(stderr, "unexpected argument: %s\n", argv[i]);
375                                 printf(usage_fmt, argv[0]);
376                                 return -1;
377                         }
378                         opt_fname = argv[i];
379                 }
380         }
381         return 0;
382 }