From fa052219e3bae79161ffbf8f4e23a57dc2ba4ca1 Mon Sep 17 00:00:00 2001 From: uz Date: Sun, 26 Feb 2012 15:27:36 +0000 Subject: [PATCH] First version of PCX reading routine is completed but mostly untested. git-svn-id: svn://svn.cc65.org/cc65/trunk@5555 b7a2c559-68d2-44c3-8de9-860c34a00d81 --- src/sp65/color.c | 69 +++++++++++ src/sp65/color.h | 30 +++++ src/sp65/make/gcc.mak | 1 + src/sp65/palette.c | 16 +++ src/sp65/palette.h | 3 + src/sp65/pcx.c | 267 +++++++++++++++++++++++++++++++++++++----- 6 files changed, 358 insertions(+), 28 deletions(-) create mode 100644 src/sp65/color.c diff --git a/src/sp65/color.c b/src/sp65/color.c new file mode 100644 index 000000000..e1abd79be --- /dev/null +++ b/src/sp65/color.c @@ -0,0 +1,69 @@ +/*****************************************************************************/ +/* */ +/* color.c */ +/* */ +/* Color definition for the sp65 sprite and bitmap utility */ +/* */ +/* */ +/* */ +/* (C) 2012, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include "color.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +#if !defined(HAVE_INLINE) + +Color RGB (unsigned char R, unsigned char G, unsigned char B) +/* Generate a color value */ +{ + Color C; + C.R = R; C.G = G; C.B = B; C.A = 0; + return C; +} + + + +Color RGBA (unsigned char R, unsigned char G, unsigned char B, unsigned char A) +/* Generate a color value */ +{ + Color C; + C.R = R; C.G = G; C.B = B; C.A = A; + return C; +} + +#endif + + + diff --git a/src/sp65/color.h b/src/sp65/color.h index 6502bc9ff..3646837f5 100644 --- a/src/sp65/color.h +++ b/src/sp65/color.h @@ -60,6 +60,36 @@ struct Color { /*****************************************************************************/ + +#if defined(HAVE_INLINE) + +INLINE Color RGB (unsigned char R, unsigned char G, unsigned char B) +/* Generate a color value */ +{ + Color C; + C.R = R; C.G = G; C.B = B; C.A = 0; + return C; +} + +INLINE Color RGBA (unsigned char R, unsigned char G, unsigned char B, unsigned char A) +/* Generate a color value */ +{ + Color C; + C.R = R; C.G = G; C.B = B; C.A = A; + return C; +} + +#else + +Color RGB (unsigned char R, unsigned char G, unsigned char B); +/* Generate a color value */ + +Color RGBA (unsigned char R, unsigned char G, unsigned char B, unsigned char A); +/* Generate a color value */ + +#endif + + /* End of color.h */ diff --git a/src/sp65/make/gcc.mak b/src/sp65/make/gcc.mak index 94cb579c6..ed3ebf318 100644 --- a/src/sp65/make/gcc.mak +++ b/src/sp65/make/gcc.mak @@ -23,6 +23,7 @@ LDFLAGS = # List of all object files OBJS = bitmap.o \ + color.o \ error.o \ fileio.o \ main.o \ diff --git a/src/sp65/palette.c b/src/sp65/palette.c index 3ada051ba..f113d9670 100644 --- a/src/sp65/palette.c +++ b/src/sp65/palette.c @@ -66,6 +66,22 @@ Palette* NewPalette (unsigned Entries) +Palette* NewMonochromePalette (void) +/* Create and return a palette with two entries (black and white) */ +{ + /* Create a new palette */ + Palette* P = NewPalette (2); + + /* Set the two colors */ + P->Entries[0] = RGBA (0x00, 0x00, 0x00, 0x00); + P->Entries[1] = RGBA (0xFF, 0xFF, 0xFF, 0x00); + + /* Return the new palette */ + return P; +} + + + void FreePalette (Palette* P) /* Free a dynamically allocated palette */ { diff --git a/src/sp65/palette.h b/src/sp65/palette.h index c925f81c0..05a10adf9 100644 --- a/src/sp65/palette.h +++ b/src/sp65/palette.h @@ -66,6 +66,9 @@ struct Palette { Palette* NewPalette (unsigned Entries); /* Create a new palette with the given number of entries */ +Palette* NewMonochromePalette (void); +/* Create and return a palette with two entries (black and white) */ + void FreePalette (Palette* P); /* Free a dynamically allocated palette */ diff --git a/src/sp65/pcx.c b/src/sp65/pcx.c index 5903a9066..3a9ccb3b0 100644 --- a/src/sp65/pcx.c +++ b/src/sp65/pcx.c @@ -56,6 +56,7 @@ /* Some PCX constants */ #define PCX_MAGIC_ID 0x0A +#define PCX_MAX_PLANES 4 /* A raw PCX header is just a block of bytes */ typedef unsigned char RawPCXHeader[128]; @@ -63,25 +64,25 @@ typedef unsigned char RawPCXHeader[128]; /* Structured PCX header */ typedef struct PCXHeader PCXHeader; struct PCXHeader { - unsigned Id; - unsigned FileVersion; - unsigned Compressed; - unsigned BPP; - unsigned XMin; - unsigned YMin; - unsigned XMax; - unsigned YMax; - unsigned XDPI; - unsigned YDPI; - unsigned Planes; - unsigned BytesPerPlane; - unsigned PalInfo; - unsigned ScreenWidth; - unsigned ScreenHeight; + unsigned Id; + unsigned FileVersion; + unsigned Compressed; + unsigned BPP; + unsigned XMin; + unsigned YMin; + unsigned XMax; + unsigned YMax; + unsigned XDPI; + unsigned YDPI; + unsigned Planes; + unsigned BytesPerPlane; + unsigned PalInfo; + unsigned ScreenWidth; + unsigned ScreenHeight; /* Calculated data */ - unsigned Width; - unsigned Height; + unsigned Width; + unsigned Height; }; /* Read a little endian word from a byte array at offset O */ @@ -142,9 +143,16 @@ static PCXHeader* ReadPCXHeader (FILE* F, const char* Name) Error ("Unsupported compression (%d) in PCX file `%s'", P->Compressed, Name); } - if (P->BPP != 1 && P->BPP != 4 && P->BPP != 8) { - Error ("Unsupported bit depth (%u) in PCX file `%s'", - P->BPP, Name); + /* We support: + * - one plane with either 1 or 8 bits per pixel + * - three planes with 8 bits per pixel + * - four planes with 8 bits per pixel (does this exist?) + */ + if (!((P->BPP == 1 && P->Planes == 1) || + (P->BPP == 8 && (P->Planes == 1 || P->Planes == 3 || P->Planes == 4)))) { + /* We could support others, but currently we don't */ + Error ("Unsupported PCX format: %u planes, %u bpp in PCX file `%s'", + P->Planes, P->BPP, Name); } if (P->PalInfo != 1 && P->PalInfo != 2) { Error ("Unsupported palette info (%u) in PCX file `%s'", @@ -164,8 +172,8 @@ static PCXHeader* ReadPCXHeader (FILE* F, const char* Name) static void DumpPCXHeader (const PCXHeader* P, const char* Name) /* Dump the header of the PCX file in readable form to stdout */ { - printf ("File name: %s\n", Name); - printf ("PCX Version: "); + printf ("File name: %s\n", Name); + printf ("PCX Version: "); switch (P->FileVersion) { case 0: puts ("2.5"); break; case 2: puts ("2.8 with palette"); break; @@ -173,12 +181,56 @@ static void DumpPCXHeader (const PCXHeader* P, const char* Name) case 4: puts ("PCX for Windows without palette"); break; case 5: puts ("3.0"); break; } - printf ("Image type: %s\n", P->PalInfo? "color" : "grayscale"); - printf ("Compression: %s\n", P->Compressed? "RLE" : "None"); - printf ("Structure: %u planes of %u bits\n", P->Planes, P->BPP); - printf ("Bounding box: [%u/%u - %u/%u]\n", P->XMin, P->YMin, P->XMax, P->YMax); - printf ("Resolution: %u/%u DPI\n", P->XDPI, P->YDPI); - printf ("Screen size: %u/%u\n", P->ScreenWidth, P->ScreenHeight); + printf ("Image type: %s\n", P->PalInfo? "color" : "grayscale"); + printf ("Compression: %s\n", P->Compressed? "RLE" : "None"); + printf ("Structure: %u planes of %u bits\n", P->Planes, P->BPP); + printf ("Bounding box: [%u/%u - %u/%u]\n", P->XMin, P->YMin, P->XMax, P->YMax); + printf ("Resolution: %u/%u DPI\n", P->XDPI, P->YDPI); + printf ("Screen size: %u/%u\n", P->ScreenWidth, P->ScreenHeight); + printf ("Bytes per plane: %u\n", P->BytesPerPlane); +} + + + +static void ReadPlane (FILE* F, PCXHeader* P, unsigned char* L) +/* Read one (possibly compressed) plane from the file */ +{ + if (P->Compressed) { + + /* Uncompress RLE data */ + unsigned Remaining = P->Width; + while (Remaining) { + + unsigned char C; + + /* Read the next byte */ + unsigned char B = Read8 (F); + + /* Check for a run length */ + if ((B & 0xC0) == 0xC0) { + C = (B & 0x3F); /* Count */ + B = Read8 (F); /* Value */ + } else { + C = 1; + } + + /* Write the data to the buffer */ + if (C > Remaining) { + C = Remaining; + } + memset (L, B, C); + + /* Bump counters */ + L += C; + Remaining -= C; + + } + } else { + + /* Just read one line */ + ReadData (F, L, P->Width); + + } } @@ -188,6 +240,11 @@ Bitmap* ReadPCXFile (const char* Name) { PCXHeader* P; Bitmap* B; + unsigned char* L; + Pixel* Px; + unsigned X, Y; + + /* Open the file */ FILE* F = fopen (Name, "rb"); @@ -203,6 +260,160 @@ Bitmap* ReadPCXFile (const char* Name) DumpPCXHeader (P, Name); } + /* Create the bitmap */ + B = NewBitmap (P->Width, P->Height); + + /* Determine the type of the bitmap */ + switch (P->Planes) { + case 1: B->Type = (P->PalInfo? bmIndexed : bmMonochrome); break; + case 3: B->Type = bmRGB; break; + case 4: B->Type = bmRGBA; break; + default:Internal ("Unexpected number of planes"); + } + + /* Remember the PCX header in the tag */ + B->Tag = P; + + /* Allocate memory for the scan line */ + L = xmalloc (P->Width); + + /* Read the pixel data */ + Px = B->Data; + if (P->Planes == 1) { + + /* This is either monochrome or indexed */ + if (P->BPP == 1) { + /* Monochrome */ + for (Y = 0, Px = B->Data; Y < P->Height; ++Y) { + + unsigned I; + unsigned char Mask; + + /* Read the plane */ + ReadPlane (F, P, L); + + /* Create pixels */ + for (X = 0, I = 0, Mask = 0x01; X < P->Width; ++Px) { + Px->Index = (L[I] & Mask) != 0; + if (Mask == 0x80) { + Mask = 0x01; + ++I; + } else { + Mask <<= 1; + } + } + + } + } else { + /* One plane with 8bpp is indexed */ + for (Y = 0, Px = B->Data; Y < P->Height; ++Y) { + + /* Read the plane */ + ReadPlane (F, P, L); + + /* Create pixels */ + for (X = 0; X < P->Width; ++X, ++Px) { + Px->Index = L[X]; + } + } + } + } else { + /* 3 or 4 planes are RGB or RGBA (don't know if this exists) */ + for (Y = 0, Px = B->Data; Y < P->Height; ++Y) { + + /* Read the R plane and move the data */ + ReadPlane (F, P, L); + for (X = 0; X < P->Width; ++X, ++Px) { + Px->C.R = L[X]; + } + + /* Read the G plane and move the data */ + ReadPlane (F, P, L); + for (X = 0; X < P->Width; ++X, ++Px) { + Px->C.G = L[X]; + } + + /* Read the B plane and move the data */ + ReadPlane (F, P, L); + for (X = 0; X < P->Width; ++X, ++Px) { + Px->C.B = L[X]; + } + + /* Either read the A plane or clear it */ + if (P->Planes == 4) { + ReadPlane (F, P, L); + for (X = 0; X < P->Width; ++X, ++Px) { + Px->C.A = L[X]; + } + } else { + for (X = 0; X < P->Width; ++X, ++Px) { + Px->C.A = 0; + } + } + } + } + + /* One plane means we have a palette which is either part of the header + * or follows. + */ + if (B->Type == bmMonochrome) { + + /* Create the monochrome palette */ + B->Pal = NewMonochromePalette (); + + } else if (B->Type == bmIndexed) { + + unsigned Count; + unsigned I; + unsigned char Palette[256][3]; + unsigned long EndPos; + + /* Determine the current file position */ + unsigned long CurPos = FileGetPos (F); + + /* Seek to the end of the file */ + (void) fseek (F, 0, SEEK_END); + + /* Get this position */ + EndPos = FileGetPos (F); + + /* There's a palette if the old location is 769 bytes from the end */ + if (EndPos - CurPos == sizeof (Palette) + 1) { + + /* Seek back */ + FileSetPos (F, CurPos); + + /* Check for palette marker */ + if (Read8 (F) != 0x0C) { + Error ("Invalid palette marker in PCX file `%s'", Name); + } + + /* Read the palette */ + ReadData (F, Palette, sizeof (Palette)); + Count = 256; + + } else if (EndPos == CurPos) { + + /* The palette is in the header */ + FileSetPos (F, 16); + ReadData (F, Palette, 48); + Count = 16; + + } else { + Error ("Error in PCX file `%s': %lu bytes at end of pixel data", + Name, EndPos - CurPos); + } + + /* Create the palette from the data */ + B->Pal = NewPalette (Count); + for (I = 0; I < Count; ++I) { + B->Pal->Entries[I].R = Palette[I][0]; + B->Pal->Entries[I].G = Palette[I][1]; + B->Pal->Entries[I].B = Palette[I][2]; + B->Pal->Entries[I].A = 0; + } + } + /* Close the file */ fclose (F); -- 2.39.2