From cb6ec46d1e7bc4f57c45fa7108415d7c4ed8aa22 Mon Sep 17 00:00:00 2001
From: Sam Hocevar <sam@hocevar.net>
Date: Tue, 16 Dec 2003 23:06:54 +0000
Subject: [PATCH]   * examples/view.c:     + If Imlib2 is not available, use a
 custom simple BMP loader.     + Draw the bottom bar one line higher to leave
 room for a status line.     + '?' now properly toggles help.   *
 examples/Makefile.am:     + Always build cacaview.

---
 examples/Makefile.am |   5 +-
 examples/view.c      | 366 ++++++++++++++++++++++++++++++++++++-------
 2 files changed, 315 insertions(+), 56 deletions(-)

diff --git a/examples/Makefile.am b/examples/Makefile.am
index 976873b..8dd4fa5 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -20,12 +20,15 @@ caca_spritedit_SOURCES = spritedit.c
 caca_spritedit_LDADD = ../src/libcaca.a @CACA_LIBS@
 caca_spritedit_CPPFLAGS = -I$(top_srcdir)/src
 
-if USE_IMLIB2
 cacaview = cacaview
 cacaview_SOURCES = view.c
 cacaview_LDADD = ../src/libcaca.a @CACA_LIBS@
 cacaview_CPPFLAGS = -I$(top_srcdir)/src -DX_DISPLAY_MISSING=1
+if USE_IMLIB2
 cacaview_CFLAGS = `imlib2-config --cflags`
 cacaview_LDFLAGS = `imlib2-config --libs`
+else
+cacaview_CFLAGS =
+cacaview_LDFLAGS =
 endif
 
diff --git a/examples/view.c b/examples/view.c
index a09db88..fb133db 100644
--- a/examples/view.c
+++ b/examples/view.c
@@ -28,24 +28,44 @@
 #include <malloc.h>
 #include <unistd.h>
 
-#include <Imlib2.h>
+#if defined(HAVE_IMLIB2_H)
+#   include <Imlib2.h>
+#else
+#   include <stdio.h>
+#endif
 
 #include "caca.h"
 
+/* Local macros */
+#define STATUS_DITHERING 1
+#define STATUS_ANTIALIASING 2
+#define STATUS_BACKGROUND 3
+
 /* Local functions */
 static void load_image(const char *);
+static void unload_image(void);
 static void draw_checkers(unsigned int, unsigned int,
                           unsigned int, unsigned int);
+#if !defined(HAVE_IMLIB2_H)
+static int freadint(FILE *);
+static int freadshort(FILE *);
+static int freadchar(FILE *);
+#endif
 
 /* Local variables */
+#if defined(HAVE_IMLIB2_H)
 Imlib_Image image = NULL;
+#endif
 char *pixels = NULL;
 struct caca_bitmap *bitmap = NULL;
