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