source: libcaca/trunk/examples/cacaview.c @ 522

Last change on this file since 522 was 522, checked in by Sam Hocevar, 15 years ago
  • Changed the licensing to WTFPL, as per all copyright holders' permission.
  • Property svn:keywords set to Id
File size: 18.9 KB
Line 
1/*
2 *  cacaview      image viewer for libcaca
3 *  Copyright (c) 2003 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  $Id: cacaview.c 522 2006-03-04 00:35:45Z 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
16#include <stdio.h>
17#include <string.h>
18#include <stdlib.h>
19
20#if defined(HAVE_IMLIB2_H)
21#   include <Imlib2.h>
22#else
23#   include <stdio.h>
24#endif
25
26#if defined(HAVE_SLEEP)
27#   include <windows.h>
28#endif
29
30#include "caca.h"
31
32/* Local macros */
33#define MODE_IMAGE 1
34#define MODE_FILES 2
35
36#define STATUS_DITHERING 1
37#define STATUS_ANTIALIASING 2
38#define STATUS_BACKGROUND 3
39
40#define ZOOM_FACTOR 1.08f
41#define ZOOM_MAX 50
42#define PAD_STEP 0.15
43
44/* Local functions */
45static void print_status(void);
46static void print_help(int, int);
47static void set_zoom(int);
48static void load_image(char const *);
49static void unload_image(void);
50static void draw_checkers(int, int, int, int);
51#if !defined(HAVE_IMLIB2_H)
52static int freadint(FILE *);
53static int freadshort(FILE *);
54static int freadchar(FILE *);
55#endif
56
57/* Local variables */
58#if defined(HAVE_IMLIB2_H)
59Imlib_Image image = NULL;
60#endif
61char *pixels = NULL;
62struct caca_bitmap *bitmap = NULL;
63unsigned int w, h, depth, bpp, rmask, gmask, bmask, amask;
64#if !defined(HAVE_IMLIB2_H)
65unsigned int red[256], green[256], blue[256], alpha[256];
66#endif
67
68float zoomtab[ZOOM_MAX + 1];
69float xfactor = 1.0, yfactor = 1.0, dx = 0.5, dy = 0.5;
70int zoom = 0, fullscreen = 0, mode, ww, wh;
71
72int main(int argc, char **argv)
73{
74    int quit = 0, update = 1, help = 0, status = 0;
75    int reload = 0;
76
77    char **list = NULL;
78    int current = 0, items = 0, opts = 1;
79    int i;
80
81    /* Initialise libcaca */
82    if(caca_init())
83    {
84        fprintf(stderr, "%s: unable to initialise libcaca\n", argv[0]);
85        return 1;
86    }
87
88    /* Set the window title */
89    caca_set_window_title("cacaview");
90
91    ww = caca_get_width();
92    wh = caca_get_height();
93
94    /* Fill the zoom table */
95    zoomtab[0] = 1.0;
96    for(i = 0; i < ZOOM_MAX; i++)
97        zoomtab[i + 1] = zoomtab[i] * 1.08;
98
99    /* Load items into playlist */
100    for(i = 1; i < argc; i++)
101    {
102        /* Skip options except after `--' */
103        if(opts && argv[i][0] == '-')
104        {
105            if(argv[i][1] == '-' && argv[i][2] == '\0')
106                opts = 0;
107            continue;
108        }
109
110        /* Add argv[i] to the list */
111        if(items)
112            list = realloc(list, (items + 1) * sizeof(char *));
113        else
114            list = malloc(sizeof(char *));
115        list[items] = argv[i];
116        items++;
117
118        reload = 1;
119    }
120
121    /* Go ! */
122    while(!quit)
123    {
124        unsigned int const event_mask = CACA_EVENT_KEY_PRESS
125                                      | CACA_EVENT_RESIZE
126                                      | CACA_EVENT_MOUSE_PRESS;
127        unsigned int event, new_status = 0, new_help = 0;
128
129        if(update)
130            event = caca_get_event(event_mask);
131        else
132            event = caca_wait_event(event_mask);
133
134        while(event)
135        {
136            if(event & CACA_EVENT_MOUSE_PRESS)
137            {
138                if((event & 0x00ffffff) == 1)
139                {
140                    if(items) current = (current + 1) % items;
141                    reload = 1;
142                }
143                else if((event & 0x00ffffff) == 2)
144                {
145                    if(items) current = (items + current - 1) % items;
146                    reload = 1;
147                }
148            }
149            else if(event & CACA_EVENT_KEY_PRESS) switch(event & 0x00ffffff)
150            {
151            case 'n':
152            case 'N':
153                if(items) current = (current + 1) % items;
154                reload = 1;
155                break;
156            case 'p':
157            case 'P':
158                if(items) current = (items + current - 1) % items;
159                reload = 1;
160                break;
161            case 'f':
162            case 'F':
163                fullscreen = ~fullscreen;
164                update = 1;
165                set_zoom(zoom);
166                break;
167            case 'b':
168                i = 1 + caca_get_feature(CACA_BACKGROUND);
169                if(i > CACA_BACKGROUND_MAX) i = CACA_BACKGROUND_MIN;
170                caca_set_feature(i);
171                new_status = STATUS_BACKGROUND;
172                update = 1;
173                break;
174            case 'B':
175                i = -1 + caca_get_feature(CACA_BACKGROUND);
176                if(i < CACA_BACKGROUND_MIN) i = CACA_BACKGROUND_MAX;
177                caca_set_feature(i);
178                new_status = STATUS_BACKGROUND;
179                update = 1;
180                break;
181            case 'a':
182                i = 1 + caca_get_feature(CACA_ANTIALIASING);
183                if(i > CACA_ANTIALIASING_MAX) i = CACA_ANTIALIASING_MIN;
184                caca_set_feature(i);
185                new_status = STATUS_ANTIALIASING;
186                update = 1;
187                break;
188            case 'A':
189                i = -1 + caca_get_feature(CACA_ANTIALIASING);
190                if(i < CACA_ANTIALIASING_MIN) i = CACA_ANTIALIASING_MAX;
191                caca_set_feature(i);
192                new_status = STATUS_ANTIALIASING;
193                update = 1;
194                break;
195            case 'd':
196                i = 1 + caca_get_feature(CACA_DITHERING);
197                if(i > CACA_DITHERING_MAX) i = CACA_DITHERING_MIN;
198                caca_set_feature(i);
199                new_status = STATUS_DITHERING;
200                update = 1;
201                break;
202            case 'D':
203                i = -1 + caca_get_feature(CACA_DITHERING);
204                if(i < CACA_DITHERING_MIN) i = CACA_DITHERING_MAX;
205                caca_set_feature(i);
206                new_status = STATUS_DITHERING;
207                update = 1;
208                break;
209            case '+':
210                update = 1;
211                set_zoom(zoom + 1);
212                break;
213            case '-':
214                update = 1;
215                set_zoom(zoom - 1);
216                break;
217            case 'x':
218            case 'X':
219                update = 1;
220                set_zoom(0);
221                break;
222            case 'k':
223            case 'K':
224            case CACA_KEY_UP:
225                if(yfactor > 1.0) dy -= PAD_STEP / yfactor;
226                if(dy < 0.0) dy = 0.0;
227                update = 1;
228                break;
229            case 'j':
230            case 'J':
231            case CACA_KEY_DOWN:
232                if(yfactor > 1.0) dy += PAD_STEP / yfactor;
233                if(dy > 1.0) dy = 1.0;
234                update = 1;
235                break;
236            case 'h':
237            case 'H':
238            case CACA_KEY_LEFT:
239                if(xfactor > 1.0) dx -= PAD_STEP / xfactor;
240                if(dx < 0.0) dx = 0.0;
241                update = 1;
242                break;
243            case 'l':
244            case 'L':
245            case CACA_KEY_RIGHT:
246                if(xfactor > 1.0) dx += PAD_STEP / xfactor;
247                if(dx > 1.0) dx = 1.0;
248                update = 1;
249                break;
250            case '?':
251                new_help = !help;
252                update = 1;
253                break;
254            case 'q':
255            case 'Q':
256                quit = 1;
257                break;
258            }
259            else if(event == CACA_EVENT_RESIZE)
260            {
261                caca_refresh();
262                ww = caca_get_width();
263                wh = caca_get_height();
264                update = 1;
265                set_zoom(zoom);
266            }
267
268            if(status || new_status)
269                status = new_status;
270
271            if(help || new_help)
272                help = new_help;
273
274            event = caca_get_event(CACA_EVENT_KEY_PRESS);
275        }
276
277        if(items && reload)
278        {
279            char *buffer;
280            int len = strlen(" Loading `%s'... ") + strlen(list[current]);
281
282            if(len < ww + 1)
283                len = ww + 1;
284
285            buffer = malloc(len);
286
287            sprintf(buffer, " Loading `%s'... ", list[current]);
288            buffer[ww] = '\0';
289            caca_set_color(CACA_COLOR_WHITE, CACA_COLOR_BLUE);
290            caca_putstr((ww - strlen(buffer)) / 2, wh / 2, buffer);
291            caca_refresh();
292            ww = caca_get_width();
293            wh = caca_get_height();
294
295            unload_image();
296            load_image(list[current]);
297            reload = 0;
298
299            /* Reset image-specific runtime variables */
300            dx = dy = 0.5;
301            update = 1;
302            set_zoom(0);
303
304            free(buffer);
305        }
306
307        caca_clear();
308
309        if(!items)
310        {
311            caca_set_color(CACA_COLOR_WHITE, CACA_COLOR_BLUE);
312            caca_printf(ww / 2 - 5, wh / 2, " No image. ");
313        }
314        else if(!pixels)
315        {
316#if defined(HAVE_IMLIB2_H)
317#   define ERROR_STRING " Error loading `%s'. "
318#else
319#   define ERROR_STRING " Error loading `%s'. Only BMP is supported. "
320#endif
321            char *buffer;
322            int len = strlen(ERROR_STRING) + strlen(list[current]);
323
324            if(len < ww + 1)
325                len = ww + 1;
326
327            buffer = malloc(len);
328
329            sprintf(buffer, ERROR_STRING, list[current]);
330            buffer[ww] = '\0';
331            caca_set_color(CACA_COLOR_WHITE, CACA_COLOR_BLUE);
332            caca_putstr((ww - strlen(buffer)) / 2, wh / 2, buffer);
333            free(buffer);
334        }
335        else
336        {
337            float xdelta, ydelta;
338            int y, height;
339
340            y = fullscreen ? 0 : 1;
341            height = fullscreen ? wh : wh - 3;
342
343            xdelta = (xfactor > 1.0) ? dx : 0.5;
344            ydelta = (yfactor > 1.0) ? dy : 0.5;
345
346            draw_checkers(ww * (1.0 - xfactor) / 2,
347                          y + height * (1.0 - yfactor) / 2,
348                          ww * (1.0 + xfactor) / 2,
349                          y + height * (1.0 + yfactor) / 2);
350
351            caca_draw_bitmap(ww * (1.0 - xfactor) * xdelta,
352                             y + height * (1.0 - yfactor) * ydelta,
353                             ww * (xdelta + (1.0 - xdelta) * xfactor),
354                             y + height * (ydelta + (1.0 - ydelta) * yfactor),
355                             bitmap, pixels);
356        }
357
358        if(!fullscreen)
359        {
360            print_status();
361
362            caca_set_color(CACA_COLOR_LIGHTGRAY, CACA_COLOR_BLACK);
363            switch(status)
364            {
365                case STATUS_ANTIALIASING:
366                    caca_printf(0, wh - 1, "Antialiasing: %s",
367                  caca_get_feature_name(caca_get_feature(CACA_ANTIALIASING)));
368                    break;
369                case STATUS_DITHERING:
370                    caca_printf(0, wh - 1, "Dithering: %s",
371                  caca_get_feature_name(caca_get_feature(CACA_DITHERING)));
372                    break;
373                case STATUS_BACKGROUND:
374                    caca_printf(0, wh - 1, "Background: %s",
375                  caca_get_feature_name(caca_get_feature(CACA_BACKGROUND)));
376                    break;
377            }
378        }
379
380        if(help)
381        {
382            print_help(ww - 25, 2);
383        }
384
385        caca_refresh();
386        update = 0;
387    }
388
389    /* Clean up */
390    unload_image();
391    caca_end();
392
393    return 0;
394}
395
396static void print_status(void)
397{
398    caca_set_color(CACA_COLOR_WHITE, CACA_COLOR_BLUE);
399    caca_draw_line(0, 0, ww - 1, 0, ' ');
400    caca_draw_line(0, wh - 2, ww - 1, wh - 2, '-');
401    caca_putstr(0, 0, "q:Quit  np:Next/Prev  +-x:Zoom  "
402                      "hjkl:Move  d:Dithering  a:Antialias");
403    caca_putstr(ww - strlen("?:Help"), 0, "?:Help");
404    caca_printf(3, wh - 2, "cacaview %s", VERSION);
405    caca_printf(ww - 14, wh - 2, "(zoom: %s%i)", zoom > 0 ? "+" : "", zoom);
406
407    caca_set_color(CACA_COLOR_LIGHTGRAY, CACA_COLOR_BLACK);
408    caca_draw_line(0, wh - 1, ww - 1, wh - 1, ' ');
409}
410
411static void print_help(int x, int y)
412{
413    static char const *help[] =
414    {
415        " +: zoom in             ",
416        " -: zoom out            ",
417        " x: reset zoom          ",
418        " ---------------------- ",
419        " hjkl: move view        ",
420        " arrows: move view      ",
421        " ---------------------- ",
422        " a: antialiasing method ",
423        " d: dithering method    ",
424        " b: background mode     ",
425        " ---------------------- ",
426        " ?: help                ",
427        " q: quit                ",
428        NULL
429    };
430
431    int i;
432
433    caca_set_color(CACA_COLOR_WHITE, CACA_COLOR_BLUE);
434
435    for(i = 0; help[i]; i++)
436        caca_putstr(x, y + i, help[i]);
437}
438
439static void set_zoom(int new_zoom)
440{
441    int height;
442
443    zoom = new_zoom;
444
445    if(zoom > ZOOM_MAX) zoom = ZOOM_MAX;
446    if(zoom < -ZOOM_MAX) zoom = -ZOOM_MAX;
447
448    ww = caca_get_width();
449    height = fullscreen ? wh : wh - 3;
450
451    xfactor = (zoom < 0) ? 1.0 / zoomtab[-zoom] : zoomtab[zoom];
452    yfactor = xfactor * ww / height * h / w
453               * caca_get_height() / caca_get_width()
454               * caca_get_window_width() / caca_get_window_height();
455
456    if(yfactor > xfactor)
457    {
458        float tmp = xfactor;
459        xfactor = tmp * tmp / yfactor;
460        yfactor = tmp;
461    }
462}
463
464static void unload_image(void)
465{
466#if defined(HAVE_IMLIB2_H)
467    if(image)
468        imlib_free_image();
469    image = NULL;
470    pixels = NULL;
471#else
472    if(pixels)
473        free(pixels);
474    pixels = NULL;
475#endif
476    if(bitmap)
477        caca_free_bitmap(bitmap);
478    bitmap = NULL;
479}
480
481static void load_image(char const *name)
482{
483#if defined(HAVE_IMLIB2_H)
484    /* Load the new image */
485    image = imlib_load_image(name);
486
487    if(!image)
488        return;
489
490    imlib_context_set_image(image);
491    pixels = (char *)imlib_image_get_data_for_reading_only();
492    w = imlib_image_get_width();
493    h = imlib_image_get_height();
494    rmask = 0x00ff0000;
495    gmask = 0x0000ff00;
496    bmask = 0x000000ff;
497    amask = 0xff000000;
498    bpp = 32;
499    depth = 4;
500
501    /* Create the libcaca bitmap */
502    bitmap = caca_create_bitmap(bpp, w, h, depth * w,
503                                rmask, gmask, bmask, amask);
504    if(!bitmap)
505    {
506        imlib_free_image();
507        image = NULL;
508    }
509
510#else
511    /* Try to load a BMP file */
512    FILE *fp;
513    unsigned int i, colors, offset, tmp, planes;
514
515    fp = fopen(name, "rb");
516    if(!fp)
517        return;
518
519    if(freadshort(fp) != 0x4d42)
520    {
521        fclose(fp);
522        return;
523    }
524
525    freadint(fp); /* size */
526    freadshort(fp); /* reserved 1 */
527    freadshort(fp); /* reserved 2 */
528
529    offset = freadint(fp);
530
531    tmp = freadint(fp); /* header size */
532    if(tmp == 40)
533    {
534        w = freadint(fp);
535        h = freadint(fp);
536        planes = freadshort(fp);
537        bpp = freadshort(fp);
538
539        tmp = freadint(fp); /* compression */
540        if(tmp != 0)
541        {
542            fclose(fp);
543            return;
544        }
545
546        freadint(fp); /* sizeimage */
547        freadint(fp); /* xpelspermeter */
548        freadint(fp); /* ypelspermeter */
549        freadint(fp); /* biclrused */
550        freadint(fp); /* biclrimportantn */
551
552        colors = (offset - 54) / 4;
553        for(i = 0; i < colors && i < 256; i++)
554        {
555            blue[i] = freadchar(fp) * 16;
556            green[i] = freadchar(fp) * 16;
557            red[i] = freadchar(fp) * 16;
558            alpha[i] = 0;
559            freadchar(fp);
560        }
561    }
562    else if(tmp == 12)
563    {
564        w = freadint(fp);
565        h = freadint(fp);
566        planes = freadshort(fp);
567        bpp = freadshort(fp);
568
569        colors = (offset - 26) / 3;
570        for(i = 0; i < colors && i < 256; i++)
571        {
572            blue[i] = freadchar(fp);
573            green[i] = freadchar(fp);
574            red[i] = freadchar(fp);
575            alpha[i] = 0;
576        }
577    }
578    else
579    {
580        fclose(fp);
581        return;
582    }
583
584    /* Fill the rest of the palette */
585    for(i = colors; i < 256; i++)
586        blue[i] = green[i] = red[i] = alpha[i] = 0;
587
588    depth = (bpp + 7) / 8;
589
590    /* Sanity check */
591    if(!w || w > 0x10000 || !h || h > 0x10000 || planes != 1 /*|| bpp != 24*/)
592    {
593        fclose(fp);
594        return;
595    }
596
597    /* Allocate the pixel buffer */
598    pixels = malloc(w * h * depth);
599    if(!pixels)
600    {
601        fclose(fp);
602        return;
603    }
604
605    memset(pixels, 0, w * h * depth);
606
607    /* Read the bitmap data */
608    for(i = h; i--; )
609    {
610        unsigned int j, k, bits = 0;
611
612        switch(bpp)
613        {
614            case 1:
615                for(j = 0; j < w; j++)
616                {
617                    k = j % 32;
618                    if(k == 0)
619                        bits = freadint(fp);
620                    pixels[w * i * depth + j] =
621                        (bits >> ((k & ~0xf) + 0xf - (k & 0xf))) & 0x1;
622                }
623                break;
624            case 4:
625                for(j = 0; j < w; j++)
626                {
627                    k = j % 8;
628                    if(k == 0)
629                        bits = freadint(fp);
630                    pixels[w * i * depth + j] =
631                        (bits >> (4 * ((k & ~0x1) + 0x1 - (k & 0x1)))) & 0xf;
632                }
633                break;
634            default:
635                /* Works for 8bpp, but also for 16, 24 etc. */
636                fread(pixels + w * i * depth, w * depth, 1, fp);
637                /* Pad reads to 4 bytes */
638                tmp = (w * depth) % 4;
639                tmp = (4 - tmp) % 4;
640                while(tmp--)
641                    freadchar(fp);
642                break;
643        }
644    }
645
646    switch(depth)
647    {
648    case 3:
649        rmask = 0xff0000;
650        gmask = 0x00ff00;
651        bmask = 0x0000ff;
652        amask = 0x000000;
653        break;
654    case 2: /* XXX: those are the 16 bits values */
655        rmask = 0x7c00;
656        gmask = 0x03e0;
657        bmask = 0x001f;
658        amask = 0x0000;
659        break;
660    case 1:
661    default:
662        bpp = 8;
663        rmask = gmask = bmask = amask = 0;
664        break;
665    }
666
667    fclose(fp);
668
669    /* Create the libcaca bitmap */
670    bitmap = caca_create_bitmap(bpp, w, h, depth * w,
671                                rmask, gmask, bmask, amask);
672    if(!bitmap)
673    {
674        free(pixels);
675        pixels = NULL;
676        return;
677    }
678
679    if(bpp == 8)
680        caca_set_bitmap_palette(bitmap, red, green, blue, alpha);
681#endif
682}
683
684static void draw_checkers(int x1, int y1, int x2, int y2)
685{
686    int xn, yn;
687
688    if(x2 + 1 > (int)caca_get_width()) x2 = caca_get_width() - 1;
689    if(y2 + 1 > (int)caca_get_height()) y2 = caca_get_height() - 1;
690
691    for(yn = y1 > 0 ? y1 : 0; yn <= y2; yn++)
692        for(xn = x1 > 0 ? x1 : 0; xn <= x2; xn++)
693    {
694        if((((xn - x1) / 5) ^ ((yn - y1) / 3)) & 1)
695            caca_set_color(CACA_COLOR_LIGHTGRAY, CACA_COLOR_DARKGRAY);
696        else
697            caca_set_color(CACA_COLOR_DARKGRAY, CACA_COLOR_LIGHTGRAY);
698        caca_putchar(xn, yn, ' ');
699    }
700}
701
702#if !defined(HAVE_IMLIB2_H)
703static int freadint(FILE *fp)
704{
705    unsigned char buffer[4];
706    fread(buffer, 4, 1, fp);
707    return ((int)buffer[3] << 24) | ((int)buffer[2] << 16)
708             | ((int)buffer[1] << 8) | ((int)buffer[0]);
709}
710
711static int freadshort(FILE *fp)
712{
713    unsigned char buffer[2];
714    fread(buffer, 2, 1, fp);
715    return ((int)buffer[1] << 8) | ((int)buffer[0]);
716}
717
718static int freadchar(FILE *fp)
719{
720    unsigned char buffer;
721    fread(&buffer, 1, 1, fp);
722    return (int)buffer;
723}
724#endif
725
Note: See TracBrowser for help on using the repository browser.