libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | README | LICENSE

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 }