decode_exif_metadata.cc (5281B)
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 C++ example decodes a JPEG XL image in one shot (all input bytes 7 // available at once). The example outputs the pixels and color information to a 8 // floating point image and an ICC profile on disk. 9 10 #include <jxl/decode.h> 11 #include <jxl/decode_cxx.h> 12 #include <limits.h> 13 #include <stdint.h> 14 #include <stdio.h> 15 #include <string.h> 16 17 #include <vector> 18 19 bool DecodeJpegXlExif(const uint8_t* jxl, size_t size, 20 std::vector<uint8_t>* exif) { 21 auto dec = JxlDecoderMake(nullptr); 22 23 // We're only interested in the Exif boxes in this example, so don't 24 // subscribe to events related to pixel data. 25 if (JXL_DEC_SUCCESS != JxlDecoderSubscribeEvents( 26 dec.get(), JXL_DEC_BOX | JXL_DEC_BOX_COMPLETE)) { 27 fprintf(stderr, "JxlDecoderSubscribeEvents failed\n"); 28 return false; 29 } 30 bool support_decompression = true; 31 if (JXL_DEC_SUCCESS != JxlDecoderSetDecompressBoxes(dec.get(), JXL_TRUE)) { 32 fprintf(stderr, 33 "NOTE: decompressing brob boxes not supported with the currently " 34 "used jxl library.\n"); 35 support_decompression = false; 36 } 37 38 JxlDecoderSetInput(dec.get(), jxl, size); 39 JxlDecoderCloseInput(dec.get()); 40 41 const constexpr size_t kChunkSize = 65536; 42 size_t output_pos = 0; 43 44 for (;;) { 45 JxlDecoderStatus status = JxlDecoderProcessInput(dec.get()); 46 if (status == JXL_DEC_ERROR) { 47 fprintf(stderr, "Decoder error\n"); 48 return false; 49 } else if (status == JXL_DEC_NEED_MORE_INPUT) { 50 fprintf(stderr, "Error, already provided all input\n"); 51 return false; 52 } else if (status == JXL_DEC_BOX) { 53 if (!exif->empty()) { 54 size_t remaining = JxlDecoderReleaseBoxBuffer(dec.get()); 55 exif->resize(exif->size() - remaining); 56 // No need to wait for JXL_DEC_SUCCESS or decode other boxes. 57 return true; 58 } 59 JxlBoxType type; 60 status = JxlDecoderGetBoxType(dec.get(), type, 61 TO_JXL_BOOL(support_decompression)); 62 if (JXL_DEC_SUCCESS != status) { 63 fprintf(stderr, "Error, failed to get box type\n"); 64 return false; 65 } 66 if (!memcmp(type, "Exif", 4)) { 67 exif->resize(kChunkSize); 68 JxlDecoderSetBoxBuffer(dec.get(), exif->data(), exif->size()); 69 } 70 } else if (status == JXL_DEC_BOX_NEED_MORE_OUTPUT) { 71 size_t remaining = JxlDecoderReleaseBoxBuffer(dec.get()); 72 output_pos += kChunkSize - remaining; 73 exif->resize(exif->size() + kChunkSize); 74 JxlDecoderSetBoxBuffer(dec.get(), exif->data() + output_pos, 75 exif->size() - output_pos); 76 } else if (status == JXL_DEC_BOX_COMPLETE) { 77 if (!exif->empty()) { 78 size_t remaining = JxlDecoderReleaseBoxBuffer(dec.get()); 79 exif->resize(exif->size() - remaining); 80 return true; 81 } 82 return true; 83 } else { 84 fprintf(stderr, "Unknown decoder status\n"); 85 return false; 86 } 87 } 88 } 89 90 bool LoadFile(const char* filename, std::vector<uint8_t>* out) { 91 FILE* file = fopen(filename, "rb"); 92 if (!file) { 93 return false; 94 } 95 96 if (fseek(file, 0, SEEK_END) != 0) { 97 fclose(file); 98 return false; 99 } 100 101 long size = ftell(file); // NOLINT 102 // Avoid invalid file or directory. 103 if (size >= LONG_MAX || size < 0) { 104 fclose(file); 105 return false; 106 } 107 108 if (fseek(file, 0, SEEK_SET) != 0) { 109 fclose(file); 110 return false; 111 } 112 113 out->resize(size); 114 size_t readsize = fread(out->data(), 1, size, file); 115 if (fclose(file) != 0) { 116 return false; 117 } 118 119 return readsize == static_cast<size_t>(size); 120 } 121 122 bool WriteFile(const char* filename, const uint8_t* data, size_t size) { 123 FILE* file = fopen(filename, "wb"); 124 if (!file) { 125 fprintf(stderr, "Could not open %s for writing", filename); 126 return false; 127 } 128 fwrite(data, 1, size, file); 129 if (fclose(file) != 0) { 130 return false; 131 } 132 return true; 133 } 134 135 int main(int argc, char* argv[]) { 136 if (argc != 3) { 137 fprintf(stderr, 138 "Usage: %s <jxl> <exif>\n" 139 "Where:\n" 140 " jxl = input JPEG XL image filename\n" 141 " exif = output exif filename\n" 142 "Output files will be overwritten.\n", 143 argv[0]); 144 return 1; 145 } 146 147 const char* jxl_filename = argv[1]; 148 const char* exif_filename = argv[2]; 149 150 std::vector<uint8_t> jxl; 151 if (!LoadFile(jxl_filename, &jxl)) { 152 fprintf(stderr, "couldn't load %s\n", jxl_filename); 153 return 1; 154 } 155 156 std::vector<uint8_t> exif; 157 if (!DecodeJpegXlExif(jxl.data(), jxl.size(), &exif)) { 158 fprintf(stderr, "Error while decoding the jxl file\n"); 159 return 1; 160 } 161 if (exif.empty()) { 162 printf("No exif data present in this image\n"); 163 } else { 164 // TODO(lode): the exif box data contains the 4-byte TIFF header at the 165 // beginning, check whether this is desired to be part of the output, or 166 // should be removed. 167 if (!WriteFile(exif_filename, exif.data(), exif.size())) { 168 fprintf(stderr, "Error while writing the exif file\n"); 169 return 1; 170 } 171 printf("Successfully wrote %s\n", exif_filename); 172 } 173 return 0; 174 }