#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include <string.h>
/* #define DEBUG */
#if defined(DEBUG)
#include "xmalloc.h"
#else
#define xmalloc(x, y) malloc(x)
#define xfree(x, y) free(x)
#define xrealloc(x, y, z) realloc(x, y)
#define xmallocdump()
#endif
/* for xmalloc.c */
#define IDRGB 1001
#define IDPALETTE 1002
#define IDBMP 1003
#define ID_GETLINE 1004
typedef struct {
unsigned char bfType1;
unsigned char bfType2;
unsigned long bfSize;
unsigned short bfReserved1;
unsigned short bfReserved2;
unsigned long bfOffBits;
} BitmapFileHeader;
int bmHeaderCheck(FILE *fp, BitmapFileHeader *bh) {
assert(sizeof(unsigned short) == 2);
assert(sizeof(unsigned long) == 4);
if (fread(&(bh->bfType1), sizeof(unsigned char), 1, fp) != 1)
goto error_NotRead;
if (fread(&(bh->bfType2), sizeof(unsigned char), 1, fp) != 1)
goto error_NotRead;
if (fread(&(bh->bfSize), sizeof(unsigned long), 1, fp) != 1)
goto error_NotRead;
if (fread(&(bh->bfReserved1), sizeof(unsigned short), 1, fp) != 1)
goto error_NotRead;
if (fread(&(bh->bfReserved2), sizeof(unsigned short), 1, fp) != 1)
goto error_NotRead;
if (fread(&(bh->bfOffBits), sizeof(unsigned long), 1, fp) != 1)
goto error_NotRead;
if (bh->bfType1 != 'B' || bh->bfType2 != 'M')
goto error_NotBitmap;
if (bh->bfReserved1 != 0 || bh->bfReserved2 != 0)
goto error_NotBitmap;
#if 0
printf("read:bfType1: %c\n", bh->bfType1);
printf("read:bfType2: %c\n", bh->bfType2);
printf("read:bfSize: %lu\n", bh->bfSize);
printf("read:bfReserved1: %u\n", bh->bfReserved1);
printf("read:bfReserved2: %u\n", bh->bfReserved2);
printf("read:bfOffBits: %lu\n", bh->bfOffBits);
putchar('\n');
#endif
return 1;
error_NotBitmap:
fprintf(stderr, "cannot find bmp header\n");
return 0;
error_NotRead:
fprintf(stderr, "cannot read bmp header\n");
return 0;
}
typedef struct {
unsigned long biSize;
long biWidth;
long biHeight;
unsigned short biPlanes;
unsigned short biBitCount;
unsigned long biCompression;
unsigned long biSizeImage;
long biXPixPerMeter;
long biYPixPerMeter;
unsigned long biClrUsed;
unsigned long biClrImportant;
} BitmapInfoHeader;
int bmInfoHeaderCheck(FILE *fp, BitmapInfoHeader *bi) {
assert(sizeof(unsigned short) == 2);
assert(sizeof(unsigned long) == 4);
assert(sizeof(long) == 4);
if (fread(&(bi->biSize), sizeof(unsigned long), 1, fp) != 1)
goto error_NotRead;
if (bi->biSize == 12) {
bi->biWidth = 0;
if (fread(&(bi->biWidth), sizeof(short), 1, fp) != 1)
goto error_NotRead;
bi->biHeight = 0;
if (fread(&(bi->biHeight), sizeof(short), 1, fp) != 1)
goto error_NotRead;
if (fread(&(bi->biPlanes), sizeof(unsigned short), 1, fp) != 1)
goto error_NotRead;
if (fread(&(bi->biBitCount), sizeof(unsigned short), 1, fp) != 1)
goto error_NotRead;
if (bi->biWidth > 32768)
bi->biWidth = -bi->biWidth;
if (bi->biHeight > 32768)
bi->biWidth = -bi->biHeight;
} else if (bi->biSize == 40) {
if (fread(&(bi->biWidth), sizeof(long), 1, fp) != 1)
goto error_NotRead;
if (fread(&(bi->biHeight), sizeof(long), 1, fp) != 1)
goto error_NotRead;
if (fread(&(bi->biPlanes), sizeof(unsigned short), 1, fp) != 1)
goto error_NotRead;
if (fread(&(bi->biBitCount), sizeof(unsigned short), 1, fp) != 1)
goto error_NotRead;
if (fread(&(bi->biCompression), sizeof(unsigned long), 1, fp) != 1)
goto error_NotRead;
if (fread(&(bi->biSizeImage), sizeof(unsigned long), 1, fp) != 1)
goto error_NotRead;
if (fread(&(bi->biXPixPerMeter), sizeof(long), 1, fp) != 1)
goto error_NotRead;
if (fread(&(bi->biYPixPerMeter), sizeof(long), 1, fp) != 1)
goto error_NotRead;
if (fread(&(bi->biClrUsed), sizeof(unsigned long), 1, fp) != 1)
goto error_NotRead;
if (fread(&(bi->biClrImportant), sizeof(unsigned long), 1, fp) != 1)
goto error_NotRead;
} else
goto error_NotSupported1;
#if 0
printf("read:biSize: %lu\n", bi->biSize);
printf("read:biWidth: %ld\n", bi->biWidth);
printf("read:biHeight: %ld\n", bi->biHeight);
printf("read:biPlanes: %u\n", bi->biPlanes);
printf("read:biBitcount: %u\n", bi->biBitCount);
if (bi->biSize == 40) {
printf("read:biCompression: %lu\n", bi->biCompression);
printf("read:biSizeImage: %lu\n", bi->biSizeImage);
printf("read:biXPixPerMeter %ld\n", bi->biXPixPerMeter);
printf("read:biYPixPerMeter %ld\n", bi->biYPixPerMeter);
printf("read:biClrUsed: %lu\n", bi->biClrUsed);
printf("read:biClrImporant: %lu\n", bi->biClrImportant);
}
#endif
if (bi->biSize != 40)
goto error_NotSupported1;
if (bi->biPlanes != 1)
goto error_NotSupported2;
if (bi->biBitCount != 24 && bi->biBitCount != 8)
goto error_NotSupported3;
if (bi->biCompression != 0)
goto error_NotSupported4;
return 1;
error_NotSupported1:
fprintf(stderr, "info header size: this format is not supported\n");
return 0;
error_NotSupported2:
fprintf(stderr, "biPlanes: this format is not supported\n");
return 0;
error_NotSupported3:
fprintf(stderr, "biBitCount: this format is not supported\n");
return 0;
error_NotSupported4:
fprintf(stderr, "biCompression: this format is not supported\n");
return 0;
error_NotRead:
fprintf(stderr, "cannot read bmp info header\n");
return 0;
}
int isFtellGood(FILE *fp, unsigned long pos) {
return (unsigned long)ftell(fp) == pos;
}
long iabs(long n) {
return (n > 0) ? n : -n;
}
void task_read24(FILE *fp, BitmapFileHeader *bheader, BitmapInfoHeader *binfo,
unsigned char **dataR, unsigned char **dataG,
unsigned char **dataB)
{
long x, y, n;
unsigned char dummy;
int c;
if (!isFtellGood(fp, bheader->bfOffBits)) {
fprintf(stderr, "Header or Image Data is corrupted.\n");
return;
}
*dataR = xmalloc(sizeof(char) * iabs(binfo->biWidth) * iabs(binfo->biHeight), IDRGB);
*dataG = xmalloc(sizeof(char) * iabs(binfo->biWidth) * iabs(binfo->biHeight), IDRGB);
*dataB = xmalloc(sizeof(char) * iabs(binfo->biWidth) * iabs(binfo->biHeight), IDRGB);
if (*dataR == NULL || *dataG == NULL || *dataB == NULL) {
fprintf(stderr, "cannot alloc. enough memory.\n");
return;
}
for (y = 0; y < iabs(binfo->biHeight); y++) {
c = 0;
for (x = 0; x < iabs(binfo->biWidth); x++) {
fread((*dataB + y * iabs(binfo->biWidth) + x), 1, 1, fp);
fread((*dataG + y * iabs(binfo->biWidth) + x), 1, 1, fp);
fread((*dataR + y * iabs(binfo->biWidth) + x), 1, 1, fp);
c += 3;
}
while (c % 4 != 0) {
fread(&dummy, 1, 1, fp);
c++;
}
}
n = iabs(binfo->biWidth) * 3;
if (n % 4 > 0)
n += 4 - (n % 4);
binfo->biSizeImage = n * iabs(binfo->biHeight);
bheader->bfSize = binfo->biSizeImage + binfo->biSize + 14;
}
void task_read_palette(FILE *fp, BitmapFileHeader *bh, BitmapInfoHeader *bi,
unsigned char **dataPalette)
{
int i, max;
unsigned char dummy;
if ((max = bi->biClrUsed) == 0)
max = 256;
*dataPalette = xmalloc(sizeof(char) * 3 * max, IDPALETTE);
if (*dataPalette == NULL) {
fprintf(stderr, "cannot alloc. enough memory.\n");
return;
}
for (i = 0; i < max; i++) {
fread((*dataPalette + 3 * i + 0), 1, 1, fp);
fread((*dataPalette + 3 * i + 1), 1, 1, fp);
fread((*dataPalette + 3 * i + 2), 1, 1, fp);
fread(&dummy, 1, 1, fp);
}
}
void task_read8(FILE *fp, BitmapFileHeader *bheader, BitmapInfoHeader *binfo,
unsigned char **dataR, unsigned char **dataG,
unsigned char **dataB, unsigned char *dataPalette)
{
long x, y, n;
unsigned char data;
int c;
if (!isFtellGood(fp, bheader->bfOffBits)) {
fprintf(stderr, "Header or Image Data is corrupted.\n");
return;
}
*dataR = xmalloc(sizeof(char) * iabs(binfo->biWidth) * iabs(binfo->biHeight), IDRGB);
*dataG = xmalloc(sizeof(char) * iabs(binfo->biWidth) * iabs(binfo->biHeight), IDRGB);
*dataB = xmalloc(sizeof(char) * iabs(binfo->biWidth) * iabs(binfo->biHeight), IDRGB);
if (*dataR == NULL || *dataG == NULL || *dataB == NULL) {
fprintf(stderr, "cannot alloc. enough memory.\n");
return;
}
for (y = 0; y < iabs(binfo->biHeight); y++) {
c = 0;
for (x = 0; x < iabs(binfo->biWidth); x++) {
fread(&data, 1, 1, fp);
*(*dataB + y * iabs(binfo->biWidth) + x) = dataPalette[3 * data + 0];
*(*dataG + y * iabs(binfo->biWidth) + x) = dataPalette[3 * data + 1];
*(*dataR + y * iabs(binfo->biWidth) + x) = dataPalette[3 * data + 2];
c++;
}
while (c % 4 != 0) {
fread(&data, 1, 1, fp);
c++;
}
}
n = iabs(binfo->biWidth) * 3;
if (n % 4 > 0)
n += 4 - (n % 4);
binfo->biSizeImage = n * iabs(binfo->biHeight);
bheader->bfSize = binfo->biSizeImage + binfo->biSize + 14;
bheader->bfOffBits = 54;
binfo->biBitCount = 24;
binfo->biClrUsed = 0;
binfo->biClrImportant = 0;
}
void task_write_header(FILE *fp, BitmapFileHeader *bh) {
assert(sizeof(unsigned short) == 2);
assert(sizeof(unsigned long) == 4);
fwrite(&(bh->bfType1), sizeof(unsigned char), 1, fp);
fwrite(&(bh->bfType2), sizeof(unsigned char), 1, fp);
fwrite(&(bh->bfSize), sizeof(unsigned long), 1, fp);
fwrite(&(bh->bfReserved1), sizeof(unsigned short), 1, fp);
fwrite(&(bh->bfReserved2), sizeof(unsigned short), 1, fp);
fwrite(&(bh->bfOffBits), sizeof(unsigned long), 1, fp);
#if 0
printf("write:bfType1: %c\n", bh->bfType1);
printf("write:bfType2: %c\n", bh->bfType2);
printf("write:bfSize: %lu\n", bh->bfSize);
printf("write:bfReserved1: %u\n", bh->bfReserved1);
printf("write:bfReserved2: %u\n", bh->bfReserved2);
printf("write:bfOffBits: %lu\n", bh->bfOffBits);
#endif
}
void task_write_info(FILE *fp, BitmapInfoHeader *bi) {
assert(sizeof(unsigned short) == 2);
assert(sizeof(unsigned long) == 4);
assert(sizeof(long) == 4);
fwrite(&(bi->biSize), sizeof(unsigned long), 1, fp);
fwrite(&(bi->biWidth), sizeof(long), 1, fp);
fwrite(&(bi->biHeight), sizeof(long), 1, fp);
fwrite(&(bi->biPlanes), sizeof(unsigned short), 1, fp);
fwrite(&(bi->biBitCount), sizeof(unsigned short), 1, fp);
fwrite(&(bi->biCompression), sizeof(unsigned long), 1, fp);
fwrite(&(bi->biSizeImage), sizeof(unsigned long), 1, fp);
fwrite(&(bi->biXPixPerMeter), sizeof(long), 1, fp);
fwrite(&(bi->biYPixPerMeter), sizeof(long), 1, fp);
fwrite(&(bi->biClrUsed), sizeof(unsigned long), 1, fp);
fwrite(&(bi->biClrImportant), sizeof(unsigned long), 1, fp);
#if 0
printf("write:biSize: %lu\n", bi->biSize);
printf("write:biWidth: %ld\n", bi->biWidth);
printf("write:biHeight: %ld\n", bi->biHeight);
printf("write:biPlanes: %u\n", bi->biPlanes);
printf("write:biBitcount: %u\n", bi->biBitCount);
printf("write:biCompression: %lu\n", bi->biCompression);
printf("write:biSizeImage: %lu\n", bi->biSizeImage);
printf("write:biXPixPerMeter %ld\n", bi->biXPixPerMeter);
printf("write:biYPixPerMeter %ld\n", bi->biYPixPerMeter);
printf("write:biClrUsed: %lu\n", bi->biClrUsed);
printf("write:biClrImporant: %lu\n", bi->biClrImportant);
#endif
}
void task_write24(FILE *fp,
BitmapFileHeader *bh, BitmapInfoHeader *bi,
unsigned char *dataR,
unsigned char *dataG,
unsigned char *dataB) {
int x, y;
int c;
unsigned char dummy = '\0';
task_write_header(fp, bh);
task_write_info(fp, bi);
for (y = 0; y < iabs(bi->biHeight); y++) {
c = 0;
for (x = 0; x < iabs(bi->biWidth); x++) {
fwrite(&dataB[y * iabs(bi->biWidth) + x], 1, 1, fp);
fwrite(&dataG[y * iabs(bi->biWidth) + x], 1, 1, fp);
fwrite(&dataR[y * iabs(bi->biWidth) + x], 1, 1, fp);
c += 3;
}
while (c % 4 != 0) {
fwrite(&dummy, 1, 1, fp);
c++;
}
}
}
/* ------------------------------------------------------------------------- */
struct BMP24 {
BitmapFileHeader bh;
BitmapInfoHeader bi;
unsigned char *dataR;
unsigned char *dataG;
unsigned char *dataB;
unsigned char *dataPalette;
};
int BMP24_read(FILE *fp, struct BMP24 *bmp) {
if (!bmHeaderCheck(fp, &(bmp->bh)))
return 0;
if (!bmInfoHeaderCheck(fp, &(bmp->bi)))
return 0;
bmp->dataR = bmp->dataG = bmp->dataB = bmp->dataPalette = NULL;
if ((bmp->bi).biBitCount == 24) {
task_read24(fp, &(bmp->bh), &(bmp->bi),
&(bmp->dataR), &(bmp->dataG), &(bmp->dataB));
} else if ((bmp->bi).biBitCount == 8) {
task_read_palette(fp, &bmp->bh, &bmp->bi, &bmp->dataPalette);
task_read8(fp, &bmp->bh, &bmp->bi,
&bmp->dataR, &bmp->dataG, &bmp->dataB, bmp->dataPalette);
xfree(bmp->dataPalette, IDPALETTE);
bmp->dataPalette = NULL;
}
if (!(bmp->dataR && bmp->dataG && bmp->dataB)) {
xfree(bmp->dataR, IDRGB);
xfree(bmp->dataG, IDRGB);
xfree(bmp->dataB, IDRGB);
return 0;
}
return 1;
}
void BMP24_write(FILE *fp, struct BMP24 *bmp) {
task_write24(fp, &bmp->bh, &bmp->bi,
bmp->dataR, bmp->dataG, bmp->dataB);
}
void _BMP24_allocation(struct BMP24 **w_bmp, long width, long height) {
int n;
*w_bmp = xmalloc(sizeof(struct BMP24), IDBMP);
if (*w_bmp == NULL) {
fprintf(stderr, "error(bmp24)\n");
return;
}
n = width * 3;
if (n % 4 > 0)
n += 4 - (n % 4);
(*w_bmp)->bi.biSizeImage = n * height;
(*w_bmp)->bi.biSize = 40;
(*w_bmp)->bh.bfSize = (*w_bmp)->bi.biSizeImage + (*w_bmp)->bi.biSize + 14;
(*w_bmp)->bh.bfType1 = 'B';
(*w_bmp)->bh.bfType2 = 'M';
(*w_bmp)->bh.bfReserved1 = 0;
(*w_bmp)->bh.bfReserved2 = 0;
(*w_bmp)->bh.bfOffBits = 54;
(*w_bmp)->bi.biPlanes = 1;
(*w_bmp)->bi.biBitCount = 24;
(*w_bmp)->bi.biCompression = 0;
(*w_bmp)->bi.biHeight = height;
(*w_bmp)->bi.biWidth = width;
(*w_bmp)->bi.biClrUsed = 0;
(*w_bmp)->bi.biClrImportant = 0;
(*w_bmp)->bi.biXPixPerMeter = 30;
(*w_bmp)->bi.biYPixPerMeter = 30;
(*w_bmp)->dataR = (*w_bmp)->dataG = (*w_bmp)->dataB = NULL;
(*w_bmp)->dataPalette = NULL;
for (;;) {
if (((*w_bmp)->dataR = xmalloc(width * height, IDRGB)) == 0)
break;
if (((*w_bmp)->dataG = xmalloc(width * height, IDRGB)) == 0)
break;
if (((*w_bmp)->dataB = xmalloc(width * height, IDRGB)) == 0)
break;
/* OK finished */
return;
}
/* exception */
xfree((*w_bmp)->dataR, IDRGB);
xfree((*w_bmp)->dataG, IDRGB);
xfree((*w_bmp)->dataB, IDRGB);
xfree(*w_bmp, IDBMP);
*w_bmp = 0;
return;
}
void BMP24_release(struct BMP24 *bmp) {
if (bmp->dataR) xfree(bmp->dataR, IDRGB);
if (bmp->dataG) xfree(bmp->dataG, IDRGB);
if (bmp->dataB) xfree(bmp->dataB, IDRGB);
if (bmp->dataPalette) xfree(bmp->dataPalette, IDPALETTE);
}
/* ------------------------------------------------------------------------- */
#define BUFFSIZE 3 /* >= 2 */
char *mygetline(FILE *fp) {
static char inbuff[BUFFSIZE];
char *outbuff_malloc, *tmpbuff;
char *p, *r;
int fEOL;
if ((outbuff_malloc = xmalloc(1, ID_GETLINE)) == NULL) {
return NULL;
}
*outbuff_malloc = '\0';
fEOL = 0;
do {
r = fgets(inbuff, BUFFSIZE, fp);
if (r == NULL)
break;
for (p = inbuff; *p != '\0'; p++)
;
if (*(p - 1) == '\n')
fEOL = 1;
if ((tmpbuff = xrealloc(outbuff_malloc, strlen(outbuff_malloc) + strlen(inbuff) + 1, ID_GETLINE)) == NULL) {
xfree(outbuff_malloc, ID_GETLINE);
return NULL;
}
strcat(tmpbuff, inbuff);
outbuff_malloc = tmpbuff;
} while (!fEOL);
if (strlen(outbuff_malloc) > 0) {
for (p = outbuff_malloc; *p != '\0'; p++)
;
if (*(p - 1) == '\n')
*(p - 1) = '\0';
return outbuff_malloc;
}
xfree(outbuff_malloc, ID_GETLINE);
return NULL;
}
char *cutToken(char *p, char **s) {
char *t, *q;
int flag;
if (p == 0)
return 0;
flag = 0;
for (q = p; *q == ' ' && *q != '\0'; q++)
;
if (*q == '\0')
return 0;
t = q;
q++;
for (; *q != ' ' && *q != '\0'; q++)
;
if (*q == '\0')
flag = 1;
*q = '\0';
if (flag)
*s = 0;
else
*s = q + 1;
return t;
}
void task_pgm2bmp(FILE *fppgm, FILE *fpbmp) {
struct BMP24 *bmp;
long width, height, y, x, d;
char *line, *p, *q;
int c;
/* header */
if ((line = mygetline(fppgm)) == 0) {
fprintf(stderr, "cannot read pgm file: header\n");
return;
}
if (strcmp(line, "P2") != 0) {
fprintf(stderr, "pgm header %s: not supported\n", line);
xfree(line, ID_GETLINE);
return;
}
xfree(line, ID_GETLINE);
/* width, height */
if ((line = mygetline(fppgm)) == 0) {
fprintf(stderr, "cannot read pgm file: width/height\n");
return;
}
if (sscanf(line, "%ld %ld", &width, &height) != 2) {
fprintf(stderr, "pgm width/height: bad format\n");
xfree(line, ID_GETLINE);
return;
}
xfree(line, ID_GETLINE);
/* depth */
if ((line = mygetline(fppgm)) == 0) {
fprintf(stderr, "cannot read pgm file: depth\n");
return;
}
if (sscanf(line, "%ld", &d) != 1) {
fprintf(stderr, "pgm depth: bad format\n");
xfree(line, ID_GETLINE);
return;
}
xfree(line, ID_GETLINE);
bmp = 0;
_BMP24_allocation(&bmp, width, height);
if (bmp == 0) {
fprintf(stderr, "cannot allocate enough memory(_BMP24_allocation)\n");
return;
}
for (y = height - 1; y >= 0; --y) {
if ((line = mygetline(fppgm)) == 0) {
fprintf(stderr, "pgm body: bad format(too small : height).\n");
xfree(line, ID_GETLINE);
return;
}
p = line;
for (x = 0; x < width; x++) {
if ((q = cutToken(p, &p)) == 0) {
fprintf(stderr, "pgm body: bad format(too small : height).\n");
xfree(line, ID_GETLINE);
BMP24_release(bmp);
xfree(bmp, IDBMP);
return;
}
c = atoi(q);
bmp->dataR[y * width + x] = bmp->dataG[y * width + x] = bmp->dataB[y * width + x] = (unsigned char)(255 * c / d);
}
xfree(line, ID_GETLINE);
}
BMP24_write(fpbmp, bmp);
BMP24_release(bmp);
xfree(bmp, IDBMP);
}
int main(int argc, char **argv) {
FILE *fpbmp, *fppgm;
if (argc != 3) {
fprintf(stderr, "usage: %s <filename:pgm> <filename:bmp>\n", argv[0]);
exit(-1);
}
if ((fppgm = fopen(argv[1], "r")) == 0) {
fprintf(stderr, "cannot open the .pgm file for reading\n");
exit(-1);
}
if ((fpbmp = fopen(argv[2], "w")) == 0) {
fprintf(stderr, "cannot open the .bmp file for writing\n");
fclose(fpbmp);
exit(-1);
}
task_pgm2bmp(fppgm, fpbmp);
fclose(fpbmp);
fclose(fppgm);
xmallocdump();
return 0;
}
/* end */