Last active
December 7, 2016 23:54
-
-
Save arcollector/4d93901102537da2287b76271e703998 to your computer and use it in GitHub Desktop.
PCX (24 bit / 8bit) to PPM (24 bit) converter in C
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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