/*--pbio.c----------------------------------------------------------* * * * Routines to read & write PC Paintbrush files. * * * * Date Who Changes * * --------- --- -------------------------------------------------- * * 31-Mar-89 mjc band read & write to allow large files. * * 19-Apr-89 mjc 256 color palette added at end of paintbrush file. * * * * TBD: haven't added scroll-offset logic to "to planar" case, since * ANIMDISK is only 8 bpp. *------------------------------------------------------------------*/ #include "main.h" #include #include #include #include #include #include #define local static /* RowBytes computes the number of bytes in a row, from the width in pixels.*/ #define RowBytes(w) (((w) + 15) >> 4 << 1) /*------------------------------------------------------------------ * 256 Color Palette * * When reading the paintbrush file, the 256 color palette is * sought only if the version number in the header is 5 and the * bits per pixel * planes == 8. The last 769 bytes of the file * must start with 0x0C, and be followed by the 768 byte palette. * * When writing the paintbrush file, we write * version 5 in the header, and append the palette to the end of * the file in the same format as above (0x0C followed by 768 * bytes of palette). *------------------------------------------------------------------ */ /* Return SUCCESS, FAIL, or specific errors ... */ #define PB_NOT_ENUF_MEM 2 #define PB_NO_256_PALETTE 3 local WORD readCount; /* For compression. */ local UBYTE readChar; /* --- Forward declarations. */ void GetByte(WORD file); BOOL PutBytes(BYTE far * ptr, WORD bytes, WORD file); /*-----------------------------------------------------------------*/ typedef struct { BYTE manufacturer; BYTE version; BYTE encoding; BYTE bitsperpixel; WORD xmin, ymin, xmax, ymax; WORD hres, vres; BYTE colormap[16][3]; BYTE reserved; BYTE nplanes; WORD bytesperline; } PCPAINTBRUSH_HEADER; local PCPAINTBRUSH_HEADER *pbh = NULL; /* paintbrush header pointer */ /* Global pointer so can allocate in header, use fields in body. */ /*--------------------- Bad Paintbrush Header ----------------------*/ #define BadPbHeader(s) (FAIL) /*-------------------- Invalid Paintbrush File ---------------------*/ #define InvalidPbFile() (FAIL) #define WrongPbFileType() (FAIL) /* Expected different data format.*/ #define PCXNo256PaletteMsg() /*-----------------------------------------------------------------* * Read the header of a pc paintbrush file. * Sets bmHdr fields. Calls reSize if necessary. * Returns SUCCESS or FAIL. *-----------------------------------------------------------------*/ BOOL ReadPCX_Header(WORD file, BITMAP *bitmap, LONG *palette, BitMapHeader *bmHdr) { register WORD i; UBYTE c, palet[256][3]; if (!pbh) /* Test in case we come through twice w/o freeing. */ pbh = malloc(sizeof(PCPAINTBRUSH_HEADER) + 4); if (!pbh) return (FAIL); if (read(file, (char *)pbh, sizeof(PCPAINTBRUSH_HEADER)) != sizeof(PCPAINTBRUSH_HEADER)) return (InvalidPbFile()); /* --- To start of pixel data. */ if (0 > lseek(file, (LONG) 128, SEEK_SET)) return (InvalidPbFile()); /* --- Check that the header info is valid. */ if (pbh->manufacturer != 10) return (InvalidPbFile()); /* --- Expecting version with palette at end. */ if (pbh->version != 5) return (BadPbHeader(s_wrongPBVersion)); if (pbh->encoding != 1) return (BadPbHeader(s_unknownEncoding)); if (pbh->bitsperpixel != 1 && pbh->nplanes != 1) return (BadPbHeader(s_wrongLayout)); if (pbh->bitsperpixel != 8) return (WrongPbFileType()); /* --- Compute dimensions and # colors. */ setmem(bmHdr, sizeof(BitMapHeader), 0); bmHdr->w = 1 + pbh->xmax - pbh->xmin; bmHdr->h = 1 + pbh->ymax - pbh->ymin; bmHdr->nPlanes = pbh->bitsperpixel * pbh->nplanes; /* MAY BE PACKED. */ /* --- Get the color map. */ if (palette != NULL) { /* If a 256 color palette should be present at the end of the * file, try to fetch it from disk and copy it to the palette. */ c = 0; if (lseek(file, (LONG)-769, SEEK_END) >= 0) if (read(file, &c, 1) == 1) if (read(file, (UBYTE *)&palet[0][0], 768) == 768) if (c == 0x0C) for (i = 0; i < 256; ++i) palette[i] = ((LONG)palet[i][0] << 16) + ((LONG)palet[i][1] << 8) + ((LONG)palet[i][2]); /* --- To start of pixel data (again). */ lseek(file, (LONG)128, SEEK_SET); if (c != 0x0C) { /* ---- We couldn't read a 256 color palette at the end * of the file. */ PCXNo256PaletteMsg(); return (PB_NO_256_PALETTE); } } return (SUCCESS); /* Header done; BODY yet to do. */ } /*-----------------------------------------------------------------* * Read the pixel data into the bitmap. Return SUCCESS or FAIL. *-----------------------------------------------------------------*/ #define MaxPCXPlanes 8 /* 256-color, no mask. */ BOOL ReadPCX_BODY(WORD file, BITMAP *bitmap, BitMapHeader *bmHdr) { register WORD i, iPlane; BOOL result = SUCCESS; BYTE far *dest; WORD bodyNRows, srcPlanesToUse; WORD bodyIRow; /* Increment from 0..bodyNRows-1. */ BYTE far *dests[MaxPCXPlanes]; /* Ptrs to dest scan lines. */ WORD useBytes; /* --------- Initialization. ---------- */ readCount = 0; /* For compression. */ bodyNRows = MIN(bmHdr->h, bitmap->box.h); srcPlanesToUse = MIN(bitmap->planes + bpp_minus1, bmHdr->nPlanes); /* ---------- Read all scan lines. ---------- */ OpenIOBuffer(FALSE); for (bodyIRow = 0; bodyIRow < bodyNRows; ++bodyIRow) { readCount = 0; /* Just in case, supposed to start anew each line. */ useBytes = MIN(bitmap->width, pbh->bytesperline); /* ----- Read a scan line. */ dest = (BYTE far *)BMLineStart(bitmap, bodyIRow, 0); for (i = 0; i < pbh->bytesperline; i++) { GetByte(file); if (i < useBytes) *dest++ = readChar; } } CloseIOBuffer(file); done: return (result); } /*--------------------- Close Paintbrush File ---------------------*/ /* --- NOTE: if change, must change WritePCX_Done, since it calls this. */ void ReadPCX_Done() { /* --- Free pbh, which is shared by header & body routines. */ if (pbh) free(pbh); pbh = NULL; } void WritePCX_Done() { ReadPCX_Done(); } /*---------------------------- Get Byte ---------------------------*/ /* Set readCount & readChar. At EOF or file error, returns 0's forever. */ void GetByte(WORD file) { UBYTE c; /* --- if in the middle of a run of encoded bytes, don't read any * more */ if (readCount > 0) { --readCount; return; } readCount = 0; if (GReadOne(file, &c) != 1) { sawEOF: readCount = 32767; /* Huge count of 0's to pad the end. */ readChar = 0; return; } if ((c & 0xc0) == 0xc0) { readCount = (0x3f & c) - 1; /* "-1": consuming one of count. */ if (GReadOne(file, &c) != 1) goto sawEOF; } readChar = c; } /*----------------------------------------------------------------------* Output File Routines & Variables *----------------------------------------------------------------------*/ /*---------------------------------------------------------------* Output header for a PC Paintbrush file with the given number of bitsperpixel and planes. One of these two parms must be 1. ASSUMES OKAY TO TRASH buffer[]. Returns SUCCESS or FAIL. *---------------------------------------------------------------*/ BOOL WritePCX_Header(WORD file, BITMAP *bitmap, LONG *palette) { register PCPAINTBRUSH_HEADER *ppbh; WORD i; #define GAP_SIZE (128 - sizeof(PCPAINTBRUSH_HEADER)) UBYTE buffer1[GAP_SIZE]; /* --- init the header that we'll write. * Note: since DPaint never reads & writes at the same time, * it is okay to share the same read & write structure, * unlike in CONVERT.EXE. */ if (!pbh) /* Test in case we come through twice w/o * freeing. */ pbh = malloc(sizeof(PCPAINTBRUSH_HEADER) + 4); if (!pbh) return (FAIL); ppbh = pbh; /* Register variable for smaller, faster code. */ setmem(ppbh, sizeof(PCPAINTBRUSH_HEADER) + 4, 0); ppbh->manufacturer = 10; ppbh->version = 5; ppbh->encoding = 1; ppbh->bitsperpixel = bpp; ppbh->xmin = ppbh->ymin = 0; ppbh->xmax = bitmap->box.w - 1; ppbh->ymax = bitmap->box.h - 1; ppbh->hres = N_COLUMNS; ppbh->vres = N_LINES; /* --- bytesperline doesn't take into account multiple planes. * Output in same format as bitmap (planar vs packed). */ ppbh->bytesperline = bitmap->width; ppbh->nplanes = bitmap->planes; ppbh->reserved = 0; /* --- First 16 colors of our palette info. */ for (i = 0; i < 16; ++i) { ppbh->colormap[i][0] = (BYTE)(palette[i] >> 16); ppbh->colormap[i][1] = (BYTE)(palette[i] >> 8); ppbh->colormap[i][2] = (BYTE) palette[i]; } /* --- write the 128-byte header */ if (GWrite(file, (char *)ppbh, sizeof(PCPAINTBRUSH_HEADER)) != sizeof(PCPAINTBRUSH_HEADER)) goto error; setmem(buffer1, GAP_SIZE, 0); if (GWrite(file, buffer1, GAP_SIZE) != GAP_SIZE) error: return (FAIL); return (SUCCESS); } /* --------------- WritePCX_BODY --------------- * Write the pixel data. * Returns SUCCESS or an error condition. * ------------------------------------------------------------------- */ WORD WritePCX_BODY(WORD file, BITMAP *bitmap) { register WORD y; /* --- Write to a bit-packed file. */ for (y = 0; y < bitmap->box.h; ++y) { /* for each line in band */ if (PutBytes((BYTE far *)BMLineStart(bitmap, y, 0), pbh->bytesperline, file)) return FAIL; } return SUCCESS; } /*-------------------- Output any 256-color palette ------------------------*/ BOOL WritePCX_256Palette(WORD file, LONG *palette) { register WORD i; UBYTE c, palet[256][3]; if (pbh->version == 5 && pbh->bitsperpixel == 8) { for (i = 0; i < 256; ++i) { palet[i][0] = (BYTE)(palette[i] >> 16); palet[i][1] = (BYTE)(palette[i] >> 8); palet[i][2] = (BYTE) palette[i]; } c = 0x0C; if (GWrite(file, &c, 1) != 1 || GWrite(file, (char *)&palet[0][0], 768) != 768) return FAIL; } return SUCCESS; } /*------------------------------------------------------------------- * Write bytes to a file, handling packing as it goes. * Returns SUCCESS or FAIL. *------------------------------------------------------------------- */ BOOL PutBytes(BYTE far *ptr, WORD bytes, WORD file) { register WORD startbyte, count; char b; while (bytes > 0) { /* --- check for a repeating byte value */ startbyte = *ptr++; --bytes; count = 1; while (*ptr == startbyte && bytes > 0 && count < 63) { ++ptr; --bytes; ++count; } /* --- If we can pack the sequence, or if we have to add a * byte before it because the top 2 bits of the value * are 1's, write a packed sequence of 2 bytes. * Otherwise, just write the byte value. */ if (count > 1 || (startbyte & 0xc0) == 0xc0) { b = 0xc0 | count; if (GWrite(file, &b, 1) != 1) return FAIL; } b = startbyte; if (GWrite(file, &b, 1) != 1) return FAIL; } return SUCCESS; }