/* This code is ripped from Autotrace-0.29. Small modifications by pts. */
/* input-bmp.ci: reads any bitmap I could get for testing */
#ifdef __GNUC__
#ifndef __clang__
#pragma implementation
#endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* #include "types.h" */
#include "at_bitmap.h"
/* #include "message.h" */
/* #include "xstd.h" */
/* #include "input-bmp.h" */
#define MAXCOLORS 256
/* #define Image long */
#define BitSet(byte, bit) (((byte) & (bit)) == (bit))
#define ReadOK(file,buffer,len) (fread(buffer, len, 1, file) != 0)
struct Bitmap_File_Head_Struct
{
char zzMagic[2]; /* 00 "BM" */
unsigned long bfSize; /* 02 */
unsigned short zzHotX; /* 06 */
unsigned short zzHotY; /* 08 */
unsigned long bfOffs; /* 0A */
unsigned long biSize; /* 0E */
} Bitmap_File_Head;
struct Bitmap_Head_Struct
{
unsigned long biWidth; /* 12 */
unsigned long biHeight; /* 16 */
unsigned short biPlanes; /* 1A */
unsigned short biBitCnt; /* 1C */
unsigned long biCompr; /* 1E */
unsigned long biSizeIm; /* 22 */
unsigned long biXPels; /* 26 */
unsigned long biYPels; /* 2A */
unsigned long biClrUsed; /* 2E */
unsigned long biClrImp; /* 32 */
/* 36 */
} Bitmap_Head;
static long ToL (unsigned char *);
static short ToS (unsigned char *);
static int ReadColorMap (FILE *,
unsigned char[256][3],
int,
int,
int *);
static unsigned char *ReadImage (FILE *,
int,
int,
unsigned char[256][3],
int,
int,
int,
int);
#if PTS_SAM2P
bitmap_type bmp_load_image (FILE* filename)
#else
bitmap_type bmp_load_image (at_string filename)
#endif
{
FILE *fd;
unsigned char buffer[64];
int ColormapSize, rowbytes, Maps=0, Grey;
unsigned char ColorMap[256][3];
bitmap_type image;
#if PTS_SAM2P /**** pts ****/
fd=filename;
#else
fd = fopen (filename, "rb");
if (!fd)
FATAL1 ("Can't open \"%s\"\n", filename);
#endif
/* It is a File. Now is it a Bitmap? Read the shortest possible header.*/
if (!ReadOK(fd, buffer, 18) || (strncmp((const char *)buffer,"BM",2)))
#if PTS_SAM2P /**** pts ****/
FATALP ("BMP: Not a valid BMP file");
#else
FATAL ("Not a valid BMP file %s\n");
#endif
/* bring them to the right byteorder. Not too nice, but it should work */
Bitmap_File_Head.bfSize = ToL (&buffer[0x02]);
Bitmap_File_Head.zzHotX = ToS (&buffer[0x06]);
Bitmap_File_Head.zzHotY = ToS (&buffer[0x08]);
Bitmap_File_Head.bfOffs = ToL (&buffer[0x0a]);
Bitmap_File_Head.biSize = ToL (&buffer[0x0e]);
/* What kind of bitmap is it? */
if (Bitmap_File_Head.biSize == 12) /* OS/2 1.x ? */
{
if (!ReadOK (fd, buffer, 8))
FATALP ("BMP: Error reading BMP file header #1");
Bitmap_Head.biWidth = ToS (&buffer[0x00]); /* 12 */
Bitmap_Head.biHeight = ToS (&buffer[0x02]); /* 14 */
Bitmap_Head.biPlanes = ToS (&buffer[0x04]); /* 16 */
Bitmap_Head.biBitCnt = ToS (&buffer[0x06]); /* 18 */
Bitmap_Head.biCompr = 0;
Bitmap_Head.biSizeIm = 0;
Bitmap_Head.biXPels = Bitmap_Head.biYPels = 0;
Bitmap_Head.biClrUsed = 0;
Maps = 3;
}
else if (Bitmap_File_Head.biSize == 40) /* Windows 3.x */
{
if (!ReadOK (fd, buffer, Bitmap_File_Head.biSize - 4))
FATALP ("BMP: Error reading BMP file header #2");
Bitmap_Head.biWidth =ToL (&buffer[0x00]); /* 12 */
Bitmap_Head.biHeight =ToL (&buffer[0x04]); /* 16 */
Bitmap_Head.biPlanes =ToS (&buffer[0x08]); /* 1A */
Bitmap_Head.biBitCnt =ToS (&buffer[0x0A]); /* 1C */
Bitmap_Head.biCompr =ToL (&buffer[0x0C]); /* 1E */
Bitmap_Head.biSizeIm =ToL (&buffer[0x10]); /* 22 */
Bitmap_Head.biXPels =ToL (&buffer[0x14]); /* 26 */
Bitmap_Head.biYPels =ToL (&buffer[0x18]); /* 2A */
Bitmap_Head.biClrUsed =ToL (&buffer[0x1C]); /* 2E */
Bitmap_Head.biClrImp =ToL (&buffer[0x20]); /* 32 */
/* 36 */
Maps = 4;
}
else if (Bitmap_File_Head.biSize <= 64) /* Probably OS/2 2.x */
{
if (!ReadOK (fd, buffer, Bitmap_File_Head.biSize - 4))
FATALP ("BMP: Error reading BMP file header #3");
Bitmap_Head.biWidth =ToL (&buffer[0x00]); /* 12 */
Bitmap_Head.biHeight =ToL (&buffer[0x04]); /* 16 */
Bitmap_Head.biPlanes =ToS (&buffer[0x08]); /* 1A */
Bitmap_Head.biBitCnt =ToS (&buffer[0x0A]); /* 1C */
Bitmap_Head.biCompr =ToL (&buffer[0x0C]); /* 1E */
Bitmap_Head.biSizeIm =ToL (&buffer[0x10]); /* 22 */
Bitmap_Head.biXPels =ToL (&buffer[0x14]); /* 26 */
Bitmap_Head.biYPels =ToL (&buffer[0x18]); /* 2A */
Bitmap_Head.biClrUsed =ToL (&buffer[0x1C]); /* 2E */
Bitmap_Head.biClrImp =ToL (&buffer[0x20]); /* 32 */
/* 36 */
Maps = 3;
}
else
FATALP ("BMP: Error reading BMP file header #4");
/* Valid options 1, 4, 8, 16, 24, 32 */
/* 16 is awful, we should probably shoot whoever invented it */
/* There should be some colors used! */
ColormapSize = (Bitmap_File_Head.bfOffs - Bitmap_File_Head.biSize - 14) / Maps;
if ((Bitmap_Head.biClrUsed == 0) && (Bitmap_Head.biBitCnt <= 8))
Bitmap_Head.biClrUsed = ColormapSize;
/* Sanity checks */
if (Bitmap_Head.biHeight == 0 || Bitmap_Head.biWidth == 0)
FATALP ("BMP: Error reading BMP file header #5");
if (Bitmap_Head.biPlanes != 1)
FATALP ("BMP: Error reading BMP file header #6");
if (ColormapSize > 256 || Bitmap_Head.biClrUsed > 256)
FATALP ("BMP: Error reading BMP file header #7");
/* Windows and OS/2 declare filler so that rows are a multiple of
* word length (32 bits == 4 bytes)
*/
rowbytes= ( (Bitmap_Head.biWidth * Bitmap_Head.biBitCnt - 1) / 32) * 4 + 4;
#ifdef DEBUG
printf("\nSize: %u, Colors: %u, Bits: %u, Width: %u, Height: %u, Comp: %u, Zeile: %u\n",
Bitmap_File_Head.bfSize,Bitmap_Head.biClrUsed,Bitmap_Head.biBitCnt,Bitmap_Head.biWidth,
Bitmap_Head.biHeight, Bitmap_Head.biCompr, rowbytes);
#endif
/* Get the Colormap */
if (ReadColorMap (fd, ColorMap, ColormapSize, Maps, &Grey) == -1)
FATALP ("BMP: Cannot read the colormap");
#ifdef DEBUG
printf("Colormap read\n");
#endif
/* Get the Image and return the ID or -1 on error*/
image.bitmap = ReadImage (fd,
Bitmap_Head.biWidth,
Bitmap_Head.biHeight,
ColorMap,
Bitmap_Head.biBitCnt,
Bitmap_Head.biCompr,
rowbytes,
Grey);
BITMAP_WIDTH (image) = (at_dimen_t) Bitmap_Head.biWidth;
BITMAP_HEIGHT (image) = (at_dimen_t) Bitmap_Head.biHeight;
BITMAP_PLANES (image) = Grey ? 1 : 3;
return (image);
}
static int
ReadColorMap (FILE *fd,
unsigned char buffer[256][3],
int number,
int size,
int *grey)
{
int i;
unsigned char rgb[4];
*grey=(number>2);
for (i = 0; i < number ; i++)
{
if (!ReadOK (fd, rgb, size))
FATALP ("BMP: Bad colormap");
/* Bitmap save the colors in another order! But change only once! */
buffer[i][0] = rgb[2];
buffer[i][1] = rgb[1];
buffer[i][2] = rgb[0];
*grey = ((*grey) && (rgb[0]==rgb[1]) && (rgb[1]==rgb[2]));
}
return 0;
}
static unsigned char*
ReadImage (FILE *fd,
int width,
int height,
unsigned char cmap[256][3],
int bpp,
int compression,
int rowbytes,
int grey)
{
unsigned char v,howmuch;
int xpos = 0, ypos = 0;
unsigned char *image;
unsigned char *temp, *buffer;
long rowstride, channels;
unsigned short rgb;
int i, j;
if (bpp >= 16) /* color image */
{
XMALLOCT (image, unsigned char*, width * height * 3 * sizeof (unsigned char));
channels = 3;
}
else if (grey) /* grey image */
{
XMALLOCT (image, unsigned char*, width * height * 1 * sizeof (unsigned char));
channels = 1;
}
else /* indexed image */
{
XMALLOCT (image, unsigned char*, width * height * 1 * sizeof (unsigned char));
channels = 1;
}
XMALLOCT (buffer, unsigned char*, rowbytes);
rowstride = width * channels;
ypos = height - 1; /* Bitmaps begin in the lower left corner */
switch (bpp) {
case 32:
{
while (ReadOK (fd, buffer, rowbytes))
{
temp = image + (ypos * rowstride);
for (xpos= 0; xpos < width; ++xpos)
{
*(temp++)= buffer[xpos * 4 + 2];
*(temp++)= buffer[xpos * 4 + 1];
*(temp++)= buffer[xpos * 4];
}
--ypos; /* next line */
}
}
break;
case 24:
{
while (ReadOK (fd, buffer, rowbytes))
{
temp = image + (ypos * rowstride);
for (xpos= 0; xpos < width; ++xpos)
{
*(temp++)= buffer[xpos * 3 + 2];
*(temp++)= buffer[xpos * 3 + 1];
*(temp++)= buffer[xpos * 3];
}
--ypos; /* next line */
}
}
break;
case 16:
{
while (ReadOK (fd, buffer, rowbytes))
{
temp = image + (ypos * rowstride);
for (xpos= 0; xpos < width; ++xpos)
{
rgb= ToS(&buffer[xpos * 2]);
*(temp++)= (unsigned char)(((rgb >> 10) & 0x1f) * 8);
*(temp++)= (unsigned char)(((rgb >> 5) & 0x1f) * 8);
*(temp++)= (unsigned char)(((rgb) & 0x1f) * 8);
}
--ypos; /* next line */
}
}
break;
case 8:
case 4:
case 1:
{
if (compression == 0)
{
while (ReadOK (fd, &v, 1))
{
for (i = 1; (i <= (8 / bpp)) && (xpos < width); i++, xpos++)
{
temp = (unsigned char*) (image + (ypos * rowstride) + (xpos * channels));
*temp= (unsigned char)(( v & ( ((1<<bpp)-1) << (8-(i*bpp)) ) ) >> (8-(i*bpp)));
}
if (xpos == width)
{
(void) ReadOK (fd, buffer, rowbytes - 1 -
(width * bpp - 1) / 8);
ypos--;
xpos = 0;
}
if (ypos < 0)
break;
}
break;
}
else
{
while (ypos >= 0 && xpos <= width)
{
(void) ReadOK (fd, buffer, 2);
if ((unsigned char) buffer[0] != 0)
/* Count + Color - record */
{
for (j = 0; ((unsigned char) j < (unsigned char) buffer[0]) && (xpos < width);)
{
#ifdef DEBUG2
printf("%u %u | ",xpos,width);
#endif
for (i = 1;
((i <= (8 / bpp)) &&
(xpos < width) &&
((unsigned char) j < (unsigned char) buffer[0]));
i++, xpos++, j++)
{
temp = image + (ypos * rowstride) + (xpos * channels);
*temp = (unsigned char) ((buffer[1] & (((1<<bpp)-1) << (8 - (i * bpp)))) >> (8 - (i * bpp)));
}
}
}
if (((unsigned char) buffer[0] == 0) && ((unsigned char) buffer[1] > 2))
/* uncompressed record */
{
howmuch = buffer[1];
for (j = 0; j < howmuch; j += (8 / bpp))
{
(void) ReadOK (fd, &v, 1);
i = 1;
while ((i <= (8 / bpp)) && (xpos < width))
{
temp = image + (ypos * rowstride) + (xpos * channels);
*temp = (unsigned char) ((v & (((1<<bpp)-1) << (8-(i*bpp)))) >> (8-(i*bpp)));
i++;
xpos++;
}
}
if ((howmuch % 2) && (bpp==4))
howmuch++;
if ((howmuch / (8 / bpp)) % 2)
(void) ReadOK (fd, &v, 1);
/*if odd(x div (8 div bpp )) then blockread(f,z^,1);*/
}
if (((unsigned char) buffer[0] == 0) && ((unsigned char) buffer[1]==0))
/* Line end */
{
ypos--;
xpos = 0;
}
if (((unsigned char) buffer[0]==0) && ((unsigned char) buffer[1]==1))
/* Bitmap end */
{
break;
}
if (((unsigned char) buffer[0]==0) && ((unsigned char) buffer[1]==2))
/* Deltarecord */
{
(void) ReadOK (fd, buffer, 2);
xpos += (unsigned char) buffer[0];
ypos -= (unsigned char) buffer[1];
}
}
break;
}
}
break;
default:
/* This is very bad, we should not be here */
;
}
/* fclose (fd); */
if (bpp <= 8)
{
unsigned char *temp2, *temp3;
unsigned char index;
temp2 = temp = image;
XMALLOCT (image, unsigned char*, width * height * 3 * sizeof (unsigned char));
temp3 = image;
for (ypos = 0; ypos < height; ypos++)
{
for (xpos = 0; xpos < width; xpos++)
{
index = *temp2++;
*temp3++ = cmap[index][0];
if (!grey)
{
*temp3++ = cmap[index][1];
*temp3++ = cmap[index][2];
}
}
}
XFREE (temp);
}
XFREE (buffer);
return image;
}
#if 0 /**** pts ****/
FILE *errorfile;
char *prog_name = "bmp";
char *filename;
int interactive_bmp;
#endif
static long
ToL (unsigned char *puffer)
{
return (puffer[0] | puffer[1]<<8 | puffer[2]<<16 | puffer[3]<<24);
}
static short
ToS (unsigned char *puffer)
{
return ((short)(puffer[0] | puffer[1]<<8));
}