libjxl

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

streaming_fuzzer.cc (10636B)


      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 <jxl/decode.h>
      7 #include <jxl/decode_cxx.h>
      8 #include <jxl/encode.h>
      9 #include <jxl/encode_cxx.h>
     10 #include <jxl/thread_parallel_runner.h>
     11 #include <jxl/thread_parallel_runner_cxx.h>
     12 #include <limits.h>
     13 #include <stdint.h>
     14 #include <stdlib.h>
     15 #include <string.h>
     16 
     17 #include <algorithm>
     18 #include <cstdint>
     19 #include <functional>
     20 #include <hwy/targets.h>
     21 #include <random>
     22 #include <vector>
     23 
     24 #include "lib/jxl/base/status.h"
     25 #include "lib/jxl/test_image.h"
     26 
     27 namespace {
     28 
     29 struct FuzzSpec {
     30   uint32_t xsize;
     31   uint32_t ysize;
     32   bool grayscale;
     33   bool alpha;
     34   uint8_t bit_depth;  // 1 - 16
     35 
     36   struct IntOptionSpec {
     37     JxlEncoderFrameSettingId flag;
     38     int min;
     39     int max;
     40     int value;
     41   };
     42 
     43   std::vector<IntOptionSpec> int_options = {
     44       IntOptionSpec{JXL_ENC_FRAME_SETTING_EFFORT, 1, 9, 0},
     45       IntOptionSpec{JXL_ENC_FRAME_SETTING_DECODING_SPEED, 0, 4, 0},
     46       IntOptionSpec{JXL_ENC_FRAME_SETTING_NOISE, -1, 1, 0},
     47       IntOptionSpec{JXL_ENC_FRAME_SETTING_DOTS, -1, 1, 0},
     48       IntOptionSpec{JXL_ENC_FRAME_SETTING_PATCHES, -1, 1, 0},
     49       IntOptionSpec{JXL_ENC_FRAME_SETTING_EPF, -1, 3, 0},
     50       IntOptionSpec{JXL_ENC_FRAME_SETTING_GABORISH, -1, 1, 0},
     51       IntOptionSpec{JXL_ENC_FRAME_SETTING_MODULAR, -1, 1, 0},
     52       IntOptionSpec{JXL_ENC_FRAME_SETTING_KEEP_INVISIBLE, -1, 1, 0},
     53       IntOptionSpec{JXL_ENC_FRAME_SETTING_RESPONSIVE, -1, 1, 0},
     54       IntOptionSpec{JXL_ENC_FRAME_SETTING_PROGRESSIVE_AC, -1, 1, 0},
     55       IntOptionSpec{JXL_ENC_FRAME_SETTING_QPROGRESSIVE_AC, -1, 1, 0},
     56       IntOptionSpec{JXL_ENC_FRAME_SETTING_PROGRESSIVE_DC, -1, 1, 0},
     57       IntOptionSpec{JXL_ENC_FRAME_SETTING_PALETTE_COLORS, -1, 255, 0},
     58       IntOptionSpec{JXL_ENC_FRAME_SETTING_LOSSY_PALETTE, -1, 1, 0},
     59       IntOptionSpec{JXL_ENC_FRAME_SETTING_COLOR_TRANSFORM, -1, 2, 0},
     60       IntOptionSpec{JXL_ENC_FRAME_SETTING_MODULAR_COLOR_SPACE, -1, 41, 0},
     61       IntOptionSpec{JXL_ENC_FRAME_SETTING_MODULAR_GROUP_SIZE, -1, 3, 0},
     62       IntOptionSpec{JXL_ENC_FRAME_SETTING_MODULAR_PREDICTOR, -1, 15, 0},
     63       IntOptionSpec{JXL_ENC_FRAME_SETTING_MODULAR_NB_PREV_CHANNELS, -1, 11, 0},
     64   };
     65 
     66   struct FloatOptionSpec {
     67     JxlEncoderFrameSettingId flag;
     68     float possible_values[4];
     69     float value;
     70   };
     71 
     72   std::vector<FloatOptionSpec> float_options = {
     73       FloatOptionSpec{JXL_ENC_FRAME_SETTING_CHANNEL_COLORS_GLOBAL_PERCENT,
     74                       {-1, 0, 50, 100},
     75                       -1},
     76       FloatOptionSpec{JXL_ENC_FRAME_SETTING_CHANNEL_COLORS_GROUP_PERCENT,
     77                       {-1, 0, 50, 100},
     78                       -1},
     79       FloatOptionSpec{JXL_ENC_FRAME_SETTING_MODULAR_MA_TREE_LEARNING_PERCENT,
     80                       {-1, 0, 50, 100},
     81                       -1},
     82       FloatOptionSpec{
     83           JXL_ENC_FRAME_SETTING_PHOTON_NOISE, {-1, 200, 1600, 10000}, -1},
     84   };
     85 
     86   uint8_t num_threads;
     87 
     88   float distance;  // 0.01 - 25
     89 
     90   // Tiled to cover the entire image area.
     91   uint16_t pixel_data[4][64][64];
     92 
     93   static FuzzSpec FromData(const uint8_t* data, size_t len) {
     94     size_t pos = 0;
     95     auto u8 = [&]() -> uint8_t {
     96       if (pos == len) return 0;
     97       return data[pos++];
     98     };
     99     auto b1 = [&]() -> bool { return static_cast<bool>(u8() % 2); };
    100     auto u16 = [&]() -> uint16_t { return (uint16_t{u8()} << 8) | u8(); };
    101     FuzzSpec spec;
    102     spec.xsize = uint32_t{u16()} + 1;
    103     spec.ysize = uint32_t{u16()} + 1;
    104     constexpr uint64_t kMaxSize = 1 << 24;
    105     if (spec.xsize * uint64_t{spec.ysize} > kMaxSize) {
    106       spec.ysize = kMaxSize / spec.xsize;
    107     }
    108     spec.grayscale = b1();
    109     spec.alpha = b1();
    110     spec.bit_depth = u8() % 16 + 1;
    111     // constants chosen so to cover the entire 0.01 - 25 range.
    112     spec.distance = u8() % 2 ? 0.0 : 0.01 + 0.00038132 * u16();
    113 
    114     JXL_CHECK(spec.float_options[2].flag ==
    115               JXL_ENC_FRAME_SETTING_MODULAR_MA_TREE_LEARNING_PERCENT);
    116     JXL_CHECK(spec.int_options[15].flag ==
    117               JXL_ENC_FRAME_SETTING_COLOR_TRANSFORM);
    118     if (spec.distance != 0 || spec.int_options[15].value == 0) {
    119       spec.float_options[2].possible_values[1] = 1;
    120     }
    121 
    122     spec.num_threads = u8();
    123 
    124     for (auto& int_opt : spec.int_options) {
    125       int_opt.value = u8() % (int_opt.max - int_opt.min + 1) + int_opt.min;
    126     }
    127     for (auto& float_opt : spec.float_options) {
    128       float_opt.value = float_opt.possible_values[u8() % 4];
    129     }
    130 
    131     for (auto& x : spec.pixel_data) {
    132       for (auto& y : x) {
    133         for (auto& p : y) {
    134           p = u16();
    135         }
    136       }
    137     }
    138 
    139     return spec;
    140   }
    141 };
    142 
    143 std::vector<uint8_t> Encode(const FuzzSpec& spec, bool streaming) {
    144   auto runner = JxlThreadParallelRunnerMake(nullptr, spec.num_threads);
    145   JxlEncoderPtr enc_ptr = JxlEncoderMake(/*memory_manager=*/nullptr);
    146   JxlEncoder* enc = enc_ptr.get();
    147 
    148   JXL_CHECK(JxlEncoderSetParallelRunner(enc, JxlThreadParallelRunner,
    149                                         runner.get()) == JXL_ENC_SUCCESS);
    150   JxlEncoderFrameSettings* frame_settings =
    151       JxlEncoderFrameSettingsCreate(enc, nullptr);
    152 
    153   JXL_CHECK(JxlEncoderSetFrameDistance(frame_settings, spec.distance) ==
    154             JXL_ENC_SUCCESS);
    155 
    156   for (const auto& opt : spec.int_options) {
    157     JXL_CHECK(JxlEncoderFrameSettingsSetOption(frame_settings, opt.flag,
    158                                                opt.value) == JXL_ENC_SUCCESS);
    159   }
    160   for (const auto& opt : spec.float_options) {
    161     if (opt.value != -1) {
    162       JXL_CHECK(JxlEncoderFrameSettingsSetFloatOption(
    163                     frame_settings, opt.flag, opt.value) == JXL_ENC_SUCCESS);
    164     }
    165   }
    166 
    167   JXL_CHECK(JxlEncoderFrameSettingsSetOption(
    168                 frame_settings, JXL_ENC_FRAME_SETTING_BUFFERING,
    169                 streaming ? 3 : 0) == JXL_ENC_SUCCESS);
    170 
    171   JxlBasicInfo basic_info;
    172   JxlEncoderInitBasicInfo(&basic_info);
    173   basic_info.num_color_channels = spec.grayscale ? 1 : 3;
    174   basic_info.xsize = spec.xsize;
    175   basic_info.ysize = spec.ysize;
    176   basic_info.bits_per_sample = spec.bit_depth;
    177   basic_info.uses_original_profile = JXL_FALSE;
    178   uint32_t nchan = basic_info.num_color_channels;
    179   if (spec.alpha) {
    180     nchan += 1;
    181     basic_info.alpha_bits = spec.bit_depth;
    182     basic_info.num_extra_channels = 1;
    183   }
    184   JXL_CHECK(JxlEncoderSetBasicInfo(enc, &basic_info) == JXL_ENC_SUCCESS);
    185   if (spec.alpha) {
    186     JxlExtraChannelInfo info;
    187     memset(&info, 0, sizeof(info));
    188     info.type = JxlExtraChannelType::JXL_CHANNEL_ALPHA;
    189     info.bits_per_sample = spec.bit_depth;
    190     JxlEncoderSetExtraChannelInfo(enc, 0, &info);
    191   }
    192   JxlColorEncoding color_encoding;
    193   memset(&color_encoding, 0, sizeof(color_encoding));
    194   color_encoding.color_space = spec.grayscale
    195                                    ? JxlColorSpace::JXL_COLOR_SPACE_GRAY
    196                                    : JxlColorSpace::JXL_COLOR_SPACE_RGB;
    197   color_encoding.transfer_function =
    198       JxlTransferFunction::JXL_TRANSFER_FUNCTION_SRGB;
    199   color_encoding.primaries = JxlPrimaries::JXL_PRIMARIES_2100;
    200   color_encoding.white_point = JxlWhitePoint::JXL_WHITE_POINT_D65;
    201   color_encoding.rendering_intent =
    202       JxlRenderingIntent::JXL_RENDERING_INTENT_RELATIVE;
    203   JXL_CHECK(JxlEncoderSetColorEncoding(enc, &color_encoding) ==
    204             JXL_ENC_SUCCESS);
    205 
    206   JxlFrameHeader frame_header;
    207   JxlEncoderInitFrameHeader(&frame_header);
    208   // TODO(szabadka) Add more frame header options.
    209   JXL_CHECK(JxlEncoderSetFrameHeader(frame_settings, &frame_header) ==
    210             JXL_ENC_SUCCESS);
    211   JxlPixelFormat pixelformat = {nchan, JXL_TYPE_UINT16, JXL_LITTLE_ENDIAN, 0};
    212   std::vector<uint16_t> pixels(spec.xsize * static_cast<uint64_t>(spec.ysize) *
    213                                nchan);
    214   for (size_t y = 0; y < spec.ysize; y++) {
    215     for (size_t x = 0; x < spec.xsize; x++) {
    216       for (size_t c = 0; c < nchan; c++) {
    217         pixels[(y * spec.xsize + x) * nchan + c] =
    218             spec.pixel_data[c][y % 64][x % 64];
    219       }
    220     }
    221   }
    222   JXL_CHECK(JxlEncoderAddImageFrame(frame_settings, &pixelformat, pixels.data(),
    223                                     pixels.size() * sizeof(uint16_t)) ==
    224             JXL_ENC_SUCCESS);
    225   JxlEncoderCloseInput(enc);
    226   // Reading compressed output
    227   JxlEncoderStatus process_result = JXL_ENC_NEED_MORE_OUTPUT;
    228   std::vector<uint8_t> buf(1024);
    229   size_t written = 0;
    230   while (process_result == JXL_ENC_NEED_MORE_OUTPUT) {
    231     buf.resize(buf.size() * 2);
    232     uint8_t* next_out = buf.data() + written;
    233     size_t avail_out = buf.size() - written;
    234     process_result = JxlEncoderProcessOutput(enc, &next_out, &avail_out);
    235     written = next_out - buf.data();
    236   }
    237   JXL_CHECK(process_result == JXL_ENC_SUCCESS);
    238   buf.resize(written);
    239 
    240   return buf;
    241 }
    242 
    243 std::vector<float> Decode(const std::vector<uint8_t>& data) {
    244   // Multi-threaded parallel runner.
    245   auto dec = JxlDecoderMake(nullptr);
    246   JXL_CHECK(JxlDecoderSubscribeEvents(
    247                 dec.get(), JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE) ==
    248             JXL_DEC_SUCCESS);
    249 
    250   JxlBasicInfo info;
    251   JxlPixelFormat format = {3, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0};
    252 
    253   std::vector<float> pixels;
    254 
    255   JxlDecoderSetInput(dec.get(), data.data(), data.size());
    256   JxlDecoderCloseInput(dec.get());
    257 
    258   for (;;) {
    259     JxlDecoderStatus status = JxlDecoderProcessInput(dec.get());
    260 
    261     if (status == JXL_DEC_BASIC_INFO) {
    262       JXL_CHECK(JxlDecoderGetBasicInfo(dec.get(), &info) == JXL_DEC_SUCCESS);
    263     } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
    264       size_t buffer_size;
    265       JXL_CHECK(JxlDecoderImageOutBufferSize(dec.get(), &format,
    266                                              &buffer_size) == JXL_DEC_SUCCESS);
    267       pixels.resize(buffer_size / sizeof(float));
    268       void* pixels_buffer = static_cast<void*>(pixels.data());
    269       size_t pixels_buffer_size = pixels.size() * sizeof(float);
    270       JXL_CHECK(JxlDecoderSetImageOutBuffer(dec.get(), &format, pixels_buffer,
    271                                             pixels_buffer_size) ==
    272                 JXL_DEC_SUCCESS);
    273     } else if (status == JXL_DEC_FULL_IMAGE || status == JXL_DEC_SUCCESS) {
    274       return pixels;
    275     } else {
    276       // Unexpected status
    277       JXL_CHECK(false);
    278     }
    279   }
    280 }
    281 
    282 int TestOneInput(const uint8_t* data, size_t size) {
    283   auto spec = FuzzSpec::FromData(data, size);
    284   auto enc_default = Encode(spec, false);
    285   auto enc_streaming = Encode(spec, true);
    286   auto dec_default = Decode(enc_default);
    287   auto dec_streaming = Decode(enc_streaming);
    288   if (dec_default != dec_streaming) {
    289     return 1;
    290   }
    291   return 0;
    292 }
    293 
    294 }  // namespace
    295 
    296 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
    297   return TestOneInput(data, size);
    298 }