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

Last change on this file since 316 was 316, checked in by Sam Hocevar, 19 years ago
  • src/io.c: + caca_get_event() and caca_wait_event() now accept a mask as an argument

in order to select events.

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