Skip to content

Instantly share code, notes, and snippets.

@arcollector
Last active December 7, 2016 23:54
Show Gist options
  • Save arcollector/4d93901102537da2287b76271e703998 to your computer and use it in GitHub Desktop.
Save arcollector/4d93901102537da2287b76271e703998 to your computer and use it in GitHub Desktop.
PCX (24 bit / 8bit) to PPM (24 bit) converter in C
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct PCX_HeaderStruct {
uint8_t sig;
uint8_t ver;
uint8_t isCompress;
uint8_t bitsPerPixel;
uint16_t xMin,yMin,xMax,yMax;
uint16_t hDotsPerInch,vDotsPerInch;
uint8_t pal16[48];
uint8_t reserved1;
uint8_t numPlanes;
uint16_t bytesPerRow;
uint16_t palType;
uint16_t width,height;
uint8_t reserved2[54];
uint8_t pal256[768];
};
typedef struct PCX_HeaderStruct PCX_Header;
struct PCX_RawStruct {
unsigned char *data;
long length,expectedLength;
};
typedef struct PCX_RawStruct PCX_Raw;
struct RGBStruct {
unsigned char r,g,b;
};
typedef struct RGBStruct RGB;
struct CanvasStruct {
RGB *data;
long width,height;
long length,expectedLength;
};
typedef struct CanvasStruct Canvas;
static int DEBUG = 0;
int PCX_ReadHeader(FILE *fp,PCX_Header *header) {
fseek(fp,0,SEEK_SET);
if(fgetc(fp) != 10) {
printf("bad signature\n");
return 0;
}
if(fgetc(fp) != 5) {
printf("unsupported PCX version\n");
return 0;
}
if(fgetc(fp) != 1) {
printf("compression type not supported\n");
return 0;
}
header->bitsPerPixel = fgetc(fp);
if(header->bitsPerPixel != 8) {
printf("unsupported bitsPerPixel: %d\n",
header->bitsPerPixel);
return 0;
}
fread(&header->xMin,2,1,fp);
fread(&header->yMin,2,1,fp);
fread(&header->xMax,2,1,fp);
fread(&header->yMax,2,1,fp);
fseek(fp,65,SEEK_SET);
header->numPlanes = fgetc(fp);
if(header->numPlanes != 3 &&
header->numPlanes != 1) {
printf("unsupported numPlanes: %d\n",
header->numPlanes);
return 0;
}
fread(&header->bytesPerRow,2,1,fp);
header->width = header->xMax - header->xMin + 1;
header->height = header->yMax - header->yMin + 1;
if(header->numPlanes == 1) { // get 256 palette
fseek(fp,-768,SEEK_END);
if(feof(fp)) {
printf("missing 256 palette\n");
return 0;
}
for(int i = 0; i < 768;) {
unsigned char r = fgetc(fp),
g = fgetc(fp),
b = fgetc(fp);
header->pal256[i++] = r;
header->pal256[i++] = g;
header->pal256[i++] = b;
}
}
printf("numPlanes: %d\n",header->numPlanes);
printf("bytesPerRow: %d\n",header->bytesPerRow);
printf("width: %d, height: %d\n",
header->width,header->height);
return 1;
}
int PCX_Decode(FILE *fp, PCX_Header *header,
Canvas *canvas) {
int isDummyByte = (header->bytesPerRow - header->width) == 1;
printf("isDummyByte: %d\n",isDummyByte);
PCX_Raw raw;
raw.expectedLength = header->height *
header->numPlanes *
header->width;
raw.data = malloc(raw.expectedLength);
if(!raw.data) {
printf("cannot allocate memory for raw.data\n");
return 0;
}
raw.length = 0;
canvas->expectedLength = header->height *
header->width;
canvas->data = calloc(canvas->expectedLength,
sizeof(RGB));
if(!canvas->data) {
free(raw.data);
printf("cannot allocate memory for canvas.data\n");
return 0;
}
canvas->length = 0;
canvas->width = header->width;
canvas->height = header->height;
fseek(fp,128,SEEK_SET);
long decodingCursor; // for debugging usage
DEBUG = 0;
for(long row = 0;
row < header->height*header->numPlanes;
row++) {
decodingCursor = 1;
for(long x = 0; x < header->bytesPerRow; ) {
unsigned char ch = fgetc(fp);
if(DEBUG) printf("%ld) raw: %d",decodingCursor,ch);
long count = 1;
if((ch & 0xc0) == 0xc0) {
count = ch & 0x3f;
ch = fgetc(fp);
}
if(DEBUG) printf(" count: %ld char: %d\n", count,ch);
decodingCursor++;
if(isDummyByte &&
(x+1) == header->bytesPerRow) {
if(DEBUG) printf(" not writing this run\n");
x++;
continue;
}
for(long j = 0; j < count; j++) {
// many bytes left and width complete
if(isDummyByte &&
(j+1) < count &&
(x+1) == header->bytesPerRow) {
j++; // ignore this byte
x = 0; // start new row
row++;
if(DEBUG) printf("2)row: %ld\n",row);
// 1 byte left and width complete
// ignore this byte
} else if(isDummyByte &&
(j+1) == count &&
(x+1) == header->bytesPerRow) {
x++; // end row
break;
}
raw.data[raw.length++] = ch;
x++;
// width complete and more bytes to repeat
if(!isDummyByte &&
(j+1) < count &&
(x) == header->bytesPerRow) {
x = 0; // new row
row++;
if(DEBUG) printf("1)row: %ld\n",row);
}
}
}
if(DEBUG) printf("3)row: %ld\n",row);
}
printf("raw.length: %ld, raw.expectedLength: %ld\n",raw.length,raw.expectedLength);
if(raw.length != raw.expectedLength) {
printf("decoding algorithm not working!\n");
free(raw.data);
return 0;
}
// 24bit image
if(header->numPlanes == 3) {
for(long offset = 0; offset < raw.length;
offset += (header->width*3)) {
for(long x = 0; x < header->width; x++) {
unsigned char r = raw.data[offset + x],
g = raw.data[offset + x+header->width],
b = raw.data[offset + x+header->width*2];
//printf("%d,%d,%d \n",r,g,b);
canvas->data[canvas->length].r = r;
canvas->data[canvas->length].g = g;
canvas->data[canvas->length].b = b;
canvas->length++;
}
}
// 8bit with 256 palette
} else { // numPlanes == 1
for(long i = 0; i < raw.length; i++) {
unsigned char colorIndex = raw.data[i];
long address = colorIndex * 3;
unsigned char r = header->pal256[address],
g = header->pal256[address+1],
b = header->pal256[address+2];
//printf("%d,%d,%d \n",r,g,b);
canvas->data[canvas->length].r = r;
canvas->data[canvas->length].g = g;
canvas->data[canvas->length].b = b;
canvas->length++;
}
}
free(raw.data);
return 1;
}
int PPM_Save(Canvas *canvas, char *fileName) {
int addExt = 0;
char *point = strrchr(fileName,'.');
if(!point || (
strcmp(point,".PPM") != 0 &&
strcmp(point,".ppm") != 0
)
) {
addExt = 1;
char *newFileName = malloc(strlen(fileName)+5);
sprintf(newFileName,"%s.ppm",fileName);
fileName = newFileName;
//printf("string: %s\n",fileName);
}
FILE *fp = fopen(fileName,"wb");
if(!fp) {
printf("cant create file %s\n",fileName);
if(addExt) free(fileName);
return 0;
}
if(addExt) free(fileName);
fputs("P3 ",fp);
fprintf(fp,"%ld %ld ",canvas->width,canvas->height);
fputs("255\n",fp);
long i = 0;
for(long y = 0; y < canvas->height; y++) {
for(long x = 0; x < canvas->width; x++) {
RGB color = canvas->data[i++];
fprintf(fp,"%3d %3d %3d\t",
color.r,color.g,color.b);
}
fputc('\n',fp);
}
fclose(fp);
return 1;
}
void Canvas_Free(Canvas *canvas) {
if(canvas->data) {
free(canvas->data);
}
canvas->data = NULL;
}
int main(int argc, char *args[]) {
if(argc != 3) {
printf("i need a pcx file and the output file name string as args\n");
return 0;
}
char *fileName = args[1];
FILE *fp = fopen(fileName,"rb");
if(!fp) {
printf("file %s does not exist\n",fileName);
return 0;
}
PCX_Header header;
if(!PCX_ReadHeader(fp,&header)){
printf("fail to decode header: %s\n",fileName);
fclose(fp);
return 0;
}
Canvas canvas;
if(!PCX_Decode(fp,&header,&canvas)) {
printf("pcx decoding fail: %s\n",fileName);
fclose(fp);
Canvas_Free(&canvas);
return 0;
}
fclose(fp);
char *outputFile = args[2];
if(!PPM_Save(&canvas,outputFile)) {
printf("cannot create ppm file\n");
Canvas_Free(&canvas);
return 0;
}
Canvas_Free(&canvas);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment