/* * ppm2ppm.c */ #include #include #define NO_MODIFY #ifndef BYTE #define BYTE unsigned char #endif #ifndef UINT #define UINT unsigned long #endif #ifndef BOOL #define BOOL unsigned char #endif #ifndef TRUE #define TRUE (BOOL) 1 #endif #ifndef FALSE #define FALSE (BOOL) 0 #endif #define PBM 1 #define PGM 2 #define PPM 3 #define PAM 4 typedef struct rgb_col_struct { BYTE r; BYTE g; BYTE b; } rgb_col; typedef struct hsv_col_struct { BYTE h; BYTE s; BYTE v; } hsv_col; /* function prototypes */ int main (int argc, char *argv[]); void usage (); BOOL ppm2ppm (FILE *rd_file, FILE *wr_file, BOOL verbose); void get_token (FILE *rd_file, char *token); UINT get_p23 (FILE *rd_file, int bits); UINT get_p56 (FILE *rd_file, int bits); hsv_col rgb2hsv(rgb_col rgb); rgb_col hsv2rgb(hsv_col hsv); /* * main */ int main(int argc, char *argv[]) { FILE *fp_rd = stdin; FILE *fp_wr = stdout; BOOL verbose = FALSE; int argi; for (argi = 1; argi < argc; argi++) { if (argv[argi][0] == '-') { switch (argv[argi][1]) { case 'v': verbose = TRUE; break; case 'h': case '?': usage(); exit(0); break; default: fprintf (stderr, "ppm2ppm\n"); fprintf (stderr, "Error: unknown option %s\n", argv[argi]); usage(); exit(1); break; } /* end switch */ } else if (fp_rd == stdin) { if ((fp_rd = fopen (argv[argi], "rb")) == NULL) { fprintf (stderr, "ppm2ppm\n"); fprintf (stderr, "Error: file %s does not exist\n", argv[argi]); exit (1); } } else if (fp_wr == stdout) { if ((fp_wr = fopen (argv[argi], "wb")) == NULL) { fprintf (stderr, "ppm2ppm\n"); fprintf (stderr, "Error: can not create output file %s\n", argv[argi]); exit (1); } } else { fprintf (stderr, "ppm2ppm\n"); fprintf (stderr, "Error: too many parameters\n"); usage(); exit (1); } } /* end for */ /* call the conversion program itself */ if (ppm2ppm (fp_rd, fp_wr, verbose) == FALSE) { fprintf (stderr, "ppm2ppm\n"); fprintf (stderr, "Error: unsuccessful converting to pnm-image\n"); exit (1); } /* close input file */ fclose (fp_rd); /* close output file */ fclose (fp_wr); return 0; } /* * usage */ void usage() { fprintf (stderr, "ppm2ppm\n"); fprintf (stderr, " by Willem van Schaik, 2018\n"); fprintf (stderr, "Usage: ppm2ppm [options] \n"); fprintf (stderr, " or: ppm2ppm [options] \n"); fprintf (stderr, " or: ... | ppm2ppm [options]\n"); fprintf (stderr, "Options:\n"); fprintf (stderr, " -v[erbose] show details\n"); fprintf (stderr, " -h[elp] | -? print this help-information\n"); } /* * ppm2ppm */ BOOL ppm2ppm (FILE *rd_file, FILE *wr_file, BOOL verbose) { BYTE *pixels = NULL; BYTE *pix_ptr = NULL; UINT row_bytes; rgb_col pix_rgb; hsv_col pix_hsv; char type_token[16]; char width_token[16]; char height_token[16]; char maxval_token[16]; int type = 0; UINT width, height; UINT maxval = 0; int bits = 0; int channels = 0; int row, col; BOOL raw; int i; int l; /* read header of PGM/PPM file */ get_token(rd_file, type_token); if (type_token[0] != 'P') { type = 0; return FALSE; } else if ((type_token[1] == '1') || (type_token[1] == '4')) { /* PBM image */ type = PBM; fprintf (stderr, "ppm2ppm\n"); fprintf (stderr, "Error: %s is a PBM file\n"); exit(1); } else if ((type_token[1] == '2') || (type_token[1] == '5')) { /* PGM image */ type = PGM; raw = (type_token[1] == '5'); get_token(rd_file, width_token); sscanf (width_token, "%lu", &width); get_token(rd_file, height_token); sscanf (height_token, "%lu", &height); get_token(rd_file, maxval_token); sscanf (maxval_token, "%lu", &maxval); channels = 1; if (maxval <= 1) bits = 1; else if (maxval <= 3) bits = 2; else if (maxval <= 15) bits = 4; else if (maxval <= 255) bits = 8; else /* if (maxval <= 65535) */ { fprintf (stderr, "ppm2ppm\n"); fprintf (stderr, "Error: maxval 65535 is not supported\n"); exit(1); } if (verbose) { fprintf (stderr, "width = %lu\n", width); fprintf (stderr, "height = %lu\n", height); fprintf (stderr, "maxval = %lu\n", maxval); fprintf (stderr, "bits = %d\n", bits); } } else if ((type_token[1] == '3') || (type_token[1] == '6')) { /* PPM image */ type = PPM; raw = (type_token[1] == '6'); get_token(rd_file, width_token); sscanf (width_token, "%lu", &width); get_token(rd_file, height_token); sscanf (height_token, "%lu", &height); get_token(rd_file, maxval_token); sscanf (maxval_token, "%lu", &maxval); channels = 3; if (maxval <= 1) bits = 1; else if (maxval <= 3) bits = 2; else if (maxval <= 15) bits = 4; else if (maxval <= 255) bits = 8; else /* if (maxval <= 65535) */ { fprintf (stderr, "ppm2ppm\n"); fprintf (stderr, "Error: maxval 65535 is not supported\n"); exit(1); } if (verbose) { fprintf (stderr, "width = %lu\n", width); fprintf (stderr, "height = %lu\n", height); fprintf (stderr, "maxval = %lu\n", maxval); fprintf (stderr, "bits = %d\n", bits); } } else if (type_token[1] == '7') { /* PAM image */ type = PAM; fprintf (stderr, "ppm2ppm\n"); fprintf (stderr, "Error: %s is a PAM file\n"); exit(1); } else { return FALSE; } /* row_bytes is the width x number of channels */ row_bytes = width * channels; if ((pixels = (BYTE *) malloc (height * row_bytes * sizeof (BYTE))) == NULL) return FALSE; /* read data from PGM/PPM file */ pix_ptr = pixels; for (row = 0; row < height; row++) { for (col = 0; col < width; col++) { for (i = 0; i < channels; i++) { if (!raw) *pix_ptr++ = get_p23 (rd_file, bits); else *pix_ptr++ = get_p56 (rd_file, bits); } /* end for channels */ } /* end for col */ } /* end for row */ /* modify pixel data */ pix_ptr = pixels; for (row = 0; row < height; row++) { for (col = 0; col < width; col++) { /* PGM image */ if (type == PGM) { int p = *pix_ptr; p = 2 * (p - 128) + 128; /* gray image increase contrast */ if (p < 0) p = 0; else if (p > 255) p = 255; *pix_ptr++ = (BYTE) p; } /* PPM image */ else if (type == PPM) { pix_rgb.r = *(pix_ptr + 0); pix_rgb.g = *(pix_ptr + 1); pix_rgb.b = *(pix_ptr + 2); pix_hsv = rgb2hsv (pix_rgb); /* convert from rgb to hsv */ int h = pix_hsv.h; h += 96; /* color shift - hue is 0..255 for red to green to blue to red */ if (h > 255) h -= 256; pix_hsv.h = (BYTE) h; pix_rgb = hsv2rgb (pix_hsv); /* convert back to rgb */ *pix_ptr++ = (BYTE) pix_rgb.r; *pix_ptr++ = (BYTE) pix_rgb.g; *pix_ptr++ = (BYTE) pix_rgb.b; } } /* end for col */ } /* end for row */ /* write header to output file */ fprintf (wr_file, "%s\n", type_token); fprintf (wr_file, "%d %d\n", (int) width, (int) height); fprintf (wr_file, "%ld\n", ((1L << (int) bits) - 1L)); /* write data to PGM/PPM file */ pix_ptr = pixels; l = 0; for (row = 0; row < height; row++) { for (col = 0; col < width; col++) { for (i = 0; i < channels; i++) { if (!raw) { fprintf (wr_file, "%ld ", (long) (*pix_ptr++ >> (8 - bits))); l++; if (l >= 24) { fprintf (wr_file, "\n"); l = 0; } } else { fputc ((int) (*pix_ptr++ >> (8 - bits)) , wr_file); } } } /* end for col */ if (!raw) { fprintf (wr_file, "\n"); l = 0; } } /* end for row */ if (pixels != (unsigned char*) NULL) free (pixels); return TRUE; } /* end of ppm2ppm */ /* * get_token() - gets the first string after whitespace */ void get_token(FILE *rd_file, char *token) { int i = 0; /* remove white-space and comment-line */ do { token[i] = (unsigned char) fgetc (rd_file); if (token[i] == '#') { /* read comment line */ do { token[i] = (unsigned char) fgetc (rd_file); } while (token[i] != '\n'); } } while ((token[i] == '\n') || (token[i] == '\r') || (token[i] == ' ')); /* read string */ do { i++; token[i] = (unsigned char) fgetc (rd_file); } while ((token[i] != '\n') && (token[i] != '\r') && (token[i] != ' ')); token[i] = '\0'; return; } /* get_p23() - takes first (numeric) string and converts into number, * using the bit-depth to fill up a byte (0Ah -> AAh) */ UINT get_p23 (FILE *rd_file, int bits) { static UINT mask = 0; BYTE token[16]; UINT ret; int i = 0; if (mask == 0) for (i = 0; i < bits; i++) mask = (mask << 1) | 0x01; get_token (rd_file, token); sscanf (token, "%lu", &ret); ret &= mask; if (bits < 8) for (i = 0; i < (8 / bits); i++) ret = (ret << bits) | ret; return ret; } /* * get_p56() - takes next byte and converts into next pixel value, * using the bit-depth to fill up a byte (0Ah -> AAh) */ UINT get_p56 (FILE *rd_file, int bits) { BYTE val = 0; BYTE ret = 0; int i; val = fgetc (rd_file); if (bits < 8) { for (i = 0; i < (8 / bits); i++) ret = ret | (val << (i * bits)); } else { ret = val; } return ret; } /* * rgb2hsv - convert Red/Green/Blue color definition into Hue/Saturation/Value */ hsv_col rgb2hsv(rgb_col rgb) { hsv_col hsv; unsigned char rgbMin, rgbMax; rgbMin = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b); rgbMax = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b); hsv.v = rgbMax; if (hsv.v == 0) { hsv.h = 0; hsv.s = 0; return hsv; } hsv.s = 255 * (long int) (rgbMax - rgbMin) / hsv.v; if (hsv.s == 0) { hsv.h = 0; return hsv; } if (rgbMax == rgb.r) hsv.h = 0 + 43 * (rgb.g - rgb.b) / (rgbMax - rgbMin); else if (rgbMax == rgb.g) hsv.h = 85 + 43 * (rgb.b - rgb.r) / (rgbMax - rgbMin); else hsv.h = 171 + 43 * (rgb.r - rgb.g) / (rgbMax - rgbMin); return hsv; } /* * hsv2rgb - convert Hue/Saturation/Value color definition into Red/Green/Blue */ rgb_col hsv2rgb(hsv_col hsv) { rgb_col rgb; unsigned char region, remainder, p, q, t; if (hsv.s == 0) { rgb.r = hsv.v; rgb.g = hsv.v; rgb.b = hsv.v; return rgb; } region = hsv.h / 43; remainder = (hsv.h - (region * 43)) * 6; p = (hsv.v * (255 - hsv.s)) >> 8; q = (hsv.v * (255 - ((hsv.s * remainder) >> 8))) >> 8; t = (hsv.v * (255 - ((hsv.s * (255 - remainder)) >> 8))) >> 8; switch (region) { case 0: rgb.r = hsv.v; rgb.g = t; rgb.b = p; break; case 1: rgb.r = q; rgb.g = hsv.v; rgb.b = p; break; case 2: rgb.r = p; rgb.g = hsv.v; rgb.b = t; break; case 3: rgb.r = p; rgb.g = q; rgb.b = hsv.v; break; case 4: rgb.r = t; rgb.g = p; rgb.b = hsv.v; break; default: rgb.r = hsv.v; rgb.g = p; rgb.b = q; break; } return rgb; } /* end of source */