libjxl

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

decoder_jni.cc (8582B)


      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/jni/org/jpeg/jpegxl/wrapper/decoder_jni.h"
      7 
      8 #include <jni.h>
      9 #include <jxl/codestream_header.h>
     10 #include <jxl/decode.h>
     11 #include <jxl/thread_parallel_runner.h>
     12 #include <jxl/types.h>
     13 
     14 #include <cstdint>
     15 #include <cstdlib>
     16 
     17 namespace {
     18 
     19 template <typename From, typename To>
     20 bool StaticCast(const From& from, To* to) {
     21   To tmp = static_cast<To>(from);
     22   // Check sign is preserved.
     23   if ((from < 0 && tmp > 0) || (from > 0 && tmp < 0)) return false;
     24   // Check value is preserved.
     25   if (from != static_cast<From>(tmp)) return false;
     26   *to = tmp;
     27   return true;
     28 }
     29 
     30 bool BufferToSpan(JNIEnv* env, jobject buffer, uint8_t** data, size_t* size) {
     31   if (buffer == nullptr) return true;
     32 
     33   *data = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
     34   if (*data == nullptr) return false;
     35   return StaticCast(env->GetDirectBufferCapacity(buffer), size);
     36 }
     37 
     38 enum class Status { OK = 0, FATAL_ERROR = -1, NOT_ENOUGH_INPUT = 1 };
     39 
     40 bool IsOk(Status status) { return status == Status::OK; }
     41 
     42 #define FAILURE(M) Status::FATAL_ERROR
     43 
     44 constexpr const size_t kLastPixelFormat = 3;
     45 constexpr const size_t kNoPixelFormat = static_cast<size_t>(-1);
     46 
     47 JxlPixelFormat ToPixelFormat(size_t pixel_format) {
     48   if (pixel_format == 0) {
     49     // RGBA, 4 x byte per pixel, no scanline padding.
     50     return {/*num_channels=*/4, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, /*align=*/0};
     51   } else if (pixel_format == 1) {
     52     // RGBA, 4 x float16 per pixel, no scanline padding.
     53     return {/*num_channels=*/4, JXL_TYPE_FLOAT16, JXL_LITTLE_ENDIAN,
     54             /*align=*/0};
     55   } else if (pixel_format == 2) {
     56     // RGB, 4 x byte per pixel, no scanline padding.
     57     return {/*num_channels=*/3, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, /*align=*/0};
     58   } else if (pixel_format == 3) {
     59     // RGB, 4 x float16 per pixel, no scanline padding.
     60     return {/*num_channels=*/3, JXL_TYPE_FLOAT16, JXL_LITTLE_ENDIAN,
     61             /*align=*/0};
     62   } else {
     63     abort();
     64     return {0, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0};
     65   }
     66 }
     67 
     68 Status DoDecode(JNIEnv* env, jobject data_buffer, size_t* info_pixels_size,
     69                 size_t* info_icc_size, JxlBasicInfo* info, size_t pixel_format,
     70                 jobject pixels_buffer, jobject icc_buffer) {
     71   if (data_buffer == nullptr) return FAILURE("No data buffer");
     72 
     73   uint8_t* data = nullptr;
     74   size_t data_size = 0;
     75   if (!BufferToSpan(env, data_buffer, &data, &data_size)) {
     76     return FAILURE("Failed to access data buffer");
     77   }
     78 
     79   uint8_t* pixels = nullptr;
     80   size_t pixels_size = 0;
     81   if (!BufferToSpan(env, pixels_buffer, &pixels, &pixels_size)) {
     82     return FAILURE("Failed to access pixels buffer");
     83   }
     84 
     85   uint8_t* icc = nullptr;
     86   size_t icc_size = 0;
     87   if (!BufferToSpan(env, icc_buffer, &icc, &icc_size)) {
     88     return FAILURE("Failed to access ICC buffer");
     89   }
     90 
     91   JxlDecoder* dec = JxlDecoderCreate(nullptr);
     92 
     93   constexpr size_t kNumThreads = 0;  // Do everything in this thread.
     94   void* runner = JxlThreadParallelRunnerCreate(nullptr, kNumThreads);
     95 
     96   struct Defer {
     97     JxlDecoder* dec;
     98     void* runner;
     99     ~Defer() {
    100       JxlThreadParallelRunnerDestroy(runner);
    101       JxlDecoderDestroy(dec);
    102     }
    103   } defer{dec, runner};
    104 
    105   auto status =
    106       JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner);
    107   if (status != JXL_DEC_SUCCESS) {
    108     return FAILURE("Failed to set parallel runner");
    109   }
    110   status = JxlDecoderSubscribeEvents(
    111       dec, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE | JXL_DEC_COLOR_ENCODING);
    112   if (status != JXL_DEC_SUCCESS) {
    113     return FAILURE("Failed to subscribe for events");
    114   }
    115   status = JxlDecoderSetInput(dec, data, data_size);
    116   if (status != JXL_DEC_SUCCESS) {
    117     return FAILURE("Failed to set input");
    118   }
    119   status = JxlDecoderProcessInput(dec);
    120   if (status == JXL_DEC_NEED_MORE_INPUT) {
    121     return Status::NOT_ENOUGH_INPUT;
    122   }
    123   if (status != JXL_DEC_BASIC_INFO) {
    124     return FAILURE("Unexpected notification (want: basic info)");
    125   }
    126   if (info_pixels_size) {
    127     JxlPixelFormat format = ToPixelFormat(pixel_format);
    128     status = JxlDecoderImageOutBufferSize(dec, &format, info_pixels_size);
    129     if (status != JXL_DEC_SUCCESS) {
    130       return FAILURE("Failed to get pixels size");
    131     }
    132   }
    133   if (info) {
    134     status = JxlDecoderGetBasicInfo(dec, info);
    135     if (status != JXL_DEC_SUCCESS) {
    136       return FAILURE("Failed to get basic info");
    137     }
    138   }
    139   status = JxlDecoderProcessInput(dec);
    140   if (status != JXL_DEC_COLOR_ENCODING) {
    141     return FAILURE("Unexpected notification (want: color encoding)");
    142   }
    143   if (info_icc_size) {
    144     status = JxlDecoderGetICCProfileSize(dec, JXL_COLOR_PROFILE_TARGET_DATA,
    145                                          info_icc_size);
    146     if (status != JXL_DEC_SUCCESS) *info_icc_size = 0;
    147   }
    148   if (icc && icc_size > 0) {
    149     status = JxlDecoderGetColorAsICCProfile(dec, JXL_COLOR_PROFILE_TARGET_DATA,
    150                                             icc, icc_size);
    151     if (status != JXL_DEC_SUCCESS) {
    152       return FAILURE("Failed to get ICC");
    153     }
    154   }
    155   if (pixels) {
    156     JxlPixelFormat format = ToPixelFormat(pixel_format);
    157     status = JxlDecoderProcessInput(dec);
    158     if (status != JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
    159       return FAILURE("Unexpected notification (want: need out buffer)");
    160     }
    161     status = JxlDecoderSetImageOutBuffer(dec, &format, pixels, pixels_size);
    162     if (status != JXL_DEC_SUCCESS) {
    163       return FAILURE("Failed to set out buffer");
    164     }
    165     status = JxlDecoderProcessInput(dec);
    166     if (status != JXL_DEC_FULL_IMAGE) {
    167       return FAILURE("Unexpected notification (want: full image)");
    168     }
    169     status = JxlDecoderProcessInput(dec);
    170     if (status != JXL_DEC_SUCCESS) {
    171       return FAILURE("Unexpected notification (want: success)");
    172     }
    173   }
    174 
    175   return Status::OK;
    176 }
    177 
    178 }  // namespace
    179 
    180 #ifdef __cplusplus
    181 extern "C" {
    182 #endif
    183 
    184 JNIEXPORT void JNICALL
    185 Java_org_jpeg_jpegxl_wrapper_DecoderJni_nativeGetBasicInfo(
    186     JNIEnv* env, jobject /*jobj*/, jintArray ctx, jobject data_buffer) {
    187   jint context[6] = {0};
    188   env->GetIntArrayRegion(ctx, 0, 1, context);
    189 
    190   JxlBasicInfo info = {};
    191   size_t pixels_size = 0;
    192   size_t icc_size = 0;
    193   size_t pixel_format = 0;
    194 
    195   Status status = Status::OK;
    196 
    197   if (IsOk(status)) {
    198     pixel_format = context[0];
    199     if (pixel_format == kNoPixelFormat) {
    200       // OK
    201     } else if (pixel_format > kLastPixelFormat) {
    202       status = FAILURE("Unrecognized pixel format");
    203     }
    204   }
    205 
    206   if (IsOk(status)) {
    207     bool want_output_size = (pixel_format != kNoPixelFormat);
    208     if (want_output_size) {
    209       status = DoDecode(
    210           env, data_buffer, &pixels_size, &icc_size, &info, pixel_format,
    211           /* pixels_buffer= */ nullptr, /* icc_buffer= */ nullptr);
    212     } else {
    213       status =
    214           DoDecode(env, data_buffer, /* info_pixels_size= */ nullptr,
    215                    /* info_icc_size= */ nullptr, &info, pixel_format,
    216                    /* pixels_buffer= */ nullptr, /* icc_buffer= */ nullptr);
    217     }
    218   }
    219 
    220   if (IsOk(status)) {
    221     bool ok = true;
    222     ok &= StaticCast(info.xsize, context + 1);
    223     ok &= StaticCast(info.ysize, context + 2);
    224     ok &= StaticCast(pixels_size, context + 3);
    225     ok &= StaticCast(icc_size, context + 4);
    226     ok &= StaticCast(info.alpha_bits, context + 5);
    227     if (!ok) status = FAILURE("Invalid value");
    228   }
    229 
    230   context[0] = static_cast<int>(status);
    231 
    232   env->SetIntArrayRegion(ctx, 0, 6, context);
    233 }
    234 
    235 /**
    236  * Get image pixel data.
    237  *
    238  * @param ctx {out_status} tuple
    239  * @param data [in] Buffer with encoded JXL stream
    240  * @param pixels [out] Buffer to place pixels to
    241  */
    242 JNIEXPORT void JNICALL Java_org_jpeg_jpegxl_wrapper_DecoderJni_nativeGetPixels(
    243     JNIEnv* env, jobject /* jobj */, jintArray ctx, jobject data_buffer,
    244     jobject pixels_buffer, jobject icc_buffer) {
    245   jint context[1] = {0};
    246   env->GetIntArrayRegion(ctx, 0, 1, context);
    247 
    248   size_t pixel_format = 0;
    249 
    250   Status status = Status::OK;
    251 
    252   if (IsOk(status)) {
    253     // Unlike getBasicInfo, "no-pixel-format" is not supported.
    254     pixel_format = context[0];
    255     if (pixel_format > kLastPixelFormat) {
    256       status = FAILURE("Unrecognized pixel format");
    257     }
    258   }
    259 
    260   if (IsOk(status)) {
    261     status = DoDecode(env, data_buffer, /* info_pixels_size= */ nullptr,
    262                       /* info_icc_size= */ nullptr, /* info= */ nullptr,
    263                       pixel_format, pixels_buffer, icc_buffer);
    264   }
    265 
    266   context[0] = static_cast<int>(status);
    267   env->SetIntArrayRegion(ctx, 0, 1, context);
    268 }
    269 
    270 #undef FAILURE
    271 
    272 #ifdef __cplusplus
    273 }
    274 #endif