decode_oneshot.cc (8483B)
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/codestream_header.h> 11 #include <jxl/decode.h> 12 #include <jxl/decode_cxx.h> 13 #include <jxl/resizable_parallel_runner.h> 14 #include <jxl/resizable_parallel_runner_cxx.h> 15 #include <jxl/types.h> 16 #include <limits.h> 17 #include <stdint.h> 18 #include <stdio.h> 19 #include <string.h> 20 21 #include <vector> 22 23 /** Decodes JPEG XL image to floating point pixels and ICC Profile. Pixel are 24 * stored as floating point, as interleaved RGBA (4 floating point values per 25 * pixel), line per line from top to bottom. Pixel values have nominal range 26 * 0..1 but may go beyond this range for HDR or wide gamut. The ICC profile 27 * describes the color format of the pixel data. 28 */ 29 bool DecodeJpegXlOneShot(const uint8_t* jxl, size_t size, 30 std::vector<float>* pixels, size_t* xsize, 31 size_t* ysize, std::vector<uint8_t>* icc_profile) { 32 // Multi-threaded parallel runner. 33 auto runner = JxlResizableParallelRunnerMake(nullptr); 34 35 auto dec = JxlDecoderMake(nullptr); 36 if (JXL_DEC_SUCCESS != 37 JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BASIC_INFO | 38 JXL_DEC_COLOR_ENCODING | 39 JXL_DEC_FULL_IMAGE)) { 40 fprintf(stderr, "JxlDecoderSubscribeEvents failed\n"); 41 return false; 42 } 43 44 if (JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner(dec.get(), 45 JxlResizableParallelRunner, 46 runner.get())) { 47 fprintf(stderr, "JxlDecoderSetParallelRunner failed\n"); 48 return false; 49 } 50 51 JxlBasicInfo info; 52 JxlPixelFormat format = {4, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0}; 53 54 JxlDecoderSetInput(dec.get(), jxl, size); 55 JxlDecoderCloseInput(dec.get()); 56 57 for (;;) { 58 JxlDecoderStatus status = JxlDecoderProcessInput(dec.get()); 59 60 if (status == JXL_DEC_ERROR) { 61 fprintf(stderr, "Decoder error\n"); 62 return false; 63 } else if (status == JXL_DEC_NEED_MORE_INPUT) { 64 fprintf(stderr, "Error, already provided all input\n"); 65 return false; 66 } else if (status == JXL_DEC_BASIC_INFO) { 67 if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec.get(), &info)) { 68 fprintf(stderr, "JxlDecoderGetBasicInfo failed\n"); 69 return false; 70 } 71 *xsize = info.xsize; 72 *ysize = info.ysize; 73 JxlResizableParallelRunnerSetThreads( 74 runner.get(), 75 JxlResizableParallelRunnerSuggestThreads(info.xsize, info.ysize)); 76 } else if (status == JXL_DEC_COLOR_ENCODING) { 77 // Get the ICC color profile of the pixel data 78 size_t icc_size; 79 if (JXL_DEC_SUCCESS != 80 JxlDecoderGetICCProfileSize(dec.get(), JXL_COLOR_PROFILE_TARGET_DATA, 81 &icc_size)) { 82 fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n"); 83 return false; 84 } 85 icc_profile->resize(icc_size); 86 if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile( 87 dec.get(), JXL_COLOR_PROFILE_TARGET_DATA, 88 icc_profile->data(), icc_profile->size())) { 89 fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n"); 90 return false; 91 } 92 } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) { 93 size_t buffer_size; 94 if (JXL_DEC_SUCCESS != 95 JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size)) { 96 fprintf(stderr, "JxlDecoderImageOutBufferSize failed\n"); 97 return false; 98 } 99 if (buffer_size != *xsize * *ysize * 16) { 100 fprintf(stderr, "Invalid out buffer size %d %d\n", 101 static_cast<int>(buffer_size), 102 static_cast<int>(*xsize * *ysize * 16)); 103 return false; 104 } 105 pixels->resize(*xsize * *ysize * 4); 106 void* pixels_buffer = static_cast<void*>(pixels->data()); 107 size_t pixels_buffer_size = pixels->size() * sizeof(float); 108 if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format, 109 pixels_buffer, 110 pixels_buffer_size)) { 111 fprintf(stderr, "JxlDecoderSetImageOutBuffer failed\n"); 112 return false; 113 } 114 } else if (status == JXL_DEC_FULL_IMAGE) { 115 // Nothing to do. Do not yet return. If the image is an animation, more 116 // full frames may be decoded. This example only keeps the last one. 117 } else if (status == JXL_DEC_SUCCESS) { 118 // All decoding successfully finished. 119 // It's not required to call JxlDecoderReleaseInput(dec.get()) here since 120 // the decoder will be destroyed. 121 return true; 122 } else { 123 fprintf(stderr, "Unknown decoder status\n"); 124 return false; 125 } 126 } 127 } 128 129 /** Writes to .pfm file (Portable FloatMap). Gimp, tev viewer and ImageMagick 130 * support viewing this format. 131 * The input pixels are given as 32-bit floating point with 4-channel RGBA. 132 * The alpha channel will not be written since .pfm does not support it. 133 */ 134 bool WritePFM(const char* filename, const float* pixels, size_t xsize, 135 size_t ysize) { 136 FILE* file = fopen(filename, "wb"); 137 if (!file) { 138 fprintf(stderr, "Could not open %s for writing", filename); 139 return false; 140 } 141 uint32_t endian_test = 1; 142 uint8_t little_endian[4]; 143 memcpy(little_endian, &endian_test, 4); 144 145 fprintf(file, "PF\n%d %d\n%s\n", static_cast<int>(xsize), 146 static_cast<int>(ysize), little_endian[0] ? "-1.0" : "1.0"); 147 for (int y = ysize - 1; y >= 0; y--) { 148 for (size_t x = 0; x < xsize; x++) { 149 for (size_t c = 0; c < 3; c++) { 150 const float* f = &pixels[(y * xsize + x) * 4 + c]; 151 fwrite(f, 4, 1, file); 152 } 153 } 154 } 155 if (fclose(file) != 0) { 156 return false; 157 } 158 return true; 159 } 160 161 bool LoadFile(const char* filename, std::vector<uint8_t>* out) { 162 FILE* file = fopen(filename, "rb"); 163 if (!file) { 164 return false; 165 } 166 167 if (fseek(file, 0, SEEK_END) != 0) { 168 fclose(file); 169 return false; 170 } 171 172 long size = ftell(file); 173 // Avoid invalid file or directory. 174 if (size >= LONG_MAX || size < 0) { 175 fclose(file); 176 return false; 177 } 178 179 if (fseek(file, 0, SEEK_SET) != 0) { 180 fclose(file); 181 return false; 182 } 183 184 out->resize(size); 185 size_t readsize = fread(out->data(), 1, size, file); 186 if (fclose(file) != 0) { 187 return false; 188 } 189 190 return readsize == static_cast<size_t>(size); 191 } 192 193 bool WriteFile(const char* filename, const uint8_t* data, size_t size) { 194 FILE* file = fopen(filename, "wb"); 195 if (!file) { 196 fprintf(stderr, "Could not open %s for writing", filename); 197 return false; 198 } 199 fwrite(data, 1, size, file); 200 if (fclose(file) != 0) { 201 return false; 202 } 203 return true; 204 } 205 206 int main(int argc, char* argv[]) { 207 if (argc != 4) { 208 fprintf(stderr, 209 "Usage: %s <jxl> <pfm> <icc>\n" 210 "Where:\n" 211 " jxl = input JPEG XL image filename\n" 212 " pfm = output Portable FloatMap image filename\n" 213 " icc = output ICC color profile filename\n" 214 "Output files will be overwritten.\n", 215 argv[0]); 216 return 1; 217 } 218 219 const char* jxl_filename = argv[1]; 220 const char* pfm_filename = argv[2]; 221 const char* icc_filename = argv[3]; 222 223 std::vector<uint8_t> jxl; 224 if (!LoadFile(jxl_filename, &jxl)) { 225 fprintf(stderr, "couldn't load %s\n", jxl_filename); 226 return 1; 227 } 228 229 std::vector<float> pixels; 230 std::vector<uint8_t> icc_profile; 231 size_t xsize = 0; 232 size_t ysize = 0; 233 if (!DecodeJpegXlOneShot(jxl.data(), jxl.size(), &pixels, &xsize, &ysize, 234 &icc_profile)) { 235 fprintf(stderr, "Error while decoding the jxl file\n"); 236 return 1; 237 } 238 if (!WritePFM(pfm_filename, pixels.data(), xsize, ysize)) { 239 fprintf(stderr, "Error while writing the PFM image file\n"); 240 return 1; 241 } 242 if (!WriteFile(icc_filename, icc_profile.data(), icc_profile.size())) { 243 fprintf(stderr, "Error while writing the ICC profile file\n"); 244 return 1; 245 } 246 printf("Successfully wrote %s and %s\n", pfm_filename, icc_filename); 247 return 0; 248 }