source: libcaca/trunk/src/cacaview.c @ 865

Last change on this file since 865 was 859, checked in by Sam Hocevar, 14 years ago
  • Removed duplicate uint*_t defines from *_internal.h and included common.h in all .c files that needed it.
  • Property svn:keywords set to Id
File size: 14.9 KB
Line 
1/*
2 *  cacaview      image viewer for libcaca
3 *  Copyright (c) 2003-2006 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  $Id: cacaview.c 859 2006-04-24 20:35:59Z sam $
7 *
8 *  This program is free software; you can redistribute it and/or
9 *  modify it under the terms of the Do What The Fuck You Want To
10 *  Public License, Version 2, as published by Sam Hocevar. See
11 *  http://sam.zoy.org/wtfpl/COPYING for more details.
12 */
13
14#include "config.h"
15#include "common.h"
16
17#include <stdio.h>
18#include <string.h>
19#include <stdlib.h>
20
21#if defined(HAVE_SLEEP)
22#   include <windows.h>
23#endif
24
25#include "cucul.h"
26#include "caca.h"
27#include "common-image.h"
28
29/* Local macros */
30#define MODE_IMAGE 1
31#define MODE_FILES 2
32
33#define STATUS_DITHERING 1
34#define STATUS_ANTIALIASING 2
35#define STATUS_BACKGROUND 3
36
37#define ZOOM_FACTOR 1.08f
38#define ZOOM_MAX 50
39#define GAMMA_FACTOR 1.04f
40#define GAMMA_MAX 100
41#define GAMMA(g) (((g) < 0) ? 1.0 / gammatab[-(g)] : gammatab[(g)])
42#define PAD_STEP 0.15
43
44/* libcucul/libcaca contexts */
45cucul_canvas_t *cv; caca_display_t *dp;
46
47/* Local functions */
48static void print_status(void);
49static void print_help(int, int);
50static void set_zoom(int);
51static void set_gamma(int);
52static void draw_checkers(int, int, int, int);
53
54/* Local variables */
55struct image *im = NULL;
56
57float zoomtab[ZOOM_MAX + 1];
58float gammatab[GAMMA_MAX + 1];
59float xfactor = 1.0, yfactor = 1.0, dx = 0.5, dy = 0.5;
60int zoom = 0, g = 0, fullscreen = 0, mode, ww, wh;
61
62int main(int argc, char **argv)
63{
64    int quit = 0, update = 1, help = 0, status = 0;
65    int reload = 0;
66
67    char **list = NULL;
68    int current = 0, items = 0, opts = 1;
69    int i;
70
71    /* Initialise libcucul */
72    cv = cucul_create_canvas(0, 0);
73    if(!cv)
74    {
75        fprintf(stderr, "%s: unable to initialise libcucul\n", argv[0]);
76        return 1;
77    }
78
79    dp = caca_create_display(cv);
80    if(!dp)
81    {
82        fprintf(stderr, "%s: unable to initialise libcaca\n", argv[0]);
83        return 1;
84    }
85
86    /* Set the window title */
87    caca_set_display_title(dp, "cacaview");
88
89    ww = cucul_get_canvas_width(cv);
90    wh = cucul_get_canvas_height(cv);
91
92    /* Fill the zoom table */
93    zoomtab[0] = 1.0;
94    for(i = 0; i < ZOOM_MAX; i++)
95        zoomtab[i + 1] = zoomtab[i] * ZOOM_FACTOR;
96
97    /* Fill the gamma table */
98    gammatab[0] = 1.0;
99    for(i = 0; i < GAMMA_MAX; i++)
100        gammatab[i + 1] = gammatab[i] * GAMMA_FACTOR;
101
102    /* Load items into playlist */
103    for(i = 1; i < argc; i++)
104    {
105        /* Skip options except after `--' */
106        if(opts && argv[i][0] == '-')
107        {
108            if(argv[i][1] == '-' && argv[i][2] == '\0')
109                opts = 0;
110            continue;
111        }
112
113        /* Add argv[i] to the list */
114        if(items)
115            list = realloc(list, (items + 1) * sizeof(char *));
116        else
117            list = malloc(sizeof(char *));
118        list[items] = argv[i];
119        items++;
120
121        reload = 1;
122    }
123
124    /* Go ! */
125    while(!quit)
126    {
127        caca_event_t ev;
128        unsigned int const event_mask = CACA_EVENT_KEY_PRESS
129                                      | CACA_EVENT_RESIZE
130                                      | CACA_EVENT_MOUSE_PRESS;
131        unsigned int new_status = 0, new_help = 0;
132        int event;
133
134        if(update)
135            event = caca_get_event(dp, event_mask, &ev, 0);
136        else
137            event = caca_get_event(dp, event_mask, &ev, -1);
138
139        while(event)
140        {
141            if(ev.type & CACA_EVENT_MOUSE_PRESS)
142            {
143                if(ev.data.mouse.button == 1)
144                {
145                    if(items) current = (current + 1) % items;
146                    reload = 1;
147                }
148                else if(ev.data.mouse.button == 2)
149                {
150                    if(items) current = (items + current - 1) % items;
151                    reload = 1;
152                }
153            }
154            else if(ev.type & CACA_EVENT_KEY_PRESS) switch(ev.data.key.ch)
155            {
156            case 'n':
157            case 'N':
158                if(items) current = (current + 1) % items;
159                reload = 1;
160                break;
161            case 'p':
162            case 'P':
163                if(items) current = (items + current - 1) % items;
164                reload = 1;
165                break;
166            case 'f':
167            case 'F':
168                fullscreen = ~fullscreen;
169                update = 1;
170                set_zoom(zoom);
171                break;
172#if 0 /* FIXME */
173            case 'b':
174                i = 1 + cucul_get_feature(cv, CUCUL_BACKGROUND);
175                if(i > CUCUL_BACKGROUND_MAX) i = CUCUL_BACKGROUND_MIN;
176                cucul_set_feature(cv, i);
177                new_status = STATUS_BACKGROUND;
178                update = 1;
179                break;
180            case 'B':
181                i = -1 + cucul_get_feature(cv, CUCUL_BACKGROUND);
182                if(i < CUCUL_BACKGROUND_MIN) i = CUCUL_BACKGROUND_MAX;
183                cucul_set_feature(cv, i);
184                new_status = STATUS_BACKGROUND;
185                update = 1;
186                break;
187            case 'a':
188                i = 1 + cucul_get_feature(cv, CUCUL_ANTIALIASING);
189                if(i > CUCUL_ANTIALIASING_MAX) i = CUCUL_ANTIALIASING_MIN;
190                cucul_set_feature(cv, i);
191                new_status = STATUS_ANTIALIASING;
192                update = 1;
193                break;
194            case 'A':
195                i = -1 + cucul_get_feature(cv, CUCUL_ANTIALIASING);
196                if(i < CUCUL_ANTIALIASING_MIN) i = CUCUL_ANTIALIASING_MAX;
197                cucul_set_feature(cv, i);
198                new_status = STATUS_ANTIALIASING;
199                update = 1;
200                break;
201            case 'd':
202                i = 1 + cucul_get_feature(cv, CUCUL_DITHERING);
203                if(i > CUCUL_DITHERING_MAX) i = CUCUL_DITHERING_MIN;
204                cucul_set_feature(cv, i);
205                new_status = STATUS_DITHERING;
206                update = 1;
207                break;
208            case 'D':
209                i = -1 + cucul_get_feature(cv, CUCUL_DITHERING);
210                if(i < CUCUL_DITHERING_MIN) i = CUCUL_DITHERING_MAX;
211                cucul_set_feature(cv, i);
212                new_status = STATUS_DITHERING;
213                update = 1;
214                break;
215#endif
216            case '+':
217                update = 1;
218                set_zoom(zoom + 1);
219                break;
220            case '-':
221                update = 1;
222                set_zoom(zoom - 1);
223                break;
224            case 'G':
225                update = 1;
226                set_gamma(g + 1);
227                break;
228            case 'g':
229                update = 1;
230                set_gamma(g - 1);
231                break;
232            case 'x':
233            case 'X':
234                update = 1;
235                set_zoom(0);
236                set_gamma(0);
237                break;
238            case 'k':
239            case 'K':
240            case CACA_KEY_UP:
241                if(yfactor > 1.0) dy -= PAD_STEP / yfactor;
242                if(dy < 0.0) dy = 0.0;
243                update = 1;
244                break;
245            case 'j':
246            case 'J':
247            case CACA_KEY_DOWN:
248                if(yfactor > 1.0) dy += PAD_STEP / yfactor;
249                if(dy > 1.0) dy = 1.0;
250                update = 1;
251                break;
252            case 'h':
253            case 'H':
254            case CACA_KEY_LEFT:
255                if(xfactor > 1.0) dx -= PAD_STEP / xfactor;
256                if(dx < 0.0) dx = 0.0;
257                update = 1;
258                break;
259            case 'l':
260            case 'L':
261            case CACA_KEY_RIGHT:
262                if(xfactor > 1.0) dx += PAD_STEP / xfactor;
263                if(dx > 1.0) dx = 1.0;
264                update = 1;
265                break;
266            case '?':
267                new_help = !help;
268                update = 1;
269                break;
270            case 'q':
271            case 'Q':
272                quit = 1;
273                break;
274            }
275            else if(ev.type == CACA_EVENT_RESIZE)
276            {
277                caca_refresh_display(dp);
278                ww = ev.data.resize.w;
279                wh = ev.data.resize.h;
280                update = 1;
281                set_zoom(zoom);
282            }
283
284            if(status || new_status)
285                status = new_status;
286
287            if(help || new_help)
288                help = new_help;
289
290            event = caca_get_event(dp, CACA_EVENT_KEY_PRESS, &ev, 0);
291        }
292
293        if(items && reload)
294        {
295            char *buffer;
296            int len = strlen(" Loading `%s'... ") + strlen(list[current]);
297
298            if(len < ww + 1)
299                len = ww + 1;
300
301            buffer = malloc(len);
302
303            sprintf(buffer, " Loading `%s'... ", list[current]);
304            buffer[ww] = '\0';
305            cucul_set_color(cv, CUCUL_COLOR_WHITE, CUCUL_COLOR_BLUE);
306            cucul_putstr(cv, (ww - strlen(buffer)) / 2, wh / 2, buffer);
307            caca_refresh_display(dp);
308            ww = cucul_get_canvas_width(cv);
309            wh = cucul_get_canvas_height(cv);
310
311            if(im)
312                unload_image(im);
313            im = load_image(list[current]);
314            reload = 0;
315
316            /* Reset image-specific runtime variables */
317            dx = dy = 0.5;
318            update = 1;
319            set_zoom(0);
320            set_gamma(0);
321
322            free(buffer);
323        }
324
325        cucul_set_color(cv, CUCUL_COLOR_WHITE, CUCUL_COLOR_BLACK);
326        cucul_clear_canvas(cv);
327
328        if(!items)
329        {
330            cucul_set_color(cv, CUCUL_COLOR_WHITE, CUCUL_COLOR_BLUE);
331            cucul_printf(cv, ww / 2 - 5, wh / 2, " No image. ");
332        }
333        else if(!im)
334        {
335#if defined(HAVE_IMLIB2_H)
336#   define ERROR_STRING " Error loading `%s'. "
337#else
338#   define ERROR_STRING " Error loading `%s'. Only BMP is supported. "
339#endif
340            char *buffer;
341            int len = strlen(ERROR_STRING) + strlen(list[current]);
342
343            if(len < ww + 1)
344                len = ww + 1;
345
346            buffer = malloc(len);
347
348            sprintf(buffer, ERROR_STRING, list[current]);
349            buffer[ww] = '\0';
350            cucul_set_color(cv, CUCUL_COLOR_WHITE, CUCUL_COLOR_BLUE);
351            cucul_putstr(cv, (ww - strlen(buffer)) / 2, wh / 2, buffer);
352            free(buffer);
353        }
354        else
355        {
356            float xdelta, ydelta;
357            int y, height;
358
359            y = fullscreen ? 0 : 1;
360            height = fullscreen ? wh : wh - 3;
361
362            xdelta = (xfactor > 1.0) ? dx : 0.5;
363            ydelta = (yfactor > 1.0) ? dy : 0.5;
364
365            draw_checkers(ww * (1.0 - xfactor) / 2,
366                          y + height * (1.0 - yfactor) / 2,
367                          ww * xfactor, height * yfactor);
368
369            cucul_dither_bitmap(cv, ww * (1.0 - xfactor) * xdelta,
370                            y + height * (1.0 - yfactor) * ydelta,
371                            ww * xfactor + 1, height * yfactor + 1,
372                            im->dither, im->pixels);
373        }
374
375        if(!fullscreen)
376        {
377            print_status();
378
379#if 0 /* FIXME */
380            cucul_set_color(cv, CUCUL_COLOR_LIGHTGRAY, CUCUL_COLOR_BLACK);
381            switch(status)
382            {
383                case STATUS_ANTIALIASING:
384                    cucul_printf(cv, 0, wh - 1, "Antialiasing: %s",
385                  cucul_get_feature_name(cucul_get_feature(cv, CUCUL_ANTIALIASING)));
386                    break;
387                case STATUS_DITHERING:
388                    cucul_printf(cv, 0, wh - 1, "Dithering: %s",
389                  cucul_get_feature_name(cucul_get_feature(cv, CUCUL_DITHERING)));
390                    break;
391                case STATUS_BACKGROUND:
392                    cucul_printf(cv, 0, wh - 1, "Background: %s",
393                  cucul_get_feature_name(cucul_get_feature(cv, CUCUL_BACKGROUND)));
394                    break;
395            }
396#endif
397        }
398
399        if(help)
400        {
401            print_help(ww - 26, 2);
402        }
403
404        caca_refresh_display(dp);
405        update = 0;
406    }
407
408    /* Clean up */
409    if(im)
410        unload_image(im);
411    caca_free_display(dp);
412    cucul_free_canvas(cv);
413
414    return 0;
415}
416
417static void print_status(void)
418{
419    cucul_set_color(cv, CUCUL_COLOR_WHITE, CUCUL_COLOR_BLUE);
420    cucul_draw_line(cv, 0, 0, ww - 1, 0, " ");
421    cucul_draw_line(cv, 0, wh - 2, ww - 1, wh - 2, "-");
422    cucul_putstr(cv, 0, 0, "q:Quit  np:Next/Prev  +-x:Zoom  gG:Gamma  "
423                           "hjkl:Move  d:Dither  a:Antialias");
424    cucul_putstr(cv, ww - strlen("?:Help"), 0, "?:Help");
425    cucul_printf(cv, 3, wh - 2, "cacaview %s", VERSION);
426    cucul_printf(cv, ww - 30, wh - 2, "(gamma: %#.3g)", GAMMA(g));
427    cucul_printf(cv, ww - 14, wh - 2, "(zoom: %s%i)", zoom > 0 ? "+" : "", zoom);
428
429    cucul_set_color(cv, CUCUL_COLOR_LIGHTGRAY, CUCUL_COLOR_BLACK);
430    cucul_draw_line(cv, 0, wh - 1, ww - 1, wh - 1, " ");
431}
432
433static void print_help(int x, int y)
434{
435    static char const *help[] =
436    {
437        " +: zoom in              ",
438        " -: zoom out             ",
439        " g: decrease gamma       ",
440        " G: increase gamma       ",
441        " x: reset zoom and gamma ",
442        " ----------------------- ",
443        " hjkl: move view         ",
444        " arrows: move view       ",
445        " ----------------------- ",
446        " a: antialiasing method  ",
447        " d: dithering method     ",
448        " b: background mode      ",
449        " ----------------------- ",
450        " ?: help                 ",
451        " q: quit                 ",
452        NULL
453    };
454
455    int i;
456
457    cucul_set_color(cv, CUCUL_COLOR_WHITE, CUCUL_COLOR_BLUE);
458
459    for(i = 0; help[i]; i++)
460        cucul_putstr(cv, x, y + i, help[i]);
461}
462
463static void set_zoom(int new_zoom)
464{
465    int height;
466
467    if(!im)
468        return;
469
470    zoom = new_zoom;
471
472    if(zoom > ZOOM_MAX) zoom = ZOOM_MAX;
473    if(zoom < -ZOOM_MAX) zoom = -ZOOM_MAX;
474
475    ww = cucul_get_canvas_width(cv);
476    height = fullscreen ? wh : wh - 3;
477
478    xfactor = (zoom < 0) ? 1.0 / zoomtab[-zoom] : zoomtab[zoom];
479    yfactor = xfactor * ww / height * im->h / im->w
480               * cucul_get_canvas_height(cv) / cucul_get_canvas_width(cv)
481               * caca_get_display_width(dp) / caca_get_display_height(dp);
482
483    if(yfactor > xfactor)
484    {
485        float tmp = xfactor;
486        xfactor = tmp * tmp / yfactor;
487        yfactor = tmp;
488    }
489}
490
491static void set_gamma(int new_gamma)
492{
493    if(!im)
494        return;
495
496    g = new_gamma;
497
498    if(g > GAMMA_MAX) g = GAMMA_MAX;
499    if(g < -GAMMA_MAX) g = -GAMMA_MAX;
500
501    cucul_set_dither_gamma(im->dither,
502                           (g < 0) ? 1.0 / gammatab[-g] : gammatab[g]);
503}
504
505static void draw_checkers(int x, int y, int w, int h)
506{
507    int xn, yn;
508
509    if(x + w > (int)cucul_get_canvas_width(cv))
510        w = cucul_get_canvas_width(cv) - x;
511    if(y + h > (int)cucul_get_canvas_height(cv))
512        h = cucul_get_canvas_height(cv) - y;
513
514    for(yn = y > 0 ? y : 0; yn < y + h; yn++)
515        for(xn = x > 0 ? x : 0; xn < x + w; xn++)
516    {
517        if((((xn - x) / 5) ^ ((yn - y) / 3)) & 1)
518            cucul_set_color(cv, CUCUL_COLOR_LIGHTGRAY, CUCUL_COLOR_DARKGRAY);
519        else
520            cucul_set_color(cv, CUCUL_COLOR_DARKGRAY, CUCUL_COLOR_LIGHTGRAY);
521        cucul_putchar(cv, xn, yn, ' ');
522    }
523}
524
Note: See TracBrowser for help on using the repository browser.