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