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
This commit is contained in:
parent
6893f52a50
commit
fa052219e3
6 changed files with 358 additions and 28 deletions
69
src/sp65/color.c
Normal file
69
src/sp65/color.c
Normal file
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
/* End of color.h */
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ LDFLAGS =
|
||||||
# List of all object files
|
# List of all object files
|
||||||
|
|
||||||
OBJS = bitmap.o \
|
OBJS = bitmap.o \
|
||||||
|
color.o \
|
||||||
error.o \
|
error.o \
|
||||||
fileio.o \
|
fileio.o \
|
||||||
main.o \
|
main.o \
|
||||||
|
|
|
@ -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)
|
void FreePalette (Palette* P)
|
||||||
/* Free a dynamically allocated palette */
|
/* Free a dynamically allocated palette */
|
||||||
{
|
{
|
||||||
|
|
|
@ -66,6 +66,9 @@ struct Palette {
|
||||||
Palette* NewPalette (unsigned Entries);
|
Palette* NewPalette (unsigned Entries);
|
||||||
/* Create a new palette with the given number of 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);
|
void FreePalette (Palette* P);
|
||||||
/* Free a dynamically allocated palette */
|
/* Free a dynamically allocated palette */
|
||||||
|
|
||||||
|
|
267
src/sp65/pcx.c
267
src/sp65/pcx.c
|
@ -56,6 +56,7 @@
|
||||||
|
|
||||||
/* Some PCX constants */
|
/* Some PCX constants */
|
||||||
#define PCX_MAGIC_ID 0x0A
|
#define PCX_MAGIC_ID 0x0A
|
||||||
|
#define PCX_MAX_PLANES 4
|
||||||
|
|
||||||
/* A raw PCX header is just a block of bytes */
|
/* A raw PCX header is just a block of bytes */
|
||||||
typedef unsigned char RawPCXHeader[128];
|
typedef unsigned char RawPCXHeader[128];
|
||||||
|
@ -63,25 +64,25 @@ typedef unsigned char RawPCXHeader[128];
|
||||||
/* Structured PCX header */
|
/* Structured PCX header */
|
||||||
typedef struct PCXHeader PCXHeader;
|
typedef struct PCXHeader PCXHeader;
|
||||||
struct PCXHeader {
|
struct PCXHeader {
|
||||||
unsigned Id;
|
unsigned Id;
|
||||||
unsigned FileVersion;
|
unsigned FileVersion;
|
||||||
unsigned Compressed;
|
unsigned Compressed;
|
||||||
unsigned BPP;
|
unsigned BPP;
|
||||||
unsigned XMin;
|
unsigned XMin;
|
||||||
unsigned YMin;
|
unsigned YMin;
|
||||||
unsigned XMax;
|
unsigned XMax;
|
||||||
unsigned YMax;
|
unsigned YMax;
|
||||||
unsigned XDPI;
|
unsigned XDPI;
|
||||||
unsigned YDPI;
|
unsigned YDPI;
|
||||||
unsigned Planes;
|
unsigned Planes;
|
||||||
unsigned BytesPerPlane;
|
unsigned BytesPerPlane;
|
||||||
unsigned PalInfo;
|
unsigned PalInfo;
|
||||||
unsigned ScreenWidth;
|
unsigned ScreenWidth;
|
||||||
unsigned ScreenHeight;
|
unsigned ScreenHeight;
|
||||||
|
|
||||||
/* Calculated data */
|
/* Calculated data */
|
||||||
unsigned Width;
|
unsigned Width;
|
||||||
unsigned Height;
|
unsigned Height;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Read a little endian word from a byte array at offset O */
|
/* 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'",
|
Error ("Unsupported compression (%d) in PCX file `%s'",
|
||||||
P->Compressed, Name);
|
P->Compressed, Name);
|
||||||
}
|
}
|
||||||
if (P->BPP != 1 && P->BPP != 4 && P->BPP != 8) {
|
/* We support:
|
||||||
Error ("Unsupported bit depth (%u) in PCX file `%s'",
|
* - one plane with either 1 or 8 bits per pixel
|
||||||
P->BPP, Name);
|
* - 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) {
|
if (P->PalInfo != 1 && P->PalInfo != 2) {
|
||||||
Error ("Unsupported palette info (%u) in PCX file `%s'",
|
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)
|
static void DumpPCXHeader (const PCXHeader* P, const char* Name)
|
||||||
/* Dump the header of the PCX file in readable form to stdout */
|
/* Dump the header of the PCX file in readable form to stdout */
|
||||||
{
|
{
|
||||||
printf ("File name: %s\n", Name);
|
printf ("File name: %s\n", Name);
|
||||||
printf ("PCX Version: ");
|
printf ("PCX Version: ");
|
||||||
switch (P->FileVersion) {
|
switch (P->FileVersion) {
|
||||||
case 0: puts ("2.5"); break;
|
case 0: puts ("2.5"); break;
|
||||||
case 2: puts ("2.8 with palette"); 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 4: puts ("PCX for Windows without palette"); break;
|
||||||
case 5: puts ("3.0"); break;
|
case 5: puts ("3.0"); break;
|
||||||
}
|
}
|
||||||
printf ("Image type: %s\n", P->PalInfo? "color" : "grayscale");
|
printf ("Image type: %s\n", P->PalInfo? "color" : "grayscale");
|
||||||
printf ("Compression: %s\n", P->Compressed? "RLE" : "None");
|
printf ("Compression: %s\n", P->Compressed? "RLE" : "None");
|
||||||
printf ("Structure: %u planes of %u bits\n", P->Planes, P->BPP);
|
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 ("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 ("Resolution: %u/%u DPI\n", P->XDPI, P->YDPI);
|
||||||
printf ("Screen size: %u/%u\n", P->ScreenWidth, P->ScreenHeight);
|
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;
|
PCXHeader* P;
|
||||||
Bitmap* B;
|
Bitmap* B;
|
||||||
|
unsigned char* L;
|
||||||
|
Pixel* Px;
|
||||||
|
unsigned X, Y;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Open the file */
|
/* Open the file */
|
||||||
FILE* F = fopen (Name, "rb");
|
FILE* F = fopen (Name, "rb");
|
||||||
|
@ -203,6 +260,160 @@ Bitmap* ReadPCXFile (const char* Name)
|
||||||
DumpPCXHeader (P, 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 */
|
/* Close the file */
|
||||||
fclose (F);
|
fclose (F);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue