Spelling fixes (patch by ka7, #890, rebased on top of master)
[openjpeg.git] / src / bin / mj2 / meta_out.c
1 /* meta_out.c */
2 /* Dump MJ2, JP2 metadata (partial so far) to xml file */
3 /* Callable from mj2_to_metadata */
4 /* Contributed to Open JPEG by Glenn Pearson, contract software developer, U.S. National Library of Medicine.
5
6 The base code in this file was developed by the author as part of a video archiving
7 project for the U.S. National Library of Medicine, Bethesda, MD.
8 It is the policy of NLM (and U.S. government) to not assert copyright.
9
10 A non-exclusive copy of this code has been contributed to the Open JPEG project.
11 Except for copyright, inclusion of the code within Open JPEG for distribution and use
12 can be bound by the Open JPEG open-source license and disclaimer, expressed elsewhere.
13 */
14
15 #include <windows.h> /* for time functions */
16
17 #include "opj_includes.h"
18 #include "mj2.h"
19
20 #include <time.h>
21 #include "meta_out.h"
22
23 static BOOL notes = TRUE;
24 static BOOL sampletables = FALSE;
25 static BOOL raw = TRUE;
26 static BOOL derived = TRUE;
27
28 opj_tcp_t *j2k_default_tcp;
29
30 /* Forwards */
31 int xml_write_overall_header(FILE *file, FILE *xmlout, opj_mj2_t * movie,
32                              unsigned int sampleframe, opj_event_mgr_t *event_mgr);
33 int xml_write_moov(FILE *file, FILE *xmlout, opj_mj2_t * movie,
34                    unsigned int sampleframe, opj_event_mgr_t *event_mgr);
35
36 void uint_to_chars(unsigned int value, char* buf);
37
38 void xml_write_trak(FILE* file, FILE* xmlout, mj2_tk_t *track,
39                     unsigned int tnum, unsigned int sampleframe, opj_event_mgr_t *event_mgr);
40 void xml_write_tkhd(FILE* file, FILE* xmlout, mj2_tk_t *track,
41                     unsigned int tnum);
42 void xml_write_udta(FILE* file, FILE* xmlout, mj2_tk_t *track,
43                     unsigned int tnum);
44 void xml_write_mdia(FILE* file, FILE* xmlout, mj2_tk_t *track,
45                     unsigned int tnum);
46 void xml_write_stbl(FILE* file, FILE* xmlout, mj2_tk_t *track,
47                     unsigned int tnum);
48
49 void UnixTimeToFileTime(time_t t, LPFILETIME pft);
50 void UnixTimeToSystemTime(time_t t, LPSYSTEMTIME pst);
51 void xml_time_out(FILE* xmlout, time_t t);
52
53 void int16_to_3packedchars(short int value, char* buf);
54
55 void xml_write_moov_udta(FILE* xmlout, opj_mj2_t * movie);
56 void xml_write_free_and_skip(FILE* xmlout, opj_mj2_t * movie);
57 void xml_write_uuid(FILE* xmlout, opj_mj2_t * movie);
58
59 int xml_out_frame(FILE* file, FILE* xmlout, mj2_sample_t *sample,
60                   unsigned int snum, opj_event_mgr_t *event_mgr);
61
62 void xml_out_frame_siz(FILE* xmlout, opj_image_t *img, opj_cp_t *cp);
63 void xml_out_frame_cod(FILE* xmlout, opj_tcp_t *tcp);
64 void xml_out_frame_coc(FILE* xmlout, opj_tcp_t *tcp,
65                        int numcomps); /* opj_image_t *img); */
66 BOOL same_component_style(opj_tccp_t *tccp1, opj_tccp_t *tccp2);
67 void xml_out_frame_qcd(FILE* xmlout, opj_tcp_t *tcp);
68 void xml_out_frame_qcc(FILE* xmlout, opj_tcp_t *tcp,
69                        int numcomps); /* opj_image_t *img); */
70 BOOL same_component_quantization(opj_tccp_t *tccp1, opj_tccp_t *tccp2);
71 void xml_out_frame_rgn(FILE* xmlout, opj_tcp_t *tcp,
72                        int numcomps);/* opj_image_t *img);*/
73 void xml_out_frame_poc(FILE* xmlout, opj_tcp_t *tcp);
74 void xml_out_frame_ppm(FILE* xmlout, opj_cp_t *cp);
75 void xml_out_frame_ppt(FILE* xmlout, opj_tcp_t *tcp);
76 void xml_out_frame_tlm(FILE*
77                        xmlout); /* j2k_default_tcp is passed globally */ /* NO-OP.  TLM NOT SAVED IN DATA STRUCTURE */
78 void xml_out_frame_plm(FILE*
79                        xmlout); /* j2k_default_tcp is passed globally */ /* NO-OP.  PLM NOT SAVED IN DATA STRUCTURE.  opt in main; can be used in conjunction with PLT */
80 void xml_out_frame_plt(FILE* xmlout,
81                        opj_tcp_t *tcp); /* NO-OP.  PLM NOT SAVED IN DATA STRUCTURE.  opt in main; can be used in conjunction with PLT */
82 void xml_out_frame_crg(FILE*
83                        xmlout); /* j2k_default_tcp is passed globally */ /* opt in main; */
84 void xml_out_frame_com(FILE* xmlout,
85                        opj_tcp_t *tcp); /* NO-OP.  COM NOT SAVED IN DATA STRUCTURE */ /* opt in main; */
86 void xml_out_dump_hex(FILE* xmlout, char *data, int data_len, char* s);
87 void xml_out_dump_hex_and_ascii(FILE* xmlout, char *data, int data_len,
88                                 char* s);
89 void xml_out_frame_jp2h(FILE* xmlout, opj_jp2_t *jp2_struct);
90 #ifdef NOTYET
91 /* Shown with cp, extended, as data structure... but it could be a new different one */
92 void xml_out_frame_jp2i(FILE* xmlout,
93                         opj_cp_t *cp);/* IntellectualProperty 'jp2i' (no restrictions on location) */
94 void xml_out_frame_xml(FILE* xmlout,
95                        opj_cp_t *cp); /* XML 'xml\040' (0x786d6c20).  Can appear multiply */
96 void xml_out_frame_uuid(FILE* xmlout,
97                         opj_cp_t *cp); /* UUID 'uuid' (top level only) */
98 void xml_out_frame_uinf(FILE* xmlout,
99                         opj_cp_t *cp); /* UUIDInfo 'uinf', includes UUIDList 'ulst' and URL 'url\40' */
100 void xml_out_frame_unknown_type(FILE* xmlout, opj_cp_t *cp);
101 #endif
102
103
104 void xml_write_init(BOOL n, BOOL t, BOOL r, BOOL d)
105 {
106     /* Init file globals */
107     notes = n;
108     sampletables = t;
109     raw = r;
110     derived = d;
111 }
112
113 int xml_write_struct(FILE* file, FILE *xmlout, opj_mj2_t * movie,
114                      unsigned int sampleframe, char* stringDTD, opj_event_mgr_t *event_mgr)
115 {
116
117     if (stringDTD != NULL) {
118         fprintf(xmlout, "<?xml version=\"1.0\" standalone=\"no\"?>\n");
119         /* stringDTD is known to start with "SYSTEM " or "PUBLIC " */
120         /* typical: SYSTEM mj2_to_metadata.dtd */
121         stringDTD[6] =
122             '\0'; /* Break into two strings at space, so quotes can be inserted. */
123         fprintf(xmlout, "<!DOCTYPE MJ2_File %s \"%s\">\n", stringDTD, stringDTD + 7);
124         stringDTD[6] = ' '; /* restore for sake of debugger or memory allocator */
125     } else {
126         fprintf(xmlout, "<?xml version=\"1.0\" standalone=\"yes\"?>\n");
127     }
128
129     fprintf(xmlout, "<MJ2_File>\n");
130     xml_write_overall_header(file, xmlout, movie, sampleframe, event_mgr);
131     fprintf(xmlout, "</MJ2_File>");
132     return 0;
133 }
134
135 /* ------------- */
136
137 int xml_write_overall_header(FILE *file, FILE *xmlout, opj_mj2_t * movie,
138                              unsigned int sampleframe, opj_event_mgr_t *event_mgr)
139 {
140     int i;
141     char buf[5];
142     buf[4] = '\0';
143
144     fprintf(xmlout,
145             "  <JP2 BoxType=\"jP[space][space]\" Signature=\"0x0d0a870a\" />\n");
146     // Called after structure initialized by mj2_read_ftyp
147     fprintf(xmlout,   "  <FileType BoxType=\"ftyp\">\n");
148     uint_to_chars(movie->brand, buf);
149     fprintf(xmlout,   "    <Brand>%s</Brand>\n",
150             buf);    /* 4 character; BR              */
151     fprintf(xmlout,   "    <MinorVersion>%u</MinorVersion>\n",
152             movie->minversion);    /* 4 char; MinV            */
153     fprintf(xmlout,   "    <CompatibilityList Count=\"%d\">\n", movie->num_cl);
154     for (i = movie->num_cl - 1; i > -1;
155             i--) { /* read routine stored in reverse order, so let's undo damage */
156         uint_to_chars(movie->cl[i], buf);
157         fprintf(xmlout, "      <CompatibleBrand>%s</CompatibleBrand>\n",
158                 buf);    /*4 characters, each CLi */
159     }
160     fprintf(xmlout,   "    </CompatibilityList>\n");
161     fprintf(xmlout,   "  </FileType>\n");
162     xml_write_moov(file, xmlout, movie, sampleframe, event_mgr);
163     // To come?              <mdat>  // This is the container for media data that can also be accessed through track structures,
164     // so is redundant, and simply not of interest as metadata
165     //                       <moof>  // Allows incremental build up of movie.  Probably not in Simple Profile
166     xml_write_free_and_skip(xmlout,
167                             movie); /* NO OP so far */ /* May be a place where user squirrels metadata */
168     xml_write_uuid(xmlout,
169                    movie); /* NO OP so far */ /* May be a place where user squirrels metadata */
170     return 0;
171 }
172
173 /* ------------- */
174
175 int xml_write_moov(FILE *file, FILE *xmlout, opj_mj2_t * movie,
176                    unsigned int sampleframe, opj_event_mgr_t *event_mgr)
177 {
178     unsigned int tnum;
179     mj2_tk_t *track;
180
181     fprintf(xmlout,   "  <MovieBox BoxType=\"moov\">\n");
182     fprintf(xmlout,   "    <MovieHeader BoxType=\"mvhd\">\n");
183     fprintf(xmlout,   "      <CreationTime>\n");
184     if (raw) {
185         fprintf(xmlout, "        <InSeconds>%u</InSeconds>\n", movie->creation_time);
186     }
187     if (notes) {
188         fprintf(xmlout,
189                 "        <!-- Seconds since start of Jan. 1, 1904 UTC (Greenwich) -->\n");
190     }
191     /*  2082844800 = seconds between 1/1/04 and 1/1/70 */
192     /* There's still a time zone offset problem not solved... but spec is ambiguous as to whether stored time
193        should be local or UTC */
194     if (derived) {
195         fprintf(xmlout, "        <AsLocalTime>");
196         xml_time_out(xmlout, movie->creation_time - 2082844800);
197         fprintf(xmlout, "</AsLocalTime>\n");
198     }
199     fprintf(xmlout,   "      </CreationTime>\n");
200     fprintf(xmlout,   "      <ModificationTime>\n");
201     if (raw) {
202         fprintf(xmlout, "        <InSeconds>%u</InSeconds>\n",
203                 movie->modification_time);
204     }
205     if (derived) {
206         fprintf(xmlout, "        <AsLocalTime>");
207         xml_time_out(xmlout, movie->modification_time - 2082844800);
208         fprintf(xmlout, "</AsLocalTime>\n");
209     }
210     fprintf(xmlout,   "      </ModificationTime>\n");
211     fprintf(xmlout,   "      <Timescale>%d</Timescale>\n", movie->timescale);
212     if (notes) {
213         fprintf(xmlout, "      <!-- Timescale defines time units in one second -->\n");
214     }
215     fprintf(xmlout,
216             "      <Rate>\n");        /* Rate to play presentation  (default = 0x00010000)          */
217     if (notes) {
218         fprintf(xmlout,
219                 "      <!-- Rate to play presentation is stored as fixed-point binary 16.16 value. Decimal value is approximation. -->\n");
220         fprintf(xmlout,
221                 "      <!-- Rate is expressed relative to normal (default) value of 0x00010000 (1.0) -->\n");
222     }
223     if (raw) {
224         fprintf(xmlout, "        <AsHex>0x%08x</AsHex>\n", movie->rate);
225     }
226     if (derived) {
227         fprintf(xmlout, "        <AsDecimal>%12.6f</AsDecimal>\n",
228                 (double)movie->rate / (double)0x00010000);
229     }
230     fprintf(xmlout,   "      </Rate>\n");
231     fprintf(xmlout,   "      <Duration>\n");
232     if (raw) {
233         fprintf(xmlout, "        <InTimeUnits>%u</InTimeUnits>\n", movie->duration);
234     }
235     if (derived) {
236         fprintf(xmlout, "        <InSeconds>%12.3f</InSeconds>\n",
237                 (double)movie->duration / (double)
238                 movie->timescale);    // Make this double later to get fractional seconds
239     }
240     fprintf(xmlout,   "      </Duration>\n");
241 #ifdef CURRENTSTRUCT
242     movie->volume = movie->volume << 8;
243 #endif
244     fprintf(xmlout,   "      <Volume>\n");
245     if (notes) {
246         fprintf(xmlout,
247                 "      <!-- Audio volume stored as fixed-point binary 8.8 value. Decimal value is approximation. -->\n");
248         fprintf(xmlout,
249                 "      <!-- Full, normal (default) value is 0x0100 (1.0) -->\n");
250     }
251     if (raw) {
252         fprintf(xmlout, "        <AsHex>0x%04x</AsHex>\n", movie->volume);
253     }
254     if (derived) {
255         fprintf(xmlout, "        <AsDecimal>%6.3f</AsDecimal>\n",
256                 (double)movie->volume / (double)0x0100);
257     }
258     fprintf(xmlout,   "      </Volume>\n");
259 #ifdef CURRENTSTRUCT
260     if (notes) {
261         fprintf(xmlout,
262                 "      <!-- Current m2j_to_metadata implementation always shows bits to right of decimal as zeroed. -->\n");
263     }
264     movie->volume = movie->volume >> 8;
265 #endif
266     /* Transformation matrix for video                            */
267     fprintf(xmlout,   "      <TransformationMatrix>\n");
268     if (notes) {
269         fprintf(xmlout,
270                 "      <!-- 3 x 3 Video Transformation Matrix {a,b,u,c,d,v,x,y,w}.  Required: u=0, v=0, w=1 -->\n");
271         fprintf(xmlout,
272                 "      <!-- Maps decompressed point (p,q) to rendered point (ap + cq + x, bp + dq + y) -->\n");
273         fprintf(xmlout,
274                 "      <!-- Stored as Fixed Point Hex: all are binary 16.16, except u,v,w are 2.30 -->\n");
275         fprintf(xmlout,
276                 "      <!-- Unity = 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 -->\n");
277     }
278     fprintf(xmlout,   "        <TMa>0x%08x</TMa>\n", movie->trans_matrix[0]);
279     fprintf(xmlout,   "        <TMb>0x%08x</TMb>\n", movie->trans_matrix[1]);
280     fprintf(xmlout,   "        <TMu>0x%08x</TMu>\n", movie->trans_matrix[2]);
281     fprintf(xmlout,   "        <TMc>0x%08x</TMc>\n", movie->trans_matrix[3]);
282     fprintf(xmlout,   "        <TMd>0x%08x</TMd>\n", movie->trans_matrix[4]);
283     fprintf(xmlout,   "        <TMv>0x%08x</TMv>\n", movie->trans_matrix[5]);
284     fprintf(xmlout,   "        <TMx>0x%08x</TMx>\n", movie->trans_matrix[6]);
285     fprintf(xmlout,   "        <TMy>0x%08x</TMy>\n", movie->trans_matrix[7]);
286     fprintf(xmlout,   "        <TMw>0x%08x</TMw>\n", movie->trans_matrix[8]);
287     fprintf(xmlout,   "      </TransformationMatrix>\n");
288     fprintf(xmlout,   "    </MovieHeader>\n");
289
290     fprintf(xmlout,   "    <Statistics>\n");
291     fprintf(xmlout,   "      <TracksFound>\n");
292     fprintf(xmlout,   "        <Video>%d</Video>\n", movie->num_vtk);
293     fprintf(xmlout,   "        <Audio>%d</Audio>\n", movie->num_stk);
294     fprintf(xmlout,   "        <Hint>%d</Hint>\n", movie->num_htk);
295     if (notes) {
296         fprintf(xmlout,
297                 "        <!-- Hint tracks for streaming video are not part of MJ2, but are a defined extension. -->\n");
298     }
299     /* See Part 3 Amend 2 Section 4.2 for relation of MJ2 to Part 12 Sections 7 and 10 hints */
300     fprintf(xmlout,   "      </TracksFound>\n");
301     fprintf(xmlout,   "    </Statistics>\n");
302     /* Idea for the future:  It would be possible to add code to verify that the file values:
303       1) are legal and self-consistent
304     2) comply with particular JP2 and/or MJ2 profiles.
305     This could be reported here as additional XML elements */
306
307     // Find first video track
308     tnum = 0;
309     while (movie->tk[tnum].track_type != 0) {
310         tnum ++;
311     }
312
313     track = &(movie->tk[tnum]);
314     // For now, output info on first video track
315     xml_write_trak(file, xmlout, track, tnum, sampleframe, event_mgr);
316
317     // to come:                <MovieExtends mvek> // possibly not in Simple Profile
318     xml_write_moov_udta(xmlout,
319                         movie); /* NO OP so far */ /* <UserDataBox udta> contains <CopyrightBox cprt> */
320     fprintf(xmlout,   "  </MovieBox>\n");
321     return 0;
322 }
323
324 /* --------------- */
325
326 void uint_to_chars(unsigned int value, char* buf)
327 {
328     /* buf is at least char[5] */
329     int i;
330     for (i = 3; i >= 0; i--) {
331         buf[i] = (value & 0x000000ff);
332         value = (value >> 8);
333     }
334     buf[4] = '\0'; /* Precautionary */
335 }
336
337 /* ------------- */
338
339 /* WINDOWS SPECIFIC */
340
341 void UnixTimeToFileTime(time_t t, LPFILETIME pft)
342 {
343     /* Windows specific.  From MS Q167296 */
344     /* 'time_t' represents seconds since midnight January 1, 1970 UTC (coordinated universal time). */
345     /* 64-bit FILETIME structure represents the number of 100-nanosecond intervals since January 1, 1601 UTC (coordinate universal time). */
346     LONGLONG ll; /* LONGLONG is a 64-bit value. */
347     ll = Int32x32To64(t, 10000000) + 116444736000000000;
348     pft->dwLowDateTime = (DWORD)ll;
349     /* pft->dwLowDateTime = (DWORD)(0x00000000ffffffff & ll); */
350     pft->dwHighDateTime = (DWORD)(ll >> 32);
351 }
352 // Once the UNIX time is converted to a FILETIME structure,
353 // other Win32 time formats can be easily obtained by using Win32 functions such
354 // as FileTimeToSystemTime() and FileTimeToDosDateTime().
355
356 /* ------------- */
357
358 void UnixTimeToSystemTime(time_t t, LPSYSTEMTIME pst)
359 {
360     /* Windows specific */
361     FILETIME ft;
362     UnixTimeToFileTime(t, &ft);
363     FileTimeToLocalFileTime(&ft, &ft);   /* Adjust from UTC to local time zone */
364     FileTimeToSystemTime(&ft, pst);
365 }
366
367 /* ------------- */
368
369 void xml_time_out(FILE* xmlout, time_t t)
370 {
371     /* Windows specific */
372     SYSTEMTIME st;
373     char szLocalDate[255], szLocalTime[255];
374     UnixTimeToSystemTime(t, &st);
375     GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, szLocalDate, 255);
376     GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, szLocalTime, 255);
377     fprintf(xmlout, "%s %s", szLocalDate, szLocalTime);
378 }
379
380 /* END WINDOWS SPECIFIC */
381
382 /* ------------- */
383
384 void xml_write_moov_udta(FILE* xmlout, opj_mj2_t * movie)
385 {
386     /* Compare with xml_write_udta */
387 #ifdef NOTYET
388     /* NO-OP so far.  Optional UserData 'udta' (zero or one in moov or each trak)
389        can contain multiple Copyright 'cprt' with different language codes */
390     /* There may be nested non-standard boxes within udta */
391     IMAGINE movie->udta, movie->copyright_count,
392             movie->copyright_language[i](array of 16bit ints),
393             movie->copyright_notice[i](array of buffers)
394             PROBABLY ALSO NEED movie->udta_len or special handler for non - standard boxes
395             char buf[5];
396     int i;
397
398     if (movie->udta != 1) {
399         return;    /* Not present */
400     }
401
402     fprintf(xmlout,    "    <UserData BoxType=\"udta\">\n");
403     for (i = 0; i < movie->copyright_count; i++) {
404         fprintf(xmlout,  "      <Copyright BoxType=\"cprt\"> Instance=\"%d\">\n",
405                 i + 1);
406         int16_to_3packedchars((short int)movie->copyright_languages[i], buf);
407         fprintf(xmlout,  "        <Language>%s</Language>\n", buf);    /* 3 chars */
408         fprintf(xmlout,  "        <Notice>%s</Notice>\n", movie->copyright_notices[i]);
409         fprintf(xmlout,  "      </Copyright>\n", i + 1);
410     }
411     /* TO DO: Non-standard boxes */
412     fprintf(xmlout,    "    </UserData>\n");
413 #endif
414 }
415
416 void xml_write_free_and_skip(FILE* xmlout, opj_mj2_t * movie)
417 {
418 #ifdef NOTYET
419     /* NO-OP so far.  There can be zero or more instances of free and/or skip
420        at the top level of the file.  This may be a place where the user squirrel's metadata.
421      Let's assume unstructured, and do a dump */
422     IMAGINE movie->free_and_skip, movie->free_and_skip_count,
423             movie->free_and_skip_content[i](array of buffers),
424             movie->free_and_skip_len[i](array of ints), movie->is_skip[i](array of BOOL)
425             int i;
426
427     if (movie->free_and_skip != 1) {
428         return;    /* Not present */
429     }
430
431     for (i = 0; i < movie->free_and_skip_count; i++) {
432         if (movie->is_skip[i]) {
433             fprintf(xmlout,    "  <Skip BoxType=\"skip\">\n");
434         } else {
435             fprintf(xmlout,    "  <Free BoxType=\"free\">\n");
436         }
437
438         xml_out_dump_hex_and_ascii(xmlout, movie->free_and_skip_contents[i],
439                                    movie->free_and_skip_len[i]);
440
441         if (movie->is_skip[i]) {
442             fprintf(xmlout,    "  </Skip>\n");
443         } else {
444             fprintf(xmlout,    "  </Free>\n");
445         }
446     }
447 #endif
448 }
449
450 void xml_write_uuid(FILE* xmlout, opj_mj2_t * movie)
451 {
452     /* Universal Unique IDs of 16 bytes.  */
453 #ifdef NOTYET
454     /* NO-OP so far.  There can be zero or more instances of private uuid boxes in a file.
455        This function supports the top level of the file, but uuid may be elsewhere [not yet supported].
456      This may be a place where the user squirrel's metadata.  Let's assume unstructured, and do a dump */
457     IMAGINE movie->uuid, movie->uuid_count,
458             movie->uuid_content[i](array of buffers),
459             movie->uuid_len[i](array of ints),
460             movie->uuid_type[i](array of 17 - byte(16 + null termination) buffers)
461             int i;
462
463     if (movie->uuid != 1) {
464         return;    /* Not present */
465     }
466
467     for (i = 0; i < movie->uuid_count; i++) {
468         fprintf(xmlout,    "  <PrivateExtension BoxType=\"uuid\" UUID=\"%s\">\n",
469                 movie->uuid_type[i]);
470         // See Part III section 5.2.1, 6.1, 6.2
471         xml_out_dump_hex_and_ascii(xmlout, movie->uuid_contents[i], movie->uuid_len[i]);
472         fprintf(xmlout,    "  </PrivateExtension>\n");
473     }
474 #endif
475 }
476
477 /* ------------- */
478
479 void xml_write_trak(FILE* file, FILE* xmlout, mj2_tk_t *track,
480                     unsigned int tnum, unsigned int sampleframe, opj_event_mgr_t *event_mgr)
481 {
482     fprintf(xmlout,    "    <Track BoxType=\"trak\" Instance=\"%d\">\n", tnum);
483     xml_write_tkhd(file, xmlout, track, tnum);
484     // TO DO: TrackReferenceContainer 'tref'  just used in hint track
485     // TO DO: EditListContainer 'edts', contains EditList 'elst' with media-time, segment-duration, media-rate
486     xml_write_mdia(file, xmlout, track, tnum);
487     xml_write_udta(file, xmlout, track,
488                    tnum); // NO-OP so far.  Optional UserData 'udta', can contain multiple Copyright 'cprt'
489
490     if (track->track_type == 0) { /* Only do for visual track */
491         /* sampleframe is from user option -f.  1 = first frame */
492         /* sampleframe of 0 is a user requests: no jp2 header */
493         /* Treat out-of-bounds values in the same way */
494         if (sampleframe > 0 && sampleframe <= track->num_samples) {
495             mj2_sample_t *sample;
496             unsigned int snum;
497
498             snum = sampleframe - 1;
499             // Someday maybe do a smart range scan... for (snum=0; snum < track->num_samples; snum++){
500             //  fprintf(stdout,"Frame %d: ",snum+1);
501             sample = &track->sample[snum];
502             if (xml_out_frame(file, xmlout, sample, snum, event_mgr)) {
503                 return;    /* Not great error handling here */
504             }
505         }
506     }
507     fprintf(xmlout,    "    </Track>\n");
508 }
509
510 /* ------------- */
511
512 void xml_write_tkhd(FILE* file, FILE* xmlout, mj2_tk_t *track,
513                     unsigned int tnum)
514 {
515     fprintf(xmlout,    "      <TrackHeader BoxType=\"tkhd\">\n");
516     if (notes) {
517         fprintf(xmlout,
518                 "      <!-- Not shown here: CreationTime, ModificationTime, Duration. -->\n");
519         fprintf(xmlout,
520                 "      <!-- These 3 fields are reported under MediaHeader below.   When reading these 3, -->\n");
521         fprintf(xmlout,
522                 "      <!-- m2j_to_metadata currently doesn't distinguish between TrackHeader and MediaHeader source. -->\n");
523         fprintf(xmlout,
524                 "      <!-- If both found, value read from MediaHeader is used. -->\n");
525     }
526     fprintf(xmlout,    "        <TrackID>%u</TrackID>\n", track->track_ID);
527     if (track->track_type == 0) { /* For visual track */
528         fprintf(xmlout,  "        <TrackLayer>%d</TrackLayer>\n", track->layer);
529         if (notes) {
530             fprintf(xmlout,
531                     "        <!-- front-to-back ordering of video tracks. 0 = normal, -1 is closer, etc. -->\n");
532         }
533     }
534     if (track->track_type != 0) { /* volume irrelevant for visual track */
535 #ifdef CURRENTSTRUCT
536         track->volume = track->volume << 8;
537 #endif
538         fprintf(xmlout,  "        <Volume>\n");
539         if (notes) {
540             fprintf(xmlout,
541                     "          <!-- Track audio volume stored as fixed-point binary 8.8 value. Decimal value is approximation. -->\n");
542             fprintf(xmlout,
543                     "          <!-- Full, normal (default) value is 0x0100 (1.0) -->\n");
544         }
545         if (raw) {
546             fprintf(xmlout, "          <AsHex>0x%04x</AsHex>\n", track->volume);
547         }
548         if (derived) {
549             fprintf(xmlout, "          <AsDecimal>%6.3f</AsDecimal>\n",
550                     (double)track->volume / (double)0x0100);
551         }
552         fprintf(xmlout,  "        </Volume>\n");
553 #ifdef CURRENTSTRUCT
554         if (notes) {
555             fprintf(xmlout,
556                     "        <!-- Current m2j_to_metadata implementation always shows bits to right of decimal as zeroed. -->\n");
557         }
558         track->volume = track->volume >> 8;
559 #endif
560     }
561     if (track->track_type == 0) {
562         /* Transformation matrix for video                            */
563         fprintf(xmlout,  "        <TransformationMatrix>\n");
564         if (notes) {
565             fprintf(xmlout,
566                     "          <!-- Comments about matrix in MovieHeader apply here as well. -->\n");
567             fprintf(xmlout,
568                     "          <!-- This matrix is applied before MovieHeader one. -->\n");
569         }
570         fprintf(xmlout,  "          <TMa>0x%08x</TMa>\n", track->trans_matrix[0]);
571         fprintf(xmlout,  "          <TMb>0x%08x</TMb>\n", track->trans_matrix[1]);
572         fprintf(xmlout,  "          <TMu>0x%08x</TMu>\n", track->trans_matrix[2]);
573         fprintf(xmlout,  "          <TMc>0x%08x</TMc>\n", track->trans_matrix[3]);
574         fprintf(xmlout,  "          <TMd>0x%08x</TMd>\n", track->trans_matrix[4]);
575         fprintf(xmlout,  "          <TMv>0x%08x</TMv>\n", track->trans_matrix[5]);
576         fprintf(xmlout,  "          <TMx>0x%08x</TMx>\n", track->trans_matrix[6]);
577         fprintf(xmlout,  "          <TMy>0x%08x</TMy>\n", track->trans_matrix[7]);
578         fprintf(xmlout,  "          <TMw>0x%08x</TMw>\n", track->trans_matrix[8]);
579         fprintf(xmlout,  "        </TransformationMatrix>\n");
580     }
581 #ifdef CURRENTSTRUCT
582     track->w = track->w << 16;
583     track->h = track->h << 16;
584 #endif
585     if (notes) {
586         fprintf(xmlout,
587                 "        <!-- Width and Height in pixels are for the presentation; frames will be scaled to this. -->\n");
588         fprintf(xmlout,
589                 "        <!-- Both stored as fixed-point binary 16.16 values. Decimal values are approximations. -->\n");
590     }
591     fprintf(xmlout,    "        <Width>\n");
592     if (raw) {
593         fprintf(xmlout,  "          <AsHex>0x%08x</AsHex>\n", track->w);
594     }
595     if (derived) {
596         fprintf(xmlout,  "          <AsDecimal>%12.6f</AsDecimal>\n",
597                 (double)track->w / (double)
598                 0x00010000);    /* Rate to play presentation  (default = 0x00010000)          */
599     }
600     fprintf(xmlout,    "        </Width>\n");
601     fprintf(xmlout,    "        <Height>\n");
602     if (raw) {
603         fprintf(xmlout,  "          <AsHex>0x%08x</AsHex>\n", track->h);
604     }
605     if (derived) {
606         fprintf(xmlout,  "          <AsDecimal>%12.6f</AsDecimal>\n",
607                 (double)track->h / (double)
608                 0x00010000);    /* Rate to play presentation  (default = 0x00010000)          */
609     }
610     fprintf(xmlout,    "        </Height>\n");
611 #ifdef CURRENTSTRUCT
612     if (notes) {
613         fprintf(xmlout,
614                 "        <!-- Current m2j_to_metadata implementation always shows bits to right of decimal as zeroed. -->\n");
615         fprintf(xmlout,
616                 "        <!-- Also, width and height values shown here will actually be those read from track's <VisualSampleEntry> if given. -->\n");
617     }
618     track->w = track->w >> 16;
619     track->h = track->h >> 16;
620 #endif
621     fprintf(xmlout,    "      </TrackHeader>\n");
622 }
623
624 /* ------------- */
625
626 void xml_write_udta(FILE* file, FILE* xmlout, mj2_tk_t *track,
627                     unsigned int tnum)
628 {
629     /* NO-OP so far.  Optional UserData 'udta' (zero or one in moov or each trak)
630        can contain multiple Copyright 'cprt' with different language codes */
631     /* There may be nested non-standard boxes within udta */
632 #ifdef NOTYET
633     IMAGINE track->udta, track->copyright_count,
634             track->copyright_language[i](array of 16bit ints),
635             track->copyright_notice[i](array of buffers)
636             PROBABLY ALSO NEED track->udta_len or special handler for non - standard boxes
637             char buf[5];
638     int i;
639
640     if (track->udta != 1) {
641         return;    /* Not present */
642     }
643
644     fprintf(xmlout,    "      <UserData BoxType=\"udta\">\n");
645     for (i = 0; i < track->copyright_count; i++) {
646         fprintf(xmlout,  "        <Copyright BoxType=\"cprt\"> Instance=\"%d\">\n",
647                 i + 1);
648         int16_to_3packedchars((short int)track->copyright_languages[i], buf);
649         fprintf(xmlout,  "          <Language>%s</Language>\n", buf);    /* 3 chars */
650         fprintf(xmlout,  "          <Notice>%s</Notice>\n",
651                 track->copyright_notices[i]);
652         fprintf(xmlout,  "        </Copyright>\n", i + 1);
653     }
654     /* TO DO: Non-standard boxes */
655     fprintf(xmlout,    "      </UserData>\n");
656 #endif
657 }
658
659 /* ------------- */
660
661 void xml_write_mdia(FILE* file, FILE* xmlout, mj2_tk_t *track,
662                     unsigned int tnum)
663 {
664     char buf[5];
665     int i, k;
666     buf[4] = '\0';
667
668     fprintf(xmlout,    "      <Media BoxType=\"mdia\">\n");
669     fprintf(xmlout,    "        <MediaHeader BoxType=\"mdhd\">\n");
670     fprintf(xmlout,    "          <CreationTime>\n");
671     if (raw) {
672         fprintf(xmlout,  "            <InSeconds>%u</InSeconds>\n",
673                 track->creation_time);
674     }
675     if (notes) {
676         fprintf(xmlout,
677                 "            <!-- Seconds since start of Jan. 1, 1904 UTC (Greenwich) -->\n");
678     }
679     /*  2082844800 = seconds between 1/1/04 and 1/1/70 */
680     /* There's still a time zone offset problem not solved... but spec is ambiguous as to whether stored time
681        should be local or UTC */
682     if (derived) {
683         fprintf(xmlout,  "            <AsLocalTime>");
684         xml_time_out(xmlout, track->creation_time - 2082844800);
685         fprintf(xmlout, "</AsLocalTime>\n");
686     }
687     fprintf(xmlout,    "          </CreationTime>\n");
688     fprintf(xmlout,    "          <ModificationTime>\n");
689     if (raw) {
690         fprintf(xmlout,  "            <InSeconds>%u</InSeconds>\n",
691                 track->modification_time);
692     }
693     if (derived) {
694         fprintf(xmlout,  "            <AsLocalTime>");
695         xml_time_out(xmlout, track->modification_time - 2082844800);
696         fprintf(xmlout, "</AsLocalTime>\n");
697     }
698     fprintf(xmlout,    "          </ModificationTime>\n");
699     fprintf(xmlout,    "          <Timescale>%d</Timescale>\n", track->timescale);
700     if (notes) {
701         fprintf(xmlout,
702                 "          <!-- Timescale defines time units in one second -->\n");
703     }
704     fprintf(xmlout,    "          <Duration>\n");
705     if (raw) {
706         fprintf(xmlout,  "            <InTimeUnits>%u</InTimeUnits>\n",
707                 track->duration);
708     }
709     if (derived) {
710         fprintf(xmlout,  "            <InSeconds>%12.3f</InSeconds>\n",
711                 (double)track->duration / (double)
712                 track->timescale);    // Make this double later to get fractional seconds
713     }
714     fprintf(xmlout,    "          </Duration>\n");
715     int16_to_3packedchars((short int)track->language, buf);
716     fprintf(xmlout,    "          <Language>%s</Language>\n", buf);    /* 3 chars */
717     fprintf(xmlout,    "        </MediaHeader>\n");
718     fprintf(xmlout,    "        <HandlerReference BoxType=\"hdlr\">\n");
719     switch (track->track_type) {
720     case 0:
721         fprintf(xmlout,
722                 "          <HandlerType Code=\"vide\">video media track</HandlerType>\n");
723         break;
724     case 1:
725         fprintf(xmlout,  "          <HandlerType Code=\"soun\">Sound</HandlerType>\n");
726         break;
727     case 2:
728         fprintf(xmlout,  "          <HandlerType Code=\"hint\">Hint</HandlerType>\n");
729         break;
730     }
731     if (notes) {
732         fprintf(xmlout,
733                 "          <!-- String value shown is not actually read from file. -->\n");
734         fprintf(xmlout,
735                 "          <!-- Shown value is one used for our encode. -->\n");
736     }
737     fprintf(xmlout,    "        </HandlerReference>\n");
738     fprintf(xmlout,    "        <MediaInfoContainer BoxType=\"minf\">\n");
739     switch (track->track_type) {
740     case 0:
741         fprintf(xmlout,  "          <VideoMediaHeader BoxType=\"vmhd\">\n");
742         fprintf(xmlout,  "            <GraphicsMode>0x%02x</GraphicsMode>\n",
743                 track->graphicsmode);
744         if (notes) {
745             fprintf(xmlout, "            <!-- Enumerated values of graphics mode: -->\n");
746             fprintf(xmlout, "            <!--  0x00 = copy (over existing image); -->\n");
747             fprintf(xmlout,
748                     "            <!--  0x24 = transparent; 'blue-screen' this image using opcolor; -->\n");
749             fprintf(xmlout,
750                     "            <!--  0x100 = alpha; alpha-blend this image -->\n");
751             /*    fprintf(xmlout,"            <!--  0x101 = whitealpha; alpha-blend this image, which has been blended with white; -->\n"); This was evidently dropped upon amendment */
752             fprintf(xmlout,
753                     "            <!--  0x102 = pre-multiplied black alpha; image has been already been alpha-blended with black. -->\n");
754             fprintf(xmlout,
755                     "            <!--  0x110 = component alpha; blend alpha channel(s) and color channels individually. -->\n");
756         }
757         fprintf(xmlout,  "            <Opcolor>\n");
758         fprintf(xmlout,  "              <Red>0x%02x</Red>\n", track->opcolor[0]);
759         fprintf(xmlout,  "              <Green>0x%02x</Green>\n", track->opcolor[1]);
760         fprintf(xmlout,  "              <Blue>0x%02x</Blue>\n", track->opcolor[2]);
761         fprintf(xmlout,  "            </Opcolor>\n");
762         fprintf(xmlout,  "          </VideoMediaHeader>\n");
763         break;
764     case 1:
765         fprintf(xmlout,  "          <SoundMediaHeader BoxType=\"smhd\">\n");
766 #ifdef CURRENTSTRUCT
767         track->balance = track->balance << 8;
768 #endif
769         fprintf(xmlout,  "            <Balance>\n");
770         if (notes) {
771             fprintf(xmlout,
772                     "              <!-- Track audio balance fixes mono track in stereo space. -->\n");
773             fprintf(xmlout,
774                     "              <!-- Stored as fixed-point binary 8.8 value. Decimal value is approximation. -->\n");
775             fprintf(xmlout,
776                     "              <!-- 0.0 = center, -1.0 = full left, 1.0 = full right -->\n");
777         }
778         if (raw) {
779             fprintf(xmlout, "              <AsHex>0x%04x</AsHex>\n", track->balance);
780         }
781         if (derived) {
782             fprintf(xmlout, "              <AsDecimal>%6.3f</AsDecimal>\n",
783                     (double)track->balance / (double)0x0100);
784         }
785         fprintf(xmlout,  "            </Balance>\n");
786 #ifdef CURRENTSTRUCT
787         if (notes) {
788             fprintf(xmlout,
789                     "            <!-- Current m2j_to_metadata implementation always shows bits to right of decimal as zeroed. -->\n");
790         }
791         track->balance = track->balance >> 8;
792 #endif
793         fprintf(xmlout,  "          </SoundMediaHeader>\n");
794         break;
795     case 2:
796         fprintf(xmlout,  "          <HintMediaHeader BoxType=\"hmhd\">\n");
797         fprintf(xmlout,  "            <MaxPDU_Size>%d</MaxPDU_Size>\n",
798                 track->maxPDUsize);
799         if (notes) {
800             fprintf(xmlout,
801                     "            <!-- Size in bytes of largest PDU in this hint stream. -->\n");
802         }
803         fprintf(xmlout,  "            <AvgPDU_Size>%d</AvgPDU_Size>\n",
804                 track->avgPDUsize);
805         if (notes) {
806             fprintf(xmlout,
807                     "            <!-- Average size in bytes of a PDU over the entire presentation. -->\n");
808         }
809         fprintf(xmlout,  "            <MaxBitRate>%d</MaxBitRate>\n",
810                 track->maxbitrate);
811         if (notes) {
812             fprintf(xmlout,
813                     "            <!-- Maximum rate in bits per second over any window of 1 second. -->\n");
814         }
815         fprintf(xmlout,  "            <AvgBitRate>%d</AvgBitRate>\n",
816                 track->avgbitrate);
817         if (notes) {
818             fprintf(xmlout,
819                     "            <!-- Averate rate in bits per second over the entire presentation. -->\n");
820         }
821         fprintf(xmlout,  "            <SlidingAvgBit>%d</SlidingAvgBitRate>\n",
822                 track->slidingavgbitrate);
823         if (notes) {
824             fprintf(xmlout,
825                     "            <!-- Maximum rate in bits per second over any window of one minute. -->\n");
826         }
827         fprintf(xmlout,  "          </HintMediaHeader>\n");
828         break;
829     }
830     fprintf(xmlout,    "          <DataInfo BoxType=\"dinf\">\n");
831     fprintf(xmlout,
832             "            <DataReference BoxType=\"dref\"  URL_Count=\"%d\" URN_Count=\"%d\">\n",
833             track->num_url, track->num_urn); // table w. flags, URLs, URNs
834     // Data structure does not distinguish between single URL, single URN, or DREF table or URLs & URNs.
835     // We could infer those, but for now just present everything as a DREF table.
836     if (notes) {
837         fprintf(xmlout,
838                 "              <!-- No entries here mean that file is self-contained, as required by Simple Profile. -->\n");
839     }
840     for (k = 0; k < track->num_url; k++) {
841         fprintf(xmlout,
842                 "            <DataEntryUrlBox BoxType=\"url[space]\">\n"); // table w. flags, URLs, URNs
843         if (notes) {
844             fprintf(xmlout,
845                     "              <!-- Only the first 16 bytes of URL location are recorded in mj2_to_metadata data structure. -->\n");
846         }
847         for (i = 0; i < 4; i++) {
848             uint_to_chars(track->url[track->num_url].location[i], buf);
849             fprintf(xmlout,  "              <Location>%s</Location>\n");
850         }
851         fprintf(xmlout,
852                 "            </DataEntryUrlBox>\n"); // table w. flags, URLs, URNs
853     }
854     for (k = 0; k < track->num_urn; k++) {
855         fprintf(xmlout,
856                 "            <DataEntryUrnBox BoxType=\"urn[space]\">\n"); // table w. flags, URLs, URNs
857         // Only the first 16 bytes are recorded in the data structure currently.
858         if (notes) {
859             fprintf(xmlout,
860                     "              <!-- Only the first 16 bytes each of URN name and optional location are recorded in mj2_to_metadata data structure. -->\n");
861         }
862         fprintf(xmlout,  "              <Name>");
863         for (i = 0; i < 4; i++) {
864             uint_to_chars(track->urn[track->num_urn].name[i], buf);
865             fprintf(xmlout, "%s", buf);
866         }
867         fprintf(xmlout,  "</Name>\n");
868         fprintf(xmlout,  "              <Location>");
869         for (i = 0; i < 4; i++) {
870             uint_to_chars(track->urn[track->num_urn].location[i], buf);
871             fprintf(xmlout, "%s");
872         }
873         fprintf(xmlout,  "</Location>\n");
874         fprintf(xmlout,  "            </DataEntryUrnBox>\n");
875     }
876     fprintf(xmlout,    "            </DataReference>\n");
877     fprintf(xmlout,    "          </DataInfo>\n");
878
879     xml_write_stbl(file, xmlout, track, tnum); /* SampleTable */
880
881     fprintf(xmlout,    "        </MediaInfoContainer>\n");
882     fprintf(xmlout,    "      </Media>\n");
883 }
884
885 /* ------------- */
886
887 void xml_write_stbl(FILE* file, FILE* xmlout, mj2_tk_t *track,
888                     unsigned int tnum)
889 {
890     char buf[5], buf33[33];
891     int i, len;
892     buf[4] = '\0';
893
894     fprintf(xmlout,      "          <SampleTable BoxType=\"stbl\">\n");
895     if (notes) {
896         fprintf(xmlout,
897                 "            <!-- What follows are specific instances of generic SampleDescription BoxType=\"stsd\" -->\n");
898     }
899     switch (track->track_type) {
900     case 0:
901         // There could be multiple instances of this, but "entry_count" is just a local at read-time.
902         // And it's used wrong, too, as count of just visual type, when it's really all 3 types.
903         // This is referred to as "smj2" within mj2.c
904         fprintf(xmlout,    "            <VisualSampleEntry BoxType=\"mjp2\">\n");
905         if (notes) {
906             fprintf(xmlout,
907                     "            <!-- If multiple instances of this box, only first is shown here. -->\n");
908             fprintf(xmlout,
909                     "            <!-- Width and Height are in pixels.  Unlike the Track Header, there is no fractional part. -->\n");
910             fprintf(xmlout,
911                     "            <!-- In mj2_to_metadata implementation, the values are not represented separately from Track Header's values. -->\n");
912         }
913         /* No shifting required.  If CURRENTSTRUCT gets changed, then may need to revisit treatment of these */
914         fprintf(xmlout,    "              <WidthAsInteger>%d</WidthAsInteger>\n",
915                 track->w);
916         fprintf(xmlout,    "              <HeightAsInteger>%d</HeightAsInteger>\n",
917                 track->h);
918 // Horizresolution and vertresolution don't require shifting, already stored right in CURRENTSTRUCT
919         if (notes) {
920             fprintf(xmlout,
921                     "              <!-- Resolutions are in pixels per inch, for the highest-resolution component (typically luminance). -->\n");
922             fprintf(xmlout,
923                     "              <!-- Both stored as fixed-point binary 16.16 values. Decimal values are approximations. -->\n");
924             fprintf(xmlout,
925                     "              <!-- Typical value for both resolutions is 0x00480000  (72.0) -->\n");
926         }
927         fprintf(xmlout,    "              <HorizontalRes>\n");
928         if (raw) {
929             fprintf(xmlout,  "                <AsHex>0x%08x</AsHex>\n",
930                     track->horizresolution);
931         }
932         if (derived) {
933             fprintf(xmlout,  "                <AsDecimal>%12.6f</AsDecimal>\n",
934                     (double)track->horizresolution / (double)
935                     0x00010000);    /* Rate to play presentation  (default = 0x00010000)          */
936         }
937         fprintf(xmlout,    "              </HorizontalRes>\n");
938         fprintf(xmlout,    "              <VerticalRes>\n");
939         if (raw) {
940             fprintf(xmlout,  "                <AsHex>0x%08x</AsHex>\n",
941                     track->vertresolution);
942         }
943         if (derived) {
944             fprintf(xmlout,  "                <AsDecimal>%12.6f</AsDecimal>\n",
945                     (double)track->vertresolution / (double)
946                     0x00010000);    /* Rate to play presentation  (default = 0x00010000)          */
947         }
948         fprintf(xmlout,    "              </VerticalRes>\n");
949
950         buf33[0] = '\0';
951         for (i = 0; i < 8; i++) {
952             uint_to_chars((unsigned int)track->compressorname[i], buf);
953             strcat(buf33,
954                    buf); /* This loads up (4 * 8) + 1 chars, but trailing ones are usually junk */
955         }
956         len = (int)
957               buf33[0]; /* First byte has string length in bytes.  There may be garbage beyond it. */
958         buf33[len + 1] = '\0'; /* Suppress it */
959         fprintf(xmlout,    "              <CompressorName>%s</CompressorName>\n",
960                 buf33 + 1); /* Start beyond first byte */
961         if (notes) {
962             fprintf(xmlout,
963                     "              <!-- Compressor name for debugging.  Standard restricts max length to 31 bytes. -->\n");
964             fprintf(xmlout,
965                     "              <!-- Usually blank or \"Motion JPEG2000\" -->\n");
966         }
967         fprintf(xmlout,    "              <Depth>0x%02x</Depth>\n", track->depth);
968         if (notes) {
969             fprintf(xmlout,  "              <!-- Depth is: -->\n");
970             fprintf(xmlout,
971                     "              <!--   0x20: alpha channels present (color or grayscale) -->\n");
972             fprintf(xmlout,  "              <!--   0x28: grayscale without alpha -->\n");
973             fprintf(xmlout,  "              <!--   0x18: color without alpha -->\n");
974         }
975
976         xml_out_frame_jp2h(xmlout, &(track->jp2_struct));  /* JP2 Header */
977
978         /* Following subboxes are optional */
979         fprintf(xmlout,    "              <FieldCoding BoxType=\"fiel\">\n");
980         fprintf(xmlout,    "                <FieldCount>%d</FieldCount>\n",
981                 (unsigned int)track->fieldcount); /* uchar as 1 byte uint */
982         if (notes) {
983             fprintf(xmlout,  "                <!-- Must be either 1 or 2 -->\n");
984         }
985         fprintf(xmlout,    "                <FieldOrder>%d</FieldOrder>\n",
986                 (unsigned int)track->fieldorder); /* uchar as 1 byte uint */
987         if (notes) {
988             fprintf(xmlout,
989                     "                <!-- When FieldCount=2, FieldOrder means: -->\n");
990             fprintf(xmlout,  "                <!--   0: Field coding unknown -->\n");
991             fprintf(xmlout,
992                     "                <!--   1: Field with topmost line is stored first in sample; fields are in temporal order -->\n");
993             fprintf(xmlout,
994                     "                <!--   6: Field with topmost line is stored second in sample; fields are in temporal order -->\n");
995             fprintf(xmlout,
996                     "                <!-- Defaults: FieldCount=1, FieldOrder=0 if FieldCoding box not present -->\n");
997             fprintf(xmlout,
998                     "                <!-- Current implementation doesn't retain whether box was actually present. -->\n");
999         }
1000         fprintf(xmlout,    "              </FieldCoding>\n");
1001
1002         fprintf(xmlout,
1003                 "              <MJP2_Profile BoxType=\"jp2p\" Count=\"%d\">\n", track->num_br);
1004         for (i = 0; i < track->num_br;
1005                 i++) { /* read routine stored in reverse order, so let's undo damage */
1006             uint_to_chars(track->br[i], buf);
1007             fprintf(xmlout,  "                <CompatibleBrand>%s</CompatibleBrand>\n",
1008                     buf);    /*4 characters, each CLi */
1009         }
1010         fprintf(xmlout,    "              </MJP2_Profile>\n");
1011
1012         fprintf(xmlout,
1013                 "              <MJP2_Prefix BoxType=\"jp2x\" Count=\"%d\">\n", track->num_jp2x);
1014         for (i = 0; i < track->num_jp2x; i++) {
1015             // We'll probably need better formatting than this
1016             fprintf(xmlout,  "                <Data>0x%02x</Data>\n",
1017                     track->jp2xdata[i]);    /* Each entry is single byte */
1018         }
1019         fprintf(xmlout,    "              </MJP2_Prefix>\n");
1020
1021         fprintf(xmlout,
1022                 "              <MJP2_SubSampling BoxType=\"jsub\">\n"); /* These values are all 1 byte */
1023         if (notes) {
1024             fprintf(xmlout,
1025                     "              <!-- Typical subsample value is 2 for 4:2:0 -->\n");
1026         }
1027         fprintf(xmlout,    "                <HorizontalSub>%d</HorizontalSub>\n",
1028                 track->hsub);
1029         fprintf(xmlout,    "                <VerticalSub>%d</VerticalSub>\n",
1030                 track->vsub);
1031         fprintf(xmlout,    "                <HorizontalOffset>%d</HorizontalOffset>\n",
1032                 track->hoff);
1033         fprintf(xmlout,    "                <VerticalOffset>%d</VerticalOffset>\n",
1034                 track->voff);
1035         if (notes) {
1036             fprintf(xmlout,
1037                     "                <!-- Typical {horizontal, vertical} chroma offset values: -->\n");
1038             fprintf(xmlout,
1039                     "                <!-- 4:2:2 format (CCIR601, H.262, MPEG2, MPEG4, recom. Exif): {0, 0} -->\n");
1040             fprintf(xmlout,
1041                     "                <!-- 4:2:2 format (JFIF):                                      {1, 0} -->\n");
1042             fprintf(xmlout,
1043                     "                <!-- 4:2:0 format (H.262, MPEG2, MPEG4):                       {0, 1} -->\n");
1044             fprintf(xmlout,
1045                     "                <!-- 4:2:0 format (MPEG1, H.261, JFIF, recom. Exif):           {1, 1} -->\n");
1046         }
1047         fprintf(xmlout,
1048                 "              </MJP2_SubSampling>\n"); /* These values are all 1 byte */
1049
1050         fprintf(xmlout,
1051                 "              <MJP2_OriginalFormat BoxType=\"orfo\">\n"); /* Part III Appx. 2 */
1052         fprintf(xmlout,
1053                 "                <OriginalFieldCount>%u</OriginalFieldCount>\n",
1054                 (unsigned int)track->or_fieldcount); /* uchar as 1-byte uint */
1055         if (notes) {
1056             fprintf(xmlout,
1057                     "                <!-- In original material before encoding.  Must be either 1 or 2 -->\n");
1058         }
1059         fprintf(xmlout,
1060                 "                <OriginalFieldOrder>%u</OriginalFieldOrder>\n",
1061                 (unsigned int)track->or_fieldorder); /* uchar as 1-byte uint */
1062         if (notes) {
1063             fprintf(xmlout,
1064                     "                <!-- When FieldCount=2, FieldOrder means: -->\n");
1065             fprintf(xmlout,  "                <!--   0: Field coding unknown -->\n");
1066             fprintf(xmlout,
1067                     "                <!--   11: Topmost line came from the earlier field; -->\n");
1068             fprintf(xmlout,
1069                     "                <!--   16:  Topmost line came form the later field. -->\n");
1070             fprintf(xmlout,
1071                     "                <!-- Defaults: FieldCount=1, FieldOrder=0 if FieldCoding box not present -->\n");
1072             fprintf(xmlout,
1073                     "                <!-- Current implementation doesn't retain whether box was actually present. -->\n");
1074         }
1075         fprintf(xmlout,    "              </MJP2_OriginalFormat>\n");
1076         fprintf(xmlout,    "            </VisualSampleEntry>\n");
1077         break;
1078     case 1:
1079     case 2:
1080         if (notes) {
1081             fprintf(xmlout,
1082                     "            <!-- mj2_to_metadata's data structure doesn't record this currently. -->\n");
1083         }
1084         break;
1085     }
1086     fprintf(xmlout,      "            <TimeToSample BoxType=\"stts\">\n");
1087     fprintf(xmlout,      "              <SampleStatistics>\n");
1088     fprintf(xmlout,      "                <TotalSamples>%d</TotalSamples>\n",
1089             track->num_samples);
1090     if (notes) {
1091         fprintf(xmlout,
1092                 "                <!-- For video, gives the total frames in the track, by summing all entries in the Sample Table -->\n");
1093     }
1094     fprintf(xmlout,      "              </SampleStatistics>\n");
1095     fprintf(xmlout,      "              <SampleEntries EntryCount=\"%d\">\n",
1096             track->num_tts);
1097     for (i = 0; i < track->num_tts; i++) {
1098         fprintf(xmlout,
1099                 "                <Table Entry=\"%u\" SampleCount=\"%d\" SampleDelta=\"%u\" />\n",
1100                 i + 1, track->tts[i].sample_count, track->tts[i].sample_delta);
1101     }
1102     fprintf(xmlout,      "              </SampleEntries>\n");
1103     fprintf(xmlout,      "            </TimeToSample>\n");
1104
1105     fprintf(xmlout,
1106             "            <SampleToChunk BoxType=\"stsc\" Count=\"%d\">\n",
1107             track->num_samplestochunk);
1108     for (i = 0; i < track->num_samplestochunk; i++) {
1109         fprintf(xmlout,    "              <FirstChunk>%u</FirstChunk>\n",
1110                 track->sampletochunk[i].first_chunk); /* 4 bytes */
1111         fprintf(xmlout,    "              <SamplesPerChunk>%u</SamplesPerChunk>\n",
1112                 track->sampletochunk[i].samples_per_chunk); /* 4 bytes */
1113         fprintf(xmlout,    "              <SampleDescrIndex>%u</SampleDescrIndex>\n",
1114                 track->sampletochunk[i].sample_descr_idx); /* 4 bytes */
1115     }
1116     fprintf(xmlout,      "            </SampleToChunk>\n");
1117     // After reading this info in, track->num_chunks is calculated and a decompressed table established internally.
1118
1119     fprintf(xmlout,      "            <SampleSize BoxType=\"stsz\">\n");
1120     if (track->same_sample_size) {
1121         // all values in track->sample[i].sample_size are equal.  Grab the first one.
1122         fprintf(xmlout,    "              <Sample_Size>%u</Sample_Size>\n",
1123                 track->sample[0].sample_size);
1124         if (notes) {
1125             fprintf(xmlout,
1126                     "              <!-- Non-zero value means all samples have that size. -->\n");
1127             fprintf(xmlout,
1128                     "              <!-- So <Sample_Count> (aka Entry_Count in std.) has no meaning, is suppressed from this output, and no table follows. -->\n");
1129         }
1130     } else {
1131         fprintf(xmlout,    "              <Sample_Size>0</Sample_Size>\n");
1132         if (notes)
1133             if (sampletables) {
1134                 fprintf(xmlout,
1135                         "              <!-- Zero value means samples have different sizes, given in table next of length Sample_Count (aka Entry_Count in std). -->\n");
1136             } else {
1137                 fprintf(xmlout,
1138                         "              <!-- Zero value means samples have different sizes, given in table (not shown) of length Sample_Count (aka Entry_Count in std). -->\n");
1139             }
1140         fprintf(xmlout,    "              <Sample_Count>%u</Sample_Count>\n",
1141                 track->num_samples);
1142         if (sampletables)
1143             for (i = 0; i < (int)track->num_samples; i++) {
1144                 fprintf(xmlout,  "              <EntrySize Num=\"%u\">%u</EntrySize>\n", i + 1,
1145                         track->sample[i].sample_size);
1146             }
1147     }
1148     fprintf(xmlout,      "            </SampleSize>\n");
1149
1150     fprintf(xmlout,      "            <ChunkOffset BoxType=\"stco\">\n");
1151     // Structure not yet - Variant ChunkLargeOffset 'co64'
1152     fprintf(xmlout,      "              <EntryCount>%u</EntryCount>\n",
1153             track->num_chunks);
1154     if (notes) {
1155         fprintf(xmlout,
1156                 "              <!-- For this implementation, EntryCount shown is one calculated during file read of <SampleToChunk> data. -->\n");
1157         fprintf(xmlout,
1158                 "              <!-- Implementation will report failure during file read of <ChunkOffset> data if read entry-count disagrees. -->\n");
1159     }
1160     if (sampletables)
1161         for (i = 0; i < (int)track->num_chunks; i++) {
1162             fprintf(xmlout,  "              <Chunk_Offset Num=\"%d\">%u</Chunk_Offset>\n",
1163                     i + 1, track->chunk[i].offset);
1164         }
1165     fprintf(xmlout,      "            </ChunkOffset>\n");
1166
1167     fprintf(xmlout,      "          </SampleTable>\n");
1168 }
1169
1170 /* ------------- */
1171
1172 int xml_out_frame(FILE* file, FILE* xmlout, mj2_sample_t *sample,
1173                   unsigned int snum, opj_event_mgr_t *event_mgr)
1174 {
1175     opj_dparameters_t parameters;   /* decompression parameters */
1176     opj_image_t *img;
1177     opj_cp_t *cp;
1178     int i;
1179     int numcomps;
1180     unsigned char* frame_codestream;
1181     opj_dinfo_t* dinfo = NULL;  /* handle to a decompressor */
1182     opj_cio_t *cio = NULL;
1183     opj_j2k_t *j2k;
1184
1185     /* JPEG 2000 compressed image data */
1186
1187     /* get a decoder handle */
1188     dinfo = opj_create_decompress(CODEC_J2K);
1189
1190     /* catch events using our callbacks and give a local context */
1191     opj_set_event_mgr((opj_common_ptr)dinfo, event_mgr, stderr);
1192
1193     /* setup the decoder decoding parameters using the current image and user parameters */
1194     parameters.cp_limit_decoding = DECODE_ALL_BUT_PACKETS;
1195     opj_setup_decoder(dinfo, &parameters);
1196
1197     frame_codestream = (unsigned char*) malloc(sample->sample_size -
1198                        8); /* Skipping JP2C marker */
1199     if (frame_codestream == NULL) {
1200         return 1;
1201     }
1202
1203     fseek(file, sample->offset + 8, SEEK_SET);
1204     fread(frame_codestream, sample->sample_size - 8, 1,
1205           file); /* Assuming that jp and ftyp markers size do */
1206
1207     /* open a byte stream */
1208     cio = opj_cio_open((opj_common_ptr)dinfo, frame_codestream,
1209                        sample->sample_size - 8);
1210
1211     /* Decode J2K to image: */
1212     img = opj_decode(dinfo, cio);
1213     if (!img) {
1214         fprintf(stderr, "ERROR -> j2k_to_image: failed to decode image!\n");
1215         opj_destroy_decompress(dinfo);
1216         opj_cio_close(cio);
1217         return 1;
1218     }
1219
1220     j2k = (opj_j2k_t*)dinfo->j2k_handle;
1221     j2k_default_tcp = j2k->default_tcp;
1222     cp = j2k->cp;
1223
1224     numcomps = img->numcomps;
1225     /*  Alignments:        "      <       To help maintain xml pretty-printing */
1226     fprintf(xmlout,      "      <JP2_Frame Num=\"%d\">\n", snum + 1);
1227     fprintf(xmlout,      "        <MainHeader>\n");
1228     /* There can be multiple codestreams; a particular image is entirely within a single codestream */
1229     /* TO DO:  A frame can be represented by two I-guess-contiguous codestreams if its interleaved. */
1230     fprintf(xmlout,      "          <StartOfCodestream Marker=\"SOC\" />\n");
1231     /* "cp" stands for "coding parameter"; "tcp" is tile coding parameters, "tccp" is tile-component coding parameters */
1232     xml_out_frame_siz(xmlout, img, cp); /* reqd in main */
1233     xml_out_frame_cod(xmlout, j2k_default_tcp); /* reqd in main */
1234     xml_out_frame_coc(xmlout, j2k_default_tcp,
1235                       numcomps); /* opt in main, at most 1 per component */
1236     xml_out_frame_qcd(xmlout, j2k_default_tcp); /* reqd in main */
1237     xml_out_frame_qcc(xmlout, j2k_default_tcp,
1238                       numcomps); /* opt in main, at most 1 per component */
1239     xml_out_frame_rgn(xmlout, j2k_default_tcp,
1240                       numcomps); /* opt, at most 1 per component */
1241     xml_out_frame_poc(xmlout,
1242                       j2k_default_tcp); /*  opt (but reqd in main or tile for any progression order changes) */
1243     /* Next four get j2k_default_tcp passed globally: */
1244 #ifdef SUPPRESS_FOR_NOW
1245     xml_out_frame_ppm(xmlout,
1246                       cp); /* opt (but either PPM or PPT [distributed in tile headers] or codestream packet header reqd) */
1247 #endif
1248     xml_out_frame_tlm(
1249         xmlout); /* NO-OP.  TLM NOT SAVED IN DATA STRUCTURE */ /* opt */
1250     xml_out_frame_plm(
1251         xmlout); /* NO-OP.  PLM NOT SAVED IN DATA STRUCTURE */ /* opt in main; can be used in conjunction with PLT */
1252     xml_out_frame_crg(
1253         xmlout); /* NO-OP.  CRG NOT SAVED IN DATA STRUCTURE */ /* opt in main; */
1254     xml_out_frame_com(xmlout,
1255                       j2k_default_tcp); /* NO-OP.  COM NOT SAVED IN DATA STRUCTURE */ /* opt in main; */
1256
1257     fprintf(xmlout,      "        </MainHeader>\n");
1258
1259     /*    TO DO: all the tile headers (sigh)  */
1260     fprintf(xmlout,      "        <TilePartHeaders Count=\"%d\">\n",
1261             cp->tileno_size);        /* size of the vector tileno */
1262     for (i = 0; i < cp->tileno_size;
1263             i++) { /* I think cp->tileno_size will be same number as (cp->tw * cp->th) or as global j2k_curtileno */
1264         // Standard seems to use zero-based # for tile-part.
1265         fprintf(xmlout,    "          <TilePartHeader Num=\"%d\" ID=\"%d\">\n", i,
1266                 cp->tileno[i]);          /* ID number of the tiles present in the codestream */
1267         fprintf(xmlout,    "            <StartOfTilePart Marker=\"SOT\" />\n");
1268         /* All markers in tile-part headers (between SOT and SOD) are optional, unless structure requires. */
1269         if (i == 0) {
1270             xml_out_frame_cod(xmlout, &(cp->tcps[i])); /* No more than 1 per tile */
1271             xml_out_frame_coc(xmlout, &(cp->tcps[i]),
1272                               numcomps); /* No more than 1 per component */
1273             xml_out_frame_qcd(xmlout, &(cp->tcps[i])); /* No more than 1 per tile */
1274             xml_out_frame_qcc(xmlout, &(cp->tcps[i]),
1275                               numcomps);  /* No more than 1 per component */
1276             xml_out_frame_rgn(xmlout, &(cp->tcps[i]),
1277                               numcomps); /* No more than 1 per component */
1278         }
1279         xml_out_frame_poc(xmlout,
1280                           &(cp->tcps[i])); /* Reqd only if any progression order changes different from main POC */
1281 #ifdef SUPPRESS_FOR_NOW
1282         xml_out_frame_ppt(xmlout,
1283                           &(cp->tcps[i])); /* Either PPT [distributed in tile headers] or PPM or codestream packet header reqd. */
1284 #endif
1285         xml_out_frame_plt(xmlout,
1286                           &(cp->tcps[i])); /* NO-OP.  PLT NOT SAVED IN DATA STRUCTURE */ /* Can be used in conjunction with main's PLM */
1287         xml_out_frame_com(xmlout,
1288                           &(cp->tcps[i])); /* NO-OP.  COM NOT SAVED IN DATA STRUCTURE */
1289         /* opj_tcp_t * cp->tcps; "tile coding parameters" */
1290         /* Maybe not: fprintf(xmlout,  "        <>%d</>, cp->matrice[i];            */ /* Fixed layer    */
1291         fprintf(xmlout,    "            <StartOfData Marker=\"SOD\" />\n");
1292         if (notes) {
1293             fprintf(xmlout,
1294                     "            <!-- Tile-part bitstream, not shown, follows tile-part header and SOD marker. -->\n");
1295         }
1296         fprintf(xmlout,    "          </TilePartHeader>\n");
1297     }
1298     fprintf(xmlout,
1299             "        </TilePartHeaders>\n");     /* size of the vector tileno */
1300
1301 #ifdef NOTYET
1302     IMAGINE the cp object has data to support the following... but we could use an
1303     new different data structure instead
1304     /* I'm unclear if the span of the original fread(frame_codestream...) included the following items if they're trailing. */
1305     /* ALSO TO DO, BUT DATA STRUCTURE DOESN'T HANDLE YET: boxes (anywhere in file except before the Filetype box): */
1306     xml_out_frame_jp2i(xmlout,
1307                        &cp); /* IntellectualProperty 'jp2i' (no restrictions on location) */
1308     xml_out_frame_xml(xmlout,
1309                       &cp); /* XML 'xml\040' (0x786d6c20).  Can appear multiply */
1310     xml_out_frame_uuid(xmlout, &cp); /* UUID 'uuid' (top level only) */
1311     xml_out_frame_uinf(xmlout,
1312                        &cp); /* UUIDInfo 'uinf', includes UUIDList 'ulst' and URL 'url\40' */
1313 #endif
1314
1315     fprintf(xmlout,      "      </JP2_Frame>\n");
1316
1317     /* Extra commentary: */
1318     if (notes) {
1319         fprintf(xmlout,
1320                 "      <!-- Given the number and size of components, mj2_to_frame would try to convert this -->\n");
1321         if (((img->numcomps == 3) && (img->comps[0].dx == img->comps[1].dx / 2)
1322                 && (img->comps[0].dx == img->comps[2].dx / 2) && (img->comps[0].dx == 1))
1323                 || (img->numcomps == 1)) {
1324             fprintf(xmlout,  "      <!-- file to a YUV movie in the normal manner. -->\n");
1325         } else if ((img->numcomps == 3) &&
1326                    (img->comps[0].dx == 1) && (img->comps[1].dx == 1) &&
1327                    (img->comps[2].dx == 1))  {// If YUV 4:4:4 input --> to bmp
1328             fprintf(xmlout,  "      <!-- YUV 4:4:4 file to a series of .bmp files. -->\n");
1329         } else {
1330             fprintf(xmlout,
1331                     "      <!-- file whose image component dimension are unknown, to a series of .j2k files. -->\n");
1332         }
1333     }
1334
1335     opj_destroy_decompress(dinfo);
1336     opj_cio_close(cio);
1337     free(frame_codestream);
1338
1339     return 0;
1340 }
1341
1342 /* ------------- */
1343
1344 void int16_to_3packedchars(short int value, char* buf)
1345 {
1346     /* This is to retrieve the 3-letter ASCII language code */
1347     /* Each char is packed into 5 bits, as difference from 0x60 */
1348     int i;
1349     for (i = 2; i >= 0; i--) {
1350         buf[i] = (value & 0x001f) + 0x60;
1351         value = (value >> 5);
1352     }
1353     buf[3] = '\0';
1354 }
1355
1356 /* ------------- */
1357
1358 void xml_out_frame_siz(FILE* xmlout, opj_image_t *img, opj_cp_t *cp)
1359 {
1360     opj_image_comp_t *comp;
1361     int i;
1362
1363     fprintf(xmlout,    "          <ImageAndFileSize Marker=\"SIZ\">\n");
1364     // This is similar to j2k.c's j2k_dump_image.
1365     // Not of interest: Lsiz, Rsiz
1366     fprintf(xmlout,    "            <Xsiz>%d</Xsiz>\n", img->x1);
1367     fprintf(xmlout,    "            <Ysiz>%d</Ysiz>\n", img->y1);
1368     if (notes) {
1369         fprintf(xmlout,
1370                 "            <!-- Xsiz, Ysiz is the size of the reference grid. -->\n");
1371     }
1372     fprintf(xmlout,    "            <XOsiz>%d</XOsiz>\n", img->x0);
1373     fprintf(xmlout,    "            <YOsiz>%d</YOsiz>\n", img->y0);
1374     if (notes) {
1375         fprintf(xmlout,
1376                 "            <!-- XOsiz, YOsiz are offsets from grid origin to image origin. -->\n");
1377     }
1378     fprintf(xmlout,    "            <XTsiz>%d</XTsiz>\n", cp->tdx);
1379     fprintf(xmlout,    "            <YTsiz>%d</YTsiz>\n", cp->tdy);
1380     if (notes) {
1381         fprintf(xmlout,
1382                 "            <!-- XTsiz, YTsiz is the size of one tile with respect to the grid. -->\n");
1383     }
1384     fprintf(xmlout,    "            <XTOsiz>%d</XTOsiz>\n", cp->tx0);
1385     fprintf(xmlout,    "            <YTOsiz>%d</YTOsiz>\n", cp->ty0);
1386     if (notes) {
1387         fprintf(xmlout,
1388                 "            <!-- XTOsiz, YTOsiz are offsets from grid origin to first tile origin. -->\n");
1389     }
1390     fprintf(xmlout,    "            <Csiz>%d</Csiz>\n", img->numcomps);
1391     if (notes) {
1392         fprintf(xmlout,
1393                 "            <!-- Csiz is the number of components in the image. -->\n");
1394         fprintf(xmlout,  "            <!-- For image components next: -->\n");
1395         fprintf(xmlout,
1396                 "            <!--   XRsiz, YRsiz denote pixel-sample-spacing on the grid, per Part I Annex B. -->\n");
1397         //fprintf(xmlout,"            <!--   XO, YO is offset of the component compared to the whole image. -->\n");
1398         fprintf(xmlout,
1399                 "            <!--   Bits per pixel (bpp) is the pixel depth. -->\n");
1400         fprintf(xmlout,
1401                 "            <!--   WidthOfData and HeightOfData are calculated values, e.g.: w = roundup((Xsiz - XOsiz)/ XRsiz) -->\n");
1402     }
1403
1404     for (i = 0; i < img->numcomps; i++) {/* image-components */
1405         comp = &(img->comps[i]);
1406         fprintf(xmlout,  "            <Component Num=\"%d\">\n", i + 1);
1407         fprintf(xmlout,  "              <Ssiz>\n");
1408         if (raw) {
1409             fprintf(xmlout, "                <AsHex>0x%02x</AsHex>\n",
1410                     (comp->sgnd << 7) & (comp->prec - 1));
1411         }
1412         if (derived) {
1413             fprintf(xmlout, "                <Signed>%d</Signed>\n", comp->sgnd);
1414             fprintf(xmlout, "                <PrecisionInBits>%d</PrecisionInBits>\n",
1415                     comp->prec);
1416         }
1417         fprintf(xmlout,  "              </Ssiz>\n");
1418         fprintf(xmlout,  "              <XRsiz>%d</XRsiz>\n", comp->dx);
1419         fprintf(xmlout,  "              <YRsiz>%d</YRsiz>\n", comp->dy);
1420         fprintf(xmlout,  "              <WidthOfData>%d</WidthOfData>\n", comp->w);
1421         fprintf(xmlout,  "              <HeightOfData>%d</HeightOfData>\n", comp->h);
1422         /* Rest of these aren't calculated when SIZ is read:
1423         fprintf(xmlout,  "              <XO>%d</XO>\n", comp->x0);
1424         fprintf(xmlout,  "              <YO>%d</YO>\n", comp->y0);
1425         if(notes)
1426           fprintf(xmlout,"              <!--  XO, YO is offset of the component compared to the whole image. -->\n");
1427         fprintf(xmlout,  "              <BitsPerPixel>%d</BitsPerPixel>\n", comp->bpp);
1428         fprintf(xmlout,  "              <NumberOfDecodedResolution>%d</NumberOfDecodedResolution>\n", comp->resno_decoded); */
1429         // SUPPRESS: n/a to mj2_to_metadata.  fprintf(xmlout,"        <Factor>%d</Factor\n", comp->factor);
1430         /* factor = number of division by 2 of the out image  compare to the original size of image */
1431         // TO DO comp->data:  int *data;            /* image-component data      */
1432
1433         fprintf(xmlout,  "            </Component>\n");
1434     }
1435     fprintf(xmlout,    "          </ImageAndFileSize>\n");
1436 }
1437
1438 /* ------------- */
1439
1440 void xml_out_frame_cod(FILE* xmlout, opj_tcp_t *tcp)
1441 {
1442     /* Could be called with tcp = &j2k_default_tcp;
1443     /* Or, for tile-part header, with &j2k_cp->tcps[j2k_curtileno]
1444     /*  Alignment for main:"          < < < <   To help maintain xml pretty-printing */
1445     /*  Alignment for tile:"            < < <   To help maintain xml pretty-printing */
1446     opj_tccp_t *tccp;
1447     int i;
1448     char spaces[13] = "            "; /* 12 spaces if tilepart*/
1449     char* s = spaces;
1450     if (tcp == j2k_default_tcp) {
1451         s++;
1452         s++; /* shorten s to 10 spaces if main */
1453     }
1454     tccp = &(tcp->tccps[0]);
1455
1456     fprintf(xmlout,      "%s<CodingStyleDefault Marker=\"COD\">\n",
1457             s); /* Required in main header */
1458     /* Not retained or of interest: Lcod */
1459     fprintf(xmlout,      "%s  <Scod>0x%02x</Scod>\n", s, tcp->csty); /* 1 byte */
1460     if (notes) {
1461         fprintf(xmlout,
1462                 "%s  <!-- For Scod, specific bits mean (where bit 0 is lowest or rightmost): -->\n",
1463                 s);
1464         fprintf(xmlout,    "%s  <!-- bit 0: Defines entropy coder precincts -->\n", s);
1465         fprintf(xmlout,
1466                 "%s  <!--        0 = (PPx=15, PPy=15); 1 = precincts defined below. -->\n", s);
1467         fprintf(xmlout,    "%s  <!-- bit 1: 1 = SOP marker may be used; 0 = not. -->\n",
1468                 s);
1469         fprintf(xmlout,    "%s  <!-- bit 2: 1 = EPH marker may be used; 0 = not. -->\n",
1470                 s);
1471     }
1472     fprintf(xmlout,      "%s  <SGcod>\n", s);
1473     fprintf(xmlout,      "%s    <ProgressionOrder>%d</ProgressionOrder>\n", s,
1474             tcp->prg); /* 1 byte, SGcod (A) */
1475     if (notes) {
1476         fprintf(xmlout,    "%s    <!-- Defined Progression Order Values are: -->\n", s);
1477         fprintf(xmlout,
1478                 "%s    <!-- 0 = LRCP; 1 = RLCP; 2 = RPCL; 3 = PCRL; 4 = CPRL -->\n", s);
1479         fprintf(xmlout,
1480                 "%s    <!-- where L = \"layer\", R = \"resolution level\", C = \"component\", P = \"position\". -->\n",
1481                 s);
1482     }
1483     fprintf(xmlout,      "%s    <NumberOfLayers>%d</NumberOfLayers>\n", s,
1484             tcp->numlayers); /* 2 bytes, SGcod (B) */
1485     fprintf(xmlout,
1486             "%s    <MultipleComponentTransformation>%d</MultipleComponentTransformation>\n",
1487             s, tcp->mct); /* 1 byte, SGcod (C).  More or less boolean */
1488     if (notes) {
1489         fprintf(xmlout,
1490                 "%s    <!-- For MCT, 0 = none, 1 = transform first 3 components for efficiency, per Part I Annex G -->\n",
1491                 s);
1492     }
1493     fprintf(xmlout,      "%s  </SGcod>\n", s);
1494     /* This code will compile only if declaration of j2k_default_tcp is changed from static (to implicit extern) in j2k.c */
1495     fprintf(xmlout,      "%s  <SPcod>\n", s);
1496     /* Internal data structure tccp defines separate defaults for each component, but they all get the same values */
1497     /* So we only have to report the first component's values here. */
1498     /* Compare j2k_read_cox(...) */
1499     fprintf(xmlout,
1500             "%s    <NumberOfDecompositionLevels>%d</NumberOfDecompositionLevels>\n", s,
1501             tccp->numresolutions - 1);   /* 1 byte, SPcox (D) */
1502     fprintf(xmlout,      "%s    <CodeblockWidth>%d</CodeblockWidth>\n", s,
1503             tccp->cblkw - 2);  /* 1 byte, SPcox (E) */
1504     fprintf(xmlout,      "%s    <CodeblockHeight>%d</CodeblockHeight>\n", s,
1505             tccp->cblkh - 2);    /* 1 byte, SPcox (F) */
1506     if (notes) {
1507         fprintf(xmlout,
1508                 "%s    <!-- CBW and CBH are non-negative, and summed cannot exceed 8 -->\n", s);
1509         fprintf(xmlout,    "%s    <!-- Codeblock dimension is 2^(value + 2) -->\n", s);
1510     }
1511     fprintf(xmlout,      "%s    <CodeblockStyle>0x%02x</CodeblockStyle>\n", s,
1512             tccp->cblksty);    /* 1 byte, SPcox (G) */
1513     if (notes) {
1514         fprintf(xmlout,
1515                 "%s    <!-- For CodeblockStyle, bits mean (with value 1=feature on, 0=off): -->\n",
1516                 s);
1517         fprintf(xmlout,
1518                 "%s    <!-- bit 0: Selective arithmetic coding bypass. -->\n", s);
1519         fprintf(xmlout,
1520                 "%s    <!-- bit 1: Reset context probabilities on coding pass boundaries. -->\n",
1521                 s);
1522         fprintf(xmlout,    "%s    <!-- bit 2: Termination on each coding pass. -->\n",
1523                 s);
1524         fprintf(xmlout,    "%s    <!-- bit 3: Vertically causal context. -->\n", s);
1525         fprintf(xmlout,    "%s    <!-- bit 4: Predictable termination. -->\n", s);
1526         fprintf(xmlout,    "%s    <!-- bit 5: Segmentation symbols are used. -->\n", s);
1527     }
1528     fprintf(xmlout,      "%s    <Transformation>%d</Transformation>\n", s,
1529             tccp->qmfbid); /* 1 byte, SPcox (H) */
1530     if (notes) {
1531         fprintf(xmlout,
1532                 "%s    <!-- For Transformation, 0=\"9-7 irreversible filter\", 1=\"5-3 reversible filter\" -->\n",
1533                 s);
1534     }
1535     if (tccp->csty & J2K_CP_CSTY_PRT) {
1536         fprintf(xmlout,    "%s    <PrecinctSize>\n", s); /* 1 byte, SPcox (I_i) */
1537         if (notes) {
1538             fprintf(xmlout,
1539                     "%s    <!-- These are size exponents PPx and PPy. May be zero only for first level (aka N(L)LL subband)-->\n",
1540                     s);
1541         }
1542         for (i = 0; i < tccp->numresolutions; i++) {
1543             fprintf(xmlout,  "%s      <PrecinctHeightAndWidth  ResolutionLevel=\"%d\">\n",
1544                     s, i);
1545             if (raw) {
1546                 fprintf(xmlout, "%s        <AsHex>0x%02x</AsHex>\n", s,
1547                         (tccp->prch[i] << 4) | tccp->prcw[i]);    /* packed into 1 byte, SPcox (G) */
1548             }
1549             if (derived) {
1550                 fprintf(xmlout, "%s        <WidthAsDecimal>%d</WidthAsDecimal>\n", s,
1551                         tccp->prcw[i]);
1552                 fprintf(xmlout, "%s        <HeightAsDecimal>%d</HeightAsDecimal>\n", s,
1553                         tccp->prch[i]);
1554             }
1555             fprintf(xmlout,  "%s      </PrecinctHeightAndWidth>\n", s, i);
1556         }
1557         fprintf(xmlout,    "%s    </PrecinctSize>\n", s); /* 1 byte, SPcox (I_i) */
1558     }
1559     fprintf(xmlout,      "%s  </SPcod>\n", s);
1560     fprintf(xmlout,      "%s</CodingStyleDefault>\n", s);
1561 }
1562
1563 /* ------------- */
1564
1565 void xml_out_frame_coc(FILE* xmlout, opj_tcp_t *tcp,
1566                        int numcomps) /* Optional in main & tile-part headers */
1567 {
1568     /* Uses global j2k_default_tcp */
1569     opj_tccp_t *tccp, *firstcomp_tccp;
1570     int i, compno;
1571     char spaces[13] = "            "; /* 12 spaces if tilepart*/
1572     char* s = spaces;
1573     if (tcp == j2k_default_tcp) {
1574         s++;
1575         s++; /* shorten s to 10 spaces if main */
1576     }
1577
1578     firstcomp_tccp = &(tcp->tccps[0]);
1579     /* Internal data structure tccp defines separate defaults for each component, set from main */
1580     /* default, then selectively overwritten. */
1581     /* Compare j2k_read_cox(...) */
1582     /* We don't really know which was the default, and which were not */
1583     /* Let's pretend that [0] is the default and all others are not */
1584     if (notes) {
1585         fprintf(xmlout,
1586                 "%s<!-- mj2_to_metadata implementation always reports component[0] as using default COD, -->\n",
1587                 s);
1588         if (tcp == j2k_default_tcp) {
1589             fprintf(xmlout,
1590                     "%s<!-- and any other component, with main-header style values different from [0], as COC. -->\n",
1591                     s);
1592         } else {
1593             fprintf(xmlout,
1594                     "%s<!-- and any other component, with tile-part-header style values different from [0], as COC. -->\n",
1595                     s);
1596         }
1597     }
1598     for (compno = 1; compno < numcomps;
1599             compno++) { /* spec says components are zero-based */
1600         tccp = &tcp->tccps[compno];
1601         if (same_component_style(firstcomp_tccp, tccp)) {
1602             continue;
1603         }
1604
1605         /*  Alignments:          "      < < < < <   To help maintain xml pretty-printing */
1606         fprintf(xmlout,      "%s<CodingStyleComponent Marker=\"COC\">\n",
1607                 s); /* Optional in main header, at most 1 per component */
1608         if (notes) {
1609             fprintf(xmlout,
1610                     "%s  <!-- See Ccoc below for zero-based component number. -->\n", s);
1611         }
1612         /* Overrides the main COD for the specific component */
1613         /* Not retained or of interest: Lcod */
1614         fprintf(xmlout,      "%s  <Scoc>0x%02x</Scoc>\n", s, tccp->csty); /* 1 byte */
1615         if (notes) {
1616             fprintf(xmlout,    "%s  <!-- Scoc defines entropy coder precincts: -->\n", s);
1617             fprintf(xmlout,
1618                     "%s  <!--   0 = maximum, namely (PPx=15, PPy=15); 1 = precincts defined below. -->\n",
1619                     s);
1620         }
1621         fprintf(xmlout,      "%s  <Ccoc>%d</Ccoc>\n", s, compno); /* 1 or 2 bytes */
1622         /* Unfortunately compo isn't retained in j2k_read_coc:  compno = cio_read(j2k_img->numcomps <= 256 ? 1 : 2);    /* Ccoc */
1623         /*if(j2k_img_numcomps <=256)
1624           component is 1 byte
1625         else
1626           compno is 2 byte */
1627
1628         /* This code will compile only if declaration of j2k_default_tcp is changed from static (to implicit extern) in j2k.c */
1629         fprintf(xmlout,      "%s  <SPcoc>\n", s);
1630         fprintf(xmlout,
1631                 "%s    <NumberOfDecompositionLevels>%d</NumberOfDecompositionLevels>\n", s,
1632                 tccp->numresolutions - 1); /* 1 byte, SPcox (D) */
1633         fprintf(xmlout,      "%s    <CodeblockWidth>%d</CodeblockWidth>\n", s,
1634                 tccp->cblkw - 2);    /* 1 byte, SPcox (E) */
1635         fprintf(xmlout,      "%s    <CodeblockHeight>%d</CodeblockHeight>\n", s,
1636                 tccp->cblkh - 2);  /* 1 byte, SPcox (F) */
1637         if (notes) {
1638             fprintf(xmlout,
1639                     "%s    <!-- CBW and CBH are non-negative, and summed cannot exceed 8 -->\n", s);
1640             fprintf(xmlout,    "%s    <!-- Codeblock dimension is 2^(value + 2) -->\n", s);
1641         }
1642         fprintf(xmlout,      "%s    <CodeblockStyle>0x%02x</CodeblockStyle>\n", s,
1643                 tccp->cblksty);  /* 1 byte, SPcox (G) */
1644         if (notes) {
1645             fprintf(xmlout,
1646                     "%s    <!-- For CodeblockStyle, bits mean (with value 1=feature on, 0=off): -->\n",
1647                     s);
1648             fprintf(xmlout,
1649                     "%s    <!-- bit 0: Selective arithmetic coding bypass. -->\n", s);
1650             fprintf(xmlout,
1651                     "%s    <!-- bit 1: Reset context probabilities on coding pass boundaries. -->\n",
1652                     s);
1653             fprintf(xmlout,    "%s    <!-- bit 2: Termination on each coding pass. -->\n",
1654                     s);
1655             fprintf(xmlout,    "%s    <!-- bit 3: Vertically causal context. -->\n", s);
1656             fprintf(xmlout,    "%s    <!-- bit 4: Predictable termination. -->\n", s);
1657             fprintf(xmlout,    "%s    <!-- bit 5: Segmentation symbols are used. -->\n", s);
1658         }
1659         fprintf(xmlout,      "%s    <Transformation>%d</Transformation>\n", s,
1660                 tccp->qmfbid);   /* 1 byte, SPcox (H) */
1661         if (notes) {
1662             fprintf(xmlout,
1663                     "%s    <!-- For Transformation, 0=\"9-7 irreversible filter\", 1=\"5-3 reversible filter\" -->\n",
1664                     s);
1665         }
1666         if (tccp->csty & J2K_CP_CSTY_PRT) {
1667             fprintf(xmlout,    "%s    <PrecinctSize>\n", s); /* 1 byte, SPcox (I_i) */
1668             if (notes) {
1669                 fprintf(xmlout,
1670                         "%s      <!-- These are size exponents PPx and PPy. May be zero only for first level (aka N(L)LL subband)-->\n",
1671                         s);
1672             }
1673             for (i = 0; i < tccp->numresolutions - 1;
1674                     i++) { /* subtract 1 to get # of decomposition levels */
1675                 fprintf(xmlout,  "%s      <PrecinctHeightAndWidth  ResolutionLevel=\"%d\">\n",
1676                         s, i);
1677                 if (raw) {
1678                     fprintf(xmlout, "%s        <AsHex>0x%02x</AsHex>\n", s,
1679                             (tccp->prch[i] << 4) | tccp->prcw[i]);    /* packed into 1 byte, SPcox (G) */
1680                 }
1681                 if (derived) {
1682                     fprintf(xmlout, "%s        <WidthAsDecimal>%d</WidthAsDecimal>\n", s,
1683                             tccp->prcw[i]);
1684                     fprintf(xmlout, "%s        <HeightAsDecimal>%d</HeightAsDecimal>\n", s,
1685                             tccp->prch[i]);
1686                 }
1687                 fprintf(xmlout,  "%s      </PrecinctHeightAndWidth>\n", s, i);
1688             }
1689             fprintf(xmlout,    "%s    </PrecinctSize>\n", s); /* 1 byte, SPcox (I_i) */
1690         }
1691         fprintf(xmlout,      "%s  </SPcoc>\n", s);
1692         fprintf(xmlout,      "%s</CodingStyleComponent>\n", s);
1693     }
1694 }
1695
1696 /* ------------- */
1697
1698 BOOL same_component_style(opj_tccp_t *tccp1, opj_tccp_t *tccp2)
1699 {
1700     int i;
1701
1702     if (tccp1->numresolutions != tccp2->numresolutions) {
1703         return FALSE;
1704     }
1705     if (tccp1->cblkw != tccp2->cblkw) {
1706         return FALSE;
1707     }
1708     if (tccp1->cblkh != tccp2->cblkh) {
1709         return FALSE;
1710     }
1711     if (tccp1->cblksty != tccp2->cblksty) {
1712         return FALSE;
1713     }
1714     if (tccp1->csty != tccp2->csty) {
1715         return FALSE;
1716     }
1717
1718     if (tccp1->csty & J2K_CP_CSTY_PRT) {
1719         for (i = 0; i < tccp1->numresolutions; i++) {
1720             if (tccp1->prcw[i] != tccp2->prcw[i] || tccp1->prch[i] != tccp2->prch[i]) {
1721                 return FALSE;
1722             }
1723         }
1724     }
1725     return TRUE;
1726 }
1727
1728 /* ------------- */
1729
1730 void xml_out_frame_qcd(FILE* xmlout, opj_tcp_t *tcp)
1731 {
1732     /* This code will compile only if declaration of j2k_default_tcp is changed from static (to implicit extern) in j2k.c */
1733     opj_tccp_t *tccp;
1734     int bandno, numbands;
1735     char spaces[13] = "            "; /* 12 spaces if tilepart*/
1736     char* s = spaces;
1737     if (tcp == j2k_default_tcp) {
1738         s++;
1739         s++; /* shorten s to 10 spaces if main */
1740     }
1741
1742     /* Compare j2k_read_qcx */
1743     fprintf(xmlout,      "%s<QuantizationDefault Marker=\"QCD\">\n",
1744             s); /* Required in main header, single occurrence */
1745     tccp = &(tcp->tccps[0]);
1746     /* Not retained or of interest: Lqcd */
1747     fprintf(xmlout,      "%s  <Sqcd>\n", s);      /* 1 byte */
1748     if (notes) {
1749         fprintf(xmlout,
1750                 "%s  <!-- Default quantization style for all components. -->\n", s);
1751     }
1752     if (raw) {
1753         fprintf(xmlout,    "%s    <AsHex>0x%02x</AsHex>\n", s,
1754                 (tccp->numgbits) << 5 | tccp->qntsty);
1755     }
1756     if (derived) {
1757         fprintf(xmlout,    "%s    <QuantizationStyle>%d</QuantizationStyle>\n", s,
1758                 tccp->qntsty);
1759     }
1760     if (notes) {
1761         fprintf(xmlout,
1762                 "%s    <!-- Quantization style (in Sqcd's low 5 bits) may be: -->\n", s);
1763         fprintf(xmlout,    "%s    <!--   0 = No quantization. SPqcd size = 8 bits-->\n",
1764                 s);
1765         fprintf(xmlout,
1766                 "%s    <!--   1 = Scalar derived (values signaled for N(L)LL subband only). Use Eq. E.5. SPqcd size = 16. -->\n",
1767                 s);
1768         fprintf(xmlout,
1769                 "%s    <!--   2 = Scalar expounded (values signaled for each subband). SPqcd size = 16. -->\n",
1770                 s);
1771     }
1772     if (derived) {
1773         fprintf(xmlout,    "%s    <NumberOfGuardBits>%d</NumberOfGuardBits>\n", s,
1774                 tccp->numgbits);
1775     }
1776     if (notes) {
1777         fprintf(xmlout,
1778                 "%s    <!-- 0-7 guard bits allowed (stored in Sqcd's high 3 bits) -->\n", s);
1779     }
1780     fprintf(xmlout,      "%s  </Sqcd>\n", s);
1781
1782     /* Problem: numbands in some cases is calculated from len, which is not retained or available here at this time */
1783     /* So we'll just dump all internal values */
1784     /* We could calculate it, but I'm having trouble believing the length equations in the standard */
1785
1786     fprintf(xmlout,      "%s  <SPqcd>\n", s);
1787     switch (tccp->qntsty) {
1788     case J2K_CCP_QNTSTY_NOQNT: /* no quantization */
1789         /* This is what standard says, but I don't believe it: len = 4 + (3*decomp); */
1790         numbands = J2K_MAXBANDS; /* should be: numbands = len - 1; */
1791         /* Better: IMAGINE numbands = tccp->stepsize_numbands; */
1792         /* Instead look for first zero exponent, quit there.  Adequate? */
1793         fprintf(xmlout,    "%s    <ReversibleStepSizeValue>\n", s);
1794         if (notes) {
1795             fprintf(xmlout,
1796                     "%s    <!-- Current mj2_to_metadata implementation dumps entire internal table, -->\n",
1797                     s);
1798             fprintf(xmlout,
1799                     "%s    <!-- until an exponent with zero value is reached. -->\n", s);
1800             fprintf(xmlout,
1801                     "%s    <!-- Exponent epsilon(b) of reversible dynamic range. -->\n", s);
1802             fprintf(xmlout,
1803                     "%s    <!-- Hex value is as stored, in high-order 5 bits. -->\n", s);
1804         }
1805         for (bandno = 0; bandno < numbands; bandno++) {
1806             if (tccp->stepsizes[bandno].expn == 0) {
1807                 break;    /* Remove when we have real numbands */
1808             }
1809             fprintf(xmlout,  "%s      <DynamicRangeExponent Subband=\"%d\">\n", s, bandno);
1810             if (raw) {
1811                 fprintf(xmlout, "%s        <AsHex>0x%02x</AsHex>\n", s,
1812                         tccp->stepsizes[bandno].expn << 3);
1813             }
1814             if (derived) {
1815                 fprintf(xmlout, "%s        <AsDecimal>%d</AsDecimal>\n", s,
1816                         tccp->stepsizes[bandno].expn);
1817             }
1818             fprintf(xmlout,  "%s      </DynamicRangeExponent>\n", s);
1819         }
1820         fprintf(xmlout,    "%s    </ReversibleStepSizeValue>\n", s);
1821         break;
1822     case J2K_CCP_QNTSTY_SIQNT:  /* scalar quantization derived */
1823         /* This is what standard says.  Should I believe it:: len = 5;
1824         /* numbands = 1; */
1825         fprintf(xmlout,    "%s    <QuantizationStepSizeValues>\n", s);
1826         if (notes) {
1827             fprintf(xmlout,
1828                     "%s    <!-- For irreversible transformation only.  See Part I Annex E Equation E.3 -->\n",
1829                     s);
1830         }
1831         fprintf(xmlout,    "%s      <QuantizationValues Subband=\"0\">\n", s);
1832         if (notes) {
1833             fprintf(xmlout,  "%s      <!-- For N(L)LL subband: >\n", s);
1834         }
1835         if (raw) {
1836             fprintf(xmlout,  "%s        <AsHex>0x%02x</AsHex>\n", s,
1837                     (tccp->stepsizes[0].expn << 11) | tccp->stepsizes[0].mant);
1838         }
1839         if (derived) {
1840             fprintf(xmlout,  "%s        <Exponent>%d</Exponent>\n", s,
1841                     tccp->stepsizes[0].expn);
1842             fprintf(xmlout,  "%s        <Mantissa>%d</Mantissa>\n", s,
1843                     tccp->stepsizes[0].mant);
1844         }
1845         fprintf(xmlout,    "%s      </QuantizationValues>\n", s);
1846         if (notes) {
1847             fprintf(xmlout,
1848                     "%s      <!-- Exponents for subbands beyond 0 are not from header, but calculated per Eq. E.5 -->\n",
1849                     s);
1850             fprintf(xmlout,
1851                     "%s      <!-- The mantissa for all subbands is the same, given by the value above. -->\n",
1852                     s);
1853             fprintf(xmlout,
1854                     "%s      <!-- Current mj2_to_metadata implementation dumps entire internal table, -->\n",
1855                     s);
1856             fprintf(xmlout,
1857                     "%s      <!-- until a subband with exponent of zero value is reached. -->\n",
1858                     s);
1859         }
1860
1861         for (bandno = 1; bandno < J2K_MAXBANDS; bandno++) {
1862             if (tccp->stepsizes[bandno].expn == 0) {
1863                 break;
1864             }
1865
1866             fprintf(xmlout,
1867                     "%s      <CalculatedExponent Subband=\"%d\">%d</CalculatedExponent>\n", s,
1868                     bandno, tccp->stepsizes[bandno].expn);
1869         }
1870
1871         fprintf(xmlout,    "%s    </QuantizationStepSizeValues>\n", s);
1872         break;
1873
1874     default: /* J2K_CCP_QNTSTY_SEQNT */ /* scalar quantization expounded */
1875         /* This is what standard says, but should I believe it: len = 5 + 6*decomp; */
1876         numbands = J2K_MAXBANDS; /* should be: (len - 1) / 2;*/
1877         /* Better: IMAGINE numbands = tccp->stepsize_numbands; */
1878         fprintf(xmlout,    "%s    <QuantizationStepSizeValues>\n", s);
1879         if (notes) {
1880             fprintf(xmlout,
1881                     "%s    <!-- For irreversible transformation only.  See Part I Annex E Equation E.3 -->\n",
1882                     s);
1883             fprintf(xmlout,
1884                     "%s    <!-- Current mj2_to_metadata implementation dumps entire internal table, -->\n",
1885                     s);
1886             fprintf(xmlout,
1887                     "%s    <!-- until a subband with mantissa and exponent of zero values is reached. -->\n",
1888                     s);
1889         }
1890         for (bandno = 0; bandno < numbands; bandno++) {
1891             if (tccp->stepsizes[bandno].expn == 0 && tccp->stepsizes[bandno].mant == 0) {
1892                 break;    /* Remove when we have real numbands */
1893             }
1894
1895             fprintf(xmlout,  "%s      <QuantizationValues Subband=\"%d\">\n", s, bandno);
1896             if (raw) {
1897                 fprintf(xmlout, "%s        <AsHex>0x%02x</AsHex>\n", s,
1898                         (tccp->stepsizes[bandno].expn << 11) | tccp->stepsizes[bandno].mant);
1899             }
1900             if (derived) {
1901                 fprintf(xmlout, "%s        <Exponent>%d</Exponent>\n", s,
1902                         tccp->stepsizes[bandno].expn);
1903                 fprintf(xmlout, "%s        <Mantissa>%d</Mantissa>\n", s,
1904                         tccp->stepsizes[bandno].mant);
1905             }
1906             fprintf(xmlout,  "%s      </QuantizationValues>\n", s);
1907         }
1908         fprintf(xmlout,    "%s    </QuantizationStepSizeValues>\n", s);
1909         break;
1910     } /* switch */
1911     fprintf(xmlout,      "%s  </SPqcd>\n", s);
1912     fprintf(xmlout,      "%s</QuantizationDefault>\n", s);
1913
1914     /*  Alignments:        "    < < < < <   To help maintain xml pretty-printing */
1915 }
1916
1917 /* ------------- */
1918
1919 void xml_out_frame_qcc(FILE* xmlout, opj_tcp_t *tcp, int numcomps)
1920 {
1921     /* Uses global j2k_default_tcp */
1922     /* This code will compile only if declaration of j2k_default_tcp is changed from static (to implicit extern) in j2k.c */
1923     opj_tccp_t *tccp, *firstcomp_tccp;
1924     int bandno, numbands;
1925     int compno;
1926     char spaces[13] = "            "; /* 12 spaces if tilepart*/
1927     char* s = spaces;
1928     if (tcp == j2k_default_tcp) {
1929         s++;
1930         s++; /* shorten s to 10 spaces if main */
1931     }
1932
1933     firstcomp_tccp = &(tcp->tccps[0]);
1934     /* Internal data structure tccp defines separate defaults for each component, set from main */
1935     /* default, then selectively overwritten. */
1936     /* Compare j2k_read_qcx(...) */
1937     /* We don't really know which was the default, and which were not */
1938     /* Let's pretend that [0] is the default and all others are not */
1939     if (notes) {
1940         fprintf(xmlout,
1941                 "%s<!-- mj2_to_metadata implementation always reports component[0] as using default QCD, -->\n",
1942                 s);
1943         if (tcp == j2k_default_tcp) {
1944             fprintf(xmlout,
1945                     "%s<!-- and any other component, with main-header quantization values different from [0], as QCC. -->\n",
1946                     s);
1947         } else {
1948             fprintf(xmlout,
1949                     "%s<!-- and any other component, with tile-part-header quantization values different from [0], as QCC. -->\n",
1950                     s);
1951         }
1952     }
1953     for (compno = 1; compno < numcomps;
1954             compno++) { /* spec says components are zero-based */
1955         tccp = &(tcp->tccps[compno]);
1956         if (same_component_quantization(firstcomp_tccp, tccp)) {
1957             continue;
1958         }
1959
1960         /* Compare j2k_read_qcx */
1961         fprintf(xmlout,
1962                 "%s<QuantizationComponent Marker=\"QCC\" Component=\"%d\">\n", s,
1963                 compno); /* Required in main header, single occurrence */
1964         tccp = &j2k_default_tcp->tccps[0];
1965         /* Not retained or perhaps of interest: Lqcd   It maybe can be calculated.  */
1966         fprintf(xmlout,      "%s  <Sqcc>\n", s);        /* 1 byte */
1967         if (notes) {
1968             fprintf(xmlout,    "%s  <!-- Quantization style for this component. -->\n", s);
1969         }
1970         if (raw) {
1971             fprintf(xmlout,    "%s    <AsHex>0x%02x</AsHex>\n", s,
1972                     (tccp->numgbits) << 5 | tccp->qntsty);
1973         }
1974         if (derived) {
1975             fprintf(xmlout,    "%s    <QuantizationStyle>%d</QuantizationStyle>\n", s,
1976                     tccp->qntsty);
1977         }
1978         if (notes) {
1979             fprintf(xmlout,
1980                     "%s    <!-- Quantization style (in Sqcc's low 5 bits) may be: -->\n", s);
1981             fprintf(xmlout,    "%s    <!--   0 = No quantization. SPqcc size = 8 bits-->\n",
1982                     s);
1983             fprintf(xmlout,
1984                     "%s    <!--   1 = Scalar derived (values signaled for N(L)LL subband only). Use Eq. E.5. SPqcc size = 16. -->\n",
1985                     s);
1986             fprintf(xmlout,
1987                     "%s    <!--   2 = Scalar expounded (values signaled for each subband). SPqcc size = 16. -->\n",
1988                     s);
1989         }
1990         if (derived) {
1991             fprintf(xmlout,    "%s    <NumberOfGuardBits>%d</NumberOfGuardBits>\n", s,
1992                     tccp->numgbits);
1993         }
1994         if (notes) {
1995             fprintf(xmlout,
1996                     "%s    <!-- 0-7 guard bits allowed (stored in Sqcc's high 3 bits) -->\n", s);
1997         }
1998         fprintf(xmlout,      "%s  </Sqcc>\n", s);
1999
2000         /* Problem: numbands in some cases is calculated from len, which is not retained or available here at this time */
2001         /* So we'll just dump all internal values */
2002         fprintf(xmlout,      "%s  <SPqcc>\n", s);
2003         switch (tccp->qntsty) {
2004         case J2K_CCP_QNTSTY_NOQNT:
2005             numbands = J2K_MAXBANDS; /* should be: numbands = len - 1; */
2006             /* Better: IMAGINE numbands = tccp->stepsize_numbands; */
2007
2008             /* Instead look for first zero exponent, quit there.  Adequate? */
2009             fprintf(xmlout,    "%s    <ReversibleStepSizeValue>\n", s);
2010             if (notes) {
2011                 fprintf(xmlout,
2012                         "%s    <!-- Current mj2_to_metadata implementation dumps entire internal table, -->\n",
2013                         s);
2014                 fprintf(xmlout,
2015                         "%s    <!-- until an exponent with zero value is reached. -->\n", s);
2016                 fprintf(xmlout,
2017                         "%s    <!-- Exponent epsilon(b) of reversible dynamic range. -->\n", s);
2018                 fprintf(xmlout,
2019                         "%s    <!-- Hex value is as stored, in high-order 5 bits. -->\n", s);
2020             }
2021             for (bandno = 0; bandno < numbands; bandno++) {
2022                 if (tccp->stepsizes[bandno].expn == 0) {
2023                     break;    /* Remove this once we have real numbands */
2024                 }
2025                 fprintf(xmlout,  "%s      <Exponent Subband=\"%d\">\n", s, bandno);
2026                 if (raw) {
2027                     fprintf(xmlout, "%s        <AsHex>0x%02x</AsHex>\n", s,
2028                             tccp->stepsizes[bandno].expn << 3);
2029                 }
2030                 if (derived) {
2031                     fprintf(xmlout, "%s        <AsDecimal>%d</AsDecimal>\n", s,
2032                             tccp->stepsizes[bandno].expn);
2033                 }
2034                 fprintf(xmlout,  "%s      </Exponent>\n", s);
2035             }
2036             fprintf(xmlout,    "%s    </ReversibleStepSizeValue>\n", s);
2037             break;
2038         case J2K_CCP_QNTSTY_SIQNT:
2039             /* numbands = 1; */
2040             fprintf(xmlout,    "%s    <QuantizationStepSizeValues>\n", s);
2041             if (notes) {
2042                 fprintf(xmlout,
2043                         "%s    <!-- For irreversible transformation only.  See Part I Annex E Equation E.3 -->\n",
2044                         s);
2045             }
2046             fprintf(xmlout,    "%s      <QuantizationValuesForSubband0>\n", s);
2047             if (notes) {
2048                 fprintf(xmlout,  "%s      <!-- For N(L)LL subband: >\n", s);
2049             }
2050             if (raw) {
2051                 fprintf(xmlout,  "%s        <AsHex>0x%02x</AsHex>\n", s,
2052                         (tccp->stepsizes[0].expn << 11) | tccp->stepsizes[0].mant);
2053             }
2054             if (derived) {
2055                 fprintf(xmlout,  "%s        <Exponent>%d</Exponent>\n", s,
2056                         tccp->stepsizes[0].expn);
2057                 fprintf(xmlout,  "%s        <Mantissa>%d</Mantissa>\n", s,
2058                         tccp->stepsizes[0].mant);
2059             }
2060             fprintf(xmlout,    "%s      </QuantizationValuesForSubband0>\n", s);
2061             if (notes) {
2062                 fprintf(xmlout,
2063                         "%s      <!-- Exponents for subbands beyond 0 are not from header, but calculated per Eq. E.5 -->\n",
2064                         s);
2065                 fprintf(xmlout,
2066                         "%s      <!-- The mantissa for all subbands is the same, given by the value above. -->\n",
2067                         s);
2068                 fprintf(xmlout,
2069                         "%s      <!-- Current mj2_to_metadata implementation dumps entire internal table, -->\n",
2070                         s);
2071                 fprintf(xmlout,
2072                         "%s      <!-- until a subband with exponent of zero value is reached. -->\n",
2073                         s);
2074             }
2075
2076             for (bandno = 1; bandno < J2K_MAXBANDS; bandno++) {
2077                 if (tccp->stepsizes[bandno].expn == 0) {
2078                     break;
2079                 }
2080
2081                 fprintf(xmlout,
2082                         "%s      <CalculatedExponent Subband=\"%d\">%d</CalculatedExponent>\n", s,
2083                         bandno, tccp->stepsizes[bandno].expn);
2084             }
2085             fprintf(xmlout,    "%s    </QuantizationStepSizeValues>\n", s);
2086             break;
2087
2088         default: /* J2K_CCP_QNTSTY_SEQNT */
2089             numbands = J2K_MAXBANDS; /* should be: (len - 1) / 2;*/
2090             /* Better: IMAGINE numbands = tccp->stepsize_numbands; */
2091             fprintf(xmlout,    "%s    <QuantizationStepSizeValues>\n", s);
2092             if (notes) {
2093                 fprintf(xmlout,
2094                         "%s    <!-- For irreversible transformation only.  See Part I Annex E Equation E.3 -->\n",
2095                         s);
2096                 fprintf(xmlout,
2097                         "%s    <!-- Current mj2_to_metadata implementation dumps entire internal table, -->\n",
2098                         s);
2099                 fprintf(xmlout,
2100                         "%s    <!-- until a subband with mantissa and exponent of zero values is reached. -->\n",
2101                         s);
2102             }
2103             for (bandno = 0; bandno < numbands; bandno++) {
2104                 if (tccp->stepsizes[bandno].expn == 0 && tccp->stepsizes[bandno].mant == 0) {
2105                     break;    /* Remove this once we have real numbands count */
2106                 }
2107                 fprintf(xmlout,  "%s      <QuantizationValues Subband=\"%d\">\n", s, bandno);
2108                 if (raw) {
2109                     fprintf(xmlout, "%s        <AsHex>0x%02x</AsHex>\n", s,
2110                             (tccp->stepsizes[bandno].expn << 11) | tccp->stepsizes[bandno].mant);
2111                 }
2112                 if (derived) {
2113                     fprintf(xmlout, "%s        <Exponent>%d</Exponent>\n", s,
2114                             tccp->stepsizes[bandno].expn);
2115                     fprintf(xmlout, "%s        <Mantissa>%d</Mantissa>\n", s,
2116                             tccp->stepsizes[bandno].mant);
2117                 }
2118                 fprintf(xmlout,  "%s      </QuantizationValues>\n", s);
2119             }
2120             fprintf(xmlout,    "%s    </QuantizationStepSizeValues>\n", s);
2121             break;
2122         } /* switch */
2123         fprintf(xmlout,      "%s  </SPqcc>\n", s);
2124         fprintf(xmlout,      "%s</QuantizationComponent>\n", s);
2125     }
2126     /*  Alignments:          "    < < < < <   To help maintain xml pretty-printing */
2127 }
2128
2129 /* ------------- */
2130
2131 BOOL same_component_quantization(opj_tccp_t *tccp1, opj_tccp_t *tccp2)
2132 {
2133     int bandno, numbands;
2134
2135     if (tccp1->qntsty != tccp2->qntsty) {
2136         return FALSE;
2137     }
2138     if (tccp1->numgbits != tccp2->numgbits) {
2139         return FALSE;
2140     }
2141
2142     switch (tccp1->qntsty) {
2143     case J2K_CCP_QNTSTY_NOQNT:
2144         numbands = J2K_MAXBANDS; /* should be: numbands = len - 1; */
2145         /* Instead look for first zero exponent, quit there.  Adequate? */
2146         for (bandno = 0; bandno < numbands; bandno++) {
2147             if (tccp1->stepsizes[bandno].expn == 0) {
2148                 break;
2149             }
2150             if (tccp1->stepsizes[bandno].expn != tccp2->stepsizes[bandno].expn) {
2151                 return FALSE;
2152             }
2153         }
2154         break;
2155     case J2K_CCP_QNTSTY_SIQNT:
2156         /* numbands = 1; */
2157         if (tccp1->stepsizes[0].expn != tccp2->stepsizes[0].expn ||
2158                 tccp1->stepsizes[0].mant != tccp2->stepsizes[0].mant) {
2159             return FALSE;
2160         }
2161         /* Don't need to check remainder, since they are calculated from [0] */
2162         break;
2163
2164     default: /* J2K_CCP_QNTSTY_SEQNT */
2165         numbands = J2K_MAXBANDS; /* should be: (len - 1) / 2;*/
2166         /* This comparison may cause us problems with trailing junk values. */
2167         for (bandno = 0; bandno < numbands; bandno++) {
2168             if (tccp1->stepsizes[bandno].expn != tccp2->stepsizes[bandno].expn ||
2169                     tccp1->stepsizes[bandno].mant != tccp2->stepsizes[bandno].mant);
2170             return FALSE;
2171         }
2172         break;
2173     } /* switch */
2174     return TRUE;
2175 }
2176
2177 /* ------------- */
2178
2179 void xml_out_frame_rgn(FILE* xmlout, opj_tcp_t *tcp, int numcomps)
2180 {
2181     int compno, SPrgn;
2182     /* MJ2 files can have regions of interest if hybridized with JPX Part II */
2183     char spaces[13] = "            "; /* 12 spaces if tilepart*/
2184     char* s = spaces;
2185     if (tcp == j2k_default_tcp) {
2186         s++;
2187         s++; /* shorten s to 10 spaces if main */
2188     }
2189
2190     for (compno = 0; compno < numcomps; compno++) {
2191         SPrgn = tcp->tccps[compno].roishift;    /* 1 byte; SPrgn */
2192         if (SPrgn == 0) {
2193             continue;    /* Yet another kludge */
2194         }
2195
2196         fprintf(xmlout,    "%s<RegionOfInterest Marker=\"RGN\">\n",
2197                 s); /* Optional in main header, at most 1 per component */
2198         if (notes) {
2199             fprintf(xmlout,  "%s<!-- See Crgn below for zero-based component number. -->\n",
2200                     s);
2201         }
2202         /* Not retained or of interest: Lrgd */
2203         fprintf(xmlout,    "%s  <Srgn>0</Srgn>\n", s); /* 1 byte */
2204         if (notes) {
2205             fprintf(xmlout,
2206                     "%s  <!-- Srgn is ROI style.  Only style=0 defined: Implicit ROI (max. shift) -->\n",
2207                     s);
2208         }
2209         fprintf(xmlout,    "%s  <Crgn>%d</Crgn>\n", s, compno); /* 1 or 2 bytes */
2210         fprintf(xmlout,    "%s  <SPrgn>%d</SPrgn>\n", s, SPrgn); /* 1 byte */
2211         if (notes) {
2212             fprintf(xmlout,
2213                     "%s  <!-- SPrgn is implicit ROI shift, i.e., binary shifting of ROI coefficients above background. -->\n",
2214                     s);
2215         }
2216         fprintf(xmlout,    "</RegionOfInterest\n",
2217                 s); /* Optional in main header, at most 1 per component */
2218     }
2219 }
2220
2221 /* ------------- */
2222
2223 void xml_out_frame_poc(FILE* xmlout,
2224                        opj_tcp_t *tcp)   /* Progression Order Change */
2225 {
2226     /* Compare j2k_read_poc() */
2227     int i;
2228     opj_poc_t *poc;
2229     char spaces[13] = "            "; /* 12 spaces if tilepart*/
2230     char* s = spaces;
2231     if (tcp == j2k_default_tcp) {
2232         s++;
2233         s++; /* shorten s to 10 spaces if main */
2234     }
2235
2236     if (tcp->POC != 1) {
2237         return;    /* Not present */
2238     }
2239
2240     fprintf(xmlout,    "%s<ProgressionOrderChange Marker=\"POC\">\n",
2241             s); /* Optional in main header, at most 1 per component */
2242     /* j2k_read_poc seems to allow accumulation of default pocs from multiple POC segments, but does
2243     the spec really allow that? */
2244     /* 2 bytes, not retained; Lpoc */
2245     /* I probably didn't get this dump precisely right. */
2246     for (i = 0; i < tcp->numpocs; i++) {
2247         poc = &tcp->pocs[i];
2248         fprintf(xmlout,  "%s  <Progression Num=\"%d\">\n", s, i + 1);
2249         fprintf(xmlout,  "%S    <RSpoc>%d</RSpoc>\n", s,
2250                 poc->resno0);  /* 1 byte, RSpoc_i */
2251         if (notes) {
2252             fprintf(xmlout,
2253                     "%s    <!-- Resolution level index (inclusive) for progression start. Range: 0 to 33 -->\n",
2254                     s);
2255         }
2256         fprintf(xmlout,  "%s    <CSpoc>%d</CSpoc>\n", s,
2257                 poc->compno0);/* j2k_img->numcomps <= 256 ? 1 byte : 2 bytes; CSpoc_i */
2258         if (notes) {
2259             fprintf(xmlout,
2260                     "%s    <!-- Component index (inclusive) for progression start. -->\n", s);
2261         }
2262         fprintf(xmlout,  "%s    <LYEpoc>%d</LYEpoc>\n", s,
2263                 poc->layno1); /* int_min(cio_read(2), tcp->numlayers);   /* 2 bytes; LYEpoc_i */
2264         if (notes) {
2265             fprintf(xmlout, "%s    <!-- Layer index (exclusive) for progression end. -->\n",
2266                     s);
2267         }
2268         fprintf(xmlout,  "%s    <REpoc>%d</REpoc>\n", s,
2269                 poc->resno1); /*int_min(cio_read(1), tccp->numresolutions);    /* REpoc_i */
2270         if (notes) {
2271             fprintf(xmlout,
2272                     "%s    <!-- Resolution level index (exclusive) for progression end. Range: RSpoc to 33 -->\n",
2273                     s);
2274         }
2275         fprintf(xmlout,  "%s    <CEpoc>%d</CEpoc>\n", s,
2276                 poc->compno1); /* int_min(cio_read(j2k_img->numcomps <= 256 ? 1 : 2), j2k_img->numcomps);  /* CEpoc_i */
2277         if (notes) {
2278             fprintf(xmlout,
2279                     "%s    <!-- Component index (exclusive) for progression end.  Minimum: CSpoc -->\n",
2280                     s);
2281         }
2282         fprintf(xmlout,  "%s    <Ppoc>%d</Ppoc>\n", s, poc->prg); /* 1 byte Ppoc_i */
2283         if (notes) {
2284             fprintf(xmlout, "%s    <!-- Defined Progression Order Values are: -->\n", s);
2285             fprintf(xmlout,
2286                     "%s    <!-- 0 = LRCP; 1 = RLCP; 2 = RPCL; 3 = PCRL; 4 = CPRL -->\n", s);
2287             fprintf(xmlout,
2288                     "%s    <!-- where L = \"layer\", R = \"resolution level\", C = \"component\", P = \"position\". -->\n",
2289                     s);
2290         }
2291         fprintf(xmlout,  "%s  </Progression>\n", s);
2292     }
2293     fprintf(xmlout,    "%s</ProgressionOrderChange\n", s);
2294 }
2295
2296 /* ------------- */
2297
2298 #ifdef SUPPRESS_FOR_NOW
2299 /* Suppress PPM and PPT since we're not showing data from the third option, namely within the codestream, and
2300 that's evidently what frames_to_mj2 uses.  And a hex dump isn't so useful anyway */
2301
2302 void xml_out_frame_ppm(FILE *xmlout,
2303                        opj_cp_t *cp)   /* For main header, not tile-part (which uses PPT instead). */
2304 {
2305     /* Either the PPM or PPT is required if the packet headers are not distributed in the bit stream */
2306     /* Use of PPM and PPT are mutually exclusive. */
2307     /* Compare j2k_read_ppm() */
2308     int j;
2309
2310     if (cp->ppm != 1) {
2311         return;    /* Not present */
2312     }
2313     /* Main header uses indent of 10 spaces */
2314     fprintf(xmlout,
2315             "          <PackedPacketHeadersMainHeader Marker=\"PPM\">\n"); /* Optional in main header, but if not, must be in PPT or codestream */
2316     /* 2 bytes Lppm not saved */
2317     if (notes) {
2318         fprintf(xmlout,
2319                 "          <!-- If there are multiple PPM marker segments in the main header, -->\n");
2320         fprintf(xmlout,
2321                 "          <!-- this mj2_to_metadata implementation will report them as a single consolidated PPM header. -->\n");
2322         fprintf(xmlout,
2323                 "          <!-- The implementation can't currently segregate by tile-part. -->\n");
2324         fprintf(xmlout,
2325                 "          <!-- TO DO? further map the packet headers to xml. -->\n");
2326     }
2327
2328     /* 1 byte, not retained ; Zppm is sequence # of this PPM header */
2329     /* 4 bytes, possibly overwritten multiple times in j2k_cp->ppm_previous: Nppm */
2330     /* Use j symbol for index instead of i, to make comparable with j2k_read_ppm */
2331     /* Not real clear whether to use ppm->store or ppm_len as upper bound */
2332     fprintf(xmlout,    "            <PackedData>\n");
2333     xml_out_dump_hex(xmlout, cp->ppm_data, cp->ppm_len);
2334     /* Dump packet headers 1 byte at a time: lppm[i][j] */
2335     fprintf(xmlout,    "            </PackedData>\n");
2336     fprintf(xmlout,
2337             "          </PackedPacketHeadersMainHeader>\n"); /* Optional in main header, but if not, must be in PPT or codestream */
2338 }
2339
2340 /* ------------- */
2341
2342 void xml_out_frame_ppt(FILE *xmlout,
2343                        opj_tcp_t *tcp)   /* For tile-part header, not main (which uses PPM instead). */
2344 {
2345     /* Either the PPM or PPT is required if the packet headers are not distributed in the bit stream */
2346     /* Use of PPM and PPT are mutually exclusive. */
2347     /* Compare j2k_read_ppt() */
2348     int j;
2349
2350     if (tcp->ppt != 1) {
2351         return;    /* Not present */
2352     }
2353
2354     /* Tile-part indents are 12 spaces */
2355     fprintf(xmlout,
2356             "            <PackedPacketHeadersTilePartHeader Marker=\"PPT\">\n"); /* Optional in main header, but if not, must be in PPT or codestream */
2357     /* 2 bytes Lppm not saved */
2358     if (notes) {
2359         fprintf(xmlout,
2360                 "            <!-- If there are multiple PPT marker segments in the tile-part header, -->\n");
2361         fprintf(xmlout,
2362                 "            <!-- this mj2_to_metadata implementation will report them as a single consolidated PPT header. -->\n");
2363         fprintf(xmlout,
2364                 "            <!-- The implementation can't currently segregate by tile-part. -->\n");
2365         fprintf(xmlout,
2366                 "            <!-- TO DO? further map the packet headers to xml. -->\n");
2367     }
2368
2369     /* 1 byte, not retained ; Zppt is sequence # of this PPT header */
2370     /* 4 bytes, possibly overwritten multiple times in j2k_cp->ppt_previous: Nppt */
2371     /* Use j symbol for index instead of i, to make comparable with j2k_read_ppt */
2372     /* Not real clear whether to use ppt->store or ppt_len as upper bound */
2373     fprintf(xmlout,    "              <PackedData>\n");
2374     xml_out_dump_hex(xmlout, tcp->ppt_data, tcp->ppt_len);
2375     /* Dump packet headers 1 byte at a time: lppt[i][j] */
2376     fprintf(xmlout,    "              </PackedData>\n");
2377     fprintf(xmlout,
2378             "            </PackedPacketHeadersTileHeader>\n"); /* Optional in tile-part header, but if not, must be in PPM or codestream */
2379 }
2380 #endif SUPPRESS_FOR_NOW
2381
2382 /* ------------- */
2383
2384 void xml_out_frame_tlm(FILE*
2385                        xmlout)   /* opt, main header only.  May be multiple. */
2386 {
2387     /* Compare j2k_read_tlm()... which doesn't retain anything! */
2388     /* Plan:  Since this is only called from main header, not tilepart, use global j2k_default_tcp rather than parameter */
2389     /* Main header indents are 10 spaces */
2390 }
2391
2392 /* ------------- */
2393
2394 void xml_out_frame_plm(FILE*
2395                        xmlout)   /* opt, main header only; can be used in conjunction with tile-part's PLT */
2396 {
2397     /* NO-OP.  PLM NOT SAVED IN DATA STRUCTURE */
2398     /* Compare j2k_read_plm()... which doesn't retain anything! */
2399     /* Plan:  Since this is only called from main header, not tilepart, use global j2k_default_tcp rather than parameter */
2400     /* Main header indents are 10 spaces */
2401 }
2402
2403 /* ------------- */
2404
2405 void xml_out_frame_plt(FILE* xmlout,
2406                        opj_tcp_t *tcp)   /* opt, tile-part headers only; can be used in conjunction with main header's PLM */
2407 {
2408     /* NO-OP.  PLT NOT SAVED IN DATA STRUCTURE */
2409     /* Compare j2k_read_plt()... which doesn't retain anything! */
2410     /* Tile-part header indents are 12 spaces */
2411 }
2412
2413 /* ------------- */
2414
2415 void xml_out_frame_crg(FILE* xmlout)
2416 {
2417     /* NO-OP.  CRG NOT SAVED IN DATA STRUCTURE */ /* opt, main header only; */
2418     /* Compare j2k_read_crg()... which doesn't retain anything! */
2419     /* Plan:  Since this is only called from main header, not tilepart, use global j2k_default_tcp rather than parameter */
2420 #ifdef NOTYET
2421 THIS PSEUDOCODE IMAGINES THESE EXIST:
2422     j2k_default_tcp->crg, j2k_default_tcp->crg_i, j2k_default_tcp->crg_xcrg*,
2423                     j2k_default_tcp->crg_ycrg*
2424                     (POSSIBLY DON'T NEED crg_i, CAN GET NUMBER OR COMPONENTS FROM ELSEWHERE)
2425                      if (j2k_default_tcp->crg != 1 || j2k_default_tcp->crg_i == 0)
2426                      return; /* Not present */
2427
2428                      /* Main header indents are 10 spaces */
2429                      fprintf(xmlout,
2430                              "          <ComponentRegistration Marker=\"RG\" Count=\"%d\">\n",
2431                              j2k_default_tcp->crg_i);
2432     if (notes) {
2433         fprintf(xmlout,
2434                 "          <!-- Fine tuning of registration of components with respect to each other, -->\n");
2435             fprintf(xmlout,
2436                     "          <!-- not required but potentially helpful for decoder. -->\n");
2437             fprintf(xmlout,
2438                     "          <!-- These supplementary fractional offsets are in units of 1/65536 of the horizontal -->\n");
2439             fprintf(xmlout,
2440                     "          <!-- or vertical separation (e.g., XRsiz[i] or YRsiz[i] for component i). -->\n");
2441         }
2442     /* This isn't the most compact form of table, but is OK when number of components is small, as is likely. */
2443     for (i = 0; i < j2k_default_tcp->crg_i; i++) {
2444     fprintf(xmlout,  "            <Component Num=\"%d\">\n", i + 1);
2445         fprintf(xmlout,  "              <Xcrg>\n");
2446         if (raw) {
2447             fprintf(xmlout, "                <AsNumerator>%d</AsNumerator>\n",
2448                     j2k_default_tcp->crg_xcrg[i]);
2449         }
2450         if (derived) {
2451             /* Calculate n * 100%/65536; 4 digits after decimal point is sufficiently accurate */
2452             fprintf(xmlout, "                <AsPercentage>%.4f</AsPercentage>\n",
2453                     ((double)j2k_default_tcp->crg_xcrg[i]) / 655.36);
2454             /* We could do another calculation that include XRsiz[i]; maybe later. */
2455         }
2456         fprintf(xmlout,  "              </Xcrg>\n");
2457         fprintf(xmlout,  "              <Ycrg>\n");
2458         if (raw) {
2459             fprintf(xmlout, "                <AsNumerator>%d</AsNumerator>\n",
2460                     j2k_default_tcp->crg_ycrg[i]);
2461         }
2462         if (derived) {
2463             fprintf(xmlout, "                <AsPercentage>%f</AsPercentage>\n",
2464                     ((double)j2k_default_tcp->crg_ycrg[i]) / 655.36);
2465         }
2466         fprintf(xmlout,  "              </Ycrg>\n");
2467         fprintf(xmlout,  "            </Component>\n");
2468     }
2469
2470     fprintf(xmlout,    "          </ComponentRegistration>\n");
2471
2472 #endif
2473 }
2474
2475 /* ------------- */
2476
2477 /* Regrettably from a metadata point of view, j2k_read_com() skips over any comments in main header or tile-part-header */
2478 void xml_out_frame_com(FILE* xmlout, opj_tcp_t *tcp)
2479 {
2480     /* NO-OP.  COM NOT SAVED IN DATA STRUCTURE */ /* opt in main or tile-part headers; */
2481     /* Compare j2k_read_com()... which doesn't retain anything! */
2482 #ifdef NOTYET
2483     char spaces[13] = "            "; /* 12 spaces if tilepart*/
2484     char* s = spaces;
2485     if (tcp == &j2k_default_tcp) {
2486         s++;
2487         s++; /* shorten s to 10 spaces if main */
2488     }
2489 THIS PSEUDOCODE IMAGINES THESE EXIST:
2490     tcp->com, tcp->com_len, tcp->com_data array
2491     if (tcp->com != 1) {
2492         return;    /* Not present */
2493     }
2494
2495     fprintf(xmlout,    "%s<Comment Marker=\"COM\">\n",
2496             s); /* Optional in main or tile-part header */
2497     xml_out_dump_hex_and_ascii(tcp->com_data, tcp->com_len, s);
2498     fprintf(xmlout,    "%s</Comment>\n", s);
2499 #endif
2500 }
2501
2502 void xml_out_dump_hex(FILE* xmlout, char *data, int data_len, char* s)
2503 {
2504     /* s is a string of spaces for indent */
2505     int i;
2506
2507     /* This is called when raw is true, or there is no appropriate derived form */
2508     fprintf(xmlout,    "%s<AsHex>\n", s);
2509     fprintf(xmlout,    "%s  ", s); /* Inadequate for pretty printing */
2510     for (i = 0; i < data_len; i++) {  /* Dump packet headers */
2511         fprintf(xmlout,  "%02x", data[i]);
2512     }
2513     fprintf(xmlout,    "%s</AsHex>\n", s);
2514 }
2515
2516 /* Define this as an even number: */
2517 #define BYTES_PER_DUMP_LINE 40
2518 /* Current total width for Hex and ASCII is : 11 spaces lead + (3 * BPDL) + 2 spaces + BPDL */
2519 void xml_out_dump_hex_and_ascii(FILE* xmlout, char *data, int data_len,
2520                                 char* s)
2521 {
2522     /* s is a string of spaces for indent */
2523     int i, j;
2524
2525     if (raw) {
2526         xml_out_dump_hex(xmlout, data, data_len, s);
2527     }
2528
2529     if (derived) {
2530         fprintf(xmlout,  "%s<AsHexAndASCII>\n", s);
2531         for (i = 0; i < data_len;) {
2532             fprintf(xmlout, "%s ", s); /* Additional leading space added in loop */
2533             /* First column: hex */
2534             for (j = 0; j < BYTES_PER_DUMP_LINE; j++) { /* Dump bytes */
2535                 fprintf(xmlout, " %02x", data[i + j]);
2536             }
2537             /* Space between columns... */ fprintf(xmlout,  "  ");
2538             /* Second column: ASCII */
2539             for (j = 0; j < BYTES_PER_DUMP_LINE; j++, i++) {
2540                 if (isprint((int)data[i]) && i < data_len) {
2541                     fprintf(xmlout, "%c", data[i]);
2542                 } else {
2543                     fprintf(xmlout, " ");
2544                 }
2545             }
2546             /* If we also wanted to output UCS-2 Unicode as a third column, then entire document
2547             must use fwprintf.  Forget about it for now.  As it stands, if data is UCS-2 format but still
2548             the ASCII set, then we'll be able to read every other byte as ASCII in column 2.  If
2549             data is UTF-8 format but still ASCII, then we'll be able to read every byte as ASCII
2550             in column 2. */
2551         }
2552         fprintf(xmlout,  "%s</AsHexAndASCII>\n", s);
2553     }
2554 }
2555
2556
2557 /* ------------- */
2558
2559 void xml_out_frame_jp2h(FILE* xmlout, opj_jp2_t *jp2_struct)    /* JP2 Header */
2560 {
2561     /* Compare jp2_read_jp2h(opj_jp2_t * jp2_struct) */
2562     int i;
2563
2564     fprintf(xmlout,      "              <JP2Header BoxType=\"jp2h\">\n");
2565
2566     /* Compare jp2_read_ihdr(jp2_struct)) */
2567     fprintf(xmlout,      "                <ImageHeader BoxType=\"ihdr\">\n");
2568     fprintf(xmlout,      "                  <HEIGHT>%d</HEIGHT>\n",
2569             jp2_struct->h); /* 4 bytes */
2570     fprintf(xmlout,      "                  <WIDTH>%d</WIDTH>\n",
2571             jp2_struct->w); /* 4 bytes */
2572     if (notes) {
2573         fprintf(xmlout,
2574                 "                  <!-- HEIGHT here, if 2 fields per image, is of total deinterlaced height. -->\n");
2575     }
2576     fprintf(xmlout,      "                  <NC>%d</NC>\n",
2577             jp2_struct->numcomps); /* 2 bytes */
2578     if (notes) {
2579         fprintf(xmlout,
2580                 "                  <!-- NC is number of components -->\n");    /* 2 bytes */
2581     }
2582     fprintf(xmlout,      "                  <BPC>\n"); /* 1 byte */
2583     if (jp2_struct->bpc == 255) {
2584         fprintf(xmlout,    "                    <AsHex>0x%02x</AsHex>\n",
2585                 jp2_struct->bpc); /* 1 byte */
2586         if (notes) {
2587             fprintf(xmlout,
2588                     "                    <!-- BPC = 0xff means bits per pixel varies with component; see table below. -->\n");
2589         }
2590     } else { /* Not 0xff */
2591         if (raw) {
2592             fprintf(xmlout,  "                    <AsHex>0x%02x</AsHex>\n",
2593                     jp2_struct->bpc); /* 1 byte */
2594             if (notes) {
2595                 fprintf(xmlout,
2596                         "                    <!-- BPC = 0xff means bits per pixel varies with component; see table below. -->\n");
2597             }
2598         }
2599         if (derived) {
2600             fprintf(xmlout,  "                    <BitsPerPixel>%d</BitsPerPixel>\n",
2601                     jp2_struct->bpc & 0x7f);
2602             fprintf(xmlout,  "                    <Signed>%d</Signed>\n",
2603                     jp2_struct->bpc >> 7);
2604         }
2605     }
2606     fprintf(xmlout,      "                  </BPC>\n");
2607     fprintf(xmlout,      "                  <C>%d</C>\n",
2608             jp2_struct->C); /* 1 byte */
2609     if (notes) {
2610         fprintf(xmlout,
2611                 "                  <!-- C is compression type.  Only \"7\" is allowed to date. -->\n");    /* 2 bytes */
2612     }
2613     fprintf(xmlout,      "                  <UnkC>%d</UnkC>\n",
2614             jp2_struct->UnkC); /* 1 byte */
2615     if (notes) {
2616         fprintf(xmlout,
2617                 "                  <!-- Colourspace Unknown. 1 = unknown, 0 = known (e.g., colourspace spec is accurate) -->\n");    /* 1 byte */
2618     }
2619     fprintf(xmlout,      "                  <IPR>%d</IPR>\n",
2620             jp2_struct->IPR); /* 1 byte */
2621     if (notes) {
2622         fprintf(xmlout,
2623                 "                  <!-- IPR is 1 if frame contains an Intellectual Property box; 0 otherwise. -->\n");    /* 2 bytes */
2624     }
2625     fprintf(xmlout,      "                </ImageHeader>\n");
2626
2627     if (jp2_struct->bpc == 255) {
2628         fprintf(xmlout,    "                <BitsPerComponent BoxType=\"bpcc\">\n");
2629         if (notes) {
2630             fprintf(xmlout,
2631                     "                <!-- Pixel depth (range 1 to 38) is low 7 bits of hex value + 1 -->\n");
2632         }
2633         /* Bits per pixel varies with components */
2634         /* Compare jp2_read_bpcc(jp2_struct) */
2635         for (i = 0; i < (int)jp2_struct->numcomps; i++) {
2636             if (raw) {
2637                 fprintf(xmlout, "                  <AsHex>0x%02x</AsHex>\n",
2638                         jp2_struct->comps[i].bpcc);    /* 1 byte */
2639             }
2640             if (derived) {
2641                 fprintf(xmlout, "                  <BitsPerPixel>%d</BitsPerPixel>\n",
2642                         (jp2_struct->comps[i].bpcc & 0x7f) + 1);
2643                 fprintf(xmlout, "                  <Signed>%d</Signed>\n",
2644                         jp2_struct->comps[i].bpcc >> 7);
2645             }
2646         }
2647         fprintf(xmlout,    "                </BitsPerComponent>\n");
2648     }
2649
2650     /* Compare jp2_read_colr(jp2_struct) */
2651     fprintf(xmlout,
2652             "                <ColourSpecification BoxType=\"colr\">\n");
2653     fprintf(xmlout,      "                  <METH>%d</METH>\n",
2654             jp2_struct->meth); /* 1 byte */
2655     if (notes) {
2656         fprintf(xmlout,
2657                 "                  <!-- Valid values of specification method so far: -->\n");
2658         fprintf(xmlout,
2659                 "                  <!--   1 = Enumerated colourspace, in EnumCS field -->\n");
2660         fprintf(xmlout,
2661                 "                  <!--   2 = Restricted ICC Profile, in PROFILE field -->\n");
2662     }
2663     fprintf(xmlout,      "                  <PREC>%d</PREC>\n",
2664             jp2_struct->precedence); /* 1 byte */
2665     if (notes) {
2666         fprintf(xmlout,
2667                 "                  <!-- 0 is only valid value of precedence so far. -->\n");
2668     }
2669     fprintf(xmlout,      "                  <APPROX>%d</APPROX>\n",
2670             jp2_struct->approx); /* 1 byte */
2671     if (notes) {
2672         fprintf(xmlout,
2673                 "                  <!-- 0 is only valid value of colourspace approximation so far. -->\n");
2674     }
2675
2676     if (jp2_struct->meth == 1) {
2677         fprintf(xmlout,    "                  <EnumCS>%d</EnumCS>\n",
2678                 jp2_struct->enumcs); /* 4 bytes */
2679         if (notes) {
2680             fprintf(xmlout,
2681                     "                  <!-- Valid values of enumerated MJ2 colourspace so far: -->\n");
2682             fprintf(xmlout,
2683                     "                  <!--   16: sRGB as defined by IEC 61966-2-1. -->\n");
2684             fprintf(xmlout,
2685                     "                  <!--   17: greyscale (related to sRGB). -->\n");
2686             fprintf(xmlout,
2687                     "                  <!--   18: sRGB YCC (from JPEG 2000 Part II). -->\n");
2688             fprintf(xmlout,
2689                     "                  <!-- (Additional JPX values are defined in Part II). -->\n");
2690         }
2691     } else if (notes) {
2692         fprintf(xmlout,
2693                 "                  <!-- PROFILE is not handled by current OpenJPEG implementation. -->\n");
2694     }
2695     /* only 1 byte is read and nothing stored */
2696     fprintf(xmlout,      "                </ColourSpecification>\n");
2697
2698     /* TO DO?  No OpenJPEG support.
2699     Palette 'pclr'
2700     ComponentMapping 'cmap'
2701     ChannelDefinition 'cdef'
2702     Resolution 'res'
2703     */
2704     fprintf(xmlout,      "              </JP2Header>\n");
2705 }
2706 /* ------------- */
2707
2708 #ifdef NOTYET
2709 IMAGE these use cp structure,
2710       extended... but we could use a new data structure instead
2711       void xml_out_frame_jp2i(FILE* xmlout, opj_cp_t *cp)
2712 {
2713     /* IntellectualProperty 'jp2i' (no restrictions on location) */
2714     int i;
2715     IMAGE cp->jp2i, cp->jp2i_count, cp->jp2i_data(array of chars),
2716           cp->cp2i_len(array of ints)
2717     if (cp->jp2i != 1) {
2718         return;    /* Not present */
2719     }
2720
2721     for (i = 0; i < cp->jp2i_count; i++) {
2722         fprintf(xmlout,      "            <IntellectualProperty BoxType=\"jp2i\">\n");
2723         /* I think this can be anything, including binary, so do a dump */
2724         /* Is it better to indent or not indent this content?  Indent is better for reading, but
2725         worse for cut/paste. */
2726         xml_out_dump_hex_and_ascii(xmlout, cp->jp2i_data[i], cp->jp2i_len[i]);
2727         fprintf(xmlout,      "            </IntellectualProperty>\n");
2728     }
2729 }
2730
2731 void xml_out_frame_xml(FILE* xmlout, opj_cp_t *cp)
2732 {
2733     /* XML 'xml\040' (0x786d6c20).  Can appear multiply, before or after jp2c codestreams */
2734     IMAGE cp->xml, cp->xml_count, cp->xml_data(array of chars)
2735     MAYBE WE DON'T NEED cp->xml_len (array of ints) IF WE ASSUME xml_data IS NULL-TERMINATED.
2736     ASSUME ASSUME EACH LINE IS ENDED BY \n.
2737     int i;
2738     if (cp->xml != 1) {
2739         return;    /* Not present */
2740     }
2741
2742     for (i = 0; i < cp->xml_count; i++) {
2743         fprintf(xmlout,      "            <TextFormXML BoxType=\"xml[space]" Instance =
2744                     \"%d\">\n", i + 1);
2745         /* Is it better to indent or not indent this content?  Indent is better for reading, but
2746         worse for cut/paste. Being lazy, didn't indent here. */
2747         fprintf(xmlout,
2748                 cp->xml_data[i]); /* May be multiple lines */ /* Could check if this is well-formed */
2749         fprintf(xmlout,      "            </TextFormXML>\n");
2750     }
2751 }
2752
2753 void xml_out_frame_uuid(FILE* xmlout, opj_cp_t *cp)
2754 {
2755     /* UUID 'uuid' (top level only) */
2756     /* Part I 1.7.2 says: may appear multiply in JP2 file, anywhere except before File Type box */
2757     /* Part III 5.2.1 says: Private extensions shall be achieved through the 'uuid' type. */
2758     /* A UUID is a 16-byte value.  There is a conventional string representation for it:
2759        "0x12345678-9ABC-DEF0-1234-567890ABCDEF".  Let's assume that is what is stored in uuid_value */
2760
2761     /* Part III 6.1 Any other MJ2 box type could be alternatively written as a 'uuid' box, with value given
2762        as : 0xXXXXXXXX-0011-0010-8000-00AA00389B71, where the Xs are the boxtype in hex.  However,
2763        such a file is "not compliant; systems may choose to read [such] objects ... as equivalent to the box of
2764        the same type, or not."  Here, we choose not to. */
2765     int i;
2766     IMAGE cp->uuid, cp->uuid_count,
2767           cp->uuid_value(array of uuids...
2768                          let's say fixed-length strings) cp->uuid_data (array of char buffers), cp->uuid_len (array of ints)
2769                          if (cp->juuid != 1)
2770                          return; /* Not present */
2771
2772     for (i = 0; i < cp->uuid_count; i++) {
2773         fprintf(xmlout,      "            <UniversalUniqueID BoxType=\"uuid\">
2774                 fprintf(xmlout,      "              <UUID>%s</UUDI>\n", cp->uuid_value[i]);
2775                 fprintf(xmlout,      "              <Data>\n");
2776                 /* I think this can be anything, including binary, so do a dump */
2777                 /* Is it better to indent or not indent this content?  Indent is better for reading, but
2778                 worse for cut/paste. */
2779                 xml_out_dump_hex_and_ascii(xmlout, cp->uuid_data[i], cp->uuid_len[i]);
2780                 fprintf(xmlout,      "              </Data>\n");
2781                 fprintf(xmlout,      "            </UniversalUniqueID>\n");
2782     }
2783 }
2784
2785 void xml_out_frame_uinf(FILE* xmlout, opj_cp_t *cp)
2786 {
2787     /* UUIDInfo 'uinf', includes UUIDList 'ulst' and URL 'url\40' */
2788     /* Part I 1.7.3 says: may appear multiply in JP2 file, anywhere at the top level except before File Type box */
2789     /* So there may be multiple ulst's, and each can have multiple UUIDs listed (with a single URL) */
2790     /* This is not quite as vendor-specific as UUIDs, or at least is meant to be generally readable */
2791     /* Assume UUIDs stored in canonical string format */
2792     int i, j;
2793     IMAGE cp->uinf, cp->uinf_count, cp->uinf_ulst_nu(array of ints)
2794     cp->uinf_uuid(2 dimensional array of uuids... let's say fixed-length strings),
2795                   cp->uinf_url(array of char buffers)
2796
2797                   if (cp->uinf != 1)
2798                   return; /* Not present */
2799
2800     for (i = 0; i < cp->uuid_count; i++) {
2801         fprintf(xmlout,      "            <UUIDInfo BoxType=\"uinf\">\n");
2802             fprintf(xmlout,      "              <UUIDList BoxType=\"ulst\" Count=\"%d\">\n",
2803                     cp->cp->uinf_ulst_nu[i]);
2804             for (j = 0; j < cp->uinf_ulst_nu[i];  j++) {
2805                 fprintf(xmlout,    "              <ID Instance=\"%s\">%s</ID>\n",
2806                         cp->uuif_uuid[i][j], j + 1);
2807             }
2808             fprintf(xmlout,      "              </UUIDList>\n");
2809             fprintf(xmlout,      "              <DataEntryURL>\n");
2810             /* Could add VERS and FLAG here */
2811             fprintf(xmlout,      "                <LOC>\n");
2812             fprintf(xmlout,      "                  %s",
2813                     cp->uinf_url[i]); /* Probably single line, so indent works */ /* In theory, could check if this is well-formed, or good live link */
2814             fprintf(xmlout,      "                </LOC>\n");
2815             fprintf(xmlout,      "              </DataEntryURL>\n");
2816             fprintf(xmlout,      "            </UUIDInfo>\n");
2817         }
2818 }
2819
2820 IMAGE these use cp structure,
2821 extended... but we could use a new data structure instead
2822 void xml_out_frame_unknown_type(FILE* xmlout, opj_cp_t *cp)
2823 {
2824     /* Part III 5.2.1 says "Type fields not defined here are reserved.  Private extensions
2825        shall be acieved through the 'uuid' type." [This implies an unknown
2826        type would be an error, but then...] "Boxes not explicitly defined in this standard,
2827      or otherwise unrecognized by a reader, may be ignored."
2828      Also, it says  "the following types are not and will not be used, or used only in
2829      their existing sense, in future versions of this specification, to avoid conflict
2830      with existing content using earlier pre-standard versions of this format:
2831        clip, crgn, matt, kmat, pnot, ctab, load, imap;
2832        track reference types tmcd, chap, sync,scpt, ssrc"
2833      [But good luck figuring out the mapping.]
2834      Part III Amend. 2 4.1 is stronger: "All these specifications [of this family, e.g.,
2835      JP2 Part I, ISO Base format (Part 12) leading to MP4, Quicktime, and possibly including
2836      MJ2] require that readers ignore objects that are unrecognizable to them".
2837      */
2838     int i;
2839     IMAGE cp->unknown_type, cp->unknown_type_count,
2840           cp->unknown_type_boxtype(array of buf[5]s),
2841           cp->unknown_type_data(array of chars), cp->unknown_type_len(array of ints)
2842     if (cp->unknown_type != 1) {
2843         return;    /* Not present */
2844     }
2845
2846     for (i = 0; i < cp->unknown_type_count; i++) {
2847         fprintf(xmlout,      "            <UnknownType BoxType=\"%s\">\n",
2848                 cp->unknown_type_boxtype[i]);
2849         /* Can be anything, including binary, so do a dump */
2850         /* Is it better to indent or not indent this content?  Indent is better for reading, but
2851         worse for cut/paste. */
2852         xml_out_dump_hex_and_ascii(xmlout, cp->unknown_type_data[i],
2853                                    cp->unknown_type_len[i]);
2854         fprintf(xmlout,      "            </UnknownType>\n");
2855     }
2856 }
2857
2858 #endif