UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
pam.c
Go to the documentation of this file.
1
10/*
11 * Copyright (c) 2013-2023, CESNET z.s.p.o.
12 *
13 * All rights reserved.
14 *
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions are met:
17 *
18 * * Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 *
21 * * Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#include <ctype.h>
39#include <errno.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43
44#include "pam.h"
45
46static bool parse_pam(FILE *file, struct pam_metadata *info) {
47 char line[128];
48 while (fgets(line, sizeof line - 1, file) != NULL) {
49 if (strcmp(line, "ENDHDR\n") == 0) {
50 break;
51 }
52 if (line[0] == '#') {
53 continue;
54 }
55 char *spc = strchr(line, ' ');
56 if (spc == NULL) {
57 break;
58 }
59 const char *key = line;
60 *spc = '\0';
61 const char *val = spc + 1;
62 if (strcmp(key, "WIDTH") == 0) {
63 info->width = atoi(val);
64 } else if (strcmp(key, "HEIGHT") == 0) {
65 info->height = atoi(val);
66 } else if (strcmp(key, "DEPTH") == 0) {
67 info->depth = atoi(val);
68 } else if (strcmp(key, "MAXVAL") == 0) {
69 info->maxval = atoi(val);
70 } else if (strcmp(key, "TUPLTYPE") == 0) {
71 // ignored, value of DEPTH is sufficient to determine pixel format
72 } else {
73 fprintf(stderr, "unrecognized key %s in PAM header\n", key);
74 }
75 }
76 return true;
77}
78
79static bool check_nl(FILE *file) {
80 if (getc(file) != '\n') {
81 fprintf(stderr, "PNM maximal value isn't immediately followed by <NL>\n");
82 return false;
83 }
84 return true;
85}
86
87static bool parse_pnm(FILE *file, char pnm_id, struct pam_metadata *info) {
88 switch (pnm_id) {
89 case '1':
90 case '2':
91 case '3':
92 fprintf(stderr, "Plain (ASCII) PNM are not supported, input is P%c\n", pnm_id);
93 return false;
94 case '4':
95 info->depth = 1;
96 info->maxval = 1;
97 info->bitmap_pbm = true;
98 break;
99 case '5':
100 info->depth = 1;
101 break;
102 case '6':
103 info->depth = 3;
104 break;
105 default:
106 fprintf(stderr, "Wrong PNM type P%c\n", pnm_id);
107 return false;
108 }
109 int item_nr = 0;
110 while (!feof(file) && !ferror(file)) {
111 int val = 0;
112 if (fscanf(file, "%d", &val) == 1) {
113 switch (item_nr++) {
114 case 0:
115 info->width = val;
116 break;
117 case 1:
118 info->height = val;
119 if (info->bitmap_pbm) {
120 return check_nl(file);
121 }
122 break;
123 case 2:
124 info->maxval = val;
125 return check_nl(file);
126 }
127 } else {
128 if (getc(file) == '#') {
129 int ch;
130 while ((ch = getc(file)) != '\n' && ch != EOF)
131 ;
132 } else {
133 break;
134 }
135 }
136 }
137 fprintf(stderr, "Problem parsing PNM header, number of hdr items successfully read: %d\n", item_nr);
138 return false;
139}
140
141bool pam_read(const char *filename, struct pam_metadata *info, unsigned char **data, void *(*allocator)(size_t)) {
142 char line[128];
143 errno = 0;
144 FILE *file = fopen(filename, "rb");
145 if (!file) {
146 fprintf(stderr, "Failed to open %s: %s\n", filename, strerror(errno));
147 return false;
148 }
149 memset(info, 0, sizeof *info);
150 if (fgets(line, 4, file) == NULL) {
151 fprintf(stderr, "File '%s' read error: %s\n", filename, strerror(errno));
152 }
153 bool parse_rc = false;
154 if (strcmp(line, "P7\n") == 0) {
155 parse_rc = parse_pam(file, info);
156 } else if (strlen(line) == 3 && line[0] == 'P' && isspace(line[2])) {
157 parse_rc = parse_pnm(file, line[1], info);
158 } else {
159 fprintf(stderr, "File '%s' doesn't seem to be valid PAM or PNM.\n", filename);
160 }
161 if (!parse_rc) {
162 fclose(file);
163 return false;
164 }
165 if (info->width <= 0 || info->height <= 0) {
166 fprintf(stderr, "Unspecified/incorrect size %dx%d!\n", info->width, info->height);
167 parse_rc = false;
168 }
169 if (info->depth <= 0) {
170 fprintf(stderr, "Unspecified/incorrect depth %d!\n", info->depth);
171 parse_rc = false;
172 }
173 if (info->maxval <= 0 || info->maxval > 65535) {
174 fprintf(stderr, "Unspecified/incorrect maximal value %d!\n", info->maxval);
175 parse_rc = false;
176 }
177 if (data == NULL || allocator == NULL || !parse_rc) {
178 fclose(file);
179 return parse_rc;
180 }
181 size_t datalen = (size_t) info->depth * info->width * info->height;
182 if (info->maxval == 1 && info->bitmap_pbm) {
183 datalen = (info->width + 7) / 8 * info->height;
184 } else if (info->maxval > 255) {
185 datalen *= 2;
186 }
187 *data = (unsigned char *) allocator(datalen);
188 if (!*data) {
189 fprintf(stderr, "Unspecified depth header field!\n");
190 fclose(file);
191 return false;
192 }
193 size_t bytes_read = fread((char *) *data, 1, datalen, file);
194 if (bytes_read != datalen) {
195 fprintf(stderr, "Unable to load PAM/PNM data from file - read %zu B, expected %zu B: %s\n",
196 bytes_read, datalen, strerror(errno));
197 fclose(file);
198 return false;
199 }
200 fclose(file);
201 return true;
202}
203
204bool pam_write(const char *filename, unsigned int width, unsigned int height, int depth, int maxval, const unsigned char *data, bool pnm) {
205 errno = 0;
206 FILE *file = fopen(filename, "wb");
207 if (!file) {
208 fprintf(stderr, "Failed to open %s for writing: %s\n", filename, strerror(errno));
209 return false;
210 }
211 if (pnm) {
212 if (depth != 1 && depth != 3) {
213 fprintf(stderr, "Only 1 or 3 channels supported for PNM!\n");
214 fclose(file);
215 return false;
216 }
217 fprintf(file, "P%d\n"
218 "%u %u\n"
219 "%d\n",
220 depth == 1 ? 5 : 6,
221 width, height, maxval);
222 } else {
223 const char *tuple_type = "INVALID";
224 switch (depth) {
225 case 4: tuple_type = "RGB_ALPHA"; break;
226 case 3: tuple_type = "RGB"; break;
227 case 2: tuple_type = "GRAYSCALE_ALPHA"; break;
228 case 1: tuple_type = "GRAYSCALE"; break;
229 default: fprintf(stderr, "Wrong depth: %d\n", depth);
230 }
231 fprintf(file, "P7\n"
232 "WIDTH %u\n"
233 "HEIGHT %u\n"
234 "DEPTH %d\n"
235 "MAXVAL %d\n"
236 "TUPLTYPE %s\n"
237 "ENDHDR\n",
238 width, height, depth, maxval, tuple_type);
239 }
240 size_t len = (size_t) width * height * depth * (maxval <= 255 ? 1 : 2);
241 errno = 0;
242 size_t bytes_written = fwrite((const char *) data, 1, len, file);
243 if (bytes_written != len) {
244 fprintf(stderr, "Unable to write PAM/PNM data - length %zd, written %zd: %s\n",
245 len, bytes_written, strerror(errno));
246 }
247 fclose(file);
248 return bytes_written == len;
249}
250
int void * val
int void size_t * len
static bool parse_pnm(FILE *file, char pnm_id, struct pam_metadata *info)
Definition pam.c:87
bool pam_read(const char *filename, struct pam_metadata *info, unsigned char **data, void *(*allocator)(size_t))
Definition pam.c:141
bool pam_write(const char *filename, unsigned int width, unsigned int height, int depth, int maxval, const unsigned char *data, bool pnm)
Definition pam.c:204
static bool parse_pam(FILE *file, struct pam_metadata *info)
Definition pam.c:46
static bool check_nl(FILE *file)
Definition pam.c:79
depth
Definition types.h:125
const char * key
Definition cineform.cpp:125