ci: Add CIFuzz action
[openjpeg.git] / src / bin / jp2 / opj_dump.c
1 /*
2  * The copyright in this software is being made available under the 2-clauses
3  * BSD License, included below. This software may be subject to other third
4  * party and contributor rights, including patent rights, and no such rights
5  * are granted under this license.
6  *
7  * Copyright (c) 2010, Mathieu Malaterre, GDCM
8  * Copyright (c) 2011-2012, Centre National d'Etudes Spatiales (CNES), France
9  * Copyright (c) 2012, CS Systemes d'Information, France
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 #include "opj_config.h"
34
35 #include <stdio.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <math.h>
39
40 #ifdef _WIN32
41 #include "windirent.h"
42 #else
43 #include <dirent.h>
44 #endif /* _WIN32 */
45
46 #ifdef _WIN32
47 #include <windows.h>
48 #else
49 #include <strings.h>
50 #define _stricmp strcasecmp
51 #define _strnicmp strncasecmp
52 #endif /* _WIN32 */
53
54 #include "openjpeg.h"
55 #include "opj_getopt.h"
56 #include "convert.h"
57 #include "index.h"
58
59 #include "format_defs.h"
60 #include "opj_string.h"
61
62 typedef struct dircnt {
63     /** Buffer for holding images read from Directory*/
64     char *filename_buf;
65     /** Pointer to the buffer*/
66     char **filename;
67 } dircnt_t;
68
69
70 typedef struct img_folder {
71     /** The directory path of the folder containing input images*/
72     char *imgdirpath;
73     /** Output format*/
74     const char *out_format;
75     /** Enable option*/
76     char set_imgdir;
77     /** Enable Cod Format for output*/
78     char set_out_format;
79
80     int flag;
81 } img_fol_t;
82
83 /* -------------------------------------------------------------------------- */
84 /* Declarations                                                               */
85 static int get_num_images(char *imgdirpath);
86 static int load_images(dircnt_t *dirptr, char *imgdirpath);
87 static int get_file_format(const char *filename);
88 static char get_next_file(int imageno, dircnt_t *dirptr, img_fol_t *img_fol,
89                           opj_dparameters_t *parameters);
90 static int infile_format(const char *fname);
91
92 static int parse_cmdline_decoder(int argc, char **argv,
93                                  opj_dparameters_t *parameters, img_fol_t *img_fol);
94
95 /* -------------------------------------------------------------------------- */
96 static void decode_help_display(void)
97 {
98     fprintf(stdout, "\nThis is the opj_dump utility from the OpenJPEG project.\n"
99             "It dumps JPEG 2000 codestream info to stdout or a given file.\n"
100             "It has been compiled against openjp2 library v%s.\n\n", opj_version());
101
102     fprintf(stdout, "Parameters:\n");
103     fprintf(stdout, "-----------\n");
104     fprintf(stdout, "\n");
105     fprintf(stdout, "  -ImgDir <directory>\n");
106     fprintf(stdout, "   Image file Directory path \n");
107     fprintf(stdout, "  -i <compressed file>\n");
108     fprintf(stdout,
109             "    REQUIRED only if an Input image directory not specified\n");
110     fprintf(stdout,
111             "    Currently accepts J2K-files, JP2-files and JPT-files. The file type\n");
112     fprintf(stdout, "    is identified based on its suffix.\n");
113     fprintf(stdout, "  -o <output file>\n");
114     fprintf(stdout, "    OPTIONAL\n");
115     fprintf(stdout, "    Output file where file info will be dump.\n");
116     fprintf(stdout, "    By default it will be in the stdout.\n");
117     fprintf(stdout, "  -v "); /* FIXME WIP_MSD */
118     fprintf(stdout, "    OPTIONAL\n");
119     fprintf(stdout, "    Enable informative messages\n");
120     fprintf(stdout, "    By default verbose mode is off.\n");
121     fprintf(stdout, "\n");
122 }
123
124 /* -------------------------------------------------------------------------- */
125 static int get_num_images(char *imgdirpath)
126 {
127     DIR *dir;
128     struct dirent* content;
129     int num_images = 0;
130
131     /*Reading the input images from given input directory*/
132
133     dir = opendir(imgdirpath);
134     if (!dir) {
135         fprintf(stderr, "Could not open Folder %s\n", imgdirpath);
136         return 0;
137     }
138
139     while ((content = readdir(dir)) != NULL) {
140         if (strcmp(".", content->d_name) == 0 || strcmp("..", content->d_name) == 0) {
141             continue;
142         }
143         num_images++;
144     }
145     closedir(dir);
146     return num_images;
147 }
148
149 /* -------------------------------------------------------------------------- */
150 static int load_images(dircnt_t *dirptr, char *imgdirpath)
151 {
152     DIR *dir;
153     struct dirent* content;
154     int i = 0;
155
156     /*Reading the input images from given input directory*/
157
158     dir = opendir(imgdirpath);
159     if (!dir) {
160         fprintf(stderr, "Could not open Folder %s\n", imgdirpath);
161         return 1;
162     } else   {
163         fprintf(stderr, "Folder opened successfully\n");
164     }
165
166     while ((content = readdir(dir)) != NULL) {
167         if (strcmp(".", content->d_name) == 0 || strcmp("..", content->d_name) == 0) {
168             continue;
169         }
170
171         strcpy(dirptr->filename[i], content->d_name);
172         i++;
173     }
174     closedir(dir);
175     return 0;
176 }
177
178 /* -------------------------------------------------------------------------- */
179 static int get_file_format(const char *filename)
180 {
181     unsigned int i;
182     static const char * const extension[] = {
183         "pgx", "pnm", "pgm", "ppm", "bmp",
184         "tif", "tiff",
185         "raw", "yuv", "rawl",
186         "tga", "png",
187         "j2k", "jp2", "jpt", "j2c", "jpc",
188         "jph", /* HTJ2K with JP2 boxes */
189         "jhc" /* HTJ2K codestream */
190     };
191     static const int format[] = {
192         PGX_DFMT, PXM_DFMT, PXM_DFMT, PXM_DFMT, BMP_DFMT,
193         TIF_DFMT, TIF_DFMT,
194         RAW_DFMT, RAW_DFMT, RAWL_DFMT,
195         TGA_DFMT, PNG_DFMT,
196         J2K_CFMT, JP2_CFMT, JPT_CFMT, J2K_CFMT, J2K_CFMT,
197         JP2_CFMT, /* HTJ2K with JP2 boxes */
198         J2K_CFMT /* HTJ2K codestream */
199     };
200     const char *ext = strrchr(filename, '.');
201     if (ext == NULL) {
202         return -1;
203     }
204     ext++;
205     if (ext) {
206         for (i = 0; i < sizeof(format) / sizeof(*format); i++) {
207             if (_strnicmp(ext, extension[i], 3) == 0) {
208                 return format[i];
209             }
210         }
211     }
212
213     return -1;
214 }
215
216 /* -------------------------------------------------------------------------- */
217 static char get_next_file(int imageno, dircnt_t *dirptr, img_fol_t *img_fol,
218                           opj_dparameters_t *parameters)
219 {
220     char image_filename[OPJ_PATH_LEN], infilename[OPJ_PATH_LEN],
221          outfilename[OPJ_PATH_LEN], temp_ofname[OPJ_PATH_LEN];
222     char *temp_p, temp1[OPJ_PATH_LEN] = "";
223
224     strcpy(image_filename, dirptr->filename[imageno]);
225     fprintf(stderr, "File Number %d \"%s\"\n", imageno, image_filename);
226     parameters->decod_format = get_file_format(image_filename);
227     if (parameters->decod_format == -1) {
228         return 1;
229     }
230     if (strlen(img_fol->imgdirpath) + 1 + strlen(
231                 image_filename) + 1 > sizeof(infilename)) {
232         return 1;
233     }
234     strcpy(infilename, img_fol->imgdirpath);
235     strcat(infilename, "/");
236     strcat(infilename, image_filename);
237     if (opj_strcpy_s(parameters->infile, sizeof(parameters->infile),
238                      infilename) != 0) {
239         return 1;
240     }
241
242     /*Set output file*/
243     strcpy(temp_ofname, strtok(image_filename, "."));
244     while ((temp_p = strtok(NULL, ".")) != NULL) {
245         strcat(temp_ofname, temp1);
246         sprintf(temp1, ".%s", temp_p);
247     }
248     if (img_fol->set_out_format == 1) {
249         if (strlen(img_fol->imgdirpath) + 1 + strlen(temp_ofname) + 1 + strlen(
250                     img_fol->out_format) + 1 > sizeof(outfilename)) {
251             return 1;
252         }
253         strcpy(outfilename, img_fol->imgdirpath);
254         strcat(outfilename, "/");
255         strcat(outfilename, temp_ofname);
256         strcat(outfilename, ".");
257         strcat(outfilename, img_fol->out_format);
258         if (opj_strcpy_s(parameters->outfile, sizeof(parameters->outfile),
259                          outfilename) != 0) {
260             return 1;
261         }
262     }
263     return 0;
264 }
265
266 /* -------------------------------------------------------------------------- */
267 #define JP2_RFC3745_MAGIC "\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a"
268 #define JP2_MAGIC "\x0d\x0a\x87\x0a"
269 /* position 45: "\xff\x52" */
270 #define J2K_CODESTREAM_MAGIC "\xff\x4f\xff\x51"
271
272 static int infile_format(const char *fname)
273 {
274     FILE *reader;
275     const char *s, *magic_s;
276     int ext_format, magic_format;
277     unsigned char buf[12];
278     size_t l_nb_read;
279
280     reader = fopen(fname, "rb");
281
282     if (reader == NULL) {
283         return -1;
284     }
285
286     memset(buf, 0, 12);
287     l_nb_read = fread(buf, 1, 12, reader);
288     fclose(reader);
289     if (l_nb_read != 12) {
290         return -1;
291     }
292
293
294
295     ext_format = get_file_format(fname);
296
297     if (ext_format == JPT_CFMT) {
298         return JPT_CFMT;
299     }
300
301     if (memcmp(buf, JP2_RFC3745_MAGIC, 12) == 0 || memcmp(buf, JP2_MAGIC, 4) == 0) {
302         magic_format = JP2_CFMT;
303         magic_s = ".jp2 or .jph";
304     } else if (memcmp(buf, J2K_CODESTREAM_MAGIC, 4) == 0) {
305         magic_format = J2K_CFMT;
306         magic_s = ".j2k or .jpc or .j2c or .jhc";
307     } else {
308         return -1;
309     }
310
311     if (magic_format == ext_format) {
312         return ext_format;
313     }
314
315     s = fname + strlen(fname) - 4;
316
317     fputs("\n===========================================\n", stderr);
318     fprintf(stderr, "The extension of this file is incorrect.\n"
319             "FOUND %s. SHOULD BE %s\n", s, magic_s);
320     fputs("===========================================\n", stderr);
321
322     return magic_format;
323 }
324 /* -------------------------------------------------------------------------- */
325 /**
326  * Parse the command line
327  */
328 /* -------------------------------------------------------------------------- */
329 static int parse_cmdline_decoder(int argc, char **argv,
330                                  opj_dparameters_t *parameters, img_fol_t *img_fol)
331 {
332     int totlen, c;
333     opj_option_t long_option[] = {
334         {"ImgDir", REQ_ARG, NULL, 'y'}
335     };
336     const char optlist[] = "i:o:f:hv";
337
338     totlen = sizeof(long_option);
339     img_fol->set_out_format = 0;
340     do {
341         c = opj_getopt_long(argc, argv, optlist, long_option, totlen);
342         if (c == -1) {
343             break;
344         }
345         switch (c) {
346         case 'i': {         /* input file */
347             char *infile = opj_optarg;
348             parameters->decod_format = infile_format(infile);
349             switch (parameters->decod_format) {
350             case J2K_CFMT:
351                 break;
352             case JP2_CFMT:
353                 break;
354             case JPT_CFMT:
355                 break;
356             default:
357                 fprintf(stderr,
358                         "[ERROR] Unknown input file format: %s \n"
359                         "        Known file formats are *.j2k, *.jp2, *.jpc or *.jpt\n",
360                         infile);
361                 return 1;
362             }
363             if (opj_strcpy_s(parameters->infile, sizeof(parameters->infile), infile) != 0) {
364                 fprintf(stderr, "[ERROR] Path is too long\n");
365                 return 1;
366             }
367         }
368         break;
369
370         /* ------------------------------------------------------ */
371
372         case 'o': {   /* output file */
373             if (opj_strcpy_s(parameters->outfile, sizeof(parameters->outfile),
374                              opj_optarg) != 0) {
375                 fprintf(stderr, "[ERROR] Path is too long\n");
376                 return 1;
377             }
378         }
379         break;
380
381         /* ----------------------------------------------------- */
382         case 'f':             /* flag */
383             img_fol->flag = atoi(opj_optarg);
384             break;
385         /* ----------------------------------------------------- */
386
387         case 'h':           /* display an help description */
388             decode_help_display();
389             return 1;
390
391         /* ------------------------------------------------------ */
392
393         case 'y': {         /* Image Directory path */
394             img_fol->imgdirpath = (char*)malloc(strlen(opj_optarg) + 1);
395             if (img_fol->imgdirpath == NULL) {
396                 return 1;
397             }
398             strcpy(img_fol->imgdirpath, opj_optarg);
399             img_fol->set_imgdir = 1;
400         }
401         break;
402
403         /* ----------------------------------------------------- */
404
405         case 'v': {         /* Verbose mode */
406             parameters->m_verbose = 1;
407         }
408         break;
409
410         /* ----------------------------------------------------- */
411         default:
412             fprintf(stderr, "[WARNING] An invalid option has been ignored.\n");
413             break;
414         }
415     } while (c != -1);
416
417     /* check for possible errors */
418     if (img_fol->set_imgdir == 1) {
419         if (!(parameters->infile[0] == 0)) {
420             fprintf(stderr, "[ERROR] options -ImgDir and -i cannot be used together.\n");
421             return 1;
422         }
423         if (img_fol->set_out_format == 0) {
424             fprintf(stderr,
425                     "[ERROR] When -ImgDir is used, -OutFor <FORMAT> must be used.\n");
426             fprintf(stderr, "Only one format allowed.\n"
427                     "Valid format are PGM, PPM, PNM, PGX, BMP, TIF, TIFF, RAW, YUV and TGA.\n");
428             return 1;
429         }
430         if (!(parameters->outfile[0] == 0)) {
431             fprintf(stderr, "[ERROR] options -ImgDir and -o cannot be used together\n");
432             return 1;
433         }
434     } else {
435         if (parameters->infile[0] == 0) {
436             fprintf(stderr, "[ERROR] Required parameter is missing\n");
437             fprintf(stderr, "Example: %s -i image.j2k\n", argv[0]);
438             fprintf(stderr, "   Help: %s -h\n", argv[0]);
439             return 1;
440         }
441     }
442
443     return 0;
444 }
445
446 /* -------------------------------------------------------------------------- */
447
448 /**
449 sample error debug callback expecting no client object
450 */
451 static void error_callback(const char *msg, void *client_data)
452 {
453     (void)client_data;
454     fprintf(stdout, "[ERROR] %s", msg);
455 }
456 /**
457 sample warning debug callback expecting no client object
458 */
459 static void warning_callback(const char *msg, void *client_data)
460 {
461     (void)client_data;
462     fprintf(stdout, "[WARNING] %s", msg);
463 }
464 /**
465 sample debug callback expecting no client object
466 */
467 static void info_callback(const char *msg, void *client_data)
468 {
469     (void)client_data;
470     fprintf(stdout, "[INFO] %s", msg);
471 }
472
473 /* -------------------------------------------------------------------------- */
474 /**
475  * OPJ_DUMP MAIN
476  */
477 /* -------------------------------------------------------------------------- */
478 int main(int argc, char *argv[])
479 {
480     FILE *fout = NULL;
481
482     opj_dparameters_t parameters;           /* Decompression parameters */
483     opj_image_t* image = NULL;                  /* Image structure */
484     opj_codec_t* l_codec = NULL;                /* Handle to a decompressor */
485     opj_stream_t *l_stream = NULL;              /* Stream */
486     opj_codestream_info_v2_t* cstr_info = NULL;
487     opj_codestream_index_t* cstr_index = NULL;
488
489     OPJ_INT32 num_images, imageno;
490     img_fol_t img_fol;
491     dircnt_t *dirptr = NULL;
492
493     /* Set decoding parameters to default values */
494     opj_set_default_decoder_parameters(&parameters);
495
496     /* Initialize img_fol */
497     memset(&img_fol, 0, sizeof(img_fol_t));
498     img_fol.flag = OPJ_IMG_INFO | OPJ_J2K_MH_INFO | OPJ_J2K_MH_IND;
499
500     /* Parse input and get user encoding parameters */
501     if (parse_cmdline_decoder(argc, argv, &parameters, &img_fol) == 1) {
502         if (img_fol.imgdirpath) {
503             free(img_fol.imgdirpath);
504         }
505
506         return EXIT_FAILURE;
507     }
508
509     /* Initialize reading of directory */
510     if (img_fol.set_imgdir == 1) {
511         int it_image;
512         num_images = get_num_images(img_fol.imgdirpath);
513
514         dirptr = (dircnt_t*)malloc(sizeof(dircnt_t));
515         if (!dirptr) {
516             return EXIT_FAILURE;
517         }
518         dirptr->filename_buf = (char*)malloc((size_t)num_images * OPJ_PATH_LEN * sizeof(
519                 char)); /* Stores at max 10 image file names*/
520         if (!dirptr->filename_buf) {
521             free(dirptr);
522             return EXIT_FAILURE;
523         }
524         dirptr->filename = (char**) malloc((size_t)num_images * sizeof(char*));
525
526         if (!dirptr->filename) {
527             goto fails;
528         }
529
530         for (it_image = 0; it_image < num_images; it_image++) {
531             dirptr->filename[it_image] = dirptr->filename_buf + it_image * OPJ_PATH_LEN;
532         }
533
534         if (load_images(dirptr, img_fol.imgdirpath) == 1) {
535             goto fails;
536         }
537
538         if (num_images == 0) {
539             fprintf(stdout, "Folder is empty\n");
540             goto fails;
541         }
542     } else {
543         num_images = 1;
544     }
545
546     /* Try to open for writing the output file if necessary */
547     if (parameters.outfile[0] != 0) {
548         fout = fopen(parameters.outfile, "w");
549         if (!fout) {
550             fprintf(stderr, "ERROR -> failed to open %s for writing\n", parameters.outfile);
551             goto fails;
552         }
553     } else {
554         fout = stdout;
555     }
556
557     /* Read the header of each image one by one */
558     for (imageno = 0; imageno < num_images ; imageno++) {
559
560         fprintf(stderr, "\n");
561
562         if (img_fol.set_imgdir == 1) {
563             if (get_next_file(imageno, dirptr, &img_fol, &parameters)) {
564                 fprintf(stderr, "skipping file...\n");
565                 continue;
566             }
567         }
568
569         /* Read the input file and put it in memory */
570         /* ---------------------------------------- */
571
572         l_stream = opj_stream_create_default_file_stream(parameters.infile, 1);
573         if (!l_stream) {
574             fprintf(stderr, "ERROR -> failed to create the stream from the file %s\n",
575                     parameters.infile);
576             goto fails;
577         }
578
579         /* Read the JPEG2000 stream */
580         /* ------------------------ */
581
582         switch (parameters.decod_format) {
583         case J2K_CFMT: { /* JPEG-2000 codestream */
584             /* Get a decoder handle */
585             l_codec = opj_create_decompress(OPJ_CODEC_J2K);
586             break;
587         }
588         case JP2_CFMT: { /* JPEG 2000 compressed image data */
589             /* Get a decoder handle */
590             l_codec = opj_create_decompress(OPJ_CODEC_JP2);
591             break;
592         }
593         case JPT_CFMT: { /* JPEG 2000, JPIP */
594             /* Get a decoder handle */
595             l_codec = opj_create_decompress(OPJ_CODEC_JPT);
596             break;
597         }
598         default:
599             fprintf(stderr, "skipping file..\n");
600             opj_stream_destroy(l_stream);
601             continue;
602         }
603
604         /* catch events using our callbacks and give a local context */
605         opj_set_info_handler(l_codec, info_callback, 00);
606         opj_set_warning_handler(l_codec, warning_callback, 00);
607         opj_set_error_handler(l_codec, error_callback, 00);
608
609         parameters.flags |= OPJ_DPARAMETERS_DUMP_FLAG;
610
611         /* Setup the decoder decoding parameters using user parameters */
612         if (!opj_setup_decoder(l_codec, &parameters)) {
613             fprintf(stderr, "ERROR -> opj_dump: failed to setup the decoder\n");
614             opj_stream_destroy(l_stream);
615             opj_destroy_codec(l_codec);
616             fclose(fout);
617             goto fails;
618         }
619
620         /* Read the main header of the codestream and if necessary the JP2 boxes*/
621         if (! opj_read_header(l_stream, l_codec, &image)) {
622             fprintf(stderr, "ERROR -> opj_dump: failed to read the header\n");
623             opj_stream_destroy(l_stream);
624             opj_destroy_codec(l_codec);
625             opj_image_destroy(image);
626             fclose(fout);
627             goto fails;
628         }
629
630         opj_dump_codec(l_codec, img_fol.flag, fout);
631
632         cstr_info = opj_get_cstr_info(l_codec);
633
634         cstr_index = opj_get_cstr_index(l_codec);
635
636         /* close the byte stream */
637         opj_stream_destroy(l_stream);
638
639         /* free remaining structures */
640         if (l_codec) {
641             opj_destroy_codec(l_codec);
642         }
643
644         /* destroy the image header */
645         opj_image_destroy(image);
646
647         /* destroy the codestream index */
648         opj_destroy_cstr_index(&cstr_index);
649
650         /* destroy the codestream info */
651         opj_destroy_cstr_info(&cstr_info);
652
653     }
654
655     /* Close the output file */
656     fclose(fout);
657
658     return EXIT_SUCCESS;
659
660 fails:
661     if (dirptr) {
662         if (dirptr->filename) {
663             free(dirptr->filename);
664         }
665         if (dirptr->filename_buf) {
666             free(dirptr->filename_buf);
667         }
668         free(dirptr);
669     }
670     return EXIT_FAILURE;
671 }