-int x, y, w, h;
+int x, y;
+unsigned int w, h, depth, bpp, rmask, gmask, bmask, amask;
+unsigned int red[256], green[256], blue[256], alpha[256];
 
 int main(int argc, char **argv)
 {
-    int quit = 0, update = 1, help = 0, reload = 0, fullscreen = 0, zoom = 0;
+    int quit = 0, update = 1, help = 0, fullscreen = 0, status = 0;
+    int reload = 0, zoom = 0;
 
     char **list = NULL;
     int current = 0, items = 0, opts = 1;
@@ -86,7 +106,7 @@ int main(int argc, char **argv)
         int ww = caca_get_width();
         int wh = caca_get_height();
 
-        int event;
+        int event, new_status = 0, new_help = 0;
 
         while((event = caca_get_event()))
         {
@@ -111,36 +131,42 @@ int main(int argc, char **argv)
                 i = 1 + caca_get_feature(CACA_BACKGROUND);
                 if(i > CACA_BACKGROUND_MAX) i = CACA_BACKGROUND_MIN;
                 caca_set_feature(i);
+                new_status = STATUS_BACKGROUND;
                 update = 1;
                 break;
             case CACA_EVENT_KEY_PRESS | 'B':
                 i = -1 + caca_get_feature(CACA_BACKGROUND);
                 if(i < CACA_BACKGROUND_MIN) i = CACA_BACKGROUND_MAX;
                 caca_set_feature(i);
+                new_status = STATUS_BACKGROUND;
                 update = 1;
                 break;
             case CACA_EVENT_KEY_PRESS | 'a':
                 i = 1 + caca_get_feature(CACA_ANTIALIASING);
                 if(i > CACA_ANTIALIASING_MAX) i = CACA_ANTIALIASING_MIN;
                 caca_set_feature(i);
+                new_status = STATUS_ANTIALIASING;
                 update = 1;
                 break;
             case CACA_EVENT_KEY_PRESS | 'A':
                 i = -1 + caca_get_feature(CACA_ANTIALIASING);
                 if(i < CACA_ANTIALIASING_MIN) i = CACA_ANTIALIASING_MAX;
                 caca_set_feature(i);
+                new_status = STATUS_ANTIALIASING;
                 update = 1;
                 break;
             case CACA_EVENT_KEY_PRESS | 'd':
                 i = 1 + caca_get_feature(CACA_DITHERING);
                 if(i > CACA_DITHERING_MAX) i = CACA_DITHERING_MIN;
                 caca_set_feature(i);
+                new_status = STATUS_DITHERING;
                 update = 1;
                 break;
             case CACA_EVENT_KEY_PRESS | 'D':
                 i = -1 + caca_get_feature(CACA_DITHERING);
                 if(i < CACA_DITHERING_MIN) i = CACA_DITHERING_MAX;
                 caca_set_feature(i);
+                new_status = STATUS_DITHERING;
                 update = 1;
                 break;
             case CACA_EVENT_KEY_PRESS | '+':
@@ -181,7 +207,7 @@ int main(int argc, char **argv)
                 update = 1;
                 break;
             case CACA_EVENT_KEY_PRESS | '?':
-                help = 1;
+                new_help = !help;
                 update = 1;
                 break;
             case CACA_EVENT_KEY_PRESS | 'q':
@@ -189,6 +215,12 @@ int main(int argc, char **argv)
                 quit = 1;
                 break;
             }
+
+            if(status || new_status)
+                status = new_status;
+
+            if(help || new_help)
+                help = new_help;
         }
 
         if(items && reload)
@@ -204,6 +236,7 @@ int main(int argc, char **argv)
             caca_putstr((ww - strlen(buffer)) / 2, wh / 2, buffer);
             caca_refresh();
 
+            unload_image();
             load_image(list[current]);
             reload = 0;
             update = 1;
@@ -218,15 +251,18 @@ int main(int argc, char **argv)
         }
 
         caca_clear();
-        caca_set_color(CACA_COLOR_WHITE, CACA_COLOR_BLUE);
 
         if(!items)
+        {
+            caca_set_color(CACA_COLOR_WHITE, CACA_COLOR_BLUE);
             caca_printf(ww / 2 - 5, wh / 2, " No image. ");
-        else if(!image)
+        }
+        else if(!pixels)
         {
             char *buffer = malloc(ww + 1);
             snprintf(buffer, ww, " Error loading `%s'. ", list[current]);
             buffer[ww] = '\0';
+            caca_set_color(CACA_COLOR_WHITE, CACA_COLOR_BLUE);
             caca_putstr((ww - strlen(buffer)) / 2, wh / 2, buffer);
             free(buffer);
         }
@@ -247,112 +283,308 @@ int main(int argc, char **argv)
             int yn = h / (2 + zoom);
             if(x < xn) x = xn;
             if(y < yn) y = yn;
-            if(xn + x > w) x = w - xn;
-            if(yn + y > h) y = h - yn;
-            newbitmap = caca_create_bitmap(32, 2 * xn, 2 * yn, 4 * w,
-                                           0x00ff0000, 0x0000ff00, 0x000000ff,
-                                           0xff000000);
+            if(xn + x > (int)w) x = w - xn;
+            if(yn + y > (int)h) y = h - yn;
+            newbitmap = caca_create_bitmap(bpp, 2 * xn, 2 * yn, depth * w,
+                                           rmask, gmask, bmask, amask);
+            if(bpp == 8)
+                caca_set_bitmap_palette(newbitmap, red, green, blue, alpha);
             draw_checkers(0, fullscreen ? 0 : 1,
-                          ww - 1, fullscreen ? wh - 1 : wh - 2);
+                          ww - 1, fullscreen ? wh - 1 : wh - 3);
             caca_draw_bitmap(0, fullscreen ? 0 : 1,
-                             ww - 1, fullscreen ? wh - 1 : wh - 2,
+                             ww - 1, fullscreen ? wh - 1 : wh - 3,
                              newbitmap,
-                             pixels + 4 * (x - xn) + 4 * w * (y - yn));
+                             pixels + depth * (x - xn) + depth * w * (y - yn));
             caca_free_bitmap(newbitmap);
         }
         else
         {
             draw_checkers(0, fullscreen ? 0 : 1,
-                          ww - 1, fullscreen ? wh - 1 : wh - 2);
+                          ww - 1, fullscreen ? wh - 1 : wh - 3);
             caca_draw_bitmap(0, fullscreen ? 0 : 1,
-                             ww - 1, fullscreen ? wh - 1 : wh - 2,
+                             ww - 1, fullscreen ? wh - 1 : wh - 3,
                              bitmap, pixels);
         }
 
