libjxl

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

load_jxl.cc (6150B)


      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/viewer/load_jxl.h"
      7 
      8 #include <jxl/decode.h>
      9 #include <jxl/decode_cxx.h>
     10 #include <jxl/thread_parallel_runner_cxx.h>
     11 #include <jxl/types.h>
     12 #include <stdint.h>
     13 
     14 #include <QElapsedTimer>
     15 #include <QFile>
     16 
     17 #define CMS_NO_REGISTER_KEYWORD 1
     18 #include "lcms2.h"
     19 #undef CMS_NO_REGISTER_KEYWORD
     20 
     21 namespace jpegxl {
     22 namespace tools {
     23 
     24 namespace {
     25 
     26 struct CmsProfileCloser {
     27   void operator()(const cmsHPROFILE profile) const {
     28     if (profile != nullptr) {
     29       cmsCloseProfile(profile);
     30     }
     31   }
     32 };
     33 using CmsProfileUniquePtr =
     34     std::unique_ptr<std::remove_pointer<cmsHPROFILE>::type, CmsProfileCloser>;
     35 
     36 struct CmsTransformDeleter {
     37   void operator()(const cmsHTRANSFORM transform) const {
     38     if (transform != nullptr) {
     39       cmsDeleteTransform(transform);
     40     }
     41   }
     42 };
     43 using CmsTransformUniquePtr =
     44     std::unique_ptr<std::remove_pointer<cmsHTRANSFORM>::type,
     45                     CmsTransformDeleter>;
     46 
     47 }  // namespace
     48 
     49 QImage loadJxlImage(const QString& filename, const QByteArray& targetIccProfile,
     50                     qint64* elapsed_ns, bool* usedRequestedProfile) {
     51   auto runner = JxlThreadParallelRunnerMake(
     52       nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads());
     53 
     54   auto dec = JxlDecoderMake(nullptr);
     55 
     56 #define EXPECT_TRUE(a)                                               \
     57   do {                                                               \
     58     if (!(a)) {                                                      \
     59       fprintf(stderr, "Assertion failure (%d): %s\n", __LINE__, #a); \
     60       return QImage();                                               \
     61     }                                                                \
     62   } while (false)
     63 #define EXPECT_EQ(a, b)                                               \
     64   do {                                                                \
     65     int a_ = a;                                                       \
     66     int b_ = b;                                                       \
     67     if (a_ != b_) {                                                   \
     68       fprintf(stderr, "Assertion failure (%d): %s (%d) != %s (%d)\n", \
     69               __LINE__, #a, a_, #b, b_);                              \
     70       return QImage();                                                \
     71     }                                                                 \
     72   } while (false)
     73 
     74   EXPECT_EQ(JXL_DEC_SUCCESS,
     75             JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BASIC_INFO |
     76                                                      JXL_DEC_COLOR_ENCODING |
     77                                                      JXL_DEC_FULL_IMAGE));
     78   QFile jpegXlFile(filename);
     79   if (!jpegXlFile.open(QIODevice::ReadOnly)) {
     80     return QImage();
     81   }
     82   const QByteArray jpegXlData = jpegXlFile.readAll();
     83   if (jpegXlData.size() < 4) {
     84     return QImage();
     85   }
     86 
     87   QElapsedTimer timer;
     88   timer.start();
     89   const uint8_t* jxl_data = reinterpret_cast<const uint8_t*>(jpegXlData.data());
     90   size_t jxl_size = jpegXlData.size();
     91   JxlDecoderSetInput(dec.get(), jxl_data, jxl_size);
     92   EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec.get()));
     93   JxlBasicInfo info;
     94   EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec.get(), &info));
     95   size_t pixel_count = info.xsize * info.ysize;
     96 
     97   EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec.get()));
     98   static const JxlPixelFormat format = {4, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN,
     99                                         0};
    100   size_t icc_size;
    101   EXPECT_EQ(JXL_DEC_SUCCESS,
    102             JxlDecoderGetICCProfileSize(
    103                 dec.get(), JXL_COLOR_PROFILE_TARGET_DATA, &icc_size));
    104   std::vector<uint8_t> icc_profile(icc_size);
    105   EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetColorAsICCProfile(
    106                                  dec.get(), JXL_COLOR_PROFILE_TARGET_DATA,
    107                                  icc_profile.data(), icc_profile.size()));
    108 
    109   std::vector<float> float_pixels(pixel_count * 4);
    110   EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec.get()));
    111   EXPECT_EQ(JXL_DEC_SUCCESS,
    112             JxlDecoderSetImageOutBuffer(dec.get(), &format, float_pixels.data(),
    113                                         pixel_count * 4 * sizeof(float)));
    114   EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec.get()));
    115 
    116   std::vector<uint16_t> uint16_pixels(pixel_count * 4);
    117   const thread_local cmsContext context = cmsCreateContext(nullptr, nullptr);
    118   EXPECT_TRUE(context != nullptr);
    119   const CmsProfileUniquePtr jxl_profile(cmsOpenProfileFromMemTHR(
    120       context, icc_profile.data(), icc_profile.size()));
    121   EXPECT_TRUE(jxl_profile != nullptr);
    122   CmsProfileUniquePtr target_profile(cmsOpenProfileFromMemTHR(
    123       context, targetIccProfile.data(), targetIccProfile.size()));
    124   if (usedRequestedProfile != nullptr) {
    125     *usedRequestedProfile = (target_profile != nullptr);
    126   }
    127   if (target_profile == nullptr) {
    128     target_profile.reset(cmsCreate_sRGBProfileTHR(context));
    129   }
    130   EXPECT_TRUE(target_profile != nullptr);
    131   CmsTransformUniquePtr transform(cmsCreateTransformTHR(
    132       context, jxl_profile.get(), TYPE_RGBA_FLT, target_profile.get(),
    133       TYPE_RGBA_16, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_COPY_ALPHA));
    134   EXPECT_TRUE(transform != nullptr);
    135   cmsDoTransform(transform.get(), float_pixels.data(), uint16_pixels.data(),
    136                  pixel_count);
    137   if (elapsed_ns != nullptr) *elapsed_ns = timer.nsecsElapsed();
    138 
    139   QImage result(info.xsize, info.ysize,
    140                 info.alpha_premultiplied ? QImage::Format_RGBA64_Premultiplied
    141                                          : QImage::Format_RGBA64);
    142 
    143   for (int y = 0; y < result.height(); ++y) {
    144     QRgba64* const row = reinterpret_cast<QRgba64*>(result.scanLine(y));
    145     const uint16_t* const data = uint16_pixels.data() + result.width() * y * 4;
    146     for (int x = 0; x < result.width(); ++x) {
    147       row[x] = qRgba64(data[4 * x + 0], data[4 * x + 1], data[4 * x + 2],
    148                        data[4 * x + 3]);
    149     }
    150   }
    151   return result;
    152 }
    153 
    154 }  // namespace tools
    155 }  // namespace jpegxl