1 /*
2  * Copyright (c) 2001-2003 Michael David Adams.
3  * All rights reserved.
4  */
5 
6 /* __START_OF_JASPER_LICENSE__
7  *
8  * JasPer License Version 2.0
9  *
10  * Copyright (c) 2001-2006 Michael David Adams
11  * Copyright (c) 1999-2000 Image Power, Inc.
12  * Copyright (c) 1999-2000 The University of British Columbia
13  *
14  * All rights reserved.
15  *
16  * Permission is hereby granted, free of charge, to any person (the
17  * "User") obtaining a copy of this software and associated documentation
18  * files (the "Software"), to deal in the Software without restriction,
19  * including without limitation the rights to use, copy, modify, merge,
20  * publish, distribute, and/or sell copies of the Software, and to permit
21  * persons to whom the Software is furnished to do so, subject to the
22  * following conditions:
23  *
24  * 1.  The above copyright notices and this permission notice (which
25  * includes the disclaimer below) shall be included in all copies or
26  * substantial portions of the Software.
27  *
28  * 2.  The name of a copyright holder shall not be used to endorse or
29  * promote products derived from the Software without specific prior
30  * written permission.
31  *
32  * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS
33  * LICENSE.  NO USE OF THE SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
34  * THIS DISCLAIMER.  THE SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
35  * "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
36  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
37  * PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.  IN NO
38  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
39  * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
40  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
41  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
42  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.  NO ASSURANCES ARE
43  * PROVIDED BY THE COPYRIGHT HOLDERS THAT THE SOFTWARE DOES NOT INFRINGE
44  * THE PATENT OR OTHER INTELLECTUAL PROPERTY RIGHTS OF ANY OTHER ENTITY.
45  * EACH COPYRIGHT HOLDER DISCLAIMS ANY LIABILITY TO THE USER FOR CLAIMS
46  * BROUGHT BY ANY OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL
47  * PROPERTY RIGHTS OR OTHERWISE.  AS A CONDITION TO EXERCISING THE RIGHTS
48  * GRANTED HEREUNDER, EACH USER HEREBY ASSUMES SOLE RESPONSIBILITY TO SECURE
49  * ANY OTHER INTELLECTUAL PROPERTY RIGHTS NEEDED, IF ANY.  THE SOFTWARE
50  * IS NOT FAULT-TOLERANT AND IS NOT INTENDED FOR USE IN MISSION-CRITICAL
51  * SYSTEMS, SUCH AS THOSE USED IN THE OPERATION OF NUCLEAR FACILITIES,
52  * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL
53  * SYSTEMS, DIRECT LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH
54  * THE FAILURE OF THE SOFTWARE OR SYSTEM COULD LEAD DIRECTLY TO DEATH,
55  * PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH
56  * RISK ACTIVITIES").  THE COPYRIGHT HOLDERS SPECIFICALLY DISCLAIM ANY
57  * EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.
58  *
59  * __END_OF_JASPER_LICENSE__
60  */
61 
62 /******************************************************************************\
63 * Includes.
64 \******************************************************************************/
65 
66 #include "pgx_cod.h"
67 
68 #include "jasper/jas_tvp.h"
69 #include "jasper/jas_stream.h"
70 #include "jasper/jas_image.h"
71 #include "jasper/jas_debug.h"
72 #include "jasper/jas_math.h"
73 
74 #include <assert.h>
75 #include <ctype.h>
76 #include <stdlib.h>
77 
78 /******************************************************************************\
79 * Local types.
80 \******************************************************************************/
81 
82 typedef struct {
83 	int allow_trunc;
84 	size_t max_samples;
85 } pgx_dec_importopts_t;
86 
87 typedef enum {
88 	OPT_ALLOWTRUNC,
89 	OPT_MAXSIZE,
90 } optid_t;
91 
92 /******************************************************************************\
93 * Local prototypes.
94 \******************************************************************************/
95 
96 static int pgx_gethdr(jas_stream_t *in, pgx_hdr_t *hdr);
97 static int pgx_getdata(jas_stream_t *in, pgx_hdr_t *hdr, jas_image_t *image);
98 static int_fast32_t pgx_getword(jas_stream_t *in, bool bigendian, int prec);
99 static int pgx_getsgnd(jas_stream_t *in, bool *sgnd);
100 static int pgx_getbyteorder(jas_stream_t *in, bool *bigendian);
101 static int pgx_getc(jas_stream_t *in);
102 static int pgx_getuint32(jas_stream_t *in, uint_fast32_t *val);
103 static jas_seqent_t pgx_wordtoint(uint_fast32_t word, int prec, bool sgnd);
104 
105 /******************************************************************************\
106 * Option parsing.
107 \******************************************************************************/
108 
109 static const jas_taginfo_t pgx_decopts[] = {
110 	// Not yet supported
111 	// {OPT_ALLOWTRUNC, "allow_trunc"},
112 	{OPT_MAXSIZE, "max_samples"},
113 	{-1, 0}
114 };
115 
pgx_dec_parseopts(const char * optstr,pgx_dec_importopts_t * opts)116 static int pgx_dec_parseopts(const char *optstr, pgx_dec_importopts_t *opts)
117 {
118 	jas_tvparser_t *tvp;
119 
120 	opts->max_samples = JAS_DEC_DEFAULT_MAX_SAMPLES;
121 	opts->allow_trunc = 0;
122 
123 	if (!(tvp = jas_tvparser_create(optstr ? optstr : ""))) {
124 		return -1;
125 	}
126 
127 	while (!jas_tvparser_next(tvp)) {
128 		switch (jas_taginfo_nonull(jas_taginfos_lookup(pgx_decopts,
129 		  jas_tvparser_gettag(tvp)))->id) {
130 		case OPT_ALLOWTRUNC:
131 			opts->allow_trunc = atoi(jas_tvparser_getval(tvp));
132 			break;
133 		case OPT_MAXSIZE:
134 			opts->max_samples = strtoull(jas_tvparser_getval(tvp), 0, 10);
135 			break;
136 		default:
137 			jas_eprintf("warning: ignoring invalid option %s\n",
138 			  jas_tvparser_gettag(tvp));
139 			break;
140 		}
141 	}
142 
143 	jas_tvparser_destroy(tvp);
144 
145 	return 0;
146 }
147 
148 /******************************************************************************\
149 * Code for load operation.
150 \******************************************************************************/
151 
152 /* Load an image from a stream in the PGX format. */
153 
pgx_decode(jas_stream_t * in,const char * optstr)154 jas_image_t *pgx_decode(jas_stream_t *in, const char *optstr)
155 {
156 	jas_image_t *image;
157 	pgx_hdr_t hdr;
158 	jas_image_cmptparm_t cmptparm;
159 	pgx_dec_importopts_t opts;
160 	size_t num_samples;
161 
162 	image = 0;
163 
164 	JAS_DBGLOG(10, ("pgx_decode(%p, \"%s\")\n", in, optstr ? optstr : ""));
165 
166 	if (pgx_dec_parseopts(optstr, &opts)) {
167 		goto error;
168 	}
169 
170 	if (pgx_gethdr(in, &hdr)) {
171 		jas_eprintf("cannot get header\n");
172 		goto error;
173 	}
174 
175 	if (jas_getdbglevel() >= 10) {
176 		pgx_dumphdr(stderr, &hdr);
177 	}
178 
179 	if (!jas_safe_size_mul(hdr.width, hdr.height, &num_samples)) {
180 		jas_eprintf("image too large\n");
181 		goto error;
182 	}
183 	if (!num_samples) {
184 		jas_eprintf("image has no samples\n");
185 		goto error;
186 	}
187 	if (opts.max_samples > 0 && num_samples > opts.max_samples) {
188 		jas_eprintf(
189 		  "maximum number of samples would be exceeded (%zu > %zu)\n",
190 		  num_samples, opts.max_samples);
191 		goto error;
192 	}
193 
194 	if (!(image = jas_image_create0())) {
195 		goto error;
196 	}
197 	cmptparm.tlx = 0;
198 	cmptparm.tly = 0;
199 	cmptparm.hstep = 1;
200 	cmptparm.vstep = 1;
201 	cmptparm.width = hdr.width;
202 	cmptparm.height = hdr.height;
203 	cmptparm.prec = hdr.prec;
204 	cmptparm.sgnd = hdr.sgnd;
205 	if (jas_image_addcmpt(image, 0, &cmptparm)) {
206 		goto error;
207 	}
208 	if (pgx_getdata(in, &hdr, image)) {
209 		jas_eprintf("cannot get data\n");
210 		goto error;
211 	}
212 
213 	jas_image_setclrspc(image, JAS_CLRSPC_SGRAY);
214 	jas_image_setcmpttype(image, 0,
215 	  JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_GRAY_Y));
216 
217 	return image;
218 
219 error:
220 	if (image) {
221 		jas_image_destroy(image);
222 	}
223 	return 0;
224 }
225 
226 /******************************************************************************\
227 * Code for validate operation.
228 \******************************************************************************/
229 
pgx_validate(jas_stream_t * in)230 int pgx_validate(jas_stream_t *in)
231 {
232 	jas_uchar buf[PGX_MAGICLEN];
233 	uint_fast32_t magic;
234 
235 	assert(JAS_STREAM_MAXPUTBACK >= PGX_MAGICLEN);
236 
237 	/* Read the validation data (i.e., the data used for detecting
238 	  the format). */
239 	if (jas_stream_peek(in, buf, sizeof(buf)) != sizeof(buf))
240 		return -1;
241 
242 	/* Compute the signature value. */
243 	magic = (buf[0] << 8) | buf[1];
244 
245 	/* Ensure that the signature is correct for this format. */
246 	if (magic != PGX_MAGIC) {
247 		return -1;
248 	}
249 
250 	return 0;
251 }
252 
253 /******************************************************************************\
254 *
255 \******************************************************************************/
256 
pgx_gethdr(jas_stream_t * in,pgx_hdr_t * hdr)257 static int pgx_gethdr(jas_stream_t *in, pgx_hdr_t *hdr)
258 {
259 	int c;
260 	jas_uchar buf[2];
261 
262 	if ((c = jas_stream_getc(in)) == EOF) {
263 		goto error;
264 	}
265 	buf[0] = c;
266 	if ((c = jas_stream_getc(in)) == EOF) {
267 		goto error;
268 	}
269 	buf[1] = c;
270 	hdr->magic = buf[0] << 8 | buf[1];
271 	if (hdr->magic != PGX_MAGIC) {
272 		jas_eprintf("invalid PGX signature\n");
273 		goto error;
274 	}
275 	if ((c = pgx_getc(in)) == EOF || !isspace(c)) {
276 		goto error;
277 	}
278 	if (pgx_getbyteorder(in, &hdr->bigendian)) {
279 		jas_eprintf("cannot get byte order\n");
280 		goto error;
281 	}
282 	if (pgx_getsgnd(in, &hdr->sgnd)) {
283 		jas_eprintf("cannot get signedness\n");
284 		goto error;
285 	}
286 	if (pgx_getuint32(in, &hdr->prec)) {
287 		jas_eprintf("cannot get precision\n");
288 		goto error;
289 	}
290 	if (hdr->prec > 32) {
291 		jas_eprintf("unsupported precision\n");
292 		goto error;
293 	}
294 	if (pgx_getuint32(in, &hdr->width)) {
295 		jas_eprintf("cannot get width\n");
296 		goto error;
297 	}
298 	if (pgx_getuint32(in, &hdr->height)) {
299 		jas_eprintf("cannot get height\n");
300 		goto error;
301 	}
302 	return 0;
303 
304 error:
305 	return -1;
306 }
307 
pgx_getdata(jas_stream_t * in,pgx_hdr_t * hdr,jas_image_t * image)308 static int pgx_getdata(jas_stream_t *in, pgx_hdr_t *hdr, jas_image_t *image)
309 {
310 	jas_matrix_t *data;
311 	uint_fast32_t x;
312 	uint_fast32_t y;
313 	uint_fast32_t word;
314 	int_fast32_t v;
315 
316 	data = 0;
317 
318 	if (!(data = jas_matrix_create(1, hdr->width))) {
319 		goto error;
320 	}
321 	for (y = 0; y < hdr->height; ++y) {
322 		for (x = 0; x < hdr->width; ++x) {
323 			/* Need to adjust signed value. */
324 			if ((v = pgx_getword(in, hdr->bigendian, hdr->prec)) < 0) {
325 				goto error;
326 			}
327 			word = v;
328 			v = pgx_wordtoint(word, hdr->prec, hdr->sgnd);
329 			jas_matrix_set(data, 0, x, v);
330 		}
331 		if (jas_image_writecmpt(image, 0, 0, y, hdr->width, 1, data)) {
332 			goto error;
333 		}
334 	}
335 	jas_matrix_destroy(data);
336 	return 0;
337 
338 error:
339 	if (data) {
340 		jas_matrix_destroy(data);
341 	}
342 	return -1;
343 }
344 
pgx_getword(jas_stream_t * in,bool bigendian,int prec)345 static int_fast32_t pgx_getword(jas_stream_t *in, bool bigendian, int prec)
346 {
347 	assert(prec <= 32);
348 
349 	uint_fast32_t val;
350 	int i;
351 	int j;
352 	int c;
353 	int wordsize;
354 
355 	wordsize = (prec + 7) / 8;
356 
357 	val = 0;
358 	for (i = 0; i < wordsize; ++i) {
359 		if ((c = jas_stream_getc(in)) == EOF) {
360 			goto error;
361 		}
362 		j = bigendian ? (wordsize - 1 - i) : i;
363 		val = val | ((c & 0xffU) << (8 * j));
364 	}
365 	val &= (JAS_CAST(uint_fast32_t, 1) << prec) - 1;
366 	return val;
367 
368 error:
369 	return -1;
370 }
371 
pgx_getc(jas_stream_t * in)372 static int pgx_getc(jas_stream_t *in)
373 {
374 	int c;
375 	for (;;) {
376 		if ((c = jas_stream_getc(in)) == EOF) {
377 			return -1;
378 		}
379 		if (c != '#') {
380 			return c;
381 		}
382 		do {
383 			if ((c = jas_stream_getc(in)) == EOF) {
384 				return -1;
385 			}
386 		} while (c != '\n' && c != '\r');
387 	}
388 }
389 
pgx_getbyteorder(jas_stream_t * in,bool * bigendian)390 static int pgx_getbyteorder(jas_stream_t *in, bool *bigendian)
391 {
392 	int c;
393 	char buf[2];
394 
395 	do {
396 		if ((c = pgx_getc(in)) == EOF) {
397 			return -1;
398 		}
399 	} while (isspace(c));
400 
401 	buf[0] = c;
402 	if ((c = pgx_getc(in)) == EOF) {
403 		goto error;
404 	}
405 	buf[1] = c;
406 	if (buf[0] == 'M' && buf[1] == 'L') {
407 		*bigendian = true;
408 	} else if (buf[0] == 'L' && buf[1] == 'M') {
409 		*bigendian = false;
410 	} else {
411 		goto error;
412 	}
413 
414 	while ((c = pgx_getc(in)) != EOF && !isspace(c)) {
415 		;
416 	}
417 	if (c == EOF) {
418 		goto error;
419 	}
420 	return 0;
421 
422 error:
423 	return -1;
424 }
425 
pgx_getsgnd(jas_stream_t * in,bool * sgnd)426 static int pgx_getsgnd(jas_stream_t *in, bool *sgnd)
427 {
428 	int c;
429 
430 	do {
431 		if ((c = pgx_getc(in)) == EOF) {
432 			return -1;
433 		}
434 	} while (isspace(c));
435 
436 #if 0
437 	if (c == '+') {
438 		*sgnd = false;
439 	} else if (c == '-') {
440 		*sgnd = true;
441 	} else {
442 		*sgnd = false;
443 		if (jas_stream_ungetc(in, c)) {
444 			goto error;
445 		}
446 		return 0;
447 	}
448 
449 	while ((c = pgx_getc(in)) != EOF && !isspace(c)) {
450 		;
451 	}
452 	if (c == EOF) {
453 		goto error;
454 	}
455 #else
456 	if (c == '+' || c == '-') {
457 		*sgnd = (c == '-');
458 		while ((c = pgx_getc(in)) != EOF && !isspace(c)) {
459 			;
460 		}
461 		if (c == EOF) {
462 			goto error;
463 		}
464 	} else {
465 		*sgnd = false;
466 		if (jas_stream_ungetc(in, c)) {
467 			goto error;
468 		}
469 	}
470 #endif
471 
472 	return 0;
473 
474 error:
475 	return -1;
476 }
477 
pgx_getuint32(jas_stream_t * in,uint_fast32_t * val)478 static int pgx_getuint32(jas_stream_t *in, uint_fast32_t *val)
479 {
480 	int c;
481 	uint_fast32_t v;
482 
483 	do {
484 		if ((c = pgx_getc(in)) == EOF) {
485 			return -1;
486 		}
487 	} while (isspace(c));
488 
489 	v = 0;
490 	while (isdigit(c)) {
491 		v = 10 * v + c - '0';
492 		if ((c = pgx_getc(in)) < 0) {
493 			return -1;
494 		}
495 	}
496 	if (!isspace(c)) {
497 		return -1;
498 	}
499 	*val = v;
500 
501 	return 0;
502 }
503 
pgx_wordtoint(uint_fast32_t v,int prec,bool sgnd)504 static jas_seqent_t pgx_wordtoint(uint_fast32_t v, int prec, bool sgnd)
505 {
506 	jas_seqent_t ret;
507 	v &= (1 << prec) - 1;
508 	ret = (sgnd && (v & (1 << (prec - 1)))) ? (v - (1 << prec)) : v;
509 	return ret;
510 }
511