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

Last change on this file since 2821 was 2821, checked in by Sam Hocevar, 11 years ago

Starting refactoring to get rid of libcucul. The initial reason for the
split is rendered moot by the plugin system: when enabled, binaries do
not link directly with libX11 or libGL. I hope this is a step towards
more consisteny and clarity.

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