-        caca_set_color(CACA_COLOR_WHITE, CACA_COLOR_BLUE);
-
         if(!fullscreen)
         {
+            caca_set_color(CACA_COLOR_WHITE, CACA_COLOR_BLUE);
             caca_draw_line(0, 0, ww - 1, 0, ' ');
-            caca_draw_line(0, wh - 1, ww - 1, wh - 1, '-');
+            caca_draw_line(0, wh - 2, ww - 1, wh - 2, '-');
             caca_putstr(0, 0, "q:Quit  np:Next/Prev  +-x:Zoom  "
                               "hjkl:Move  d:Dithering  a:Antialias");
             caca_putstr(ww - strlen("?:Help"), 0, "?:Help");
-            caca_printf(3, wh - 1, "cacaview %s (%s, %s)", VERSION,
-                caca_get_feature_name(caca_get_feature(CACA_DITHERING)),
-                caca_get_feature_name(caca_get_feature(CACA_ANTIALIASING)));
-            caca_printf(ww - 14, wh - 1,
+            caca_printf(3, wh - 2, "cacaview %s", VERSION);
+            caca_printf(ww - 14, wh - 2,
                         "(zoom: %s%i)", zoom > 0 ? "+" : "", zoom);
+
+            caca_set_color(CACA_COLOR_LIGHTRED, CACA_COLOR_BLACK);
+            caca_draw_line(0, wh - 1, ww - 1, wh - 1, ' ');
+            switch(status)
+            {
+                case STATUS_ANTIALIASING:
+                    caca_printf(0, wh - 1, "Antialiasing: %s",
+                  caca_get_feature_name(caca_get_feature(CACA_ANTIALIASING)));
+                    break;
+                case STATUS_DITHERING:
+                    caca_printf(0, wh - 1, "Dithering: %s",
+                  caca_get_feature_name(caca_get_feature(CACA_DITHERING)));
+                    break;
+                case STATUS_BACKGROUND:
+                    caca_printf(0, wh - 1, "Background: %s",
+                  caca_get_feature_name(caca_get_feature(CACA_BACKGROUND)));
+                    break;
+            }
         }
 
         if(help)
         {
-            caca_putstr(ww - 22, 2,  " +: zoom in             ");
-            caca_putstr(ww - 22, 3,  " -: zoom out            ");
-            caca_putstr(ww - 22, 4,  " x: reset zoom          ");
-            caca_putstr(ww - 22, 5,  " ---------------------- ");
-            caca_putstr(ww - 22, 6,  " hjkl: move view        ");
-            caca_putstr(ww - 22, 7,  " arrows: move view      ");
-            caca_putstr(ww - 22, 8,  " ---------------------- ");
-            caca_putstr(ww - 22, 9,  " a: antialiasing method ");
-            caca_putstr(ww - 22, 9,  " d: dithering method    ");
-            caca_putstr(ww - 22, 10, " ---------------------- ");
-            caca_putstr(ww - 22, 11, " ?: help                ");
-            caca_putstr(ww - 22, 12, " q: quit                ");
-
-            help = 0;
+            caca_set_color(CACA_COLOR_WHITE, CACA_COLOR_BLUE);
+            caca_putstr(ww - 25, 2,  " +: zoom in             ");
+            caca_putstr(ww - 25, 3,  " -: zoom out            ");
+            caca_putstr(ww - 25, 4,  " x: reset zoom          ");
+            caca_putstr(ww - 25, 5,  " ---------------------- ");
+            caca_putstr(ww - 25, 6,  " hjkl: move view        ");
+            caca_putstr(ww - 25, 7,  " arrows: move view      ");
+            caca_putstr(ww - 25, 8,  " ---------------------- ");
+            caca_putstr(ww - 25, 9,  " a: antialiasing method ");
+            caca_putstr(ww - 25, 10, " d: dithering method    ");
+            caca_putstr(ww - 25, 11, " b: background mode     ");
+            caca_putstr(ww - 25, 12, " ---------------------- ");
+            caca_putstr(ww - 25, 13, " ?: help                ");
+            caca_putstr(ww - 25, 14, " q: quit                ");
         }
 
         caca_refresh();
         update = 0;
     }
 
