/* Last edited on 2006-11-15 20:32:08 by stolfi */ #define PNM_MAX_MAXVAL 65535 /* Maximum value of gray or color pixel samples in files and in memory. */ typedef uint16_t pgm_pixel_t; /* A pixel of a PGM image in memory. Note that it is always 16 bits. */ typedef struct ppm_pixel_t { uint16_t r; /* Red chanel sample. */ uint16_t g; /* Green chanel sample. */ uint16_t b; /* Blue chanel sample. */ } ppm_pixel_t; /* A pixel of a PPM image in memory. Note that each channel is 16 bits. */ #define PPM_GETR(p) ((p).r) #define PPM_GETG(p) ((p).g) #define PPM_GETB(p) ((p).b) /* Get a specific channel sample of a PPM pixel. */ #define PPM_PUTR(p,red) ((p).r = (red)) #define PPM_PUTG(p,grn) ((p).g = (grn)) #define PPM_PUTB(p,blu) ((p).b = (blu)) /* Set individual the channel samples of a PPM pixel. */ #define PPM_ASSIGN(p,red,grn,blu) \ do { (p).r = (red); (p).g = (grn); (p).b = (blu); } while ( 0 ) /* Set all three channel samples of a PPM pixel. */ #define PPM_EQUAL(p,q) \ (((p).r == (q).r) && ((p).g == (q).g) && ((p).b == (q).b)) /* TRUE iff {p} and {q} are identical PPM pixels. */ /* ---------------------------------------------------------------------- */ /* CONVERSION FROM/TO PGM/PPM IMAGES */ pnm_image_t *pnm_image_from_pgm_image(pgm_image_t *img); /* Creates a PNM image descriptor from a PGM image descriptor. Allocates a new header record, but pixel storage will be shared. */ pgm_image_t *pnm_image_to_pgm_image(pnm_image_t *img); /* Creates a PGM image descriptor from a PNM image descriptor. Allocates a new header record, but pixel storage will be shared. Fails if the argument is not a grayscale PNM image. */ pnm_image_t *pnm_image_from_ppm_image(ppm_image_t *img); /* Creates a PNM image descriptor from a PPM image descriptor. Allocates a new header record, but pixel storage will be shared. */ ppm_image_t *pnm_image_to_ppm_image(pnm_image_t *img); /* Creates a PPM image descriptor from a PNM image descriptor. Allocates a new header record, but pixel storage will be shared. Fails if the argument is not a color PNM image. */ pnm_image_t *pnm_image_from_pgm_image(pgm_image_t *img) { pnm_image_t *omg = pnm_image_new_header(img->cols, img->rows, PGM_FORMAT); omg->pgm_pix = img->pix; omg->maxval = img->maxval; return omg; } pgm_image_t *pnm_image_to_pgm_image(pnm_image_t *img) { assert(img->pixtype == PGM_FORMAT); pgm_image_t *omg = pgm_image_new_header(img->cols, img->rows); omg->pix = img->pgm_pix; omg->maxval = img->maxval; return omg; } pnm_image_t *pnm_image_from_ppm_image(ppm_image_t *img) { pnm_image_t *omg = pnm_image_new_header(img->cols, img->rows, PPM_FORMAT); omg->ppm_pix = img->pix; omg->maxval = img->maxval; return omg; } ppm_image_t *pnm_image_to_ppm_image(pnm_image_t *img) { assert(img->pixtype == PPM_FORMAT); ppm_image_t *omg = ppm_image_new_header(img->cols, img->rows); omg->pix = img->ppm_pix; omg->maxval = img->maxval; return omg; } /* ---------------------------------------------------------------------- */ else if ((format == PBM_FORMAT) || (format == RPBM_FORMAT)) { if (maxval != 1) { pnm_error("PBM maxval should be 1"); } img = pnm_image_new(cols, rows, PGM_FORMAT); img->maxval = 255; pm_message("promoting input PBM file to PGM (maxval = %d)", img->maxval); xel *xelrow = pnm_allocrow(cols); for (row = 0; row < rows; row++) { pixval *imgrow = img->pgm_pix[row]; ppm_image_read_pixels(rd, xelrow, cols, maxval, format); int col; for (col = 0; col < cols; col++) { imgrow[col] = PNM_GET1(xelrow[col]); } } pnm_freerow(xelrow); } else if ((format == RPPM_FORMAT) && (maxval <= 65535)) { /* Raw two-byte format: */ for (col = 0, pP = pix; col < cols; ++col, ++pP) { pnm_sample_t val = *pP; if (val > maxval ) { pnm_error( "value out of bounds (%u > %u)", val, maxval ); val = maxval; } pnm_write_raw_short(wr, val); } } void pnm_image_read_ppm_pixels ( FILE* rd, pnm_sample_t *smp, int cols, pnm_sample_t maxval, pnm_format_t format ) { pnm_sample_t max_byte = 255u; pnm_sample_t max_short = 65535u; int samples_per_row = 3*cols; /* Samples per row. */ int k; pnm_sample_t *sP; if (format == PPM_FORMAT) { assert(pnm_sample_is_valid(maxval, PPM_FILE_MAX_MAXVAL)); for (k = 0, sP = smp; k < samples_per_row; k++) { pnm_sample_t val = pnm_read_plain_sample(rd, maxval); *sP = val; ++sP; } } else if (format == RPPM_FORMAT) { assert(pnm_sample_is_valid(maxval, RPPM_FILE_MAX_MAXVAL)); if (maxval <= maxbyte) { for (k = 0, sP = smp; k < samples_per_row; k++) { pnm_sample_t val = pnm_read_raw_byte(rd, maxval); *sP = val; ++sP; } } else if (maxval <= maxshort) { for (k = 0, sP = smp; k < samples_per_row; k++) { pnm_sample_t val = pnm_read_raw_short(rd, maxval); *sP = val; ++sP; } } else { pnm_error("maxval (%u) is too large for raw PGM format", maxval); } } else { pnm_error("attempt to read PPM pixels from a non-PPM file"); } } void pnm_image_write_ppm_pixels ( FILE* wr, pnm_sample_t *smp, int cols, pnm_sample_t maxval, pnm_format_t format ) { pnm_sample_t *sP; pnm_sample_t max_byte = 255u; pnm_sample_t max_short = 65535u; int samples_per_row = 3*cols; /* Samples per row. */ int k; if (format == PPM_FORMAT) { /* Plain format: */ assert(pnm_sample_is_valid(maxval, PPM_FILE_MAX_MAXVAL)); int chars = 0; /* Counts characters in current line. */ for (k = 0, sP = smp; k < samples_per_row; k++) { /* Break long lines at pixel boundary: */ if ((chars > 55) && ((k % 3) == 0)) { putc('\n', wr); chars = 0; } else { putc(' ', wr); chars++; } /* Write sample in ASCII decimal: */ pnm_sample_t val = *sP; ++sP; chars += pnm_write_plain_sample(wr, val, maxval); } putc('\n', wr); } else if (format == RPPM_FORMAT) { /* Raw single-byte format: */ assert(pnm_sample_is_valid(maxval, RPPM_FILE_MAX_MAXVAL)); if (maxval <= max_byte) { /* Raw single-byte format: */ for (k = 0, sP = smp; k < samples_per_row; k++) { pnm_sample_t val = *sP; ++sP; pnm_write_raw_byte(wr, val, maxval); } } else if (maxval <= max_short) { /* Raw two-byte format: */ for (k = 0, sP = smp; k < samples_per_row; k++) { pnm_sample_t val = *sP; ++sP; pnm_write_raw_short(wr, val, maxval); } } else { pnm_error("maxval (%u) is too large for raw PPM format", maxval); } } else { pnm_error("attempt to write PPM pixels to a non-PPM file"); } fflush(wr); } void pnm_read_header ( FILE *rd, int *colsP, int *rowsP, int *chnsP, pnm_sample_t *maxvalP, bool_t *rawP, bool_t *bpsP, pnm_format_t *formatP ) /* Also sets {*bpsP} to the number of bits per sample in the file (1, 8 or 16) for `raw' variants, or to 0 for `plain' variants. */ { /* Compute external bits-per-sample {bps} (0 for `plain' variants): */ int bps; switch(format) { case PBM_FORMAT: bps = 0; break; case RPBM_FORMAT: bps = 1; break; case PGM_FORMAT: bps = 0; break; case RPGM_FORMAT: bps = (maxval <= 255 ? 8 : 16); break; case PPM_FORMAT: bps = 0; break; case RPPM_FORMAT: bps = (maxval <= 255 ? 8 : 16); break; default: assert(FALSE); } *bpsP = bps; } #define PBM_FILE_MAX_MAXVAL 1 /* The maximum {maxval} that can be used in a PBM file, raw or plain. */ #define RPBM_FILE_MAX_MAXVAL 1 /* The maximum {maxval} that can be used in a raw PBM file. */ #define PGM_FILE_MAX_MAXVAL 65535u /* The maximum {maxval} that can be used in a PGM file, raw or plain. */ #define RPGM_FILE_MAX_MAXVAL 65535u /* The maximum {maxval} that can be used in a raw PGM file. */ #define RPPM_FILE_MAX_MAXVAL 65535u /* The maximum {maxval} that can be used in a raw PPM file. */