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