libjxl

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

libjpeg_test_util.cc (10364B)


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