From 41c5b519310a01322b54d991ff3b97af486c1a9e Mon Sep 17 00:00:00 2001 From: Antal Ispanovity Date: Tue, 16 Sep 2025 09:47:38 +0200 Subject: [PATCH 1/3] fix binary format writing unify binary and ascii writing for PGM and PPM --- src/pnmio.c | 1454 ++++++++++++++++++++++++++------------------------- 1 file changed, 741 insertions(+), 713 deletions(-) diff --git a/src/pnmio.c b/src/pnmio.c index f81e588..7557f3b 100755 --- a/src/pnmio.c +++ b/src/pnmio.c @@ -1,713 +1,741 @@ -/* - * File : pnmio.c - * Description: I/O facilities for PBM, PGM, PPM (PNM) binary and ASCII images. - * Author : Nikolaos Kavvadias - * Copyright : (C) Nikolaos Kavvadias 2012-2022 - * Website : http://www.nkavvadias.com - * - * This file is part of libpnmio, and is distributed under the terms of the - * Modified BSD License. - * - * A copy of the Modified BSD License is included with this distribution - * in the file LICENSE. - * libpnmio is free software: you can redistribute it and/or modify it under the - * terms of the Modified BSD License. - * libpnmio is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the Modified BSD License for more details. - * - * You should have received a copy of the Modified BSD License along with - * libpnmio. If not, see . - */ - -#include -#include -#include -#include -#include -#include "pnmio.h" - -#define MAXLINE 1024 -#define LITTLE_ENDIAN -1 -#define BIG_ENDIAN 1 -#define GREYSCALE_TYPE 0 /* used for PFM */ -#define RGB_TYPE 1 /* used for PFM */ - - -/* get_pnm_type: - * Read the header contents of a PBM/PGM/PPM/PFM file up to the point of - * extracting its type. Valid types for a PNM image are as follows: - * PBM_ASCII = 1 - * PGM_ASCII = 2 - * PPM_ASCII = 3 - * PBM_BINARY = 4 - * PGM_BINARY = 5 - * PPM_BINARY = 6 - * PAM = 7 - * PFM_RGB = 16 - * PFM_GREYSCALE = 17 - * - * The result (pnm_type) is returned. - */ -int get_pnm_type(FILE *f) -{ - int pnm_type=0; - unsigned int i; - char magic[MAXLINE]; - char line[MAXLINE]; - - /* Read the PNM/PFM file header. */ - while (fgets(line, MAXLINE, f) != NULL) { - int flag = 0; - for (i = 0; i < strlen(line); i++) { - if (isgraph(line[i])) { - if ((line[i] == '#') && (flag == 0)) { - flag = 1; - } - } - } - if (flag == 0) { - sscanf(line, "%2s", magic); - break; - } - } - - /* NOTE: This part can be written more succinctly, however, - * it is better to have the PNM types decoded explicitly. - */ - if (strcmp(magic, "P1") == 0) { - pnm_type = PBM_ASCII; - } else if (strcmp(magic, "P2") == 0) { - pnm_type = PGM_ASCII; - } else if (strcmp(magic, "P3") == 0) { - pnm_type = PPM_ASCII; - } else if (strcmp(magic, "P4") == 0) { - pnm_type = PBM_BINARY; - } else if (strcmp(magic, "P5") == 0) { - pnm_type = PGM_BINARY; - } else if (strcmp(magic, "P6") == 0) { - pnm_type = PPM_BINARY; - } else if (strcmp(magic, "P7") == 0) { - pnm_type = PAM; - } else if (strcmp(magic, "PF") == 0) { - pnm_type = PFM_RGB; - } else if (strcmp(magic, "Pf") == 0) { - pnm_type = PFM_GREYSCALE; - } else { - fprintf(stderr, "Error: Unknown PNM/PFM file; wrong magic number!\n"); - exit(1); - } - - return (pnm_type); -} - -/* read_pbm_header: - * Read the header contents of a PBM (Portable Binary Map) file. - * Returns the number of bytes that need be allocated for the image data. - * An ASCII PBM image file follows the format: - * P1 - * - * ... - * A binary PBM image file uses P4 instead of P1 and - * the data values are represented in binary. - * NOTE1: Comment lines start with '#'. - * NOTE2: < > denote integer values (in decimal). - */ -int read_pbm_header(FILE *f, int *img_xdim, int *img_ydim, int *is_ascii) -{ - int x_val, y_val; - unsigned int i; - char magic[MAXLINE]; - char line[MAXLINE]; - int count=0; - - /* Read the PBM file header. */ - while (fgets(line, MAXLINE, f) != NULL) { - int flag = 0; - for (i = 0; i < strlen(line); i++) { - if (isgraph(line[i])) { - if ((line[i] == '#') && (flag == 0)) { - flag = 1; - } - } - } - if (flag == 0) { - if (count == 0) { - count += sscanf(line, "%2s %d %d", magic, &x_val, &y_val); - } else if (count == 1) { - count += sscanf(line, "%d %d", &x_val, &y_val); - } else if (count == 2) { - count += sscanf(line, "%d", &y_val); - } - } - if (count == 3) { - break; - } - } - - if (strcmp(magic, "P1") == 0) { - *is_ascii = 1; - } else if (strcmp(magic, "P4") == 0) { - *is_ascii = 0; - } else { - fprintf(stderr, "Error: Input file not in PBM format!\n"); - exit(1); - } - - fprintf(stderr, "Info: magic=%s, x_val=%d, y_val=%d\n", - magic, x_val, y_val); - *img_xdim = x_val; - *img_ydim = y_val; - - return *img_xdim * *img_ydim * sizeof(int); -} - -/* read_pgm_header: - * Read the header contents of a PGM (Portable Grey[scale] Map) file. - * Returns the number of bytes that need be allocated for the image data. - * An ASCII PGM image file follows the format: - * P2 - * - * - * ... - * A binary PGM image file uses P5 instead of P2 and - * the data values are represented in binary. - * NOTE1: Comment lines start with '#'. - * NOTE2: < > denote integer values (in decimal). - */ -int read_pgm_header(FILE *f, int *img_xdim, int *img_ydim, int *img_colors, int *is_ascii) -{ - int x_val, y_val, maxcolors_val; - unsigned int i; - char magic[MAXLINE]; - char line[MAXLINE]; - int count=0; - - /* Read the PGM file header. */ - while (fgets(line, MAXLINE, f) != NULL) { - int flag = 0; - for (i = 0; i < strlen(line); i++) { - if (isgraph(line[i]) && (flag == 0)) { - if ((line[i] == '#') && (flag == 0)) { - flag = 1; - } - } - } - if (flag == 0) { - if (count == 0) { - count += sscanf(line, "%2s %d %d %d", magic, &x_val, &y_val, &maxcolors_val); - } else if (count == 1) { - count += sscanf(line, "%d %d %d", &x_val, &y_val, &maxcolors_val); - } else if (count == 2) { - count += sscanf(line, "%d %d", &y_val, &maxcolors_val); - } else if (count == 3) { - count += sscanf(line, "%d", &maxcolors_val); - } - } - if (count == 4) { - break; - } - } - - if (strcmp(magic, "P2") == 0) { - *is_ascii = 1; - } else if (strcmp(magic, "P5") == 0) { - *is_ascii = 0; - } else { - fprintf(stderr, "Error: Input file not in PGM format!\n"); - exit(1); - } - - fprintf(stderr, "Info: magic=%s, x_val=%d, y_val=%d, maxcolors_val=%d\n", - magic, x_val, y_val, maxcolors_val); - *img_xdim = x_val; - *img_ydim = y_val; - *img_colors = maxcolors_val; - - return *img_xdim * *img_ydim * sizeof(int); -} - -/* read_ppm_header: - * Read the header contents of a PPM (Portable Pix[el] Map) file. - * Returns the number of bytes that need be allocated for the image data. - * An ASCII PPM image file follows the format: - * P3 - * - * - * ... - * A binary PPM image file uses P6 instead of P3 and - * the data values are represented in binary. - * NOTE1: Comment lines start with '#'. - # NOTE2: < > denote integer values (in decimal). - */ -int read_ppm_header(FILE *f, int *img_xdim, int *img_ydim, int *img_colors, int *is_ascii) -{ - int x_val, y_val, maxcolors_val; - unsigned int i; - char magic[MAXLINE]; - char line[MAXLINE]; - int count=0; - - /* Read the PPM file header. */ - while (fgets(line, MAXLINE, f) != NULL) { - int flag = 0; - for (i = 0; i < strlen(line); i++) { - if (isgraph(line[i]) && (flag == 0)) { - if ((line[i] == '#') && (flag == 0)) { - flag = 1; - } - } - } - if (flag == 0) { - if (count == 0) { - count += sscanf(line, "%2s %d %d %d", magic, &x_val, &y_val, &maxcolors_val); - } else if (count == 1) { - count += sscanf(line, "%d %d %d", &x_val, &y_val, &maxcolors_val); - } else if (count == 2) { - count += sscanf(line, "%d %d", &y_val, &maxcolors_val); - } else if (count == 3) { - count += sscanf(line, "%d", &maxcolors_val); - } - } - if (count == 4) { - break; - } - } - - if (strcmp(magic, "P3") == 0) { - *is_ascii = 1; - } else if (strcmp(magic, "P6") == 0) { - *is_ascii = 0; - } else { - fprintf(stderr, "Error: Input file not in PPM format!\n"); - exit(1); - } - - fprintf(stderr, "Info: magic=%s, x_val=%d, y_val=%d, maxcolors_val=%d\n", - magic, x_val, y_val, maxcolors_val); - *img_xdim = x_val; - *img_ydim = y_val; - *img_colors = maxcolors_val; - - return 3 * *img_xdim * *img_ydim * sizeof(int); -} - -/* read_pfm_header: - * Read the header contents of a PFM (portable float map) file. - * Returns the number of bytes that need be allocated for the image data. - * A PFM image file follows the format: - * [PF|Pf] - * - * (endianess) - * {R1}{G1}{B1} ... {RMAX}{GMAX}{BMAX} - * NOTE1: Comment lines, if allowed, start with '#'. - # NOTE2: < > denote integer values (in decimal). - # NOTE3: ( ) denote floating-point values (in decimal). - # NOTE4: { } denote floating-point values (coded in binary). - */ -int read_pfm_header(FILE *f, int *img_xdim, int *img_ydim, int *img_type, int *endianess) -{ - int x_val, y_val; - unsigned int i; - int is_rgb=0, is_greyscale=0; - float aspect_ratio=0; - char magic[MAXLINE]; - char line[MAXLINE]; - int count=0; - int num_bytes=0; - - /* Read the PFM file header. */ - while (fgets(line, MAXLINE, f) != NULL) { - int flag = 0; - for (i = 0; i < strlen(line); i++) { - if (isgraph(line[i]) && (flag == 0)) { - if ((line[i] == '#') && (flag == 0)) { - flag = 1; - } - } - } - if (flag == 0) { - if (count == 0) { - count += sscanf(line, "%2s %d %d %f", magic, &x_val, &y_val, &aspect_ratio); - } else if (count == 1) { - count += sscanf(line, "%d %d %f", &x_val, &y_val, &aspect_ratio); - } else if (count == 2) { - count += sscanf(line, "%d %f", &y_val, &aspect_ratio); - } else if (count == 3) { - count += sscanf(line, "%f", &aspect_ratio); - } - } - if (count == 4) { - break; - } - } - - if (strcmp(magic, "PF") == 0) { - is_rgb = 1; - is_greyscale = 0; - } else if (strcmp(magic, "Pf") == 0) { - is_greyscale = 0; - is_rgb = 1; - } else { - fprintf(stderr, "Error: Input file not in PFM format!\n"); - exit(1); - } - - fprintf(stderr, "Info: magic=%s, x_val=%d, y_val=%d, aspect_ratio=%f\n", - magic, x_val, y_val, aspect_ratio); - - /* FIXME: Aspect ratio different to 1.0 is not yet supported. */ - if (!floatEqualComparison(aspect_ratio, -1.0, 1E-06) && - !floatEqualComparison(aspect_ratio, 1.0, 1E-06)) { - fprintf(stderr, "Error: Aspect ratio different to -1.0 or +1.0 is unsupported!\n"); - exit(1); - } - - *img_xdim = x_val; - *img_ydim = y_val; - *img_type = is_rgb & ~is_greyscale; - if (aspect_ratio > 0.0) { - *endianess = 1; - } else { - *endianess = -1; - } - - num_bytes = *img_xdim * *img_ydim * sizeof(float); - if (is_rgb) { - num_bytes *= 3; - } - return num_bytes; -} - -/* read_pbm_data: - * Read the data contents of a PBM (portable bit map) file. - */ -void read_pbm_data(FILE *f, int *img_in, int is_ascii) -{ - int i=0, c; - int lum_val; - int k; - - /* Read the rest of the PBM file. */ - while ((c = fgetc(f)) != EOF) { - ungetc(c, f); - if (is_ascii == 1) { - if (fscanf(f, "%d", &lum_val) != 1) return; - img_in[i++] = lum_val; - } else { - lum_val = fgetc(f); - /* Decode the image contents byte-by-byte. */ - for (k = 0; k < 8; k++) { - img_in[i++] = (lum_val >> (7-k)) & 0x1; - } - } - } -} - -/* read_pgm_data: - * Read the data contents of a PGM (portable grey map) file. - */ -void read_pgm_data(FILE *f, int *img_in, int is_ascii) -{ - int i=0, c; - int lum_val; - - /* Read the rest of the PGM file. */ - while ((c = fgetc(f)) != EOF) { - ungetc(c, f); - if (is_ascii == 1) { - if (fscanf(f, "%d", &lum_val) != 1) return; - } else { - lum_val = fgetc(f); - } - img_in[i++] = lum_val; - } -} - -/* read_ppm_data: - * Read the data contents of a PPM (portable pix map) file. - */ -void read_ppm_data(FILE *f, int *img_in, int is_ascii) -{ - int i=0, c; - int r_val, g_val, b_val; - - /* Read the rest of the PPM file. */ - while ((c = fgetc(f)) != EOF) { - ungetc(c, f); - if (is_ascii == 1) { - if (fscanf(f, "%d %d %d", &r_val, &g_val, &b_val) != 3) return; - } else { - r_val = fgetc(f); - g_val = fgetc(f); - b_val = fgetc(f); - } - img_in[i++] = r_val; - img_in[i++] = g_val; - img_in[i++] = b_val; - } -} - -/* read_pfm_data: - * Read the data contents of a PFM (portable float map) file. - */ -void read_pfm_data(FILE *f, float *img_in, int img_type, int endianess) -{ - int i=0, c; - int swap = (endianess == 1) ? 0 : 1; - float r_val, g_val, b_val; - - /* Read the rest of the PFM file. */ - while ((c = fgetc(f)) != EOF) { - ungetc(c, f); - /* Read a possibly byte-swapped float. */ - if (img_type == RGB_TYPE) { - ReadFloat(f, &r_val, swap); - ReadFloat(f, &g_val, swap); - ReadFloat(f, &b_val, swap); - img_in[i++] = r_val; - img_in[i++] = g_val; - img_in[i++] = b_val; - } else if (img_type == GREYSCALE_TYPE) { - ReadFloat(f, &g_val, swap); - img_in[i++] = g_val; - } - } -} - -/* write_pbm_file: - * Write the contents of a PBM (portable bit map) file. - */ -void write_pbm_file(FILE *f, int *img_out, - int x_size, int y_size, int x_scale_val, int y_scale_val, int linevals, - int is_ascii) -{ - int i, j, x_scaled_size, y_scaled_size; - int k, v, temp, step; - - x_scaled_size = x_size * x_scale_val; - y_scaled_size = y_size * y_scale_val; - /* Write the magic number string. */ - if (is_ascii == 1) { - fprintf(f, "P1\n"); - step = 1; - } else { - fprintf(f, "P4\n"); - step = 8; - } - /* Write the image dimensions. */ - fprintf(f, "%d %d\n", x_scaled_size, y_scaled_size); - - /* Write the image data. */ - for (i = 0; i < y_scaled_size; i++) { - for (j = 0; j < x_scaled_size; j+=step) { - if (is_ascii == 1) { - fprintf(f, "%d ", img_out[i*x_scaled_size+j]); - } else { - temp = 0; - for (k = 0; k < 8; k++) { - v = img_out[i*x_scaled_size+j+k]; - temp |= (v << (7-k)); - } - fprintf(f, "%c", temp); - } - if (((i*x_scaled_size+j) % linevals) == (linevals-1)) { - fprintf(f, "\n"); - } - } - } -} - -/* write_pgm_file: - * Write the contents of a PGM (portable grey map) file. - */ -void write_pgm_file(FILE *f, int *img_out, - int x_size, int y_size, int x_scale_val, int y_scale_val, - int img_colors, int linevals, int is_ascii) -{ - int i, j, x_scaled_size, y_scaled_size; - - x_scaled_size = x_size * x_scale_val; - y_scaled_size = y_size * y_scale_val; - /* Write the magic number string. */ - if (is_ascii == 1) { - fprintf(f, "P2\n"); - } else { - fprintf(f, "P5\n"); - } - /* Write the image dimensions. */ - fprintf(f, "%d %d\n", x_scaled_size, y_scaled_size); - /* Write the maximum color/grey level allowed. */ - fprintf(f, "%d\n", img_colors); - - /* Write the image data. */ - for (i = 0; i < y_scaled_size; i++) { - for (j = 0; j < x_scaled_size; j++) { - if (is_ascii == 1) { - fprintf(f, "%d ", img_out[i*x_scaled_size+j]); - if (((i*x_scaled_size+j) % linevals) == (linevals-1)) { - fprintf(f, "\n"); - } - } else { - fprintf(f, "%c", img_out[i*x_scaled_size+j]); - } - } - } -} - -/* write_ppm_file: - * Write the contents of a PPM (portable pix map) file. - */ -void write_ppm_file(FILE *f, int *img_out, - int x_size, int y_size, int x_scale_val, int y_scale_val, - int img_colors, int is_ascii) -{ - int i, j, x_scaled_size, y_scaled_size; - - x_scaled_size = x_size * x_scale_val; - y_scaled_size = y_size * y_scale_val; - /* Write the magic number string. */ - if (is_ascii == 1) { - fprintf(f, "P3\n"); - } else { - fprintf(f, "P6\n"); - } - /* Write the image dimensions. */ - fprintf(f, "%d %d\n", x_scaled_size, y_scaled_size); - /* Write the maximum color/grey level allowed. */ - fprintf(f, "%d\n", img_colors); - - /* Write the image data. */ - for (i = 0; i < y_scaled_size; i++) { - for (j = 0; j < x_scaled_size; j++) { - if (is_ascii == 1) { - fprintf(f, "%d %d %d ", - img_out[3*(i*x_scaled_size+j)+0], - img_out[3*(i*x_scaled_size+j)+1], - img_out[3*(i*x_scaled_size+j)+2]); - if ((j % 4) == 0) { - fprintf(f, "\n"); - } - } else { - fprintf(f, "%c%c%c", - img_out[3*(i*x_scaled_size+j)+0], - img_out[3*(i*x_scaled_size+j)+1], - img_out[3*(i*x_scaled_size+j)+2]); - } - } - } -} - -/* write_pfm_file: - * Write the contents of a PFM (portable float map) file. - */ -void write_pfm_file(FILE *f, float *img_out, - int x_size, int y_size, - int img_type, int endianess) -{ - int i, j, x_scaled_size, y_scaled_size; - int swap = (endianess == 1) ? 0 : 1; - float fendian = (endianess == 1) ? +1.0 : -1.0; - - x_scaled_size = x_size; - y_scaled_size = y_size; - - /* Write the magic number string. */ - if (img_type == RGB_TYPE) { - fprintf(f, "PF\n"); - } else if (img_type == GREYSCALE_TYPE) { - fprintf(f, "Pf\n"); - } else { - fprintf(stderr, "Error: Image type invalid for PFM format!\n"); - exit(1); - } - /* Write the image dimensions. */ - fprintf(f, "%d %d\n", x_scaled_size, y_scaled_size); - /* Write the endianess/scale factor as float. */ - fprintf(f, "%f\n", fendian); - - /* Write the image data. */ - for (i = 0; i < y_scaled_size; i++) { - for (j = 0; j < x_scaled_size; j++) { - if (img_type == RGB_TYPE) { - WriteFloat(f, &img_out[3*(i*x_scaled_size+j)+0], swap); - WriteFloat(f, &img_out[3*(i*x_scaled_size+j)+1], swap); - WriteFloat(f, &img_out[3*(i*x_scaled_size+j)+2], swap); - } else if (img_type == GREYSCALE_TYPE) { - WriteFloat(f, &img_out[i*x_scaled_size+j], swap); - } - } - } -} - -/* ReadFloat: - * Read a possibly byte swapped floating-point number. - * NOTE: Assume IEEE format. - * Source: http://paulbourke.net/dataformats/pbmhdr/ - */ -int ReadFloat(FILE *fptr, float *f, int swap) -{ - unsigned char *cptr; - - if (fread(f, sizeof(float), 1, fptr) != 1) { - return (FALSE); - } - if (swap) { - cptr = (unsigned char *)f; - unsigned char tmp = cptr[0]; - cptr[0] = cptr[3]; - cptr[3] = tmp; - tmp = cptr[1]; - cptr[1] = cptr[2]; - cptr[2] = tmp; - } - return (TRUE); -} - -/* WriteFloat: - * Write a possibly byte-swapped floating-point number. - * NOTE: Assume IEEE format. - */ -int WriteFloat(FILE *fptr, float *f, int swap) -{ - unsigned char *cptr; - - if (swap) { - cptr = (unsigned char*)f; - unsigned char tmp = cptr[0]; - cptr[0] = cptr[3]; - cptr[3] = tmp; - tmp = cptr[1]; - cptr[1] = cptr[2]; - cptr[2] = tmp; - } - if (fwrite(f, sizeof(float), 1, fptr) != 1) { - return (FALSE); - } - return (TRUE); -} - -/* floatEqualComparison: - * Compare two floats and accept equality if not different than - * maxRelDiff (a specified maximum relative difference). - */ -int floatEqualComparison(float A, float B, float maxRelDiff) -{ - float largest, diff = fabs(A-B); - A = fabs(A); - B = fabs(B); - largest = (B > A) ? B : A; - if (diff <= largest * maxRelDiff) { - return 1; - } - return 0; -} - -/* frand: - * Emulate a floating-point PRNG. - * Source: http://c-faq.com/lib/rand48.html - */ -float frand(void) -{ - return rand() / (RAND_MAX + 1.0); -} +/* + * File : pnmio.c + * Description: I/O facilities for PBM, PGM, PPM (PNM) binary and ASCII images. + * Author : Nikolaos Kavvadias + * Copyright : (C) Nikolaos Kavvadias 2012-2022 + * Website : http://www.nkavvadias.com + * + * This file is part of libpnmio, and is distributed under the terms of the + * Modified BSD License. + * + * A copy of the Modified BSD License is included with this distribution + * in the file LICENSE. + * libpnmio is free software: you can redistribute it and/or modify it under the + * terms of the Modified BSD License. + * libpnmio is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the Modified BSD License for more details. + * + * You should have received a copy of the Modified BSD License along with + * libpnmio. If not, see . + */ + +#include +#include +#include +#include +#include +#include "pnmio.h" + +#define MAXLINE 1024 +#define LITTLE_ENDIAN -1 +#define BIG_ENDIAN 1 +#define GREYSCALE_TYPE 0 /* used for PFM */ +#define RGB_TYPE 1 /* used for PFM */ + +void write_ascii( FILE *f, int *img_out, int x_scaled_size, int y_scaled_size, int linevals ) +{ + for ( int i = 0; i < x_scaled_size * y_scaled_size; ++i ) + { + fprintf(f, "%d ", img_out[i]); + if ( i % linevals == linevals-1 ) + fprintf(f, "\n"); + } +} +// the unit of information is 1 byte. used for 8 bit grayscale and 24 bit rgb components +void write_binary_1( FILE *f, int *img_out, int x_scaled_size, int y_scaled_size ) +{ + char c; + for ( int i = 0; i < x_scaled_size * y_scaled_size; ++i ) + { + c = (char)img_out[i]; + fwrite(&c, sizeof(char), 1, f ); + } +} +// the unit of information is 2 bytes. used for 16 bit grayscale and 48 bit rgb components +void write_binary_2( FILE *f, int *img_out, int x_scaled_size, int y_scaled_size) +{ + short s; + for ( int i = 0; i < x_scaled_size * y_scaled_size; ++i ) + { + s = (short)img_out[i]; + fwrite(&s, sizeof(short), 1, f ); + } +} + +/* get_pnm_type: + * Read the header contents of a PBM/PGM/PPM/PFM file up to the point of + * extracting its type. Valid types for a PNM image are as follows: + * PBM_ASCII = 1 + * PGM_ASCII = 2 + * PPM_ASCII = 3 + * PBM_BINARY = 4 + * PGM_BINARY = 5 + * PPM_BINARY = 6 + * PAM = 7 + * PFM_RGB = 16 + * PFM_GREYSCALE = 17 + * + * The result (pnm_type) is returned. + */ +int get_pnm_type(FILE *f) +{ + int pnm_type=0; + unsigned int i; + char magic[MAXLINE]; + char line[MAXLINE]; + + /* Read the PNM/PFM file header. */ + while (fgets(line, MAXLINE, f) != NULL) { + int flag = 0; + for (i = 0; i < strlen(line); i++) { + if (isgraph(line[i])) { + if ((line[i] == '#') && (flag == 0)) { + flag = 1; + } + } + } + if (flag == 0) { + sscanf(line, "%2s", magic); + break; + } + } + + /* NOTE: This part can be written more succinctly, however, + * it is better to have the PNM types decoded explicitly. + */ + if (strcmp(magic, "P1") == 0) { + pnm_type = PBM_ASCII; + } else if (strcmp(magic, "P2") == 0) { + pnm_type = PGM_ASCII; + } else if (strcmp(magic, "P3") == 0) { + pnm_type = PPM_ASCII; + } else if (strcmp(magic, "P4") == 0) { + pnm_type = PBM_BINARY; + } else if (strcmp(magic, "P5") == 0) { + pnm_type = PGM_BINARY; + } else if (strcmp(magic, "P6") == 0) { + pnm_type = PPM_BINARY; + } else if (strcmp(magic, "P7") == 0) { + pnm_type = PAM; + } else if (strcmp(magic, "PF") == 0) { + pnm_type = PFM_RGB; + } else if (strcmp(magic, "Pf") == 0) { + pnm_type = PFM_GREYSCALE; + } else { + fprintf(stderr, "Error: Unknown PNM/PFM file; wrong magic number!\n"); + exit(1); + } + + return (pnm_type); +} + +/* read_pbm_header: + * Read the header contents of a PBM (Portable Binary Map) file. + * Returns the number of bytes that need be allocated for the image data. + * An ASCII PBM image file follows the format: + * P1 + * + * ... + * A binary PBM image file uses P4 instead of P1 and + * the data values are represented in binary. + * NOTE1: Comment lines start with '#'. + * NOTE2: < > denote integer values (in decimal). + */ +int read_pbm_header(FILE *f, int *img_xdim, int *img_ydim, int *is_ascii) +{ + int x_val, y_val; + unsigned int i; + char magic[MAXLINE]; + char line[MAXLINE]; + int count=0; + + /* Read the PBM file header. */ + while (fgets(line, MAXLINE, f) != NULL) { + int flag = 0; + for (i = 0; i < strlen(line); i++) { + if (isgraph(line[i])) { + if ((line[i] == '#') && (flag == 0)) { + flag = 1; + } + } + } + if (flag == 0) { + if (count == 0) { + count += sscanf(line, "%2s %d %d", magic, &x_val, &y_val); + } else if (count == 1) { + count += sscanf(line, "%d %d", &x_val, &y_val); + } else if (count == 2) { + count += sscanf(line, "%d", &y_val); + } + } + if (count == 3) { + break; + } + } + + if (strcmp(magic, "P1") == 0) { + *is_ascii = 1; + } else if (strcmp(magic, "P4") == 0) { + *is_ascii = 0; + } else { + fprintf(stderr, "Error: Input file not in PBM format!\n"); + exit(1); + } + + fprintf(stderr, "Info: magic=%s, x_val=%d, y_val=%d\n", + magic, x_val, y_val); + *img_xdim = x_val; + *img_ydim = y_val; + + return *img_xdim * *img_ydim * sizeof(int); +} + +/* read_pgm_header: + * Read the header contents of a PGM (Portable Grey[scale] Map) file. + * Returns the number of bytes that need be allocated for the image data. + * An ASCII PGM image file follows the format: + * P2 + * + * + * ... + * A binary PGM image file uses P5 instead of P2 and + * the data values are represented in binary. + * NOTE1: Comment lines start with '#'. + * NOTE2: < > denote integer values (in decimal). + */ +int read_pgm_header(FILE *f, int *img_xdim, int *img_ydim, int *img_colors, int *is_ascii) +{ + int x_val, y_val, maxcolors_val; + unsigned int i; + char magic[MAXLINE]; + char line[MAXLINE]; + int count=0; + + /* Read the PGM file header. */ + while (fgets(line, MAXLINE, f) != NULL) { + int flag = 0; + for (i = 0; i < strlen(line); i++) { + if (isgraph(line[i]) && (flag == 0)) { + if ((line[i] == '#') && (flag == 0)) { + flag = 1; + } + } + } + if (flag == 0) { + if (count == 0) { + count += sscanf(line, "%2s %d %d %d", magic, &x_val, &y_val, &maxcolors_val); + } else if (count == 1) { + count += sscanf(line, "%d %d %d", &x_val, &y_val, &maxcolors_val); + } else if (count == 2) { + count += sscanf(line, "%d %d", &y_val, &maxcolors_val); + } else if (count == 3) { + count += sscanf(line, "%d", &maxcolors_val); + } + } + if (count == 4) { + break; + } + } + + if (strcmp(magic, "P2") == 0) { + *is_ascii = 1; + } else if (strcmp(magic, "P5") == 0) { + *is_ascii = 0; + } else { + fprintf(stderr, "Error: Input file not in PGM format!\n"); + exit(1); + } + + fprintf(stderr, "Info: magic=%s, x_val=%d, y_val=%d, maxcolors_val=%d\n", + magic, x_val, y_val, maxcolors_val); + *img_xdim = x_val; + *img_ydim = y_val; + *img_colors = maxcolors_val; + + return *img_xdim * *img_ydim * sizeof(int); +} + +/* read_ppm_header: + * Read the header contents of a PPM (Portable Pix[el] Map) file. + * Returns the number of bytes that need be allocated for the image data. + * An ASCII PPM image file follows the format: + * P3 + * + * + * ... + * A binary PPM image file uses P6 instead of P3 and + * the data values are represented in binary. + * NOTE1: Comment lines start with '#'. + # NOTE2: < > denote integer values (in decimal). + */ +int read_ppm_header(FILE *f, int *img_xdim, int *img_ydim, int *img_colors, int *is_ascii) +{ + int x_val, y_val, maxcolors_val; + unsigned int i; + char magic[MAXLINE]; + char line[MAXLINE]; + int count=0; + + /* Read the PPM file header. */ + while (fgets(line, MAXLINE, f) != NULL) { + int flag = 0; + for (i = 0; i < strlen(line); i++) { + if (isgraph(line[i]) && (flag == 0)) { + if ((line[i] == '#') && (flag == 0)) { + flag = 1; + } + } + } + if (flag == 0) { + if (count == 0) { + count += sscanf(line, "%2s %d %d %d", magic, &x_val, &y_val, &maxcolors_val); + } else if (count == 1) { + count += sscanf(line, "%d %d %d", &x_val, &y_val, &maxcolors_val); + } else if (count == 2) { + count += sscanf(line, "%d %d", &y_val, &maxcolors_val); + } else if (count == 3) { + count += sscanf(line, "%d", &maxcolors_val); + } + } + if (count == 4) { + break; + } + } + + if (strcmp(magic, "P3") == 0) { + *is_ascii = 1; + } else if (strcmp(magic, "P6") == 0) { + *is_ascii = 0; + } else { + fprintf(stderr, "Error: Input file not in PPM format!\n"); + exit(1); + } + + fprintf(stderr, "Info: magic=%s, x_val=%d, y_val=%d, maxcolors_val=%d\n", + magic, x_val, y_val, maxcolors_val); + *img_xdim = x_val; + *img_ydim = y_val; + *img_colors = maxcolors_val; + + return 3 * *img_xdim * *img_ydim * sizeof(int); +} + +/* read_pfm_header: + * Read the header contents of a PFM (portable float map) file. + * Returns the number of bytes that need be allocated for the image data. + * A PFM image file follows the format: + * [PF|Pf] + * + * (endianess) + * {R1}{G1}{B1} ... {RMAX}{GMAX}{BMAX} + * NOTE1: Comment lines, if allowed, start with '#'. + # NOTE2: < > denote integer values (in decimal). + # NOTE3: ( ) denote floating-point values (in decimal). + # NOTE4: { } denote floating-point values (coded in binary). + */ +int read_pfm_header(FILE *f, int *img_xdim, int *img_ydim, int *img_type, int *endianess) +{ + int x_val, y_val; + unsigned int i; + int is_rgb=0, is_greyscale=0; + float aspect_ratio=0; + char magic[MAXLINE]; + char line[MAXLINE]; + int count=0; + int num_bytes=0; + + /* Read the PFM file header. */ + while (fgets(line, MAXLINE, f) != NULL) { + int flag = 0; + for (i = 0; i < strlen(line); i++) { + if (isgraph(line[i]) && (flag == 0)) { + if ((line[i] == '#') && (flag == 0)) { + flag = 1; + } + } + } + if (flag == 0) { + if (count == 0) { + count += sscanf(line, "%2s %d %d %f", magic, &x_val, &y_val, &aspect_ratio); + } else if (count == 1) { + count += sscanf(line, "%d %d %f", &x_val, &y_val, &aspect_ratio); + } else if (count == 2) { + count += sscanf(line, "%d %f", &y_val, &aspect_ratio); + } else if (count == 3) { + count += sscanf(line, "%f", &aspect_ratio); + } + } + if (count == 4) { + break; + } + } + + if (strcmp(magic, "PF") == 0) { + is_rgb = 1; + is_greyscale = 0; + } else if (strcmp(magic, "Pf") == 0) { + is_greyscale = 0; + is_rgb = 1; + } else { + fprintf(stderr, "Error: Input file not in PFM format!\n"); + exit(1); + } + + fprintf(stderr, "Info: magic=%s, x_val=%d, y_val=%d, aspect_ratio=%f\n", + magic, x_val, y_val, aspect_ratio); + + /* FIXME: Aspect ratio different to 1.0 is not yet supported. */ + if (!floatEqualComparison(aspect_ratio, -1.0, 1E-06) && + !floatEqualComparison(aspect_ratio, 1.0, 1E-06)) { + fprintf(stderr, "Error: Aspect ratio different to -1.0 or +1.0 is unsupported!\n"); + exit(1); + } + + *img_xdim = x_val; + *img_ydim = y_val; + *img_type = is_rgb & ~is_greyscale; + if (aspect_ratio > 0.0) { + *endianess = 1; + } else { + *endianess = -1; + } + + num_bytes = *img_xdim * *img_ydim * sizeof(float); + if (is_rgb) { + num_bytes *= 3; + } + return num_bytes; +} + +/* read_pbm_data: + * Read the data contents of a PBM (portable bit map) file. + */ +void read_pbm_data(FILE *f, int *img_in, int is_ascii) +{ + int i=0, c; + int lum_val; + int k; + + /* Read the rest of the PBM file. */ + while ((c = fgetc(f)) != EOF) { + ungetc(c, f); + if (is_ascii == 1) { + if (fscanf(f, "%d", &lum_val) != 1) return; + img_in[i++] = lum_val; + } else { + lum_val = fgetc(f); + /* Decode the image contents byte-by-byte. */ + for (k = 0; k < 8; k++) { + img_in[i++] = (lum_val >> (7-k)) & 0x1; + } + } + } +} + +/* read_pgm_data: + * Read the data contents of a PGM (portable grey map) file. + */ +void read_pgm_data(FILE *f, int *img_in, int is_ascii) +{ + int i=0, c; + int lum_val; + + /* Read the rest of the PGM file. */ + while ((c = fgetc(f)) != EOF) { + ungetc(c, f); + if (is_ascii == 1) { + if (fscanf(f, "%d", &lum_val) != 1) return; + } else { + lum_val = fgetc(f); + } + img_in[i++] = lum_val; + } +} + +/* read_ppm_data: + * Read the data contents of a PPM (portable pix map) file. + */ +void read_ppm_data(FILE *f, int *img_in, int is_ascii) +{ + int i=0, c; + int r_val, g_val, b_val; + + /* Read the rest of the PPM file. */ + while ((c = fgetc(f)) != EOF) { + ungetc(c, f); + if (is_ascii == 1) { + if (fscanf(f, "%d %d %d", &r_val, &g_val, &b_val) != 3) return; + } else { + r_val = fgetc(f); + g_val = fgetc(f); + b_val = fgetc(f); + } + img_in[i++] = r_val; + img_in[i++] = g_val; + img_in[i++] = b_val; + } +} + +/* read_pfm_data: + * Read the data contents of a PFM (portable float map) file. + */ +void read_pfm_data(FILE *f, float *img_in, int img_type, int endianess) +{ + int i=0, c; + int swap = (endianess == 1) ? 0 : 1; + float r_val, g_val, b_val; + + /* Read the rest of the PFM file. */ + while ((c = fgetc(f)) != EOF) { + ungetc(c, f); + /* Read a possibly byte-swapped float. */ + if (img_type == RGB_TYPE) { + ReadFloat(f, &r_val, swap); + ReadFloat(f, &g_val, swap); + ReadFloat(f, &b_val, swap); + img_in[i++] = r_val; + img_in[i++] = g_val; + img_in[i++] = b_val; + } else if (img_type == GREYSCALE_TYPE) { + ReadFloat(f, &g_val, swap); + img_in[i++] = g_val; + } + } +} + +/* write_pbm_file: + * Write the contents of a PBM (portable bit map) file. + */ +void write_pbm_file(FILE *f, int *img_out, + int x_size, int y_size, int x_scale_val, int y_scale_val, int linevals, + int is_ascii) +{ + int i, j, x_scaled_size, y_scaled_size; + int k, v, temp, step; + + x_scaled_size = x_size * x_scale_val; + y_scaled_size = y_size * y_scale_val; + /* Write the magic number string. */ + if (is_ascii == 1) { + fprintf(f, "P1\n"); + step = 1; + } else { + fprintf(f, "P4\n"); + step = 8; + } + /* Write the image dimensions. */ + fprintf(f, "%d %d\n", x_scaled_size, y_scaled_size); + + /* Write the image data. */ + for (i = 0; i < y_scaled_size; i++) { + for (j = 0; j < x_scaled_size; j+=step) { + if (is_ascii == 1) { + fprintf(f, "%d ", img_out[i*x_scaled_size+j]); + } else { + temp = 0; + for (k = 0; k < 8; k++) { + v = img_out[i*x_scaled_size+j+k]; + temp |= (v << (7-k)); + } + fprintf(f, "%c", temp); + } + if (((i*x_scaled_size+j) % linevals) == (linevals-1)) { + fprintf(f, "\n"); + } + } + } +} + +/* write_pgm_file: + * Write the contents of a PGM (portable grey map) file. + */ +void write_pgm_file(FILE *f, int *img_out, + int x_size, int y_size, int x_scale_val, int y_scale_val, + int img_colors, int linevals, int is_ascii) +{ + int x_scaled_size, y_scaled_size; + + x_scaled_size = x_size * x_scale_val; + y_scaled_size = y_size * y_scale_val; + /* Write the magic number string. */ + if (is_ascii == 1) { + fprintf(f, "P2\n"); + } else { + fprintf(f, "P5\n"); + } + /* Write the image dimensions. */ + fprintf(f, "%d %d\n", x_scaled_size, y_scaled_size); + /* Write the maximum color/grey level allowed. */ + fprintf(f, "%d\n", img_colors); + + /* Write the image data. */ + if ( is_ascii ) + { + write_ascii(f, img_out, x_scaled_size, y_scaled_size, linevals ); + } + else + { + if ( img_colors < 256 ) + { + write_binary_1(f, img_out, x_scaled_size, y_scaled_size); + } + else + { + write_binary_2(f, img_out, x_scaled_size, y_scaled_size); + } + } +} + +/* write_ppm_file: + * Write the contents of a PPM (portable pix map) file. + */ +void write_ppm_file(FILE *f, int *img_out, + int x_size, int y_size, int x_scale_val, int y_scale_val, + int img_colors, int is_ascii) +{ + int x_scaled_size, y_scaled_size; + + x_scaled_size = x_size * x_scale_val; + y_scaled_size = y_size * y_scale_val; + /* Write the magic number string. */ + if (is_ascii == 1) { + fprintf(f, "P3\n"); + } else { + fprintf(f, "P6\n"); + } + /* Write the image dimensions. */ + fprintf(f, "%d %d\n", x_scaled_size, y_scaled_size); + /* Write the maximum color/grey level allowed. */ + fprintf(f, "%d\n", img_colors); + + if ( is_ascii ) + { + write_ascii(f, img_out, x_scaled_size, y_scaled_size*3, x_scaled_size*3 ); + } + else + { + if ( img_colors < 256 ) + { + write_binary_1(f, img_out, x_scaled_size, y_scaled_size*3); + } + else + { + write_binary_2(f, img_out, x_scaled_size, y_scaled_size*3); + } + } +} + +/* write_pfm_file: + * Write the contents of a PFM (portable float map) file. + */ +void write_pfm_file(FILE *f, float *img_out, + int x_size, int y_size, + int img_type, int endianess) +{ + int i, j, x_scaled_size, y_scaled_size; + int swap = (endianess == 1) ? 0 : 1; + float fendian = (endianess == 1) ? +1.0 : -1.0; + + x_scaled_size = x_size; + y_scaled_size = y_size; + + /* Write the magic number string. */ + if (img_type == RGB_TYPE) { + fprintf(f, "PF\n"); + } else if (img_type == GREYSCALE_TYPE) { + fprintf(f, "Pf\n"); + } else { + fprintf(stderr, "Error: Image type invalid for PFM format!\n"); + exit(1); + } + /* Write the image dimensions. */ + fprintf(f, "%d %d\n", x_scaled_size, y_scaled_size); + /* Write the endianess/scale factor as float. */ + fprintf(f, "%f\n", fendian); + + /* Write the image data. */ + for (i = 0; i < y_scaled_size; i++) { + for (j = 0; j < x_scaled_size; j++) { + if (img_type == RGB_TYPE) { + WriteFloat(f, &img_out[3*(i*x_scaled_size+j)+0], swap); + WriteFloat(f, &img_out[3*(i*x_scaled_size+j)+1], swap); + WriteFloat(f, &img_out[3*(i*x_scaled_size+j)+2], swap); + } else if (img_type == GREYSCALE_TYPE) { + WriteFloat(f, &img_out[i*x_scaled_size+j], swap); + } + } + } +} + +/* ReadFloat: + * Read a possibly byte swapped floating-point number. + * NOTE: Assume IEEE format. + * Source: http://paulbourke.net/dataformats/pbmhdr/ + */ +int ReadFloat(FILE *fptr, float *f, int swap) +{ + unsigned char *cptr; + + if (fread(f, sizeof(float), 1, fptr) != 1) { + return (FALSE); + } + if (swap) { + cptr = (unsigned char *)f; + unsigned char tmp = cptr[0]; + cptr[0] = cptr[3]; + cptr[3] = tmp; + tmp = cptr[1]; + cptr[1] = cptr[2]; + cptr[2] = tmp; + } + return (TRUE); +} + +/* WriteFloat: + * Write a possibly byte-swapped floating-point number. + * NOTE: Assume IEEE format. + */ +int WriteFloat(FILE *fptr, float *f, int swap) +{ + unsigned char *cptr; + + if (swap) { + cptr = (unsigned char*)f; + unsigned char tmp = cptr[0]; + cptr[0] = cptr[3]; + cptr[3] = tmp; + tmp = cptr[1]; + cptr[1] = cptr[2]; + cptr[2] = tmp; + } + if (fwrite(f, sizeof(float), 1, fptr) != 1) { + return (FALSE); + } + return (TRUE); +} + +/* floatEqualComparison: + * Compare two floats and accept equality if not different than + * maxRelDiff (a specified maximum relative difference). + */ +int floatEqualComparison(float A, float B, float maxRelDiff) +{ + float largest, diff = fabs(A-B); + A = fabs(A); + B = fabs(B); + largest = (B > A) ? B : A; + if (diff <= largest * maxRelDiff) { + return 1; + } + return 0; +} + +/* frand: + * Emulate a floating-point PRNG. + * Source: http://c-faq.com/lib/rand48.html + */ +float frand(void) +{ + return rand() / (RAND_MAX + 1.0); +} From 10684ac8bfd93d959e3d34ab8760e6b517bb8dff Mon Sep 17 00:00:00 2001 From: Antal Ispanovity Date: Tue, 16 Sep 2025 09:51:42 +0200 Subject: [PATCH 2/3] formatting --- src/pnmio.c | 1482 +++++++++++++++++++++++++-------------------------- 1 file changed, 741 insertions(+), 741 deletions(-) diff --git a/src/pnmio.c b/src/pnmio.c index 7557f3b..7fea83b 100755 --- a/src/pnmio.c +++ b/src/pnmio.c @@ -1,741 +1,741 @@ -/* - * File : pnmio.c - * Description: I/O facilities for PBM, PGM, PPM (PNM) binary and ASCII images. - * Author : Nikolaos Kavvadias - * Copyright : (C) Nikolaos Kavvadias 2012-2022 - * Website : http://www.nkavvadias.com - * - * This file is part of libpnmio, and is distributed under the terms of the - * Modified BSD License. - * - * A copy of the Modified BSD License is included with this distribution - * in the file LICENSE. - * libpnmio is free software: you can redistribute it and/or modify it under the - * terms of the Modified BSD License. - * libpnmio is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the Modified BSD License for more details. - * - * You should have received a copy of the Modified BSD License along with - * libpnmio. If not, see . - */ - -#include -#include -#include -#include -#include -#include "pnmio.h" - -#define MAXLINE 1024 -#define LITTLE_ENDIAN -1 -#define BIG_ENDIAN 1 -#define GREYSCALE_TYPE 0 /* used for PFM */ -#define RGB_TYPE 1 /* used for PFM */ - -void write_ascii( FILE *f, int *img_out, int x_scaled_size, int y_scaled_size, int linevals ) -{ - for ( int i = 0; i < x_scaled_size * y_scaled_size; ++i ) - { - fprintf(f, "%d ", img_out[i]); - if ( i % linevals == linevals-1 ) - fprintf(f, "\n"); - } -} -// the unit of information is 1 byte. used for 8 bit grayscale and 24 bit rgb components -void write_binary_1( FILE *f, int *img_out, int x_scaled_size, int y_scaled_size ) -{ - char c; - for ( int i = 0; i < x_scaled_size * y_scaled_size; ++i ) - { - c = (char)img_out[i]; - fwrite(&c, sizeof(char), 1, f ); - } -} -// the unit of information is 2 bytes. used for 16 bit grayscale and 48 bit rgb components -void write_binary_2( FILE *f, int *img_out, int x_scaled_size, int y_scaled_size) -{ - short s; - for ( int i = 0; i < x_scaled_size * y_scaled_size; ++i ) - { - s = (short)img_out[i]; - fwrite(&s, sizeof(short), 1, f ); - } -} - -/* get_pnm_type: - * Read the header contents of a PBM/PGM/PPM/PFM file up to the point of - * extracting its type. Valid types for a PNM image are as follows: - * PBM_ASCII = 1 - * PGM_ASCII = 2 - * PPM_ASCII = 3 - * PBM_BINARY = 4 - * PGM_BINARY = 5 - * PPM_BINARY = 6 - * PAM = 7 - * PFM_RGB = 16 - * PFM_GREYSCALE = 17 - * - * The result (pnm_type) is returned. - */ -int get_pnm_type(FILE *f) -{ - int pnm_type=0; - unsigned int i; - char magic[MAXLINE]; - char line[MAXLINE]; - - /* Read the PNM/PFM file header. */ - while (fgets(line, MAXLINE, f) != NULL) { - int flag = 0; - for (i = 0; i < strlen(line); i++) { - if (isgraph(line[i])) { - if ((line[i] == '#') && (flag == 0)) { - flag = 1; - } - } - } - if (flag == 0) { - sscanf(line, "%2s", magic); - break; - } - } - - /* NOTE: This part can be written more succinctly, however, - * it is better to have the PNM types decoded explicitly. - */ - if (strcmp(magic, "P1") == 0) { - pnm_type = PBM_ASCII; - } else if (strcmp(magic, "P2") == 0) { - pnm_type = PGM_ASCII; - } else if (strcmp(magic, "P3") == 0) { - pnm_type = PPM_ASCII; - } else if (strcmp(magic, "P4") == 0) { - pnm_type = PBM_BINARY; - } else if (strcmp(magic, "P5") == 0) { - pnm_type = PGM_BINARY; - } else if (strcmp(magic, "P6") == 0) { - pnm_type = PPM_BINARY; - } else if (strcmp(magic, "P7") == 0) { - pnm_type = PAM; - } else if (strcmp(magic, "PF") == 0) { - pnm_type = PFM_RGB; - } else if (strcmp(magic, "Pf") == 0) { - pnm_type = PFM_GREYSCALE; - } else { - fprintf(stderr, "Error: Unknown PNM/PFM file; wrong magic number!\n"); - exit(1); - } - - return (pnm_type); -} - -/* read_pbm_header: - * Read the header contents of a PBM (Portable Binary Map) file. - * Returns the number of bytes that need be allocated for the image data. - * An ASCII PBM image file follows the format: - * P1 - * - * ... - * A binary PBM image file uses P4 instead of P1 and - * the data values are represented in binary. - * NOTE1: Comment lines start with '#'. - * NOTE2: < > denote integer values (in decimal). - */ -int read_pbm_header(FILE *f, int *img_xdim, int *img_ydim, int *is_ascii) -{ - int x_val, y_val; - unsigned int i; - char magic[MAXLINE]; - char line[MAXLINE]; - int count=0; - - /* Read the PBM file header. */ - while (fgets(line, MAXLINE, f) != NULL) { - int flag = 0; - for (i = 0; i < strlen(line); i++) { - if (isgraph(line[i])) { - if ((line[i] == '#') && (flag == 0)) { - flag = 1; - } - } - } - if (flag == 0) { - if (count == 0) { - count += sscanf(line, "%2s %d %d", magic, &x_val, &y_val); - } else if (count == 1) { - count += sscanf(line, "%d %d", &x_val, &y_val); - } else if (count == 2) { - count += sscanf(line, "%d", &y_val); - } - } - if (count == 3) { - break; - } - } - - if (strcmp(magic, "P1") == 0) { - *is_ascii = 1; - } else if (strcmp(magic, "P4") == 0) { - *is_ascii = 0; - } else { - fprintf(stderr, "Error: Input file not in PBM format!\n"); - exit(1); - } - - fprintf(stderr, "Info: magic=%s, x_val=%d, y_val=%d\n", - magic, x_val, y_val); - *img_xdim = x_val; - *img_ydim = y_val; - - return *img_xdim * *img_ydim * sizeof(int); -} - -/* read_pgm_header: - * Read the header contents of a PGM (Portable Grey[scale] Map) file. - * Returns the number of bytes that need be allocated for the image data. - * An ASCII PGM image file follows the format: - * P2 - * - * - * ... - * A binary PGM image file uses P5 instead of P2 and - * the data values are represented in binary. - * NOTE1: Comment lines start with '#'. - * NOTE2: < > denote integer values (in decimal). - */ -int read_pgm_header(FILE *f, int *img_xdim, int *img_ydim, int *img_colors, int *is_ascii) -{ - int x_val, y_val, maxcolors_val; - unsigned int i; - char magic[MAXLINE]; - char line[MAXLINE]; - int count=0; - - /* Read the PGM file header. */ - while (fgets(line, MAXLINE, f) != NULL) { - int flag = 0; - for (i = 0; i < strlen(line); i++) { - if (isgraph(line[i]) && (flag == 0)) { - if ((line[i] == '#') && (flag == 0)) { - flag = 1; - } - } - } - if (flag == 0) { - if (count == 0) { - count += sscanf(line, "%2s %d %d %d", magic, &x_val, &y_val, &maxcolors_val); - } else if (count == 1) { - count += sscanf(line, "%d %d %d", &x_val, &y_val, &maxcolors_val); - } else if (count == 2) { - count += sscanf(line, "%d %d", &y_val, &maxcolors_val); - } else if (count == 3) { - count += sscanf(line, "%d", &maxcolors_val); - } - } - if (count == 4) { - break; - } - } - - if (strcmp(magic, "P2") == 0) { - *is_ascii = 1; - } else if (strcmp(magic, "P5") == 0) { - *is_ascii = 0; - } else { - fprintf(stderr, "Error: Input file not in PGM format!\n"); - exit(1); - } - - fprintf(stderr, "Info: magic=%s, x_val=%d, y_val=%d, maxcolors_val=%d\n", - magic, x_val, y_val, maxcolors_val); - *img_xdim = x_val; - *img_ydim = y_val; - *img_colors = maxcolors_val; - - return *img_xdim * *img_ydim * sizeof(int); -} - -/* read_ppm_header: - * Read the header contents of a PPM (Portable Pix[el] Map) file. - * Returns the number of bytes that need be allocated for the image data. - * An ASCII PPM image file follows the format: - * P3 - * - * - * ... - * A binary PPM image file uses P6 instead of P3 and - * the data values are represented in binary. - * NOTE1: Comment lines start with '#'. - # NOTE2: < > denote integer values (in decimal). - */ -int read_ppm_header(FILE *f, int *img_xdim, int *img_ydim, int *img_colors, int *is_ascii) -{ - int x_val, y_val, maxcolors_val; - unsigned int i; - char magic[MAXLINE]; - char line[MAXLINE]; - int count=0; - - /* Read the PPM file header. */ - while (fgets(line, MAXLINE, f) != NULL) { - int flag = 0; - for (i = 0; i < strlen(line); i++) { - if (isgraph(line[i]) && (flag == 0)) { - if ((line[i] == '#') && (flag == 0)) { - flag = 1; - } - } - } - if (flag == 0) { - if (count == 0) { - count += sscanf(line, "%2s %d %d %d", magic, &x_val, &y_val, &maxcolors_val); - } else if (count == 1) { - count += sscanf(line, "%d %d %d", &x_val, &y_val, &maxcolors_val); - } else if (count == 2) { - count += sscanf(line, "%d %d", &y_val, &maxcolors_val); - } else if (count == 3) { - count += sscanf(line, "%d", &maxcolors_val); - } - } - if (count == 4) { - break; - } - } - - if (strcmp(magic, "P3") == 0) { - *is_ascii = 1; - } else if (strcmp(magic, "P6") == 0) { - *is_ascii = 0; - } else { - fprintf(stderr, "Error: Input file not in PPM format!\n"); - exit(1); - } - - fprintf(stderr, "Info: magic=%s, x_val=%d, y_val=%d, maxcolors_val=%d\n", - magic, x_val, y_val, maxcolors_val); - *img_xdim = x_val; - *img_ydim = y_val; - *img_colors = maxcolors_val; - - return 3 * *img_xdim * *img_ydim * sizeof(int); -} - -/* read_pfm_header: - * Read the header contents of a PFM (portable float map) file. - * Returns the number of bytes that need be allocated for the image data. - * A PFM image file follows the format: - * [PF|Pf] - * - * (endianess) - * {R1}{G1}{B1} ... {RMAX}{GMAX}{BMAX} - * NOTE1: Comment lines, if allowed, start with '#'. - # NOTE2: < > denote integer values (in decimal). - # NOTE3: ( ) denote floating-point values (in decimal). - # NOTE4: { } denote floating-point values (coded in binary). - */ -int read_pfm_header(FILE *f, int *img_xdim, int *img_ydim, int *img_type, int *endianess) -{ - int x_val, y_val; - unsigned int i; - int is_rgb=0, is_greyscale=0; - float aspect_ratio=0; - char magic[MAXLINE]; - char line[MAXLINE]; - int count=0; - int num_bytes=0; - - /* Read the PFM file header. */ - while (fgets(line, MAXLINE, f) != NULL) { - int flag = 0; - for (i = 0; i < strlen(line); i++) { - if (isgraph(line[i]) && (flag == 0)) { - if ((line[i] == '#') && (flag == 0)) { - flag = 1; - } - } - } - if (flag == 0) { - if (count == 0) { - count += sscanf(line, "%2s %d %d %f", magic, &x_val, &y_val, &aspect_ratio); - } else if (count == 1) { - count += sscanf(line, "%d %d %f", &x_val, &y_val, &aspect_ratio); - } else if (count == 2) { - count += sscanf(line, "%d %f", &y_val, &aspect_ratio); - } else if (count == 3) { - count += sscanf(line, "%f", &aspect_ratio); - } - } - if (count == 4) { - break; - } - } - - if (strcmp(magic, "PF") == 0) { - is_rgb = 1; - is_greyscale = 0; - } else if (strcmp(magic, "Pf") == 0) { - is_greyscale = 0; - is_rgb = 1; - } else { - fprintf(stderr, "Error: Input file not in PFM format!\n"); - exit(1); - } - - fprintf(stderr, "Info: magic=%s, x_val=%d, y_val=%d, aspect_ratio=%f\n", - magic, x_val, y_val, aspect_ratio); - - /* FIXME: Aspect ratio different to 1.0 is not yet supported. */ - if (!floatEqualComparison(aspect_ratio, -1.0, 1E-06) && - !floatEqualComparison(aspect_ratio, 1.0, 1E-06)) { - fprintf(stderr, "Error: Aspect ratio different to -1.0 or +1.0 is unsupported!\n"); - exit(1); - } - - *img_xdim = x_val; - *img_ydim = y_val; - *img_type = is_rgb & ~is_greyscale; - if (aspect_ratio > 0.0) { - *endianess = 1; - } else { - *endianess = -1; - } - - num_bytes = *img_xdim * *img_ydim * sizeof(float); - if (is_rgb) { - num_bytes *= 3; - } - return num_bytes; -} - -/* read_pbm_data: - * Read the data contents of a PBM (portable bit map) file. - */ -void read_pbm_data(FILE *f, int *img_in, int is_ascii) -{ - int i=0, c; - int lum_val; - int k; - - /* Read the rest of the PBM file. */ - while ((c = fgetc(f)) != EOF) { - ungetc(c, f); - if (is_ascii == 1) { - if (fscanf(f, "%d", &lum_val) != 1) return; - img_in[i++] = lum_val; - } else { - lum_val = fgetc(f); - /* Decode the image contents byte-by-byte. */ - for (k = 0; k < 8; k++) { - img_in[i++] = (lum_val >> (7-k)) & 0x1; - } - } - } -} - -/* read_pgm_data: - * Read the data contents of a PGM (portable grey map) file. - */ -void read_pgm_data(FILE *f, int *img_in, int is_ascii) -{ - int i=0, c; - int lum_val; - - /* Read the rest of the PGM file. */ - while ((c = fgetc(f)) != EOF) { - ungetc(c, f); - if (is_ascii == 1) { - if (fscanf(f, "%d", &lum_val) != 1) return; - } else { - lum_val = fgetc(f); - } - img_in[i++] = lum_val; - } -} - -/* read_ppm_data: - * Read the data contents of a PPM (portable pix map) file. - */ -void read_ppm_data(FILE *f, int *img_in, int is_ascii) -{ - int i=0, c; - int r_val, g_val, b_val; - - /* Read the rest of the PPM file. */ - while ((c = fgetc(f)) != EOF) { - ungetc(c, f); - if (is_ascii == 1) { - if (fscanf(f, "%d %d %d", &r_val, &g_val, &b_val) != 3) return; - } else { - r_val = fgetc(f); - g_val = fgetc(f); - b_val = fgetc(f); - } - img_in[i++] = r_val; - img_in[i++] = g_val; - img_in[i++] = b_val; - } -} - -/* read_pfm_data: - * Read the data contents of a PFM (portable float map) file. - */ -void read_pfm_data(FILE *f, float *img_in, int img_type, int endianess) -{ - int i=0, c; - int swap = (endianess == 1) ? 0 : 1; - float r_val, g_val, b_val; - - /* Read the rest of the PFM file. */ - while ((c = fgetc(f)) != EOF) { - ungetc(c, f); - /* Read a possibly byte-swapped float. */ - if (img_type == RGB_TYPE) { - ReadFloat(f, &r_val, swap); - ReadFloat(f, &g_val, swap); - ReadFloat(f, &b_val, swap); - img_in[i++] = r_val; - img_in[i++] = g_val; - img_in[i++] = b_val; - } else if (img_type == GREYSCALE_TYPE) { - ReadFloat(f, &g_val, swap); - img_in[i++] = g_val; - } - } -} - -/* write_pbm_file: - * Write the contents of a PBM (portable bit map) file. - */ -void write_pbm_file(FILE *f, int *img_out, - int x_size, int y_size, int x_scale_val, int y_scale_val, int linevals, - int is_ascii) -{ - int i, j, x_scaled_size, y_scaled_size; - int k, v, temp, step; - - x_scaled_size = x_size * x_scale_val; - y_scaled_size = y_size * y_scale_val; - /* Write the magic number string. */ - if (is_ascii == 1) { - fprintf(f, "P1\n"); - step = 1; - } else { - fprintf(f, "P4\n"); - step = 8; - } - /* Write the image dimensions. */ - fprintf(f, "%d %d\n", x_scaled_size, y_scaled_size); - - /* Write the image data. */ - for (i = 0; i < y_scaled_size; i++) { - for (j = 0; j < x_scaled_size; j+=step) { - if (is_ascii == 1) { - fprintf(f, "%d ", img_out[i*x_scaled_size+j]); - } else { - temp = 0; - for (k = 0; k < 8; k++) { - v = img_out[i*x_scaled_size+j+k]; - temp |= (v << (7-k)); - } - fprintf(f, "%c", temp); - } - if (((i*x_scaled_size+j) % linevals) == (linevals-1)) { - fprintf(f, "\n"); - } - } - } -} - -/* write_pgm_file: - * Write the contents of a PGM (portable grey map) file. - */ -void write_pgm_file(FILE *f, int *img_out, - int x_size, int y_size, int x_scale_val, int y_scale_val, - int img_colors, int linevals, int is_ascii) -{ - int x_scaled_size, y_scaled_size; - - x_scaled_size = x_size * x_scale_val; - y_scaled_size = y_size * y_scale_val; - /* Write the magic number string. */ - if (is_ascii == 1) { - fprintf(f, "P2\n"); - } else { - fprintf(f, "P5\n"); - } - /* Write the image dimensions. */ - fprintf(f, "%d %d\n", x_scaled_size, y_scaled_size); - /* Write the maximum color/grey level allowed. */ - fprintf(f, "%d\n", img_colors); - - /* Write the image data. */ - if ( is_ascii ) - { - write_ascii(f, img_out, x_scaled_size, y_scaled_size, linevals ); - } - else - { - if ( img_colors < 256 ) - { - write_binary_1(f, img_out, x_scaled_size, y_scaled_size); - } - else - { - write_binary_2(f, img_out, x_scaled_size, y_scaled_size); - } - } -} - -/* write_ppm_file: - * Write the contents of a PPM (portable pix map) file. - */ -void write_ppm_file(FILE *f, int *img_out, - int x_size, int y_size, int x_scale_val, int y_scale_val, - int img_colors, int is_ascii) -{ - int x_scaled_size, y_scaled_size; - - x_scaled_size = x_size * x_scale_val; - y_scaled_size = y_size * y_scale_val; - /* Write the magic number string. */ - if (is_ascii == 1) { - fprintf(f, "P3\n"); - } else { - fprintf(f, "P6\n"); - } - /* Write the image dimensions. */ - fprintf(f, "%d %d\n", x_scaled_size, y_scaled_size); - /* Write the maximum color/grey level allowed. */ - fprintf(f, "%d\n", img_colors); - - if ( is_ascii ) - { - write_ascii(f, img_out, x_scaled_size, y_scaled_size*3, x_scaled_size*3 ); - } - else - { - if ( img_colors < 256 ) - { - write_binary_1(f, img_out, x_scaled_size, y_scaled_size*3); - } - else - { - write_binary_2(f, img_out, x_scaled_size, y_scaled_size*3); - } - } -} - -/* write_pfm_file: - * Write the contents of a PFM (portable float map) file. - */ -void write_pfm_file(FILE *f, float *img_out, - int x_size, int y_size, - int img_type, int endianess) -{ - int i, j, x_scaled_size, y_scaled_size; - int swap = (endianess == 1) ? 0 : 1; - float fendian = (endianess == 1) ? +1.0 : -1.0; - - x_scaled_size = x_size; - y_scaled_size = y_size; - - /* Write the magic number string. */ - if (img_type == RGB_TYPE) { - fprintf(f, "PF\n"); - } else if (img_type == GREYSCALE_TYPE) { - fprintf(f, "Pf\n"); - } else { - fprintf(stderr, "Error: Image type invalid for PFM format!\n"); - exit(1); - } - /* Write the image dimensions. */ - fprintf(f, "%d %d\n", x_scaled_size, y_scaled_size); - /* Write the endianess/scale factor as float. */ - fprintf(f, "%f\n", fendian); - - /* Write the image data. */ - for (i = 0; i < y_scaled_size; i++) { - for (j = 0; j < x_scaled_size; j++) { - if (img_type == RGB_TYPE) { - WriteFloat(f, &img_out[3*(i*x_scaled_size+j)+0], swap); - WriteFloat(f, &img_out[3*(i*x_scaled_size+j)+1], swap); - WriteFloat(f, &img_out[3*(i*x_scaled_size+j)+2], swap); - } else if (img_type == GREYSCALE_TYPE) { - WriteFloat(f, &img_out[i*x_scaled_size+j], swap); - } - } - } -} - -/* ReadFloat: - * Read a possibly byte swapped floating-point number. - * NOTE: Assume IEEE format. - * Source: http://paulbourke.net/dataformats/pbmhdr/ - */ -int ReadFloat(FILE *fptr, float *f, int swap) -{ - unsigned char *cptr; - - if (fread(f, sizeof(float), 1, fptr) != 1) { - return (FALSE); - } - if (swap) { - cptr = (unsigned char *)f; - unsigned char tmp = cptr[0]; - cptr[0] = cptr[3]; - cptr[3] = tmp; - tmp = cptr[1]; - cptr[1] = cptr[2]; - cptr[2] = tmp; - } - return (TRUE); -} - -/* WriteFloat: - * Write a possibly byte-swapped floating-point number. - * NOTE: Assume IEEE format. - */ -int WriteFloat(FILE *fptr, float *f, int swap) -{ - unsigned char *cptr; - - if (swap) { - cptr = (unsigned char*)f; - unsigned char tmp = cptr[0]; - cptr[0] = cptr[3]; - cptr[3] = tmp; - tmp = cptr[1]; - cptr[1] = cptr[2]; - cptr[2] = tmp; - } - if (fwrite(f, sizeof(float), 1, fptr) != 1) { - return (FALSE); - } - return (TRUE); -} - -/* floatEqualComparison: - * Compare two floats and accept equality if not different than - * maxRelDiff (a specified maximum relative difference). - */ -int floatEqualComparison(float A, float B, float maxRelDiff) -{ - float largest, diff = fabs(A-B); - A = fabs(A); - B = fabs(B); - largest = (B > A) ? B : A; - if (diff <= largest * maxRelDiff) { - return 1; - } - return 0; -} - -/* frand: - * Emulate a floating-point PRNG. - * Source: http://c-faq.com/lib/rand48.html - */ -float frand(void) -{ - return rand() / (RAND_MAX + 1.0); -} +/* + * File : pnmio.c + * Description: I/O facilities for PBM, PGM, PPM (PNM) binary and ASCII images. + * Author : Nikolaos Kavvadias + * Copyright : (C) Nikolaos Kavvadias 2012-2022 + * Website : http://www.nkavvadias.com + * + * This file is part of libpnmio, and is distributed under the terms of the + * Modified BSD License. + * + * A copy of the Modified BSD License is included with this distribution + * in the file LICENSE. + * libpnmio is free software: you can redistribute it and/or modify it under the + * terms of the Modified BSD License. + * libpnmio is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the Modified BSD License for more details. + * + * You should have received a copy of the Modified BSD License along with + * libpnmio. If not, see . + */ + +#include +#include +#include +#include +#include +#include "pnmio.h" + +#define MAXLINE 1024 +#define LITTLE_ENDIAN -1 +#define BIG_ENDIAN 1 +#define GREYSCALE_TYPE 0 /* used for PFM */ +#define RGB_TYPE 1 /* used for PFM */ + +void write_ascii( FILE *f, int *img_out, int x_scaled_size, int y_scaled_size, int linevals ) +{ + for ( int i = 0; i < x_scaled_size * y_scaled_size; ++i ) + { + fprintf(f, "%d ", img_out[i]); + if ( i % linevals == linevals-1 ) + fprintf(f, "\n"); + } +} +// the unit of information is 1 byte. used for 8 bit grayscale and 24 bit rgb components +void write_binary_1( FILE *f, int *img_out, int x_scaled_size, int y_scaled_size ) +{ + char c; + for ( int i = 0; i < x_scaled_size * y_scaled_size; ++i ) + { + c = (char)img_out[i]; + fwrite(&c, sizeof(char), 1, f ); + } +} +// the unit of information is 2 bytes. used for 16 bit grayscale and 48 bit rgb components +void write_binary_2( FILE *f, int *img_out, int x_scaled_size, int y_scaled_size) +{ + short s; + for ( int i = 0; i < x_scaled_size * y_scaled_size; ++i ) + { + s = (short)img_out[i]; + fwrite(&s, sizeof(short), 1, f ); + } +} + +/* get_pnm_type: + * Read the header contents of a PBM/PGM/PPM/PFM file up to the point of + * extracting its type. Valid types for a PNM image are as follows: + * PBM_ASCII = 1 + * PGM_ASCII = 2 + * PPM_ASCII = 3 + * PBM_BINARY = 4 + * PGM_BINARY = 5 + * PPM_BINARY = 6 + * PAM = 7 + * PFM_RGB = 16 + * PFM_GREYSCALE = 17 + * + * The result (pnm_type) is returned. + */ +int get_pnm_type(FILE *f) +{ + int pnm_type=0; + unsigned int i; + char magic[MAXLINE]; + char line[MAXLINE]; + + /* Read the PNM/PFM file header. */ + while (fgets(line, MAXLINE, f) != NULL) { + int flag = 0; + for (i = 0; i < strlen(line); i++) { + if (isgraph(line[i])) { + if ((line[i] == '#') && (flag == 0)) { + flag = 1; + } + } + } + if (flag == 0) { + sscanf(line, "%2s", magic); + break; + } + } + + /* NOTE: This part can be written more succinctly, however, + * it is better to have the PNM types decoded explicitly. + */ + if (strcmp(magic, "P1") == 0) { + pnm_type = PBM_ASCII; + } else if (strcmp(magic, "P2") == 0) { + pnm_type = PGM_ASCII; + } else if (strcmp(magic, "P3") == 0) { + pnm_type = PPM_ASCII; + } else if (strcmp(magic, "P4") == 0) { + pnm_type = PBM_BINARY; + } else if (strcmp(magic, "P5") == 0) { + pnm_type = PGM_BINARY; + } else if (strcmp(magic, "P6") == 0) { + pnm_type = PPM_BINARY; + } else if (strcmp(magic, "P7") == 0) { + pnm_type = PAM; + } else if (strcmp(magic, "PF") == 0) { + pnm_type = PFM_RGB; + } else if (strcmp(magic, "Pf") == 0) { + pnm_type = PFM_GREYSCALE; + } else { + fprintf(stderr, "Error: Unknown PNM/PFM file; wrong magic number!\n"); + exit(1); + } + + return (pnm_type); +} + +/* read_pbm_header: + * Read the header contents of a PBM (Portable Binary Map) file. + * Returns the number of bytes that need be allocated for the image data. + * An ASCII PBM image file follows the format: + * P1 + * + * ... + * A binary PBM image file uses P4 instead of P1 and + * the data values are represented in binary. + * NOTE1: Comment lines start with '#'. + * NOTE2: < > denote integer values (in decimal). + */ +int read_pbm_header(FILE *f, int *img_xdim, int *img_ydim, int *is_ascii) +{ + int x_val, y_val; + unsigned int i; + char magic[MAXLINE]; + char line[MAXLINE]; + int count=0; + + /* Read the PBM file header. */ + while (fgets(line, MAXLINE, f) != NULL) { + int flag = 0; + for (i = 0; i < strlen(line); i++) { + if (isgraph(line[i])) { + if ((line[i] == '#') && (flag == 0)) { + flag = 1; + } + } + } + if (flag == 0) { + if (count == 0) { + count += sscanf(line, "%2s %d %d", magic, &x_val, &y_val); + } else if (count == 1) { + count += sscanf(line, "%d %d", &x_val, &y_val); + } else if (count == 2) { + count += sscanf(line, "%d", &y_val); + } + } + if (count == 3) { + break; + } + } + + if (strcmp(magic, "P1") == 0) { + *is_ascii = 1; + } else if (strcmp(magic, "P4") == 0) { + *is_ascii = 0; + } else { + fprintf(stderr, "Error: Input file not in PBM format!\n"); + exit(1); + } + + fprintf(stderr, "Info: magic=%s, x_val=%d, y_val=%d\n", + magic, x_val, y_val); + *img_xdim = x_val; + *img_ydim = y_val; + + return *img_xdim * *img_ydim * sizeof(int); +} + +/* read_pgm_header: + * Read the header contents of a PGM (Portable Grey[scale] Map) file. + * Returns the number of bytes that need be allocated for the image data. + * An ASCII PGM image file follows the format: + * P2 + * + * + * ... + * A binary PGM image file uses P5 instead of P2 and + * the data values are represented in binary. + * NOTE1: Comment lines start with '#'. + * NOTE2: < > denote integer values (in decimal). + */ +int read_pgm_header(FILE *f, int *img_xdim, int *img_ydim, int *img_colors, int *is_ascii) +{ + int x_val, y_val, maxcolors_val; + unsigned int i; + char magic[MAXLINE]; + char line[MAXLINE]; + int count=0; + + /* Read the PGM file header. */ + while (fgets(line, MAXLINE, f) != NULL) { + int flag = 0; + for (i = 0; i < strlen(line); i++) { + if (isgraph(line[i]) && (flag == 0)) { + if ((line[i] == '#') && (flag == 0)) { + flag = 1; + } + } + } + if (flag == 0) { + if (count == 0) { + count += sscanf(line, "%2s %d %d %d", magic, &x_val, &y_val, &maxcolors_val); + } else if (count == 1) { + count += sscanf(line, "%d %d %d", &x_val, &y_val, &maxcolors_val); + } else if (count == 2) { + count += sscanf(line, "%d %d", &y_val, &maxcolors_val); + } else if (count == 3) { + count += sscanf(line, "%d", &maxcolors_val); + } + } + if (count == 4) { + break; + } + } + + if (strcmp(magic, "P2") == 0) { + *is_ascii = 1; + } else if (strcmp(magic, "P5") == 0) { + *is_ascii = 0; + } else { + fprintf(stderr, "Error: Input file not in PGM format!\n"); + exit(1); + } + + fprintf(stderr, "Info: magic=%s, x_val=%d, y_val=%d, maxcolors_val=%d\n", + magic, x_val, y_val, maxcolors_val); + *img_xdim = x_val; + *img_ydim = y_val; + *img_colors = maxcolors_val; + + return *img_xdim * *img_ydim * sizeof(int); +} + +/* read_ppm_header: + * Read the header contents of a PPM (Portable Pix[el] Map) file. + * Returns the number of bytes that need be allocated for the image data. + * An ASCII PPM image file follows the format: + * P3 + * + * + * ... + * A binary PPM image file uses P6 instead of P3 and + * the data values are represented in binary. + * NOTE1: Comment lines start with '#'. + # NOTE2: < > denote integer values (in decimal). + */ +int read_ppm_header(FILE *f, int *img_xdim, int *img_ydim, int *img_colors, int *is_ascii) +{ + int x_val, y_val, maxcolors_val; + unsigned int i; + char magic[MAXLINE]; + char line[MAXLINE]; + int count=0; + + /* Read the PPM file header. */ + while (fgets(line, MAXLINE, f) != NULL) { + int flag = 0; + for (i = 0; i < strlen(line); i++) { + if (isgraph(line[i]) && (flag == 0)) { + if ((line[i] == '#') && (flag == 0)) { + flag = 1; + } + } + } + if (flag == 0) { + if (count == 0) { + count += sscanf(line, "%2s %d %d %d", magic, &x_val, &y_val, &maxcolors_val); + } else if (count == 1) { + count += sscanf(line, "%d %d %d", &x_val, &y_val, &maxcolors_val); + } else if (count == 2) { + count += sscanf(line, "%d %d", &y_val, &maxcolors_val); + } else if (count == 3) { + count += sscanf(line, "%d", &maxcolors_val); + } + } + if (count == 4) { + break; + } + } + + if (strcmp(magic, "P3") == 0) { + *is_ascii = 1; + } else if (strcmp(magic, "P6") == 0) { + *is_ascii = 0; + } else { + fprintf(stderr, "Error: Input file not in PPM format!\n"); + exit(1); + } + + fprintf(stderr, "Info: magic=%s, x_val=%d, y_val=%d, maxcolors_val=%d\n", + magic, x_val, y_val, maxcolors_val); + *img_xdim = x_val; + *img_ydim = y_val; + *img_colors = maxcolors_val; + + return 3 * *img_xdim * *img_ydim * sizeof(int); +} + +/* read_pfm_header: + * Read the header contents of a PFM (portable float map) file. + * Returns the number of bytes that need be allocated for the image data. + * A PFM image file follows the format: + * [PF|Pf] + * + * (endianess) + * {R1}{G1}{B1} ... {RMAX}{GMAX}{BMAX} + * NOTE1: Comment lines, if allowed, start with '#'. + # NOTE2: < > denote integer values (in decimal). + # NOTE3: ( ) denote floating-point values (in decimal). + # NOTE4: { } denote floating-point values (coded in binary). + */ +int read_pfm_header(FILE *f, int *img_xdim, int *img_ydim, int *img_type, int *endianess) +{ + int x_val, y_val; + unsigned int i; + int is_rgb=0, is_greyscale=0; + float aspect_ratio=0; + char magic[MAXLINE]; + char line[MAXLINE]; + int count=0; + int num_bytes=0; + + /* Read the PFM file header. */ + while (fgets(line, MAXLINE, f) != NULL) { + int flag = 0; + for (i = 0; i < strlen(line); i++) { + if (isgraph(line[i]) && (flag == 0)) { + if ((line[i] == '#') && (flag == 0)) { + flag = 1; + } + } + } + if (flag == 0) { + if (count == 0) { + count += sscanf(line, "%2s %d %d %f", magic, &x_val, &y_val, &aspect_ratio); + } else if (count == 1) { + count += sscanf(line, "%d %d %f", &x_val, &y_val, &aspect_ratio); + } else if (count == 2) { + count += sscanf(line, "%d %f", &y_val, &aspect_ratio); + } else if (count == 3) { + count += sscanf(line, "%f", &aspect_ratio); + } + } + if (count == 4) { + break; + } + } + + if (strcmp(magic, "PF") == 0) { + is_rgb = 1; + is_greyscale = 0; + } else if (strcmp(magic, "Pf") == 0) { + is_greyscale = 0; + is_rgb = 1; + } else { + fprintf(stderr, "Error: Input file not in PFM format!\n"); + exit(1); + } + + fprintf(stderr, "Info: magic=%s, x_val=%d, y_val=%d, aspect_ratio=%f\n", + magic, x_val, y_val, aspect_ratio); + + /* FIXME: Aspect ratio different to 1.0 is not yet supported. */ + if (!floatEqualComparison(aspect_ratio, -1.0, 1E-06) && + !floatEqualComparison(aspect_ratio, 1.0, 1E-06)) { + fprintf(stderr, "Error: Aspect ratio different to -1.0 or +1.0 is unsupported!\n"); + exit(1); + } + + *img_xdim = x_val; + *img_ydim = y_val; + *img_type = is_rgb & ~is_greyscale; + if (aspect_ratio > 0.0) { + *endianess = 1; + } else { + *endianess = -1; + } + + num_bytes = *img_xdim * *img_ydim * sizeof(float); + if (is_rgb) { + num_bytes *= 3; + } + return num_bytes; +} + +/* read_pbm_data: + * Read the data contents of a PBM (portable bit map) file. + */ +void read_pbm_data(FILE *f, int *img_in, int is_ascii) +{ + int i=0, c; + int lum_val; + int k; + + /* Read the rest of the PBM file. */ + while ((c = fgetc(f)) != EOF) { + ungetc(c, f); + if (is_ascii == 1) { + if (fscanf(f, "%d", &lum_val) != 1) return; + img_in[i++] = lum_val; + } else { + lum_val = fgetc(f); + /* Decode the image contents byte-by-byte. */ + for (k = 0; k < 8; k++) { + img_in[i++] = (lum_val >> (7-k)) & 0x1; + } + } + } +} + +/* read_pgm_data: + * Read the data contents of a PGM (portable grey map) file. + */ +void read_pgm_data(FILE *f, int *img_in, int is_ascii) +{ + int i=0, c; + int lum_val; + + /* Read the rest of the PGM file. */ + while ((c = fgetc(f)) != EOF) { + ungetc(c, f); + if (is_ascii == 1) { + if (fscanf(f, "%d", &lum_val) != 1) return; + } else { + lum_val = fgetc(f); + } + img_in[i++] = lum_val; + } +} + +/* read_ppm_data: + * Read the data contents of a PPM (portable pix map) file. + */ +void read_ppm_data(FILE *f, int *img_in, int is_ascii) +{ + int i=0, c; + int r_val, g_val, b_val; + + /* Read the rest of the PPM file. */ + while ((c = fgetc(f)) != EOF) { + ungetc(c, f); + if (is_ascii == 1) { + if (fscanf(f, "%d %d %d", &r_val, &g_val, &b_val) != 3) return; + } else { + r_val = fgetc(f); + g_val = fgetc(f); + b_val = fgetc(f); + } + img_in[i++] = r_val; + img_in[i++] = g_val; + img_in[i++] = b_val; + } +} + +/* read_pfm_data: + * Read the data contents of a PFM (portable float map) file. + */ +void read_pfm_data(FILE *f, float *img_in, int img_type, int endianess) +{ + int i=0, c; + int swap = (endianess == 1) ? 0 : 1; + float r_val, g_val, b_val; + + /* Read the rest of the PFM file. */ + while ((c = fgetc(f)) != EOF) { + ungetc(c, f); + /* Read a possibly byte-swapped float. */ + if (img_type == RGB_TYPE) { + ReadFloat(f, &r_val, swap); + ReadFloat(f, &g_val, swap); + ReadFloat(f, &b_val, swap); + img_in[i++] = r_val; + img_in[i++] = g_val; + img_in[i++] = b_val; + } else if (img_type == GREYSCALE_TYPE) { + ReadFloat(f, &g_val, swap); + img_in[i++] = g_val; + } + } +} + +/* write_pbm_file: + * Write the contents of a PBM (portable bit map) file. + */ +void write_pbm_file(FILE *f, int *img_out, + int x_size, int y_size, int x_scale_val, int y_scale_val, int linevals, + int is_ascii) +{ + int i, j, x_scaled_size, y_scaled_size; + int k, v, temp, step; + + x_scaled_size = x_size * x_scale_val; + y_scaled_size = y_size * y_scale_val; + /* Write the magic number string. */ + if (is_ascii == 1) { + fprintf(f, "P1\n"); + step = 1; + } else { + fprintf(f, "P4\n"); + step = 8; + } + /* Write the image dimensions. */ + fprintf(f, "%d %d\n", x_scaled_size, y_scaled_size); + + /* Write the image data. */ + for (i = 0; i < y_scaled_size; i++) { + for (j = 0; j < x_scaled_size; j+=step) { + if (is_ascii == 1) { + fprintf(f, "%d ", img_out[i*x_scaled_size+j]); + } else { + temp = 0; + for (k = 0; k < 8; k++) { + v = img_out[i*x_scaled_size+j+k]; + temp |= (v << (7-k)); + } + fprintf(f, "%c", temp); + } + if (((i*x_scaled_size+j) % linevals) == (linevals-1)) { + fprintf(f, "\n"); + } + } + } +} + +/* write_pgm_file: + * Write the contents of a PGM (portable grey map) file. + */ +void write_pgm_file(FILE *f, int *img_out, + int x_size, int y_size, int x_scale_val, int y_scale_val, + int img_colors, int linevals, int is_ascii) +{ + int x_scaled_size, y_scaled_size; + + x_scaled_size = x_size * x_scale_val; + y_scaled_size = y_size * y_scale_val; + /* Write the magic number string. */ + if (is_ascii == 1) { + fprintf(f, "P2\n"); + } else { + fprintf(f, "P5\n"); + } + /* Write the image dimensions. */ + fprintf(f, "%d %d\n", x_scaled_size, y_scaled_size); + /* Write the maximum color/grey level allowed. */ + fprintf(f, "%d\n", img_colors); + + /* Write the image data. */ + if ( is_ascii ) + { + write_ascii(f, img_out, x_scaled_size, y_scaled_size, linevals ); + } + else + { + if ( img_colors < 256 ) + { + write_binary_1(f, img_out, x_scaled_size, y_scaled_size); + } + else + { + write_binary_2(f, img_out, x_scaled_size, y_scaled_size); + } + } +} + +/* write_ppm_file: + * Write the contents of a PPM (portable pix map) file. + */ +void write_ppm_file(FILE *f, int *img_out, + int x_size, int y_size, int x_scale_val, int y_scale_val, + int img_colors, int is_ascii) +{ + int x_scaled_size, y_scaled_size; + + x_scaled_size = x_size * x_scale_val; + y_scaled_size = y_size * y_scale_val; + /* Write the magic number string. */ + if (is_ascii == 1) { + fprintf(f, "P3\n"); + } else { + fprintf(f, "P6\n"); + } + /* Write the image dimensions. */ + fprintf(f, "%d %d\n", x_scaled_size, y_scaled_size); + /* Write the maximum color/grey level allowed. */ + fprintf(f, "%d\n", img_colors); + + if ( is_ascii ) + { + write_ascii(f, img_out, x_scaled_size, y_scaled_size*3, x_scaled_size*3 ); + } + else + { + if ( img_colors < 256 ) + { + write_binary_1(f, img_out, x_scaled_size, y_scaled_size*3); + } + else + { + write_binary_2(f, img_out, x_scaled_size, y_scaled_size*3); + } + } +} + +/* write_pfm_file: + * Write the contents of a PFM (portable float map) file. + */ +void write_pfm_file(FILE *f, float *img_out, + int x_size, int y_size, + int img_type, int endianess) +{ + int i, j, x_scaled_size, y_scaled_size; + int swap = (endianess == 1) ? 0 : 1; + float fendian = (endianess == 1) ? +1.0 : -1.0; + + x_scaled_size = x_size; + y_scaled_size = y_size; + + /* Write the magic number string. */ + if (img_type == RGB_TYPE) { + fprintf(f, "PF\n"); + } else if (img_type == GREYSCALE_TYPE) { + fprintf(f, "Pf\n"); + } else { + fprintf(stderr, "Error: Image type invalid for PFM format!\n"); + exit(1); + } + /* Write the image dimensions. */ + fprintf(f, "%d %d\n", x_scaled_size, y_scaled_size); + /* Write the endianess/scale factor as float. */ + fprintf(f, "%f\n", fendian); + + /* Write the image data. */ + for (i = 0; i < y_scaled_size; i++) { + for (j = 0; j < x_scaled_size; j++) { + if (img_type == RGB_TYPE) { + WriteFloat(f, &img_out[3*(i*x_scaled_size+j)+0], swap); + WriteFloat(f, &img_out[3*(i*x_scaled_size+j)+1], swap); + WriteFloat(f, &img_out[3*(i*x_scaled_size+j)+2], swap); + } else if (img_type == GREYSCALE_TYPE) { + WriteFloat(f, &img_out[i*x_scaled_size+j], swap); + } + } + } +} + +/* ReadFloat: + * Read a possibly byte swapped floating-point number. + * NOTE: Assume IEEE format. + * Source: http://paulbourke.net/dataformats/pbmhdr/ + */ +int ReadFloat(FILE *fptr, float *f, int swap) +{ + unsigned char *cptr; + + if (fread(f, sizeof(float), 1, fptr) != 1) { + return (FALSE); + } + if (swap) { + cptr = (unsigned char *)f; + unsigned char tmp = cptr[0]; + cptr[0] = cptr[3]; + cptr[3] = tmp; + tmp = cptr[1]; + cptr[1] = cptr[2]; + cptr[2] = tmp; + } + return (TRUE); +} + +/* WriteFloat: + * Write a possibly byte-swapped floating-point number. + * NOTE: Assume IEEE format. + */ +int WriteFloat(FILE *fptr, float *f, int swap) +{ + unsigned char *cptr; + + if (swap) { + cptr = (unsigned char*)f; + unsigned char tmp = cptr[0]; + cptr[0] = cptr[3]; + cptr[3] = tmp; + tmp = cptr[1]; + cptr[1] = cptr[2]; + cptr[2] = tmp; + } + if (fwrite(f, sizeof(float), 1, fptr) != 1) { + return (FALSE); + } + return (TRUE); +} + +/* floatEqualComparison: + * Compare two floats and accept equality if not different than + * maxRelDiff (a specified maximum relative difference). + */ +int floatEqualComparison(float A, float B, float maxRelDiff) +{ + float largest, diff = fabs(A-B); + A = fabs(A); + B = fabs(B); + largest = (B > A) ? B : A; + if (diff <= largest * maxRelDiff) { + return 1; + } + return 0; +} + +/* frand: + * Emulate a floating-point PRNG. + * Source: http://c-faq.com/lib/rand48.html + */ +float frand(void) +{ + return rand() / (RAND_MAX + 1.0); +} From 3b9bbf8a6c23498e6b0436edd0edf61b6eab8765 Mon Sep 17 00:00:00 2001 From: Antal Ispanovity Date: Tue, 16 Sep 2025 13:27:38 +0200 Subject: [PATCH 3/3] fix 2 byte writing --- src/pnmio.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pnmio.c b/src/pnmio.c index 7fea83b..0d9236f 100755 --- a/src/pnmio.c +++ b/src/pnmio.c @@ -55,11 +55,16 @@ void write_binary_1( FILE *f, int *img_out, int x_scaled_size, int y_scaled_size // the unit of information is 2 bytes. used for 16 bit grayscale and 48 bit rgb components void write_binary_2( FILE *f, int *img_out, int x_scaled_size, int y_scaled_size) { + char upper; + char lower; short s; for ( int i = 0; i < x_scaled_size * y_scaled_size; ++i ) { s = (short)img_out[i]; - fwrite(&s, sizeof(short), 1, f ); + upper = s >> 8; + lower = s & 0xFF; + fwrite(&upper, sizeof(char), 1, f ); + fwrite(&lower, sizeof(char), 1, f ); } }