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