1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file.
5 
6 // This example prints information from the main codestream header.
7 
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 
12 #include "jxl/decode.h"
13 
PrintBasicInfo(FILE * file)14 int PrintBasicInfo(FILE* file) {
15   uint8_t* data = NULL;
16   size_t data_size = 0;
17   // In how large chunks to read from the file and try decoding the basic info.
18   const size_t chunk_size = 64;
19 
20   JxlDecoder* dec = JxlDecoderCreate(NULL);
21   if (!dec) {
22     fprintf(stderr, "JxlDecoderCreate failed\n");
23     return 0;
24   }
25 
26   JxlDecoderSetKeepOrientation(dec, 1);
27 
28   if (JXL_DEC_SUCCESS !=
29       JxlDecoderSubscribeEvents(dec,
30                                 JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING)) {
31     fprintf(stderr, "JxlDecoderSubscribeEvents failed\n");
32     JxlDecoderDestroy(dec);
33     return 0;
34   }
35 
36   JxlBasicInfo info;
37   int seen_basic_info = 0;
38 
39   for (;;) {
40     // The first time, this will output JXL_DEC_NEED_MORE_INPUT because no
41     // input is set yet, this is ok since the input is set when handling this
42     // event.
43     JxlDecoderStatus status = JxlDecoderProcessInput(dec);
44 
45     if (status == JXL_DEC_ERROR) {
46       fprintf(stderr, "Decoder error\n");
47       break;
48     } else if (status == JXL_DEC_NEED_MORE_INPUT) {
49       // The first time there is nothing to release and it returns 0, but that
50       // is ok.
51       size_t remaining = JxlDecoderReleaseInput(dec);
52       // move any remaining bytes to the front if necessary
53       if (remaining != 0) {
54         memmove(data, data + data_size - remaining, remaining);
55       }
56       // resize the buffer to append one more chunk of data
57       // TODO(lode): avoid unnecessary reallocations
58       data = (uint8_t*)realloc(data, remaining + chunk_size);
59       // append bytes read from the file behind the remaining bytes
60       size_t read_size = fread(data + remaining, 1, chunk_size, file);
61       if (read_size == 0 && feof(file)) {
62         fprintf(stderr, "Unexpected EOF\n");
63         break;
64       }
65       data_size = remaining + read_size;
66       JxlDecoderSetInput(dec, data, data_size);
67     } else if (status == JXL_DEC_SUCCESS) {
68       // Finished all processing.
69       break;
70     } else if (status == JXL_DEC_BASIC_INFO) {
71       if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec, &info)) {
72         fprintf(stderr, "JxlDecoderGetBasicInfo failed\n");
73         break;
74       }
75 
76       seen_basic_info = 1;
77 
78       printf("xsize: %u\n", info.xsize);
79       printf("ysize:  %u\n", info.ysize);
80       printf("have_container: %d\n", info.have_container);
81       printf("uses_original_profile: %d\n", info.uses_original_profile);
82       printf("bits_per_sample: %d\n", info.bits_per_sample);
83       printf("exponent_bits_per_sample: %d\n", info.exponent_bits_per_sample);
84       printf("intensity_target: %f\n", info.intensity_target);
85       printf("min_nits: %f\n", info.min_nits);
86       printf("relative_to_max_display: %d\n", info.relative_to_max_display);
87       printf("linear_below: %f\n", info.linear_below);
88       printf("have_preview: %d\n", info.have_preview);
89       if (info.have_preview) {
90         printf("preview xsize: %u\n", info.preview.xsize);
91         printf("preview ysize: %u\n", info.preview.ysize);
92       }
93       printf("have_animation: %d\n", info.have_animation);
94       if (info.have_animation) {
95         printf("ticks per second (numerator / denominator): %u / %u\n",
96                info.animation.tps_numerator, info.animation.tps_denominator);
97         printf("num_loops: %u\n", info.animation.num_loops);
98         printf("have_timecodes: %d\n", info.animation.have_timecodes);
99       }
100       printf("orientation: %d\n", info.orientation);
101       printf("num_extra_channels: %d\n", info.num_extra_channels);
102       printf("alpha_bits: %d\n", info.alpha_bits);
103       printf("alpha_exponent_bits: %d\n", info.alpha_exponent_bits);
104       printf("alpha_premultiplied: %d\n", info.alpha_premultiplied);
105 
106       for (uint32_t i = 0; i < info.num_extra_channels; i++) {
107         JxlExtraChannelInfo extra;
108         if (JXL_DEC_SUCCESS != JxlDecoderGetExtraChannelInfo(dec, i, &extra)) {
109           fprintf(stderr, "JxlDecoderGetExtraChannelInfo failed\n");
110           break;
111         }
112         printf("extra channel: %u info:\n", i);
113         printf("  type: %d\n", extra.type);
114         printf("  bits_per_sample: %u\n", extra.bits_per_sample);
115         printf("  exponent_bits_per_sample: %u\n",
116                extra.exponent_bits_per_sample);
117         printf("  dim_shift: %u\n", extra.dim_shift);
118         printf("  name_length: %u\n", extra.name_length);
119         if (extra.name_length) {
120           char* name = malloc(extra.name_length + 1);
121           if (JXL_DEC_SUCCESS != JxlDecoderGetExtraChannelName(
122                                      dec, i, name, extra.name_length + 1)) {
123             fprintf(stderr, "JxlDecoderGetExtraChannelName failed\n");
124             free(name);
125             break;
126           }
127           free(name);
128           printf("  name: %s\n", name);
129         }
130         printf("  alpha_associated: %d\n", extra.alpha_associated);
131         printf("  spot_color: %f %f %f %f\n", extra.spot_color[0],
132                extra.spot_color[1], extra.spot_color[2], extra.spot_color[3]);
133         printf("  cfa_channel: %u\n", extra.cfa_channel);
134       }
135     } else if (status == JXL_DEC_COLOR_ENCODING) {
136       JxlPixelFormat format = {4, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0};
137       JxlColorProfileTarget targets[2] = {JXL_COLOR_PROFILE_TARGET_ORIGINAL,
138                                           JXL_COLOR_PROFILE_TARGET_DATA};
139       for (size_t i = 0; i < 2; i++) {
140         JxlColorProfileTarget target = targets[i];
141         if (info.uses_original_profile) {
142           if (target != JXL_COLOR_PROFILE_TARGET_ORIGINAL) continue;
143           printf("color profile:\n");
144         } else {
145           printf(target == JXL_COLOR_PROFILE_TARGET_ORIGINAL
146                      ? "original color profile:\n"
147                      : "data color profile:\n");
148         }
149 
150         JxlColorEncoding color_encoding;
151         if (JXL_DEC_SUCCESS == JxlDecoderGetColorAsEncodedProfile(
152                                    dec, &format, target, &color_encoding)) {
153           printf("  format: JPEG XL encoded color profile\n");
154           printf("  color_space: %d\n", color_encoding.color_space);
155           printf("  white_point: %d\n", color_encoding.white_point);
156           printf("  white_point XY: %f %f\n", color_encoding.white_point_xy[0],
157                  color_encoding.white_point_xy[1]);
158           if (color_encoding.color_space == JXL_COLOR_SPACE_RGB ||
159               color_encoding.color_space == JXL_COLOR_SPACE_UNKNOWN) {
160             printf("  primaries: %d\n", color_encoding.primaries);
161             printf("  red primaries XY: %f %f\n",
162                    color_encoding.primaries_red_xy[0],
163                    color_encoding.primaries_red_xy[1]);
164             printf("  green primaries XY: %f %f\n",
165                    color_encoding.primaries_green_xy[0],
166                    color_encoding.primaries_green_xy[1]);
167             printf("  blue primaries XY: %f %f\n",
168                    color_encoding.primaries_blue_xy[0],
169                    color_encoding.primaries_blue_xy[1]);
170           }
171           printf("  transfer_function: %d\n", color_encoding.transfer_function);
172           if (color_encoding.transfer_function == JXL_TRANSFER_FUNCTION_GAMMA) {
173             printf("  transfer_function gamma: %f\n", color_encoding.gamma);
174           }
175           printf("  rendering_intent: %d\n", color_encoding.rendering_intent);
176         } else {
177           // The profile is not in JPEG XL encoded form, get as ICC profile
178           // instead.
179           printf("  format: ICC profile\n");
180           size_t profile_size;
181           if (JXL_DEC_SUCCESS != JxlDecoderGetICCProfileSize(
182                                      dec, &format, target, &profile_size)) {
183             fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n");
184             continue;
185           }
186           printf("  ICC profile size: %zu\n", profile_size);
187           if (profile_size < 132) {
188             fprintf(stderr, "ICC profile too small\n");
189             continue;
190           }
191           uint8_t* profile = (uint8_t*)malloc(profile_size);
192           if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile(dec, &format,
193                                                                 target, profile,
194                                                                 profile_size)) {
195             fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n");
196             free(profile);
197             continue;
198           }
199           printf("  CMM type: \"%.4s\"\n", profile + 4);
200           printf("  color space: \"%.4s\"\n", profile + 16);
201           printf("  rendering intent: %d\n", (int)profile[67]);
202           free(profile);
203         }
204       }
205       // This is the last expected event, no need to read the rest of the file.
206     } else {
207       fprintf(stderr, "Unexpected decoder status\n");
208       break;
209     }
210   }
211 
212   JxlDecoderDestroy(dec);
213   free(data);
214 
215   return seen_basic_info;
216 }
217 
main(int argc,char * argv[])218 int main(int argc, char* argv[]) {
219   if (argc != 2) {
220     fprintf(stderr,
221             "Usage: %s <jxl>\n"
222             "Where:\n"
223             "  jxl = input JPEG XL image filename\n",
224             argv[0]);
225     return 1;
226   }
227 
228   const char* jxl_filename = argv[1];
229 
230   FILE* file = fopen(jxl_filename, "rb");
231   if (!file) {
232     fprintf(stderr, "Failed to read file %s\n", jxl_filename);
233     return 1;
234   }
235 
236   if (!PrintBasicInfo(file)) {
237     fprintf(stderr, "Couldn't print basic info\n");
238     return 1;
239   }
240 
241   return 0;
242 }
243