-    if(bitmap)
-        caca_free_bitmap(bitmap);
-    if(image)
-        imlib_free_image();
-
     /* Clean up */
+    unload_image();
     caca_end();
 
     return 0;
 }
 
-static void load_image(const char *name)
+static void unload_image(void)
 {
-    /* Empty previous data */
+#ifdef HAVE_IMLIB2_H
     if(image)
         imlib_free_image();
-
+    image = NULL;
+    pixels = NULL;
+#else
+    if(pixels)
+        free(pixels);
+    pixels = NULL;
+#endif
     if(bitmap)
         caca_free_bitmap(bitmap);
-
-    image = NULL;
     bitmap = NULL;
+}
 
+static void load_image(const char *name)
+{
+#ifdef HAVE_IMLIB2_H
     /* Load the new image */
     image = imlib_load_image(name);
 
     if(!image)
-    {
         return;
-    }
 
     imlib_context_set_image(image);
     pixels = (char *)imlib_image_get_data_for_reading_only();
     w = imlib_image_get_width();
     h = imlib_image_get_height();
-    x = w / 2;
-    y = h / 2;
+    rmask = 0x00ff0000;
+    gmask = 0x0000ff00;
+    bmask = 0x000000ff;
+    amask = 0xff000000;
+    bpp = 32;
+    depth = 4;
 
     /* Create the libcaca bitmap */
-    bitmap = caca_create_bitmap(32, w, h, 4 * w, 0x00ff0000, 0x0000ff00,
-                                0x000000ff, 0xff000000);
+    bitmap = caca_create_bitmap(bpp, w, h, depth * w,
+                                rmask, gmask, bmask, amask);
     if(!bitmap)
     {
         imlib_free_image();
         image = NULL;
     }
+
+#else
+    /* Try to load a BMP file */
+    FILE *fp;
+    unsigned int i, colors, offset, tmp, planes;
+
+    fp = fopen(name, "rb");
+    if(!fp)
+        return;
+
+    if(freadshort(fp) != 0x4d42)
+    {
+        fclose(fp);
+        return;
+    }
+
+    freadint(fp); /* size */
+    freadshort(fp); /* reserved 1 */
+    freadshort(fp); /* reserved 2 */
+
+    offset = freadint(fp);
+
+    tmp = freadint(fp); /* header size */
+    if(tmp == 40)
+    {
+        w = freadint(fp);
+        h = freadint(fp);
+        planes = freadshort(fp);
+        bpp = freadshort(fp);
+
+        tmp = freadint(fp); /* compression */
+        if(tmp != 0)
+        {
+            fclose(fp);
+            return;
+        }
+
+        freadint(fp); /* sizeimage */
+        freadint(fp); /* xpelspermeter */
+        freadint(fp); /* ypelspermeter */
+        freadint(fp); /* biclrused */
+        freadint(fp); /* biclrimportantn */
+
+        colors = (offset - 54) / 4;
+        for(i = 0; i < colors && i < 256; i++)
+        {
+            blue[i] = freadchar(fp) * 16;
+            green[i] = freadchar(fp) * 16;
+            red[i] = freadchar(fp) * 16;
+            alpha[i] = 0;
+            freadchar(fp);
+        }
+    }
+    else if(tmp == 12)
+    {
+        w = freadint(fp);
+        h = freadint(fp);
+        planes = freadshort(fp);
+        bpp = freadshort(fp);
+
+        colors = (offset - 26) / 3;
+        for(i = 0; i < colors && i < 256; i++)
+        {
+            blue[i] = freadchar(fp);
+            green[i] = freadchar(fp);
+            red[i] = freadchar(fp);
+            alpha[i] = 0;
+        }
+    }
+    else
+    {
+        fclose(fp);
+        return;
+    }
+
+    /* Fill the rest of the palette */
+    for(i = colors; i < 256; i++)
+        blue[i] = green[i] = red[i] = alpha[i] = 0;
+
+    depth = (bpp + 7) / 8;
+
+    /* Sanity check */
+    if(!w || w > 0x10000 || !h || h > 0x10000 || planes != 1 /*|| bpp != 24*/)
+    {
+        fclose(fp);
+        return;
+    }
+
+    /* Allocate the pixel buffer */
+    pixels = malloc(w * h * depth);
+    if(!pixels)
+    {
+        fclose(fp);
+        return;
+    }
+
+    memset(pixels, 0, w * h * depth);
+
+    /* Read the bitmap data */
+    for(i = h; i--; )
+    {
+        unsigned int j, k, bits = 0;
+
+        switch(bpp)
+        {
+            case 1:
+                for(j = 0; j < w; j++)
+                {
+                    k = j % 32;
+                    if(k == 0)
+                        bits = freadint(fp);
+                    pixels[w * i * depth + j] =
+                        (bits >> ((k & ~0xf) + 0xf - (k & 0xf))) & 0xf;
+                }
+                break;
+            case 4:
+                for(j = 0; j < w; j++)
+                {
+                    k = j % 8;
+                    if(k == 0)
+                        bits = freadint(fp);
+                    pixels[w * i * depth + j] =
+                        (bits >> (4 * ((k & ~0x1) + 0x1 - (k & 0x1)))) & 0xf;
+                }
+                break;
+            default:
+                fread(pixels + w * i * depth, w * depth, 1, fp);
+                /* Pad reads to 4 bytes */
+                tmp = (4 - (w * depth) % 4) % 4;
+                while(tmp--)
+                    freadchar(fp);
+                break;
+        }
+    }
+
+    switch(depth)
+    {
+    case 3:
+        rmask = 0xff0000;
+        gmask = 0x00ff00;
+        bmask = 0x0000ff;
+        amask = 0x000000;
+        break;
+    case 2: /* XXX: those are the 16 bits values */
+        rmask = 0x7c00;
+        gmask = 0x03e0;
+        bmask = 0x001f;
+        amask = 0x0000;
+        break;
+    case 1:
+    default:
+        bpp = 8;
+        rmask = gmask = bmask = amask = 0;
+        break;
+    }
+
+    fclose(fp);
+
+    /* Create the libcaca bitmap */
+    bitmap = caca_create_bitmap(bpp, w, h, depth * w,
+                                rmask, gmask, bmask, amask);
+    if(!bitmap)
+    {
+        free(pixels);
+        pixels = NULL;
+        return;
+    }
+
+    if(bpp == 8)
+        caca_set_bitmap_palette(bitmap, red, green, blue, alpha);
+#endif
+
+    x = w / 2;
+    y = h / 2;
 }
 
 static void draw_checkers(unsigned int x1, unsigned int y1,
@@ -363,7 +595,7 @@ static void draw_checkers(unsigned int x1, unsigned int y1,
     for(yn = y1; yn <= y2; yn++)
         for(xn = x1; xn <= x2; xn++)
     {
-        if(((xn / 4) ^ (yn / 2)) & 1)
+        if((((xn - x1) / 5) ^ ((yn - y1) / 3)) & 1)
             caca_set_color(CACA_COLOR_LIGHTGRAY, CACA_COLOR_DARKGRAY);
         else
             caca_set_color(CACA_COLOR_DARKGRAY, CACA_COLOR_LIGHTGRAY);
@@ -371,3 +603,27 @@ static void draw_checkers(unsigned int x1, unsigned int y1,
     }
 }
 
+#if !defined(HAVE_IMLIB2_H)
+static int freadint(FILE *fp)
+{
+    unsigned char buffer[4];
+    fread(buffer, 4, 1, fp);
+    return ((int)buffer[3] << 24) | ((int)buffer[2] << 16)
+             | ((int)buffer[1] << 8) | ((int)buffer[0]);
+}
+
+static int freadshort(FILE *fp)
+{
+    unsigned char buffer[2];
+    fread(buffer, 2, 1, fp);
+    return ((int)buffer[1] << 8) | ((int)buffer[0]);
+}
+
+static int freadchar(FILE *fp)
+{
+    unsigned char buffer;
+    fread(&buffer, 1, 1, fp);
+    return (int)buffer;
+}
+#endif
+