This is xvbmp.c in view mode; [Download] [Up]
/*
* xvbmp.c - i/o routines for .BMP files (MS Windows 3.x)
*
* LoadBMP(fname, numcols)
* WriteBMP(fp, pic, ptype, w, h, r, g, b, numcols, style);
*/
/* Copyright Notice
* ================
* Copyright 1989, 1990, 1991, 1992, 1993 by John Bradley
*
* Permission to use, copy, and distribute XV in its entirety, for
* non-commercial purposes, is hereby granted without fee, provided that
* this license information and copyright notice appear in all copies.
*
* Note that distributing XV 'bundled' in with ANY product is considered
* to be a 'commercial purpose'.
*
* Also note that any copies of XV that are distributed MUST be built
* and/or configured to be in their 'unregistered copy' mode, so that it
* is made obvious to the user that XV is shareware, and that they should
* consider donating, or at least reading this License Info.
*
* The software may be modified for your own purposes, but modified
* versions may NOT be distributed without prior consent of the author.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the author be held liable for any damages
* arising from the use of this software.
*
* If you would like to do something with XV that this copyright
* prohibits (such as distributing it with a commercial product,
* using portions of the source in some other program, etc.), please
* contact the author (preferably via email). Arrangements can
* probably be worked out.
*
* XV is shareware for PERSONAL USE only. You may use XV for your own
* amusement, and if you find it nifty, useful, generally cool, or of
* some value to you, your non-deductable donation would be greatly
* appreciated. $25 is the suggested donation, though, of course,
* larger donations are quite welcome. Folks who donate $25 or more
* can receive a Real Nice bound copy of the XV manual for no extra
* charge.
*
* Commercial, government, and institutional users MUST register their
* copies of XV, for the exceedingly REASONABLE price of just $25 per
* workstation/X terminal. Site licenses are available for those who
* wish to run XV on a large number of machines. Contact the author
* for more details.
*
* The author may be contacted via:
* US Mail: John Bradley
* 1053 Floyd Terrace
* Bryn Mawr, PA 19010
*
* Phone: (215) 898-8813
* EMail: bradley@cis.upenn.edu
*/
#include "xv.h"
/* comments on error handling:
a truncated file is not considered a Major Error. The file is loaded, the
rest of the pic is filled with 0's.
a file with garbage characters in it is an unloadable file. All allocated
stuff is tossed, and LoadPBM returns non-zero
not being able to malloc is a Fatal Error. The program is aborted. */
#define BI_RGB 0
#define BI_RLE8 1
#define BI_RLE4 2
static long filesize;
#ifdef __STDC__
static int loadBMP1(FILE *, byte *, int, int);
static int loadBMP4(FILE *, byte *, int, int, int);
static int loadBMP8(FILE *, byte *, int, int, int);
static int loadBMP24(FILE *, byte *, int, int);
static unsigned int getshort(FILE *);
static unsigned int getint(FILE *);
static void putshort(FILE *, int);
static void putint(FILE *, int);
static void writeBMP1(FILE *, byte *, int, int);
static void writeBMP4(FILE *, byte *, int, int);
static void writeBMP8(FILE *, byte *, int, int);
static void writeBMP24(FILE *, byte *, int, int);
static int bmpError(char *, char *);
#else
static int loadBMP1(), loadBMP4(), loadBMP8(), loadBMP24();
static unsigned int getshort(), getint();
static void putshort(), putint();
static void writeBMP1(), writeBMP4(), writeBMP8(), writeBMP24();
static int bmpError();
#endif
/*******************************************/
int LoadBMP(fname, pinfo)
char *fname;
PICINFO *pinfo;
/*******************************************/
{
FILE *fp;
int i, c, c1, rv;
unsigned int bfSize, bfOffBits, biSize, biWidth, biHeight, biPlanes;
unsigned int biBitCount, biCompression, biSizeImage, biXPelsPerMeter;
unsigned int biYPelsPerMeter, biClrUsed, biClrImportant;
char *cmpstr;
byte *pic24, *pic8;
char buf[512], *bname;
/* returns '1' on success */
pic8 = pic24 = (byte *) NULL;
bname = BaseName(fname);
fp=fopen(fname,"r");
if (!fp) return (bmpError(bname, "couldn't open file"));
fseek(fp, 0L, 2); /* figure out the file size */
filesize = ftell(fp);
fseek(fp, 0L, 0);
/* read the file type (first two bytes) */
c = getc(fp); c1 = getc(fp);
if (c!='B' || c1!='M') { bmpError(bname,"file type != 'BM'"); goto ERROR; }
bfSize = getint(fp);
getshort(fp); /* reserved and ignored */
getshort(fp);
bfOffBits = getint(fp);
biSize = getint(fp);
biWidth = getint(fp);
biHeight = getint(fp);
biPlanes = getshort(fp);
biBitCount = getshort(fp);
biCompression = getint(fp);
biSizeImage = getint(fp);
biXPelsPerMeter = getint(fp);
biYPelsPerMeter = getint(fp);
biClrUsed = getint(fp);
biClrImportant = getint(fp);
if (DEBUG>1) {
fprintf(stderr,"\nLoadBMP:\tbfSize=%d, bfOffBits=%d\n",bfSize,bfOffBits);
fprintf(stderr,"\t\tbiSize=%d, biWidth=%d, biHeight=%d, biPlanes=%d\n",
biSize, biWidth, biHeight, biPlanes);
fprintf(stderr,"\t\tbiBitCount=%d, biCompression=%d, biSizeImage=%d\n",
biBitCount, biCompression, biSizeImage);
fprintf(stderr,"\t\tbiX,YPelsPerMeter=%d,%d biClrUsed=%d, biClrImp=%d\n",
biXPelsPerMeter, biYPelsPerMeter, biClrUsed, biClrImportant);
}
if (ferror(fp)) { bmpError(bname,"EOF reached in file header"); goto ERROR; }
/* error checking */
if ((biBitCount!=1 && biBitCount!=4 && biBitCount!=8 && biBitCount!=24) ||
biPlanes!=1 || biCompression>BI_RLE4) {
sprintf(buf,"Bogus BMP File! (bitCount=%d, Planes=%d, Compression=%d)",
biBitCount, biPlanes, biCompression);
bmpError(bname, buf);
goto ERROR;
}
if (((biBitCount==1 || biBitCount==24) && biCompression != BI_RGB) ||
(biBitCount==4 && biCompression==BI_RLE8) ||
(biBitCount==8 && biCompression==BI_RLE4)) {
sprintf(buf,"Bogus BMP File! (bitCount=%d, Compression=%d)",
biBitCount, biCompression);
bmpError(bname, buf);
goto ERROR;
}
/* skip ahead to colormap, using biSize */
c = biSize - 40; /* 40 bytes read from biSize to biClrImportant */
for (i=0; i<c; i++) getc(fp);
/* load up colormap, if any */
if (biBitCount!=24) {
int i, cmaplen;
cmaplen = 1 << biBitCount;
for (i=0; i<cmaplen; i++) {
pinfo->b[i] = getc(fp);
pinfo->g[i] = getc(fp);
pinfo->r[i] = getc(fp);
getc(fp); /* unused */
}
if (ferror(fp))
{ bmpError(bname,"EOF reached in BMP colormap"); goto ERROR; }
if (DEBUG>1) {
fprintf(stderr,"LoadBMP: BMP colormap: (RGB order)\n");
for (i=0; i<cmaplen; i++) {
fprintf(stderr,"%02x%02x%02x ", pinfo->r[i],pinfo->g[i],pinfo->b[i]);
}
fprintf(stderr,"\n\n");
}
}
/* create pic8 or pic24 */
if (biBitCount==24) {
pic24 = (byte *) calloc(biWidth * biHeight * 3, 1);
if (!pic24) return (bmpError(bname, "couldn't malloc 'pic24'"));
}
else {
pic8 = (byte *) calloc(biWidth * biHeight, 1);
if (!pic8) return(bmpError(bname, "couldn't malloc 'pic8'"));
}
WaitCursor();
/* load up the image */
if (biBitCount == 1) rv = loadBMP1(fp,pic8,biWidth,biHeight);
else if (biBitCount == 4) rv = loadBMP4(fp,pic8,biWidth,biHeight,
biCompression);
else if (biBitCount == 8) rv = loadBMP8(fp,pic8,biWidth,biHeight,
biCompression);
else rv = loadBMP24(fp,pic24,biWidth,biHeight);
if (rv) bmpError(bname, "File appears truncated. Winging it.\n");
fclose(fp);
if (biBitCount == 24) {
pinfo->pic = pic24;
pinfo->type = PIC24;
}
else {
pinfo->pic = pic8;
pinfo->type = PIC8;
}
cmpstr = "";
if (biCompression == BI_RLE4) cmpstr = ", RLE4 compressed";
else if (biCompression == BI_RLE8) cmpstr = ", RLE8 compressed";
pinfo->w = biWidth; pinfo->h = biHeight;
pinfo->frmType = F_BMP;
pinfo->colType = F_FULLCOLOR;
sprintf(pinfo->fullInfo, "BMP, %d bit%s per pixel%s. (%ld bytes)",
biBitCount, (biBitCount == 1) ? "" : "s",
cmpstr, filesize);
sprintf(pinfo->shrtInfo, "%dx%d BMP.", biWidth, biHeight);
pinfo->comment = (char *) NULL;
return 1;
ERROR:
fclose(fp);
return 0;
}
/*******************************************/
static int loadBMP1(fp, pic8, w, h)
FILE *fp;
byte *pic8;
int w,h;
{
int i,j,c,bitnum,padw;
byte *pp;
c = 0;
padw = ((w + 31)/32) * 32; /* 'w', padded to be a multiple of 32 */
for (i=h-1; i>=0; i--) {
pp = pic8 + (i * w);
if ((i&0x3f)==0) WaitCursor();
for (j=bitnum=0; j<padw; j++,bitnum++) {
if ((bitnum&7) == 0) { /* read the next byte */
c = getc(fp);
bitnum = 0;
}
if (j<w) {
*pp++ = (c & 0x80) ? 1 : 0;
c <<= 1;
}
}
if (ferror(fp)) break;
}
return (ferror(fp));
}
/*******************************************/
static int loadBMP4(fp, pic8, w, h, comp)
FILE *fp;
byte *pic8;
int w,h,comp;
{
int i,j,c,c1,x,y,nybnum,padw,rv;
byte *pp;
rv = 0;
c = c1 = 0;
if (comp == BI_RGB) { /* read uncompressed data */
padw = ((w + 7)/8) * 8; /* 'w' padded to a multiple of 8pix (32 bits) */
for (i=h-1; i>=0; i--) {
pp = pic8 + (i * w);
if ((i&0x3f)==0) WaitCursor();
for (j=nybnum=0; j<padw; j++,nybnum++) {
if ((nybnum & 1) == 0) { /* read next byte */
c = getc(fp);
nybnum = 0;
}
if (j<w) {
*pp++ = (c & 0xf0) >> 4;
c <<= 4;
}
}
if (ferror(fp)) break;
}
}
else if (comp == BI_RLE4) { /* read RLE4 compressed data */
x = y = 0;
pp = pic8 + x + (h-y-1)*w;
while (y<h) {
c = getc(fp); if (c == EOF) { rv = 1; break; }
if (c) { /* encoded mode */
c1 = getc(fp);
for (i=0; i<c; i++,x++,pp++)
*pp = (i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f);
}
else { /* c==0x00 : escape codes */
c = getc(fp); if (c == EOF) { rv = 1; break; }
if (c == 0x00) { /* end of line */
x=0; y++; pp = pic8 + x + (h-y-1)*w;
}
else if (c == 0x01) break; /* end of pic8 */
else if (c == 0x02) { /* delta */
c = getc(fp); x += c;
c = getc(fp); y += c;
pp = pic8 + x + (h-y-1)*w;
}
else { /* absolute mode */
for (i=0; i<c; i++, x++, pp++) {
if ((i&1) == 0) c1 = getc(fp);
*pp = (i&1) ? (c1 & 0x0f) : ((c1>>4)&0x0f);
}
if (((c&3)==1) || ((c&3)==2)) getc(fp); /* read pad byte */
}
} /* escape processing */
if (ferror(fp)) break;
} /* while */
}
else {
fprintf(stderr,"unknown BMP compression type 0x%0x\n", comp);
}
if (ferror(fp)) rv = 1;
return rv;
}
/*******************************************/
static int loadBMP8(fp, pic8, w, h, comp)
FILE *fp;
byte *pic8;
int w,h,comp;
{
int i,j,c,c1,padw,x,y,rv;
byte *pp;
rv = 0;
if (comp == BI_RGB) { /* read uncompressed data */
padw = ((w + 3)/4) * 4; /* 'w' padded to a multiple of 4pix (32 bits) */
for (i=h-1; i>=0; i--) {
pp = pic8 + (i * w);
if ((i&0x3f)==0) WaitCursor();
for (j=0; j<padw; j++) {
c = getc(fp); if (c==EOF) rv = 1;
if (j<w) *pp++ = c;
}
if (ferror(fp)) break;
}
}
else if (comp == BI_RLE8) { /* read RLE8 compressed data */
x = y = 0;
pp = pic8 + x + (h-y-1)*w;
while (y<h) {
c = getc(fp); if (c == EOF) { rv = 1; break; }
if (c) { /* encoded mode */
c1 = getc(fp);
for (i=0; i<c; i++,x++,pp++) *pp = c1;
}
else { /* c==0x00 : escape codes */
c = getc(fp); if (c == EOF) { rv = 1; break; }
if (c == 0x00) { /* end of line */
x=0; y++; pp = pic8 + x + (h-y-1)*w;
}
else if (c == 0x01) break; /* end of pic8 */
else if (c == 0x02) { /* delta */
c = getc(fp); x += c;
c = getc(fp); y += c;
pp = pic8 + x + (h-y-1)*w;
}
else { /* absolute mode */
for (i=0; i<c; i++, x++, pp++) {
c1 = getc(fp);
*pp = c1;
}
if (c & 1) getc(fp); /* odd length run: read an extra pad byte */
}
} /* escape processing */
if (ferror(fp)) break;
} /* while */
}
else {
fprintf(stderr,"unknown BMP compression type 0x%0x\n", comp);
}
if (ferror(fp)) rv = 1;
return rv;
}
/*******************************************/
static int loadBMP24(fp, pic24, w, h)
FILE *fp;
byte *pic24;
int w,h;
{
int i,j,padb;
byte *pp;
padb = (4 - ((w*3) % 4)) & 0x03; /* # of pad bytes to read at EOscanline */
for (i=h-1; i>=0; i--) {
pp = pic24 + (i * w * 3);
if ((i&0x3f)==0) WaitCursor();
for (j=0; j<w; j++) {
*pp++ = getc(fp); /* red */
*pp++ = getc(fp); /* green */
*pp++ = getc(fp); /* blue */
}
for (j=0; j<padb; j++) getc(fp);
if (ferror(fp)) break;
}
return (ferror(fp));
}
/*******************************************/
static unsigned int getshort(fp)
FILE *fp;
{
int c, c1;
c = getc(fp); c1 = getc(fp);
return ((unsigned int) c) + (((unsigned int) c1) << 8);
}
/*******************************************/
static unsigned int getint(fp)
FILE *fp;
{
int c, c1, c2, c3;
c = getc(fp); c1 = getc(fp); c2 = getc(fp); c3 = getc(fp);
return ((unsigned int) c) +
(((unsigned int) c1) << 8) +
(((unsigned int) c2) << 16) +
(((unsigned int) c3) << 24);
}
/*******************************************/
static void putshort(fp, i)
FILE *fp;
int i;
{
int c, c1;
c = ((unsigned int ) i) & 0xff; c1 = (((unsigned int) i)>>8) & 0xff;
putc(c, fp); putc(c1,fp);
}
/*******************************************/
static void putint(fp, i)
FILE *fp;
int i;
{
int c, c1, c2, c3;
c = ((unsigned int ) i) & 0xff;
c1 = (((unsigned int) i)>>8) & 0xff;
c2 = (((unsigned int) i)>>16) & 0xff;
c3 = (((unsigned int) i)>>24) & 0xff;
putc(c, fp); putc(c1,fp); putc(c2,fp); putc(c3,fp);
}
static byte pc2nc[256],r1[256],g1[256],b1[256];
/*******************************************/
int WriteBMP(fp,pic824,ptype,w,h,rmap,gmap,bmap,numcols,colorstyle)
FILE *fp;
byte *pic824;
int ptype,w,h;
byte *rmap, *gmap, *bmap;
int numcols, colorstyle;
{
/*
* if PIC8, and colorstyle == F_FULLCOLOR, F_GREYSCALE, or F_REDUCED,
* the program writes an uncompressed 4- or 8-bit image (depending on
* the value of numcols)
*
* if PIC24, and colorstyle == F_FULLCOLOR, program writes an uncompressed
* 24-bit image
* if PIC24 and colorstyle = F_GREYSCALE, program writes an uncompressed
* 8-bit image
* note that PIC24 and F_BWDITHER/F_REDUCED won't happen
*
* if colorstyle == F_BWDITHER, it writes a 1-bit image
*
*/
int i,j, nc, nbits, bperlin, cmaplen;
byte *graypic, *sp, *dp, graymap[256];
nc = nbits = cmaplen = 0;
graypic = NULL;
if (ptype == PIC24 && colorstyle == F_GREYSCALE) {
/* generate a faked 8-bit per pixel image with a grayscale cmap,
so that it can just fall through existing 8-bit code */
graypic = (byte *) malloc(w*h);
if (!graypic) FatalError("unable to malloc in WriteBMP()");
for (i=0,sp=pic824,dp=graypic; i<w*h; i++,sp+=3, dp++) {
*dp = MONO(sp[0],sp[1],sp[2]);
}
for (i=0; i<256; i++) graymap[i] = i;
rmap = gmap = bmap = graymap;
numcols = 256;
ptype = PIC8;
pic824 = graypic;
}
if (ptype == PIC24) { /* is F_FULLCOLOR */
nbits = 24;
cmaplen = 0;
nc = 0;
}
else if (ptype == PIC8) {
/* we may have duplicate colors in the colormap, and we'd prefer not to.
* build r1,g1,b1 (a contiguous, minimum set colormap), and pc2nc[], a
* array that maps 'pic8' values (0-numcols) into corresponding values
* in the r1,g1,b1 colormaps (0-nc)
*/
for (i=0; i<256; i++) { pc2nc[i] = r1[i] = g1[i] = b1[i] = 0; }
nc = 0;
for (i=0; i<numcols; i++) {
/* see if color #i is a duplicate */
for (j=0; j<i; j++) {
if (rmap[i] == rmap[j] && gmap[i] == gmap[j] &&
bmap[i] == bmap[j]) break;
}
if (j==i) { /* wasn't found */
pc2nc[i] = nc;
r1[nc] = rmap[i];
g1[nc] = gmap[i];
b1[nc] = bmap[i];
nc++;
}
else pc2nc[i] = pc2nc[j];
}
/* determine how many bits per pixel we'll be writing */
if (colorstyle == F_BWDITHER || nc <= 2) nbits = 1;
else if (nc<=16) nbits = 4;
else nbits = 8;
cmaplen = 1<<nbits; /* # of entries in cmap */
}
bperlin = ((w * nbits + 31) / 32) * 4; /* # bytes written per line */
putc('B', fp); putc('M', fp); /* BMP file magic number */
/* compute filesize and write it */
i = 14 + /* size of bitmap file header */
40 + /* size of bitmap info header */
(cmaplen * 4) + /* size of colormap */
bperlin * h; /* size of image data */
putint(fp, i);
putshort(fp, 0); /* reserved1 */
putshort(fp, 0); /* reserved2 */
putint(fp, 14 + 40 + (cmaplen * 4)); /* offset from BOfile to BObitmap */
putint(fp, 40); /* biSize: size of bitmap info header */
putint(fp, w); /* biWidth */
putint(fp, h); /* biHeight */
putshort(fp, 1); /* biPlanes: must be '1' */
putshort(fp, nbits); /* biBitCount: 1,4,8, or 24 */
putint(fp, BI_RGB); /* biCompression: BI_RGB, BI_RLE8 or BI_RLE4 */
putint(fp, bperlin*h); /* biSizeImage: size of raw image data */
putint(fp, 75 * 39); /* biXPelsPerMeter: (75dpi * 39" per meter) */
putint(fp, 75 * 39); /* biYPelsPerMeter: (75dpi * 39" per meter) */
putint(fp, nc); /* biClrUsed: # of colors used in cmap */
putint(fp, nc); /* biClrImportant: same as above */
/* write out the colormap */
for (i=0; i<cmaplen; i++) {
if (colorstyle == F_GREYSCALE) {
j = MONO(r1[i],g1[i],b1[i]);
putc(j,fp); putc(j,fp); putc(j,fp); putc(0,fp);
}
else {
putc(b1[i],fp);
putc(g1[i],fp);
putc(r1[i],fp);
putc(0,fp);
}
}
/* write out the image */
if (nbits == 1) writeBMP1 (fp, pic824, w, h);
else if (nbits == 4) writeBMP4 (fp, pic824, w, h);
else if (nbits == 8) writeBMP8 (fp, pic824, w, h);
else if (nbits == 24) writeBMP24(fp, pic824, w, h);
if (graypic) free(graypic);
if (ferror(fp)) return -1;
return 0;
}
/*******************************************/
static void writeBMP1(fp, pic8, w, h)
FILE *fp;
byte *pic8;
int w,h;
{
int i,j,c,bitnum,padw;
byte *pp;
padw = ((w + 31)/32) * 32; /* 'w', padded to be a multiple of 32 */
for (i=h-1; i>=0; i--) {
pp = pic8 + (i * w);
if ((i&0x3f)==0) WaitCursor();
for (j=bitnum=c=0; j<=padw; j++,bitnum++) {
if (bitnum == 8) { /* write the next byte */
putc(c,fp);
bitnum = c = 0;
}
c <<= 1;
if (j<w) {
c |= (pc2nc[*pp++] & 0x01);
}
}
}
}
/*******************************************/
static void writeBMP4(fp, pic8, w, h)
FILE *fp;
byte *pic8;
int w,h;
{
int i,j,c,nybnum,padw;
byte *pp;
padw = ((w + 7)/8) * 8; /* 'w' padded to a multiple of 8pix (32 bits) */
for (i=h-1; i>=0; i--) {
pp = pic8 + (i * w);
if ((i&0x3f)==0) WaitCursor();
for (j=nybnum=c=0; j<=padw; j++,nybnum++) {
if (nybnum == 2) { /* write next byte */
putc((c&0xff), fp);
nybnum = c = 0;
}
c <<= 4;
if (j<w) {
c |= (pc2nc[*pp] & 0x0f);
pp++;
}
}
}
}
/*******************************************/
static void writeBMP8(fp, pic8, w, h)
FILE *fp;
byte *pic8;
int w,h;
{
int i,j,c,padw;
byte *pp;
padw = ((w + 3)/4) * 4; /* 'w' padded to a multiple of 4pix (32 bits) */
for (i=h-1; i>=0; i--) {
pp = pic8 + (i * w);
if ((i&0x3f)==0) WaitCursor();
for (j=0; j<w; j++) putc(pc2nc[*pp++], fp);
for ( ; j<padw; j++) putc(0, fp);
}
}
/*******************************************/
static void writeBMP24(fp, pic24, w, h)
FILE *fp;
byte *pic24;
int w,h;
{
int i,j,c,padb;
byte *pp;
padb = (4 - ((w*3) % 4)) & 0x03; /* # of pad bytes to write at EOscanline */
for (i=h-1; i>=0; i--) {
pp = pic24 + (i * w * 3);
if ((i&0x3f)==0) WaitCursor();
for (j=0; j<w; j++) {
putc(*pp++, fp);
putc(*pp++, fp);
putc(*pp++, fp);
}
for (j=0; j<padb; j++) putc(0, fp);
}
}
/*******************************************/
static int bmpError(fname, st)
char *fname, *st;
{
SetISTR(ISTR_WARNING,"%s: %s", fname, st);
return 0;
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.