encode_oneshot.cc (8536B)
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 encodes a file containing a floating point image to another 7 // file containing JPEG XL image with a single frame. 8 9 #include <jxl/codestream_header.h> 10 #include <jxl/color_encoding.h> 11 #include <jxl/encode.h> 12 #include <jxl/encode_cxx.h> 13 #include <jxl/thread_parallel_runner.h> 14 #include <jxl/thread_parallel_runner_cxx.h> 15 #include <jxl/types.h> 16 #include <limits.h> 17 #include <string.h> 18 19 #include <cstdint> 20 #include <cstdio> 21 #include <sstream> 22 #include <string> 23 #include <vector> 24 25 /** 26 * Reads from .pfm file (Portable FloatMap) 27 * 28 * @param filename name of the file to read 29 * @param pixels vector to fill with loaded pixels as 32-bit floating point with 30 * 3-channel RGB 31 * @param xsize set to width of loaded image 32 * @param ysize set to height of loaded image 33 */ 34 bool ReadPFM(const char* filename, std::vector<float>* pixels, uint32_t* xsize, 35 uint32_t* ysize) { 36 FILE* file = fopen(filename, "rb"); 37 if (!file) { 38 fprintf(stderr, "Could not open %s for reading.\n", filename); 39 return false; 40 } 41 uint32_t endian_test = 1; 42 uint8_t little_endian_check[4]; 43 memcpy(little_endian_check, &endian_test, 4); 44 bool little_endian = (little_endian_check[0] == 1); 45 46 if (fseek(file, 0, SEEK_END) != 0) { 47 fclose(file); 48 return false; 49 } 50 51 long size = ftell(file); 52 // Avoid invalid file or directory. 53 if (size >= LONG_MAX || size < 0) { 54 fclose(file); 55 return false; 56 } 57 58 if (fseek(file, 0, SEEK_SET) != 0) { 59 fclose(file); 60 return false; 61 } 62 63 std::vector<char> data; 64 data.resize(size); 65 66 size_t readsize = fread(data.data(), 1, size, file); 67 if (static_cast<long>(readsize) != size) { 68 fclose(file); 69 return false; 70 } 71 if (fclose(file) != 0) { 72 return false; 73 } 74 75 std::stringstream datastream; 76 std::string datastream_content(data.data(), data.size()); 77 datastream.str(datastream_content); 78 79 std::string pf_token; 80 getline(datastream, pf_token, '\n'); 81 if (pf_token != "PF") { 82 fprintf(stderr, 83 "%s doesn't seem to be a 3 channel Portable FloatMap file (missing " 84 "'PF\\n' " 85 "bytes).\n", 86 filename); 87 return false; 88 } 89 90 std::string xsize_token; 91 getline(datastream, xsize_token, ' '); 92 *xsize = std::stoi(xsize_token); 93 94 std::string ysize_token; 95 getline(datastream, ysize_token, '\n'); 96 *ysize = std::stoi(ysize_token); 97 98 std::string endianness_token; 99 getline(datastream, endianness_token, '\n'); 100 bool input_little_endian; 101 if (endianness_token == "1.0") { 102 input_little_endian = false; 103 } else if (endianness_token == "-1.0") { 104 input_little_endian = true; 105 } else { 106 fprintf(stderr, 107 "%s doesn't seem to be a Portable FloatMap file (endianness token " 108 "isn't '1.0' or '-1.0').\n", 109 filename); 110 return false; 111 } 112 113 size_t offset = pf_token.size() + 1 + xsize_token.size() + 1 + 114 ysize_token.size() + 1 + endianness_token.size() + 1; 115 116 if (data.size() != *ysize * *xsize * 3 * 4 + offset) { 117 fprintf(stderr, 118 "%s doesn't seem to be a Portable FloatMap file (pixel data bytes " 119 "are %d, but expected %d * %d * 3 * 4 + %d (%d).\n", 120 filename, static_cast<int>(data.size()), static_cast<int>(*ysize), 121 static_cast<int>(*xsize), static_cast<int>(offset), 122 static_cast<int>(*ysize * *xsize * 3 * 4 + offset)); 123 return false; 124 } 125 126 if (little_endian != input_little_endian) { 127 fprintf(stderr, 128 "%s has a different endianness than we do, conversion is not " 129 "supported.\n", 130 filename); 131 return false; 132 } 133 134 pixels->resize(*ysize * *xsize * 3); 135 136 for (int y = *ysize - 1; y >= 0; y--) { 137 for (int x = 0; x < static_cast<int>(*xsize); x++) { 138 for (int c = 0; c < 3; c++) { 139 memcpy(pixels->data() + (y * *xsize + x) * 3 + c, data.data() + offset, 140 sizeof(float)); 141 offset += sizeof(float); 142 } 143 } 144 } 145 146 return true; 147 } 148 149 /** 150 * Compresses the provided pixels. 151 * 152 * @param pixels input pixels 153 * @param xsize width of the input image 154 * @param ysize height of the input image 155 * @param compressed will be populated with the compressed bytes 156 */ 157 bool EncodeJxlOneshot(const std::vector<float>& pixels, const uint32_t xsize, 158 const uint32_t ysize, std::vector<uint8_t>* compressed) { 159 auto enc = JxlEncoderMake(/*memory_manager=*/nullptr); 160 auto runner = JxlThreadParallelRunnerMake( 161 /*memory_manager=*/nullptr, 162 JxlThreadParallelRunnerDefaultNumWorkerThreads()); 163 if (JXL_ENC_SUCCESS != JxlEncoderSetParallelRunner(enc.get(), 164 JxlThreadParallelRunner, 165 runner.get())) { 166 fprintf(stderr, "JxlEncoderSetParallelRunner failed\n"); 167 return false; 168 } 169 170 JxlPixelFormat pixel_format = {3, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0}; 171 172 JxlBasicInfo basic_info; 173 JxlEncoderInitBasicInfo(&basic_info); 174 basic_info.xsize = xsize; 175 basic_info.ysize = ysize; 176 basic_info.bits_per_sample = 32; 177 basic_info.exponent_bits_per_sample = 8; 178 basic_info.uses_original_profile = JXL_FALSE; 179 if (JXL_ENC_SUCCESS != JxlEncoderSetBasicInfo(enc.get(), &basic_info)) { 180 fprintf(stderr, "JxlEncoderSetBasicInfo failed\n"); 181 return false; 182 } 183 184 JxlColorEncoding color_encoding = {}; 185 JXL_BOOL is_gray = TO_JXL_BOOL(pixel_format.num_channels < 3); 186 JxlColorEncodingSetToSRGB(&color_encoding, is_gray); 187 if (JXL_ENC_SUCCESS != 188 JxlEncoderSetColorEncoding(enc.get(), &color_encoding)) { 189 fprintf(stderr, "JxlEncoderSetColorEncoding failed\n"); 190 return false; 191 } 192 193 JxlEncoderFrameSettings* frame_settings = 194 JxlEncoderFrameSettingsCreate(enc.get(), nullptr); 195 196 if (JXL_ENC_SUCCESS != 197 JxlEncoderAddImageFrame(frame_settings, &pixel_format, 198 static_cast<const void*>(pixels.data()), 199 sizeof(float) * pixels.size())) { 200 fprintf(stderr, "JxlEncoderAddImageFrame failed\n"); 201 return false; 202 } 203 JxlEncoderCloseInput(enc.get()); 204 205 compressed->resize(64); 206 uint8_t* next_out = compressed->data(); 207 size_t avail_out = compressed->size() - (next_out - compressed->data()); 208 JxlEncoderStatus process_result = JXL_ENC_NEED_MORE_OUTPUT; 209 while (process_result == JXL_ENC_NEED_MORE_OUTPUT) { 210 process_result = JxlEncoderProcessOutput(enc.get(), &next_out, &avail_out); 211 if (process_result == JXL_ENC_NEED_MORE_OUTPUT) { 212 size_t offset = next_out - compressed->data(); 213 compressed->resize(compressed->size() * 2); 214 next_out = compressed->data() + offset; 215 avail_out = compressed->size() - offset; 216 } 217 } 218 compressed->resize(next_out - compressed->data()); 219 if (JXL_ENC_SUCCESS != process_result) { 220 fprintf(stderr, "JxlEncoderProcessOutput failed\n"); 221 return false; 222 } 223 224 return true; 225 } 226 227 /** 228 * Writes bytes to file. 229 */ 230 bool WriteFile(const std::vector<uint8_t>& bytes, const char* filename) { 231 FILE* file = fopen(filename, "wb"); 232 if (!file) { 233 fprintf(stderr, "Could not open %s for writing\n", filename); 234 return false; 235 } 236 if (fwrite(bytes.data(), sizeof(uint8_t), bytes.size(), file) != 237 bytes.size()) { 238 fprintf(stderr, "Could not write bytes to %s\n", filename); 239 fclose(file); 240 return false; 241 } 242 if (fclose(file) != 0) { 243 fprintf(stderr, "Could not close %s\n", filename); 244 return false; 245 } 246 return true; 247 } 248 249 int main(int argc, char* argv[]) { 250 if (argc != 3) { 251 fprintf(stderr, 252 "Usage: %s <pfm> <jxl>\n" 253 "Where:\n" 254 " pfm = input Portable FloatMap image filename\n" 255 " jxl = output JPEG XL image filename\n" 256 "Output files will be overwritten.\n", 257 argv[0]); 258 return 1; 259 } 260 261 const char* pfm_filename = argv[1]; 262 const char* jxl_filename = argv[2]; 263 264 std::vector<float> pixels; 265 uint32_t xsize; 266 uint32_t ysize; 267 if (!ReadPFM(pfm_filename, &pixels, &xsize, &ysize)) { 268 fprintf(stderr, "Couldn't load %s\n", pfm_filename); 269 return 2; 270 } 271 272 std::vector<uint8_t> compressed; 273 if (!EncodeJxlOneshot(pixels, xsize, ysize, &compressed)) { 274 fprintf(stderr, "Couldn't encode jxl\n"); 275 return 3; 276 } 277 278 if (!WriteFile(compressed, jxl_filename)) { 279 fprintf(stderr, "Couldn't write jxl file\n"); 280 return 4; 281 } 282 283 return 0; 284 }