tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

libjpeg_test_util.cc (10605B)


      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 "lib/jpegli/libjpeg_test_util.h"
      7 
      8 #include <cstdlib>
      9 #include <cstring>
     10 
     11 #include "lib/jxl/base/compiler_specific.h"
     12 #include "lib/jxl/base/include_jpeglib.h"  // NOLINT
     13 #include "lib/jxl/base/sanitizers.h"
     14 
     15 namespace jpegli {
     16 
     17 namespace {
     18 
     19 void Check(bool ok) {
     20  if (!ok) {
     21    JXL_CRASH();
     22  }
     23 }
     24 
     25 #define JPEG_API_FN(name) jpeg_##name
     26 #include "lib/jpegli/test_utils-inl.h"
     27 #undef JPEG_API_FN
     28 
     29 void ReadOutputPass(j_decompress_ptr cinfo, const DecompressParams& dparams,
     30                    TestImage* output) {
     31  JDIMENSION xoffset = 0;
     32  JDIMENSION yoffset = 0;
     33  JDIMENSION xsize_cropped = cinfo->output_width;
     34  JDIMENSION ysize_cropped = cinfo->output_height;
     35  if (dparams.crop_output) {
     36    xoffset = xsize_cropped = cinfo->output_width / 3;
     37    yoffset = ysize_cropped = cinfo->output_height / 3;
     38    jpeg_crop_scanline(cinfo, &xoffset, &xsize_cropped);
     39    Check(xsize_cropped == cinfo->output_width);
     40  }
     41  output->xsize = xsize_cropped;
     42  output->ysize = ysize_cropped;
     43  output->components = cinfo->out_color_components;
     44  if (cinfo->quantize_colors) {
     45    JSAMPLE** colormap = cinfo->colormap;
     46    jxl::msan::UnpoisonMemory(reinterpret_cast<void*>(colormap),
     47                              cinfo->out_color_components * sizeof(JSAMPLE*));
     48    for (int c = 0; c < cinfo->out_color_components; ++c) {
     49      jxl::msan::UnpoisonMemory(
     50          reinterpret_cast<void*>(colormap[c]),
     51          cinfo->actual_number_of_colors * sizeof(JSAMPLE));
     52    }
     53  }
     54  if (!cinfo->raw_data_out) {
     55    size_t stride = output->xsize * output->components;
     56    output->pixels.resize(output->ysize * stride);
     57    output->color_space = cinfo->out_color_space;
     58    if (yoffset > 0) {
     59      jpeg_skip_scanlines(cinfo, yoffset);
     60    }
     61    for (size_t y = 0; y < output->ysize; ++y) {
     62      JSAMPROW rows[] = {
     63          reinterpret_cast<JSAMPLE*>(&output->pixels[y * stride])};
     64      Check(1 == jpeg_read_scanlines(cinfo, rows, 1));
     65      jxl::msan::UnpoisonMemory(
     66          rows[0], sizeof(JSAMPLE) * cinfo->output_components * output->xsize);
     67      if (cinfo->quantize_colors) {
     68        UnmapColors(rows[0], cinfo->output_width, cinfo->out_color_components,
     69                    cinfo->colormap, cinfo->actual_number_of_colors);
     70      }
     71    }
     72    if (cinfo->output_scanline < cinfo->output_height) {
     73      jpeg_skip_scanlines(cinfo, cinfo->output_height - cinfo->output_scanline);
     74    }
     75  } else {
     76    output->color_space = cinfo->jpeg_color_space;
     77    for (int c = 0; c < cinfo->num_components; ++c) {
     78      size_t xsize = cinfo->comp_info[c].width_in_blocks * DCTSIZE;
     79      size_t ysize = cinfo->comp_info[c].height_in_blocks * DCTSIZE;
     80      std::vector<uint8_t> plane(ysize * xsize);
     81      output->raw_data.emplace_back(std::move(plane));
     82    }
     83    while (cinfo->output_scanline < cinfo->output_height) {
     84      size_t iMCU_height = cinfo->max_v_samp_factor * DCTSIZE;
     85      Check(cinfo->output_scanline == cinfo->output_iMCU_row * iMCU_height);
     86      std::vector<std::vector<JSAMPROW>> rowdata(cinfo->num_components);
     87      std::vector<JSAMPARRAY> data(cinfo->num_components);
     88      for (int c = 0; c < cinfo->num_components; ++c) {
     89        size_t xsize = cinfo->comp_info[c].width_in_blocks * DCTSIZE;
     90        size_t ysize = cinfo->comp_info[c].height_in_blocks * DCTSIZE;
     91        size_t num_lines = cinfo->comp_info[c].v_samp_factor * DCTSIZE;
     92        rowdata[c].resize(num_lines);
     93        size_t y0 = cinfo->output_iMCU_row * num_lines;
     94        for (size_t i = 0; i < num_lines; ++i) {
     95          rowdata[c][i] =
     96              y0 + i < ysize ? &output->raw_data[c][(y0 + i) * xsize] : nullptr;
     97        }
     98        data[c] = rowdata[c].data();
     99      }
    100      Check(iMCU_height == jpeg_read_raw_data(cinfo, data.data(), iMCU_height));
    101    }
    102  }
    103  Check(cinfo->total_iMCU_rows ==
    104        DivCeil(cinfo->image_height, cinfo->max_v_samp_factor * DCTSIZE));
    105 }
    106 
    107 void DecodeWithLibjpeg(const CompressParams& jparams,
    108                       const DecompressParams& dparams, j_decompress_ptr cinfo,
    109                       TestImage* output) {
    110  if (jparams.add_marker) {
    111    jpeg_save_markers(cinfo, kSpecialMarker0, 0xffff);
    112    jpeg_save_markers(cinfo, kSpecialMarker1, 0xffff);
    113  }
    114  if (!jparams.icc.empty()) {
    115    jpeg_save_markers(cinfo, JPEG_APP0 + 2, 0xffff);
    116  }
    117  Check(JPEG_REACHED_SOS == jpeg_read_header(cinfo, /*require_image=*/TRUE));
    118  if (!jparams.icc.empty()) {
    119    uint8_t* icc_data = nullptr;
    120    unsigned int icc_len = 0;  // "unpoison" via initialization
    121    Check(jpeg_read_icc_profile(cinfo, &icc_data, &icc_len));
    122    Check(icc_data);
    123    jxl::msan::UnpoisonMemory(icc_data, icc_len);
    124    Check(0 == memcmp(jparams.icc.data(), icc_data, icc_len));
    125    free(icc_data);
    126  }
    127  SetDecompressParams(dparams, cinfo);
    128  VerifyHeader(jparams, cinfo);
    129  if (dparams.output_mode == COEFFICIENTS) {
    130    jvirt_barray_ptr* coef_arrays = jpeg_read_coefficients(cinfo);
    131    Check(coef_arrays != nullptr);
    132    jxl::msan::UnpoisonMemory(coef_arrays,
    133                              cinfo->num_components * sizeof(jvirt_barray_ptr));
    134    CopyCoefficients(cinfo, coef_arrays, output);
    135  } else {
    136    Check(jpeg_start_decompress(cinfo));
    137    VerifyScanHeader(jparams, cinfo);
    138    ReadOutputPass(cinfo, dparams, output);
    139  }
    140  Check(jpeg_finish_decompress(cinfo));
    141 }
    142 
    143 }  // namespace
    144 
    145 // Verifies that an image encoded with libjpegli can be decoded with libjpeg,
    146 // and checks that the jpeg coding metadata matches jparams.
    147 void DecodeAllScansWithLibjpeg(const CompressParams& jparams,
    148                               const DecompressParams& dparams,
    149                               const std::vector<uint8_t>& compressed,
    150                               std::vector<TestImage>* output_progression) {
    151  jpeg_decompress_struct cinfo = {};
    152  const auto try_catch_block = [&]() {
    153    jpeg_error_mgr jerr;
    154    jmp_buf env;
    155    cinfo.err = jpeg_std_error(&jerr);
    156    if (setjmp(env)) {
    157      return false;
    158    }
    159    cinfo.client_data = reinterpret_cast<void*>(&env);
    160    cinfo.err->error_exit = [](j_common_ptr cinfo) {
    161      (*cinfo->err->output_message)(cinfo);
    162      jmp_buf* env = reinterpret_cast<jmp_buf*>(cinfo->client_data);
    163      jpeg_destroy(cinfo);
    164      longjmp(*env, 1);
    165    };
    166    jpeg_create_decompress(&cinfo);
    167    jpeg_mem_src(&cinfo, compressed.data(), compressed.size());
    168    if (jparams.add_marker) {
    169      jpeg_save_markers(&cinfo, kSpecialMarker0, 0xffff);
    170      jpeg_save_markers(&cinfo, kSpecialMarker1, 0xffff);
    171    }
    172    Check(JPEG_REACHED_SOS == jpeg_read_header(&cinfo, /*require_image=*/TRUE));
    173    cinfo.buffered_image = TRUE;
    174    SetDecompressParams(dparams, &cinfo);
    175    VerifyHeader(jparams, &cinfo);
    176    Check(jpeg_start_decompress(&cinfo));
    177    // start decompress should not read the whole input in buffered image mode
    178    Check(!jpeg_input_complete(&cinfo));
    179    Check(cinfo.output_scan_number == 0);
    180    int sos_marker_cnt = 1;  // read header reads the first SOS marker
    181    while (!jpeg_input_complete(&cinfo)) {
    182      Check(cinfo.input_scan_number == sos_marker_cnt);
    183      if (dparams.skip_scans && (cinfo.input_scan_number % 2) != 1) {
    184        int result = JPEG_SUSPENDED;
    185        while (result != JPEG_REACHED_SOS && result != JPEG_REACHED_EOI) {
    186          result = jpeg_consume_input(&cinfo);
    187        }
    188        if (result == JPEG_REACHED_SOS) ++sos_marker_cnt;
    189        continue;
    190      }
    191      SetScanDecompressParams(dparams, &cinfo, cinfo.input_scan_number);
    192      Check(jpeg_start_output(&cinfo, cinfo.input_scan_number));
    193      // start output sets output_scan_number, but does not change
    194      // input_scan_number
    195      Check(cinfo.output_scan_number == cinfo.input_scan_number);
    196      Check(cinfo.input_scan_number == sos_marker_cnt);
    197      VerifyScanHeader(jparams, &cinfo);
    198      TestImage output;
    199      ReadOutputPass(&cinfo, dparams, &output);
    200      output_progression->emplace_back(std::move(output));
    201      // read scanlines/read raw data does not change input/output scan number
    202      if (!cinfo.progressive_mode) {
    203        Check(cinfo.input_scan_number == sos_marker_cnt);
    204        Check(cinfo.output_scan_number == cinfo.input_scan_number);
    205      }
    206      Check(jpeg_finish_output(&cinfo));
    207      ++sos_marker_cnt;  // finish output reads the next SOS marker or EOI
    208      if (dparams.output_mode == COEFFICIENTS) {
    209        jvirt_barray_ptr* coef_arrays = jpeg_read_coefficients(&cinfo);
    210        Check(coef_arrays != nullptr);
    211        jxl::msan::UnpoisonMemory(
    212            coef_arrays, cinfo.num_components * sizeof(jvirt_barray_ptr));
    213        CopyCoefficients(&cinfo, coef_arrays, &output_progression->back());
    214      }
    215    }
    216    Check(jpeg_finish_decompress(&cinfo));
    217    return true;
    218  };
    219  Check(try_catch_block());
    220  jpeg_destroy_decompress(&cinfo);
    221 }
    222 
    223 // Returns the number of bytes read from compressed.
    224 size_t DecodeWithLibjpeg(const CompressParams& jparams,
    225                         const DecompressParams& dparams,
    226                         const uint8_t* table_stream, size_t table_stream_size,
    227                         const uint8_t* compressed, size_t len,
    228                         TestImage* output) {
    229  jpeg_decompress_struct cinfo = {};
    230  size_t bytes_read;
    231  const auto try_catch_block = [&]() {
    232    jpeg_error_mgr jerr;
    233    jmp_buf env;
    234    cinfo.err = jpeg_std_error(&jerr);
    235    if (setjmp(env)) {
    236      return false;
    237    }
    238    cinfo.client_data = reinterpret_cast<void*>(&env);
    239    cinfo.err->error_exit = [](j_common_ptr cinfo) {
    240      (*cinfo->err->output_message)(cinfo);
    241      jmp_buf* env = reinterpret_cast<jmp_buf*>(cinfo->client_data);
    242      jpeg_destroy(cinfo);
    243      longjmp(*env, 1);
    244    };
    245    jpeg_create_decompress(&cinfo);
    246    if (table_stream != nullptr) {
    247      jpeg_mem_src(&cinfo, table_stream, table_stream_size);
    248      jpeg_read_header(&cinfo, FALSE);
    249    }
    250    jpeg_mem_src(&cinfo, compressed, len);
    251    DecodeWithLibjpeg(jparams, dparams, &cinfo, output);
    252    jxl::msan::UnpoisonMemory(cinfo.src, sizeof(jpeg_source_mgr));
    253    bytes_read = len - cinfo.src->bytes_in_buffer;
    254    return true;
    255  };
    256  Check(try_catch_block());
    257  jpeg_destroy_decompress(&cinfo);
    258  return bytes_read;
    259 }
    260 
    261 void DecodeWithLibjpeg(const CompressParams& jparams,
    262                       const DecompressParams& dparams,
    263                       const std::vector<uint8_t>& compressed,
    264                       TestImage* output) {
    265  DecodeWithLibjpeg(jparams, dparams, nullptr, 0, compressed.data(),
    266                    compressed.size(), output);
    267 }
    268 
    269 }  // namespace jpegli