diff --git a/src/capture_filter/logo.cpp b/src/capture_filter/logo.cpp index 19acdee98..02deb2965 100644 --- a/src/capture_filter/logo.cpp +++ b/src/capture_filter/logo.cpp @@ -67,23 +67,23 @@ static int init(struct module *parent, const char *cfg, void **state); static void done(void *state); static struct video_frame *filter(void *state, struct video_frame *in); -using namespace std; - static bool load_logo_data_from_file(struct state_capture_filter_logo *s, const char *filename) { if (strcasecmp(filename + (MAX(strlen(filename), 4) - 4), ".pam") == 0) { - int depth = 0; bool rgb; unsigned char *data; - if (!pam_read(filename, &s->width, &s->height, &depth, NULL, &data, malloc)) { + struct pam_metadata info; + if (!pam_read(filename, &info, &data, malloc)) { return false; } - if (depth != 3 && depth != 4) { + s->width = info.width; + s->height = info.height; + if (info.depth != 3 && info.depth != 4) { cerr << "Unsupported depth passed."; free(data); return false; } - rgb = depth == 3; - int datalen = depth * s->width * s->height; + rgb = info.depth == 3; + int datalen = info.depth * s->width * s->height; if (rgb) { datalen = 4 * s->width * s->height; auto tmp = (unsigned char *) malloc(datalen); diff --git a/src/utils/pam.h b/src/utils/pam.h index 2b92c5311..46924d0a6 100644 --- a/src/utils/pam.h +++ b/src/utils/pam.h @@ -2,6 +2,9 @@ * @file utils/pam.h * @author Martin Pulec * + * Very simple library to read and write PAM/PPM files. Only binary formats + * (P5, P6) for PNM are processed, P4 is also not used. + * * This file is part of GPUJPEG. */ /* @@ -36,11 +39,13 @@ #define PAM_H_7E23A609_963A_45A8_88E2_ED4D3FDFF69F #ifdef __cplusplus +#include #include #include #include #include #else +#include #include #include #include @@ -54,27 +59,19 @@ #define PAM_ATTRIBUTE_UNUSED #endif -/** - * @pram maxval may be NULL in which case equality to 255 is checked - */ -static inline bool pam_read(const char *filename, unsigned int *width, unsigned int *height, int *depth, int *maxval, unsigned char **data, void *(*allocator)(size_t)) PAM_ATTRIBUTE_UNUSED; -static inline bool pam_read(const char *filename, unsigned int *width, unsigned int *height, int *depth, int *maxval, unsigned char **data, void *(*allocator)(size_t)) { +struct pam_metadata { + int width; + int height; + int depth; // == channel count + int maxval; +}; + +static inline bool pam_read(const char *filename, struct pam_metadata *info, unsigned char **data, void *(*allocator)(size_t)) PAM_ATTRIBUTE_UNUSED; +static bool pam_write(const char *filename, unsigned int width, unsigned int height, int depth, int maxval, const unsigned char *data, bool pnm) PAM_ATTRIBUTE_UNUSED; + +static inline void parse_pam(FILE *file, struct pam_metadata *info) { char line[128]; - errno = 0; - FILE *file = fopen(filename, "rb"); - if (!file) { - fprintf(stderr, "Failed to open %s: %s", filename, strerror(errno)); - return false; - } fgets(line, sizeof line - 1, file); - if (feof(file) || ferror(file) || strcmp(line, "P7\n") != 0) { - fprintf(stderr, "File '%s' doesn't seem to be valid PAM.\n", filename); - fclose(file); - return false; - } - fgets(line, sizeof line - 1, file); - *width = 0, *height = 0, *depth = 0; - int maxv = 0; while (!feof(file) && !ferror(file)) { if (strcmp(line, "ENDHDR\n") == 0) { break; @@ -87,21 +84,13 @@ static inline bool pam_read(const char *filename, unsigned int *width, unsigned *spc = '\0'; const char *val = spc + 1; if (strcmp(key, "WIDTH") == 0) { - *width = atoi(val); + info->width = atoi(val); } else if (strcmp(key, "HEIGHT") == 0) { - *height = atoi(val); + info->height = atoi(val); } else if (strcmp(key, "DEPTH") == 0) { - *depth = atoi(val); + info->depth = atoi(val); } else if (strcmp(key, "MAXVAL") == 0) { - maxv = atoi(val); - if (maxval == NULL && maxv != 255) { - fprintf(stderr, "Maxval 255 is assumed but %d presented.\n", maxv); - fclose(file); - return false; - } - if (maxval != NULL) { - *maxval = maxv; - } + info->maxval = atoi(val); } else if (strcmp(key, "TUPLTYPE") == 0) { // ignored - assuming MAXVAL == 255, value of DEPTH is sufficient // to determine pixel format @@ -110,17 +99,89 @@ static inline bool pam_read(const char *filename, unsigned int *width, unsigned } fgets(line, sizeof line - 1, file); } - if (*width * *height == 0) { +} + +static inline bool parse_pnm(FILE *file, char pnm_id, struct pam_metadata *info) { + switch (pnm_id) { + case '1': + case '2': + case '3': + case '4': + fprintf(stderr, "Unsupported PNM P%c\n", pnm_id); + return false; + case '5': + info->depth = 1; + break; + case '6': + info->depth = 3; + break; + default: + fprintf(stderr, "Wrong PNM P%c\n", pnm_id); + return false; + } + int item_nr = 0; + while (!feof(file) && !ferror(file)) { + int val = 0; + if (fscanf(file, "%d", &val) == 1) { + switch (item_nr++) { + case 0: + info->width = val; + break; + case 1: + info->height = val; + break; + case 2: + info->maxval = val; + getc(file); // skip whitespace following header + return true; + } + } else { + if (getc(file) == '#') { + int ch; + while ((ch = getc(file)) != '\n' && ch != EOF) + ; + } else { + break; + } + } + } + fprintf(stderr, "Problem parsing PNM header, number of hdr items successfully read: %d\n", item_nr); + return false; +} + +static inline bool pam_read(const char *filename, struct pam_metadata *info, unsigned char **data, void *(*allocator)(size_t)) { + char line[128]; + errno = 0; + FILE *file = fopen(filename, "rb"); + if (!file) { + fprintf(stderr, "Failed to open %s: %s", filename, strerror(errno)); + return false; + } + memset(info, 0, sizeof *info); + fgets(line, 4, file); + if (feof(file) || ferror(file)) { + fprintf(stderr, "File '%s' read error: %s\n", filename, strerror(errno)); + } + if (strcmp(line, "P7\n") == 0) { + parse_pam(file, info); + } else if (strlen(line) == 3 && line[0] == 'P' && isspace(line[2])) { + parse_pnm(file, line[1], info); + } else { + fprintf(stderr, "File '%s' doesn't seem to be valid PAM or PNM.\n", filename); + fclose(file); + return false; + } + if (info->width * info->height == 0) { fprintf(stderr, "Unspecified size header field!"); fclose(file); return false; } - if (*depth == 0) { + if (info->depth == 0) { fprintf(stderr, "Unspecified depth header field!"); fclose(file); return false; } - if (maxv == 0) { + if (info->maxval == 0) { fprintf(stderr, "Unspecified maximal value field!"); fclose(file); return false; @@ -129,7 +190,7 @@ static inline bool pam_read(const char *filename, unsigned int *width, unsigned fclose(file); return true; } - int datalen = *depth * *width * *height * (maxv <= 255 ? 1 : 2); + size_t datalen = (size_t) info->depth * info->width * info->height * (info->maxval <= 255 ? 1 : 2); *data = (unsigned char *) allocator(datalen); if (!*data) { fprintf(stderr, "Unspecified depth header field!"); @@ -138,7 +199,7 @@ static inline bool pam_read(const char *filename, unsigned int *width, unsigned } fread((char *) *data, datalen, 1, file); if (feof(file) || ferror(file)) { - perror("Unable to load PAM data from file"); + perror("Unable to load PAM/PNM data from file"); fclose(file); return false; } @@ -146,34 +207,45 @@ static inline bool pam_read(const char *filename, unsigned int *width, unsigned return true; } -static bool pam_write(const char *filename, unsigned int width, unsigned int height, int depth, int maxval, const unsigned char *data) PAM_ATTRIBUTE_UNUSED; -static bool pam_write(const char *filename, unsigned int width, unsigned int height, int depth, int maxval, const unsigned char *data) { +static bool pam_write(const char *filename, unsigned int width, unsigned int height, int depth, int maxval, const unsigned char *data, bool pnm) { errno = 0; FILE *file = fopen(filename, "wb"); if (!file) { fprintf(stderr, "Failed to open %s for writing: %s", filename, strerror(errno)); return false; } - const char *tuple_type = "INVALID"; - switch (depth) { - case 4: tuple_type = "RGB_ALPHA"; break; - case 3: tuple_type = "RGB"; break; - case 2: tuple_type = "GRAYSCALE_ALPHA"; break; - case 1: tuple_type = "GRAYSCALE"; break; - default: fprintf(stderr, "Wrong depth: %d\n", depth); + if (pnm) { + if (depth != 1 && depth != 3) { + fprintf(stderr, "Only 1 or 3 channels supported for PNM!\n"); + return false; + } + fprintf(file, "P%d\n" + "%u %u\n" + "%d\n", + depth == 1 ? 5 : 6, + width, height, maxval); + } else { + const char *tuple_type = "INVALID"; + switch (depth) { + case 4: tuple_type = "RGB_ALPHA"; break; + case 3: tuple_type = "RGB"; break; + case 2: tuple_type = "GRAYSCALE_ALPHA"; break; + case 1: tuple_type = "GRAYSCALE"; break; + default: fprintf(stderr, "Wrong depth: %d\n", depth); + } + fprintf(file, "P7\n" + "WIDTH %u\n" + "HEIGHT %u\n" + "DEPTH %d\n" + "MAXVAL %d\n" + "TUPLTYPE %s\n" + "ENDHDR\n", + width, height, depth, maxval, tuple_type); } - fprintf(file, "P7\n" - "WIDTH %u\n" - "HEIGHT %u\n" - "DEPTH %d\n" - "MAXVAL %d\n" - "TUPLTYPE %s\n" - "ENDHDR\n", - width, height, depth, maxval, tuple_type); fwrite((const char *) data, width * height * depth, maxval <= 255 ? 1 : 2, file); bool ret = !ferror(file); if (!ret) { - perror("Unable to write PAM data"); + perror("Unable to write PAM/PNM data"); } fclose(file); return ret; diff --git a/src/video_frame.c b/src/video_frame.c index 69e09ed82..9ada22001 100644 --- a/src/video_frame.c +++ b/src/video_frame.c @@ -59,6 +59,7 @@ #include #include #include +#include "utils/pam.h" #include "utils/y4m.h" #include "video_codec.h" #include "video_frame.h" @@ -432,7 +433,6 @@ bool save_video_frame_as_pnm(struct video_frame *frame, const char *name) { unsigned char *data = NULL, *tmp_data = NULL; struct tile *tile = &frame->tiles[0]; - int len = tile->width * tile->height * 3; if (frame->color_spec == RGB) { data = (unsigned char *) tile->data; } else if (get_bits_per_component(frame->color_spec) <= 8 || @@ -443,6 +443,7 @@ bool save_video_frame_as_pnm(struct video_frame *frame, const char *name) get_codec_name(frame->color_spec)); return false; } + int len = tile->width * tile->height * 3; data = tmp_data = (unsigned char *) malloc(len); dec (data, (const unsigned char *) tile->data, len, 0, 0, 0); } else { @@ -450,21 +451,13 @@ bool save_video_frame_as_pnm(struct video_frame *frame, const char *name) if (!data) { return false; } - len *= 2; } if (!data) { return false; } - FILE *out = fopen(name, "w"); - if(out) { - fprintf(out, "P6\n%d %d\n%d\n", tile->width, tile->height, (1<color_spec)) - 1); - if (fwrite(data, len, 1, out) != 1) { - perror("fwrite"); - } - fclose(out); - } + pam_write(name, tile->width, tile->height, 3, (1<color_spec)) - 1, data, true); free(tmp_data); return true;