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