e7eabd46b9
git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@1545 8092fc87-c524-0410-9efc-e669fe64eaf9
388 lines
11 KiB
C++
388 lines
11 KiB
C++
/*--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 <fcntl.h>
|
|
#include <malloc.h>
|
|
#include <sys\types.h>
|
|
#include <sys\stat.h>
|
|
#include <io.h>
|
|
#include <stdio.h>
|
|
|
|
#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;
|
|
}
|