libjxl

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

jxl_decoder.cc (7091B)


      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 #include "tools/wasm_demo/jxl_decoder.h"
      7 
      8 #include <jxl/decode.h>
      9 #include <jxl/decode_cxx.h>
     10 #include <jxl/thread_parallel_runner_cxx.h>
     11 
     12 #include <cstdio>
     13 #include <cstring>
     14 #include <memory>
     15 #include <vector>
     16 
     17 extern "C" {
     18 
     19 namespace {
     20 
     21 struct DecoderInstancePrivate {
     22   // Due to "Standard Layout" rules it is guaranteed that address of the entity
     23   // and its first non-static member are the same.
     24   DecoderInstance info;
     25 
     26   size_t pixels_size = 0;
     27   bool want_sdr;
     28   uint32_t display_nits;
     29   JxlPixelFormat format;
     30   JxlDecoderPtr decoder;
     31   JxlThreadParallelRunnerPtr thread_pool;
     32 
     33   std::vector<uint8_t> tail;
     34 };
     35 
     36 }  // namespace
     37 
     38 DecoderInstance* jxlCreateInstance(bool want_sdr, uint32_t display_nits) {
     39   DecoderInstancePrivate* self = new DecoderInstancePrivate();
     40 
     41   if (!self) {
     42     return nullptr;
     43   }
     44 
     45   self->want_sdr = want_sdr;
     46   self->display_nits = display_nits;
     47   JxlDataType storageFormat = want_sdr ? JXL_TYPE_UINT8 : JXL_TYPE_UINT16;
     48   self->format = {4, storageFormat, JXL_NATIVE_ENDIAN, 0};
     49   self->decoder = JxlDecoderMake(nullptr);
     50 
     51   JxlDecoder* dec = self->decoder.get();
     52 
     53   auto report_error = [&](uint32_t code, const char* text) {
     54     fprintf(stderr, "%s\n", text);
     55     delete self;
     56     return reinterpret_cast<DecoderInstance*>(code);
     57   };
     58 
     59   self->thread_pool = JxlThreadParallelRunnerMake(nullptr, 4);
     60   void* runner = self->thread_pool.get();
     61 
     62   auto status =
     63       JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner);
     64 
     65   if (status != JXL_DEC_SUCCESS) {
     66     return report_error(1, "JxlDecoderSetParallelRunner failed");
     67   }
     68 
     69   status = JxlDecoderSubscribeEvents(
     70       dec, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE |
     71                JXL_DEC_FRAME_PROGRESSION);
     72   if (JXL_DEC_SUCCESS != status) {
     73     return report_error(2, "JxlDecoderSubscribeEvents failed");
     74   }
     75 
     76   status = JxlDecoderSetProgressiveDetail(dec, kPasses);
     77   if (JXL_DEC_SUCCESS != status) {
     78     return report_error(3, "JxlDecoderSetProgressiveDetail failed");
     79   }
     80   return &self->info;
     81 }
     82 
     83 void jxlDestroyInstance(DecoderInstance* instance) {
     84   if (instance == nullptr) return;
     85   DecoderInstancePrivate* self =
     86       reinterpret_cast<DecoderInstancePrivate*>(instance);
     87   if (instance->pixels) {
     88     free(instance->pixels);
     89   }
     90   delete self;
     91 }
     92 
     93 uint32_t jxlProcessInput(DecoderInstance* instance, const uint8_t* input,
     94                          size_t input_size) {
     95   if (instance == nullptr) return static_cast<uint32_t>(-1);
     96   DecoderInstancePrivate* self =
     97       reinterpret_cast<DecoderInstancePrivate*>(instance);
     98   JxlDecoder* dec = self->decoder.get();
     99 
    100   auto report_error = [&](int code, const char* text) {
    101     fprintf(stderr, "%s\n", text);
    102     return static_cast<uint32_t>(code);
    103   };
    104 
    105   std::vector<uint8_t>& tail = self->tail;
    106   if (!tail.empty()) {
    107     tail.reserve(tail.size() + input_size);
    108     tail.insert(tail.end(), input, input + input_size);
    109     input = tail.data();
    110     input_size = tail.size();
    111   }
    112 
    113   auto status = JxlDecoderSetInput(dec, input, input_size);
    114   if (JXL_DEC_SUCCESS != status) {
    115     return report_error(-2, "JxlDecoderSetInput failed");
    116   }
    117 
    118   auto release_input = [&]() {
    119     size_t unused_input = JxlDecoderReleaseInput(dec);
    120     if (unused_input == 0) {
    121       tail.clear();
    122       return;
    123     }
    124     if (tail.empty()) {
    125       tail.insert(tail.end(), input + input_size - unused_input,
    126                   input + input_size);
    127     } else {
    128       memmove(tail.data(), tail.data() + tail.size() - unused_input,
    129               unused_input);
    130       tail.resize(unused_input);
    131     }
    132   };
    133 
    134   while (true) {
    135     status = JxlDecoderProcessInput(dec);
    136     if (JXL_DEC_SUCCESS == status) {
    137       release_input();
    138       return 0;  // ¯\_(ツ)_/¯
    139     } else if (JXL_DEC_FRAME_PROGRESSION == status) {
    140       release_input();
    141       return 1;  // ready to flush; client will decide whether it is necessary
    142     } else if (JXL_DEC_NEED_MORE_INPUT == status) {
    143       release_input();
    144       return 2;
    145     } else if (JXL_DEC_FULL_IMAGE == status) {
    146       release_input();
    147       return 0;  // final image is ready
    148     } else if (JXL_DEC_BASIC_INFO == status) {
    149       JxlBasicInfo info;
    150       status = JxlDecoderGetBasicInfo(dec, &info);
    151       if (status != JXL_DEC_SUCCESS) {
    152         release_input();
    153         return report_error(-4, "JxlDecoderGetBasicInfo failed");
    154       }
    155       instance->width = info.xsize;
    156       instance->height = info.ysize;
    157       status =
    158           JxlDecoderImageOutBufferSize(dec, &self->format, &self->pixels_size);
    159       if (status != JXL_DEC_SUCCESS) {
    160         release_input();
    161         return report_error(-6, "JxlDecoderImageOutBufferSize failed");
    162       }
    163       if (instance->pixels) {
    164         release_input();
    165         return report_error(-7, "Tried to realloc pixels");
    166       }
    167       instance->pixels = reinterpret_cast<uint8_t*>(malloc(self->pixels_size));
    168     } else if (JXL_DEC_NEED_IMAGE_OUT_BUFFER == status) {
    169       if (!self->info.pixels) {
    170         release_input();
    171         return report_error(-8, "Out buffer not allocated");
    172       }
    173       status = JxlDecoderSetImageOutBuffer(dec, &self->format, instance->pixels,
    174                                            self->pixels_size);
    175       if (status != JXL_DEC_SUCCESS) {
    176         release_input();
    177         return report_error(-9, "JxlDecoderSetImageOutBuffer failed");
    178       }
    179     } else if (JXL_DEC_COLOR_ENCODING == status) {
    180       JxlColorEncoding color_encoding;
    181       color_encoding.color_space = JXL_COLOR_SPACE_RGB;
    182       color_encoding.white_point = JXL_WHITE_POINT_D65;
    183       color_encoding.primaries =
    184           self->want_sdr ? JXL_PRIMARIES_SRGB : JXL_PRIMARIES_2100;
    185       color_encoding.transfer_function = self->want_sdr
    186                                              ? JXL_TRANSFER_FUNCTION_SRGB
    187                                              : JXL_TRANSFER_FUNCTION_PQ;
    188       color_encoding.rendering_intent = JXL_RENDERING_INTENT_PERCEPTUAL;
    189       status = JxlDecoderSetPreferredColorProfile(dec, &color_encoding);
    190       if (status != JXL_DEC_SUCCESS) {
    191         release_input();
    192         return report_error(-5, "JxlDecoderSetPreferredColorProfile failed");
    193       }
    194     } else {
    195       release_input();
    196       return report_error(-3, "Unexpected decoder status");
    197     }
    198   }
    199 
    200   release_input();
    201   return 0;
    202 }
    203 
    204 uint32_t jxlFlush(DecoderInstance* instance) {
    205   if (instance == nullptr) return static_cast<uint32_t>(-1);
    206   DecoderInstancePrivate* self =
    207       reinterpret_cast<DecoderInstancePrivate*>(instance);
    208   JxlDecoder* dec = self->decoder.get();
    209 
    210   auto report_error = [&](int code, const char* text) {
    211     fprintf(stderr, "%s\n", text);
    212     // self->result = code;
    213     return static_cast<uint32_t>(code);
    214   };
    215 
    216   if (!instance->pixels) {
    217     return report_error(-2, "Not ready to flush");
    218   }
    219 
    220   auto status = JxlDecoderFlushImage(dec);
    221   if (status != JXL_DEC_SUCCESS) {
    222     return report_error(-3, "Failed to flush");
    223   }
    224 
    225   return 0;
    226 }
    227 
    228 }  // extern "C"